/* extract p's inuse bit */
#define inuse(p)
((((mchunkptr)(((char *) (p)) + chunksize(p)))->mchunk_size) & PREV_INUSE)
仅通过下一块的 prev_in_use
位来判定当前块是否使用
/* Ptr to previous physical malloc_chunk. Only valid if prev_inuse (P). */
#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p)))
仅使用本块的 prev_size
来寻找前块的头。
/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr)(((char *) (p)) + chunksize(p)))
仅通过本块头+本块size的方式来寻找下一块的头
/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask(p) & ~(SIZE_BITS))
仅通过本块的size确定本块的大小
边界验证不严或字符串操作不合适导致出现单字节溢出
溢出字节为可控制任意字节
通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据
int main()
{
void *chunk1, *chunk2;
chunk1 = malloc(16);
chunk2 = malloc(16);
char *p = chunk1;
puts("gg");
for(int i = 0; i <= 16; i++)
{
p[i] = getchar();
}
puts("gg");
return 0;
}
溢出前
pwndbg> x/64gx 0x8402250
0x8402250: 0x0000000000000000 0x0000000000000021
0x8402260: 0x0000000000000000 0x0000000000000000
0x8402270: 0x0000000000000000 0x0000000000000021
0x8402280: 0x0000000000000000 0x0000000000000000
0x8402290: 0x0000000000000000 0x0000000000020d71
溢出后
pwndbg> x/64gx 0x8402250
0x8402250: 0x0000000000000000 0x0000000000000021
0x8402260: 0x6161616161616161 0x6161616161616161
0x8402270: 0x0000000000000061 0x0000000000000021
0x8402280: 0x0000000000000000 0x0000000000000000
0x8402290: 0x0000000000000000 0x0000000000001011
输入了17个’a’,下一个块的 prev_size
被覆盖了一个字节
溢出字节为 NULL 字节
在 size ≥ 0x100 的时候,溢出 NULL 字节prev_in_use
被清,这样前块会被认为是 free 块,可以触发unlink
int main(void)
{
char buffer[400]="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";//'a'*0x108
void *chunk1,*chunk2;
chunk1=malloc(256);
chunk2=malloc(256);
puts("gg");
strcpy(chunk1,buffer);
puts("gg");
return 0;
}
溢出前
pwndbg> x/64gx 0x8402250
0x8402250: 0x0000000000000000 0x0000000000000111
0x8402260: 0x0000000000000000 0x0000000000000000
0x8402270: 0x0000000000000000 0x0000000000000000
0x8402280: 0x0000000000000000 0x0000000000000000
0x8402290: 0x0000000000000000 0x0000000000000000
0x84022a0: 0x0000000000000000 0x0000000000000000
0x84022b0: 0x0000000000000000 0x0000000000000000
0x84022c0: 0x0000000000000000 0x0000000000000000
0x84022d0: 0x0000000000000000 0x0000000000000000
0x84022e0: 0x0000000000000000 0x0000000000000000
0x84022f0: 0x0000000000000000 0x0000000000000000
0x8402300: 0x0000000000000000 0x0000000000000000
0x8402310: 0x0000000000000000 0x0000000000000000
0x8402320: 0x0000000000000000 0x0000000000000000
0x8402330: 0x0000000000000000 0x0000000000000000
0x8402340: 0x0000000000000000 0x0000000000000000
0x8402350: 0x0000000000000000 0x0000000000000000
0x8402360: 0x0000000000000000 0x0000000000000111
0x8402370: 0x0000000000000000 0x0000000000000000
0x8402380: 0x0000000000000000 0x0000000000000000
0x8402390: 0x0000000000000000 0x0000000000000000
0x84023a0: 0x0000000000000000 0x0000000000000000
0x84023b0: 0x0000000000000000 0x0000000000000000
0x84023c0: 0x0000000000000000 0x0000000000000000
0x84023d0: 0x0000000000000000 0x0000000000000000
0x84023e0: 0x0000000000000000 0x0000000000000000
0x84023f0: 0x0000000000000000 0x0000000000000000
0x8402400: 0x0000000000000000 0x0000000000000000
0x8402410: 0x0000000000000000 0x0000000000000000
0x8402420: 0x0000000000000000 0x0000000000000000
0x8402430: 0x0000000000000000 0x0000000000000000
0x8402440: 0x0000000000000000 0x0000000000000000
溢出后
pwndbg> x/64gx 0x8402250
0x8402250: 0x0000000000000000 0x0000000000000111
0x8402260: 0x6161616161616161 0x6161616161616161
0x8402270: 0x6161616161616161 0x6161616161616161
0x8402280: 0x6161616161616161 0x6161616161616161
0x8402290: 0x6161616161616161 0x6161616161616161
0x84022a0: 0x6161616161616161 0x6161616161616161
0x84022b0: 0x6161616161616161 0x6161616161616161
0x84022c0: 0x6161616161616161 0x6161616161616161
0x84022d0: 0x6161616161616161 0x6161616161616161
0x84022e0: 0x6161616161616161 0x6161616161616161
0x84022f0: 0x6161616161616161 0x6161616161616161
0x8402300: 0x6161616161616161 0x6161616161616161
0x8402310: 0x6161616161616161 0x6161616161616161
0x8402320: 0x6161616161616161 0x6161616161616161
0x8402330: 0x6161616161616161 0x6161616161616161
0x8402340: 0x6161616161616161 0x6161616161616161
0x8402350: 0x6161616161616161 0x6161616161616161
0x8402360: 0x6161616161616161 0x0000000000000100 <== off-by-one null byte
0x8402370: 0x0000000000000000 0x0000000000000000
0x8402380: 0x0000000000000000 0x0000000000000000
0x8402390: 0x0000000000000000 0x0000000000000000
0x84023a0: 0x0000000000000000 0x0000000000000000
0x84023b0: 0x0000000000000000 0x0000000000000000
0x84023c0: 0x0000000000000000 0x0000000000000000
0x84023d0: 0x0000000000000000 0x0000000000000000
0x84023e0: 0x0000000000000000 0x0000000000000000
0x84023f0: 0x0000000000000000 0x0000000000000000
0x8402400: 0x0000000000000000 0x0000000000000000
0x8402410: 0x0000000000000000 0x0000000000000000
0x8402420: 0x0000000000000000 0x0000000000000000
0x8402430: 0x0000000000000000 0x0000000000000000
0x8402440: 0x0000000000000000 0x0000000000000000
此时 prev_size
域启用,可以伪造 prev_size
,使块之间发生重叠(新版本代码存在检查,只有2.28及以前版本可用)
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink_chunk (av, p);
使目标堆块被重新分配到某个可控的新的堆块中,实现对目标堆块任意读写。
触发unlink
off-by-one small bin
off-by-one large bin
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| A(off-by-one) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| B(allocated) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| C(target,allocated) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
prev_in_use
位保持为1否则会触发unlink导致崩溃+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| A(off-by-one) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| B(freed) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| C(target,allocated) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
off-by-one 改B的size,使其包含C,prev_in_use
位保持为1
malloc一个大小为B+C的块
通过新块对C任意读写
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| A(allocated) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| B(allocated) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| C(allocated) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| A(allocated) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| B1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| B2(target) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| C(allocated) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
A发生off-by-one,溢出一个'\x00'
覆盖B的size域的低字节,prev_in_use
位被覆盖为0,前块被认为是free块
此时再malloc两个较小块B1、B2(不能是fastbin,B1+B2<B),会分配在B块中,然后free C和B1,导致B和C合并,再malloc 和B大小相同的块,就可以得到B2
off-by-one '\x00'
覆盖B的size域低字节
free B
malloc B1, malloc B2
free C, free b1
malloc B
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| A(off-by-one) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| B(allocated) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
在A块中构造fake chunk,绕过unlink的check,覆盖B的pre_size
域 和 prev_in_use
位
free B,unlink
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-ND 4.0)进行许可。
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).