本来打算先写off-by-one和unsorted bin atack的…但是鉴于off-by-one和overlapping的联系以及,,,unsorted bin attack..看起来好像真的用处颇少…于是就偷懒先写这个了(逃
chunk extend
chunk的拓展?? 是叫这么魔性的东西吗….大概是对下一个chunk有什么操作吧QAQ,打扰了…
功能
利用条件
- 有堆的漏洞
- 可以改变下一个chunk的header域
一般就用在off-by-one了吧..绝配
利用原理
我们知道我们释放的堆块是先不还给操作系统而是由ptmalloc来接管的
而ptmalloc在获取chunk大小时的代码如下:
1 2 3 4 5
| #define chunksize(p) (chunksize_nomask(p) & ~(SIZE_BITS))
#define chunksize_nomask(p) ((p)->mchunk_size)
|
- 全部获取
- 忽略源码
而获取下一块chunk地址代码如下:
1 2
| #define next_chunk(p) ((mchunkptr)(((char *) (p)) + chunksize(p)))
|
也就是当前块地址+当前块大小
上一块chunk地址:
1 2 3 4 5 6
| #define prev_size(p) ((p)->mchunk_prev_size)
#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p))) 当前块-上一块大小
|
判断是否inuse
1 2
| ((((mchunkptr)(((char *) (p)) + chunksize(p)))->mchunk_size) & PREV_INUSE)
|
查看prev_inuse域,此域为1即为inuse
小总结
也就是说,对于如何判断本chunk相邻chunk的地址完全是通过size来确定的,因而给了我们机会,前辈们真的tql…
利用姿势
这里引用了ctf-wiki的代码,并且我做了一点点小修改:
inuse状态下
- fastbin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdlib.h> #include <stdio.h> int main(void) { void *ptr,*ptr1;
ptr=malloc(0x10); malloc(0x10); printf("[*]ptr:%p\n",ptr); *(long long *)((long long)ptr-0x8)=0x41; free(ptr); ptr1=malloc(0x30); printf("[*]ptr1:%p\n",ptr1); return 0; }
}
|
我在第一次malloc处下了断点,然后我们来看看现在堆的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| pwndbg> heap 0x602000 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20fe1 } 0x602020 PREV_INUSE { prev_size = 0, size = 135137, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
发现此时的size是0x21…这里还是想吐槽一些pwndbg把size部分改成了十进制..真的苟..这个docker我忘记改代码了…
然后程序继续
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 0x602000 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602020 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20fc1 } 0x602040 PREV_INUSE { prev_size = 0, size = 135105, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
可以看到这里已经分配完了,此时的堆
1 2 3 4 5 6
| pwndbg> x/10gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000021 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000000000 0x0000000000000021 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000020fc1
|
没有问题,下一步进行size的改写,至于为什么是chunk-8呢,因为我们知道chunk给我们的地址其实是mem的地址,也就是0x602010处,因此减8可以到pre_size处
1 2 3 4 5 6 7 8
| 0x602000 FASTBIN { prev_size = 0, size = 65, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 }
|
这里已经改写成功了,此时的heap分析出来的是这样的:
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
| pwndbg> heap 0x602000 FASTBIN { prev_size = 0, size = 65, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602040 PREV_INUSE { prev_size = 0, size = 1041, fd = 0x303a7274705d2a5b, bk = 0x6666666666663778, fd_nextsize = 0xa3836356566, bk_nextsize = 0x0 } 0x602450 PREV_INUSE { prev_size = 0, size = 134065, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
成功通过size欺骗到了ptmalloc机制
1 2 3 4 5 6 7 8
| pwndbg> x/14gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000041 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000000000 0x0000000000000021 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000000411 0x602050: 0x303a7274705d2a5b 0x6666666666663778 0x602060: 0x00000a3836356566 0x0000000000000000
|
然后我们让程序到下一个分配
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
| pwndbg> heap 0x602000 FASTBIN { prev_size = 0, size = 65, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602040 PREV_INUSE { prev_size = 0, size = 1041, fd = 0x303a7274705d2a5b, bk = 0x6666666666663778, fd_nextsize = 0xa3836356566, bk_nextsize = 0x0 } 0x602450 PREV_INUSE { prev_size = 0, size = 134065, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
此时这里虽然没变,但是
1 2
| pwndbg> p/x ptr1 $4 = 0x602010
|
也就是说我们成功分配了free掉的chunk1
程序运行结果:
1 2 3
| ╰─ [*]ptr:0x180e010 [*]ptr1:0x180e010
|
- smallbin
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <stdlib.h> #include <stdio.h>
int main() { void *ptr,*ptr1;
ptr=malloc(0x80); malloc(0x10); malloc(0x10); *(long long *)((long long)ptr-0x8)=0xb1; printf("[*]%p\n",ptr); free(ptr); ptr1=malloc(0xa0); printf("[*]%p\n",ptr1); }
|
程序运行结果:
1 2 3
| ╰─# ./pwn [*]0x7a3010 [*]0x7a3010
|
这个程序就不做深入分析了,和fastbin的一样
free状态下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdlib.h> #include <stdio.h> int main() { void *ptr,*ptr1;
ptr=malloc(0x80); malloc(0x10); printf("[*]ptr:%p\n",ptr); free(ptr);
*(long long *)((long long)ptr-0x8)=0xb1; ptr1=malloc(0xa0); printf("[*]ptr:%p\n",ptr1); }
|
程序运行结果:
1 2 3
| ╰─# ./pwn [*]ptr:0x149b010 [*]ptr:0x149b010
|
这个就是先释放,然后才进行大小的更改
通过extend后向overlapping
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdlib.h> #include <stdio.h> int main() { void *ptr,*ptr1;
ptr=malloc(0x10); malloc(0x10); malloc(0x10); malloc(0x10); printf("[*]ptr:%p\n",ptr); *(long long *)((long long)ptr-0x8)=0x61; free(ptr); ptr1=malloc(0x50); printf("[*]ptr:%p\n",ptr1); }
|
我在第一次分配处下了断点,此时的堆:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| pwndbg> heap 0x602000 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20fe1 } 0x602020 PREV_INUSE { prev_size = 0, size = 135137, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
随后分配完全结束时我们再看
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
| pwndbg> heap 0x602000 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602020 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602040 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602060 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x20f81 } 0x602080 PREV_INUSE { prev_size = 0, size = 135041, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
这时:
1 2 3 4 5 6 7 8 9 10 11
| pwndbg> x/20gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000021 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000000000 0x0000000000000021 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000000021 0x602050: 0x0000000000000000 0x0000000000000000 0x602060: 0x0000000000000000 0x0000000000000021 0x602070: 0x0000000000000000 0x0000000000000000 0x602080: 0x0000000000000000 0x0000000000020f81 0x602090: 0x0000000000000000 0x0000000000000000
|
随后我们更改了size的大小
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> heap 0x602000 FASTBIN { prev_size = 0, size = 97, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602060 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x411 } 0x602080 PREV_INUSE { prev_size = 0, size = 1041, fd = 0x303a7274705d2a5b, bk = 0xa30313032303678, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602490 PREV_INUSE { prev_size = 0, size = 134001, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
free之后我们可以看到:
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> heap 0x602000 FASTBIN { prev_size = 0, size = 97, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602060 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x411 } 0x602080 PREV_INUSE { prev_size = 0, size = 1041, fd = 0x303a7274705d2a5b, bk = 0xa30313032303678, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602490 PREV_INUSE { prev_size = 0, size = 134001, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
再分配一次
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> heap 0x602000 FASTBIN { prev_size = 0, size = 97, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x602060 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x411 } 0x602080 PREV_INUSE { prev_size = 0, size = 1041, fd = 0x303a7274705d2a5b, bk = 0xa30313032303678, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602490 PREV_INUSE { prev_size = 0, size = 134001, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
此时
1 2 3 4 5 6 7 8 9 10 11
| pwndbg> x/20gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000061 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000000000 0x0000000000000021 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000000021 0x602050: 0x0000000000000000 0x0000000000000000 0x602060: 0x0000000000000000 0x0000000000000021 0x602070: 0x0000000000000000 0x0000000000000000 0x602080: 0x0000000000000000 0x0000000000000411 0x602090: 0x303a7274705d2a5b 0x0a30313032303678
|
这时就可以进行fastbin attack了
通过extend进行前向合并
之前的后向合并都是通过更改size实现的,但extend的前向合并则不一样,但原理是类似的
后向合并是通过更改size实现的,而前向合并顾名思义需要我们更改pre_size才行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include<stdlib.h> #include<stdio.h> int main(void) { void *ptr1,*ptr2,*ptr3,*ptr4,*ptr5; ptr1=malloc(128); ptr2=malloc(0x10); ptr3=malloc(0x10); ptr4=malloc(128); malloc(0x10); printf("[*]ptr1:%p\n",ptr1); printf("[*]ptr2:%p\n",ptr2); printf("[*]ptr3:%p\n",ptr3); printf("[*]ptr4:%p\n",ptr4); free(ptr1); *(long long *)((long long)ptr4-0x8)=0x90; *(long long *)((long long)ptr4-0x10)=0xd0; free(ptr4); ptr5=malloc(0x150); printf("[*]ptr5:%p\n",ptr5); }
|
先看程序的运行结果:
1 2 3 4 5 6
| ╰─ [*]ptr1:0x10c2010 [*]ptr2:0x10c20a0 [*]ptr3:0x10c20c0 [*]ptr4:0x10c20e0 [*]ptr5:0x10c2010
|
然后再调试一下,这次我断点下在了第一free的地方
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
| pwndbg> heap 0x602000 PREV_INUSE { prev_size = 0, size = 145, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602090 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x6020b0 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x6020d0 PREV_INUSE { prev_size = 0, size = 145, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602160 FASTBIN { prev_size = 0, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x411 }
|
可以看到我们的符合smallbin和fastbin的四个chunk已经分配完成了,然后先free掉ptr1,修改ptr4的pre_inuse和pre_size
1 2 3 4 5 6 7 8 9 10 11
| pwndbg> x/20gx ptr4-0x10 0x6020d0: 0x00000000000000d0 0x0000000000000090 0x6020e0: 0x0000000000000000 0x0000000000000000 0x6020f0: 0x0000000000000000 0x0000000000000000 0x602100: 0x0000000000000000 0x0000000000000000 0x602110: 0x0000000000000000 0x0000000000000000 0x602120: 0x0000000000000000 0x0000000000000000 0x602130: 0x0000000000000000 0x0000000000000000 0x602140: 0x0000000000000000 0x0000000000000000 0x602150: 0x0000000000000000 0x0000000000000000 0x602160: 0x0000000000000000 0x0000000000000021
|
这里已经改好了,然后进行free(ptr4),此时的heap
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> heap 0x602000 PREV_INUSE { prev_size = 0, size = 353, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602160 { prev_size = 352, size = 32, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x411 } 0x602180 PREV_INUSE { prev_size = 0, size = 1041, fd = 0x3a347274705d2a5b, bk = 0x3065303230367830, fd_nextsize = 0xa, bk_nextsize = 0x0 } 0x602590 PREV_INUSE { prev_size = 0, size = 133745, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
此时的bins:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x602000 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x602000 smallbins empty largebins empty
|
然后再分配
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> heap 0x602000 PREV_INUSE { prev_size = 0, size = 353, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x602160 FASTBIN { prev_size = 352, size = 33, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x411 } 0x602180 PREV_INUSE { prev_size = 0, size = 1041, fd = 0x3a347274705d2a5b, bk = 0x3065303230367830, fd_nextsize = 0xa, bk_nextsize = 0x0 } 0x602590 PREV_INUSE { prev_size = 0, size = 133745, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 }
|
成功跨多域进行分配
1 2 3 4 5 6
| pwndbg> x/10gx 0x602010-0x10 0x602000: 0x0000000000000000 0x0000000000000161 0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78 0x602020: 0x0000000000000000 0x0000000000000000 0x602030: 0x0000000000000000 0x0000000000000000 0x602040: 0x0000000000000000 0x0000000000000000
|
over~ 得,乖乖回去写off-by-one去了