本文是自己的一点心得,没有特别地总结整理。

编译

找了半天编译的方法,结果突然发现文件夹里有个Makefile,一键make就全编译了,我觉得我就是个憨憨

first_fit

char* a = malloc(512); 经过对齐后 chunk size 为 0x210

free(a);后块a被放到 unsorted_bins 中

执行 c = malloc(500); (500+8)经过16字节对齐后 chunk size 为 0x200 ,此时small bins是空的,就从unsorted bins中找,找到了大小为0x210的块a。而0x210 的块切割后剩下的块大小为0x10,小于MINSIZE(0x20),所以不切直接分配。

然后自己测试了一下

  1. c = malloc(512-0x20+8);需要的 chunk size 为 0x190 ,对齐后还是0x190,切割后留下的0x20就放在unsorted bins里了。

  2. c = malloc(512-0x20+9); 对齐后为 0x200 ,就不切了。

fastbin_dup

fastbin,double free
fastbin free的时候只检查了和bin相连的块和当前free的块地址相不相同

fastbin_dup_into_stack

在栈里构造一个块,使得该块的size满足可更改fd的chunk所在的bin的idx,就可将该伪造的块放入该bin,得以分配。
fd改为其他的值会报错

1
*d = (unsigned long long) (((char*)(&stack_var)) - 8);

报错是因为

也就是说分配的时候会根据块size计算 idx ,并和块所在 fastbin 的 idx 进行比较,如果不对就报错

试着在data段分配一个堆块

在这里插入图片描述

| fastbin_dup_into_stack.c | Tricking malloc into returning a nearly-arbitrary pointer by abusing the fastbin freelist. | latest | 9447-search-engine, 0ctf 2017-babyheap |

fastbin_dup_consolidate

malloc_consolidate这个函数的作用就是将fastbin合并后置入unsorted bin,一般调用的情况有以下几种:

1. malloc的大小在smallbin范围内,若对应的smallbin没初始化的时候。
2. malloc的size大于small bin的范围,先调用malloc_consolidate将fastbin 合并为unsorted bin(本例的情况,再free一次,就可以在fastbin和unsortedbin中拥有同一个块)
3. top chunk不够空间
4. free某chunk时,该chunk合并前后空闲块后的大小超过了fastbin的收缩阈值(FASTBIN_CONSOLIDATION_THRESHOLD 也就是65536 0x10000)。(一般与top合并时会触发)

同时有下面两点需要注意的:

1. malloc_consolidate在合并fastbin的过程中没有对其size进行校验(House of Rabbit)
2. malloc_consolidate将合并后生成的chunk插入到unsorted bin头部

参考资料:https://ch4r1l3.github.io/2019/01/22/malloc-consolidate调用条件/

fastbin_dup_consolidate.c | Tricking malloc into returning an already-allocated heap pointer by putting a pointer on both fastbin freelist and unsorted bin freelist. | latest | Hitcon 2016 SleepyHolder |

看 ctfwiki
| unsafe_unlink.c | Exploiting free on a corrupted chunk to get arbitrary write. | < 2.26 | HITCON CTF 2014-stkof, Insomni’hack 2017-Wheel of Robots |

house_of_spirit

先初始化内存malloc(1);
不可控内存的前后内存可控的话,构造:

1
2
3
4
5
1. 块的size在fastbin范围内
2. nextchunk的size
> 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena)
没找到system_mem在哪,亲测0x21000-8可,0x21000不可
3. The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n")

free该块即可将当前块放入fastbin。

| house_of_spirit.c | Frees a fake fastbin chunk to get malloc to return a nearly-arbitrary pointer. | latest | hack.lu CTF 2014-OREO |

poison_null_byte

需要构造的点:

1
2
3
4
5
6
7
8
P是size被null的块,Q是P的nextchunk
1. chunksize(P) == prev_size (next_chunk(P)) 这个size是offbyone后的size,next_chunk是根据P的size算的,prev_size (next_chunk(P)) 可以自己伪造为offbyone后的size(因为offbyone后的size一定<=原始size)
2.分割P为块A和B
3.free A,free Q,A和Q会 consolidate, B就被overlap了
free的时候
- 检查前一个chunk空闲吗(检查本块的prev_inuse)
- 检查后一个是不是top chunk
- 检查后一个chunk空闲吗(nextinuse = inuse_bit_at_offset(nextchunk, nextsize);)

利用的点就在于free Q的时候,没有检查 prev_size(Q) == chunksize(pre_chunk(Q))
glibc2.23检查还没那么严格,不检查chunksize( p ) != prevsize)
在这里插入图片描述
2.29是要检查的了(我看的在线版是2.29而已),这个利用方法就失效了。
在这里插入图片描述
| poison_null_byte.c | Exploiting a single null byte overflow. | < 2.26 | PlaidCTF 2015-plaiddb |

house_of_lore(small bin)

可以修改 small bin 的最后一个 chunk 的 bk 为我们指定内存地址的 fake chunk,并且同时满足之后的 bck->fd != victim 的检测,那么我们就可以使得 small bin 的 bk 恰好为我们构造的 fake chunk。也就是说,当下一次申请 small bin 的时候,我们就会分配到指定位置的 fake chunk。
具体看ctfwiki吧
| house_of_lore.c | Tricking malloc into returning a nearly-arbitrary pointer by abusing the smallbin freelist. | < 2.26 | |

overlapping_chunks(更改块大小)

A|B|C
A的size改为A+B的size
free(A)
malloc(sizeof(A+B)) 即可overlap B
| overlapping_chunks.c | Exploit the overwrite of a freed chunk size in the unsorted bin in order to make a new allocation overlap with an existing chunk | < 2.26 | hack.lu CTF 2015-bookstore, Nuit du Hack 2016-night-deamonic-heap |

overlapping_chunks_2(向高地址合并)

不考虑和top合并, 块均非fastbin
A|B|C
A的size改为A+B的size
free( C )
free( A )
malloc(A+B+C)
B就被overlap了
| overlapping_chunks_2.c | Exploit the overwrite of an in use chunk size in order to make a new allocation overlap with an existing chunk | latest | |

house_of_force(top chunk)

前提:

1
2
能够以溢出等方式控制到 top chunk 的 size 域
能够自由地控制堆分配尺寸的大小

例子中把top chunk的size设为-1 (0xffffffff),然后malloc(需控制的地址-0x10-top的地址-0x8):例子中需控制的地址在bss段上(heap低地址处),因此malloc的是个负数。top被分配后指向了需控制的地址-0x10的位置,然后再从top里分配一次就可以任意地址写了。
| house_of_force.c | Exploiting the Top Chunk (Wilderness) header in order to get malloc to return a nearly-arbitrary pointer | < 2.29 | Boston Key Party 2016-cookbook, BCTF 2016-bcloud |

unsorted_bin_into_stack

构造:

1
2
3
4
5
1. free块a(size为y)到unsortd bin,修改a->bk为栈中地址 stack_buffer
2. 构造栈中块的size z、构造bk里存放的是一个可写的地址(比较苛刻,很难找到这两者同时满足的块)
stack_buffer[1] = 0x100 + 0x10;
stack_buffer[3] = (intptr_t)(stack_buffer+10);
3. malloc(z)即可在栈中的块任意写。

| unsorted_bin_into_stack.c | Exploiting the overwrite of a freed chunk on unsorted bin freelist to return a nearly-arbitrary pointer. | < 2.26 | |

unsorted_bin_attack

把要取出的unsorted chunk的bk改为[global_max_fast]-0x10,就可以将一个很大的值(unsorted bin的头地址 )写到global_max_fast。

  • 覆盖了chunk的fd也没关系,因为根本没用上
  • 改了之后unsorted bin头的bk指向了[global_max_fast]-0x10,再插入 chunk 时,可能会出现问题,但这时候块都变成fastbin了,所以一般也没啥问题(我觉得)
  • 没有bck->fd != victim检查才行

| unsorted_bin_attack.c | Exploiting the overwrite of a freed chunk on unsorted bin freelist to write a large value into arbitrary address | < 2.26 | 0ctf 2016-zerostorage |

large_bin_attack

构造

1
2
3
4
5
6
7
1. 一个 large bin chunk 可以实现两个地址内容的修改。将 bk 和 bk_nextsize 改为某两个地址,这两个地址会被写入同一个堆地址。
p是一个 large bin chunk 的地址
p2[0] = 0;//fd
p2[1] = (unsigned long)(&stack_var2 - 2);//2表示2*8
p2[2] = 0;//fd_nextsize
p2[3] = (unsigned long)(&stack_var4 - 4);
2. 从 unsorted bin 中来的 large bin chunk 要紧跟在被构造过的 chunk 的后面(比构造过的chunk大),新来的chunk的地址就是被写入的堆地址。

| large_bin_attack.c | Exploiting the overwrite of a freed chunk on large bin freelist to write a large value into arbitrary address | < 2.26 | 0ctf 2018-heapstorm2 |

house_of_einherjar

前提是有一个offbyone漏洞。

后向合并的代码

1
2
3
4
5
6
if (!prev_inuse(p)) {
prevsize = prev_size(p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

构造这个块关键在于通过unlink检查:(P是伪造的将被consolidate的低地址的块,P|Q,Q被nullbyte)

1
2
3
4
5
6
7
8
1. 利用 unlink 漏洞的时候:
p->fd = &p-3*4
p->bk = &p-2*4
在这里利用时,因为没有办法找到 &p , 所以直接让:
p->fd = p
p->bk = p
2. chunksize(P) != prev_size (next_chunk(P)) next_chunk是根据P的size算的,也就是说只要P偏移size处的值为size即可。
3. Q的PREINUSE位为0,prev_size(Q)需覆盖到P的头部,free(Q)即可获得P+Q的空闲chunk

和poison_null_byte的区别在于,
4. 向低地址合并的时候,house_of_einherjar低地址的块是自己伪造的,而poison_null_byte低地址的块是free来的
5. 对于P|Q,poison_null_byte是P被nullbyte,而且是先free P,再nullbyte,所以保留了pre_size(Q),nullbyte后构造假的pre_size(nextchunk( P )),free Q即可获得P+Q,例子里还把P给分成了A|B(因为P的size已经被nullbyte了,所以怎么分都不会再影响pre_size(Q)),然后free A(不free也行,但为了通过size和unlink检查,就得构造:

1
2
3
*(size_t*)(b1+0x100) = 0x110;//chunksize( A ) != prev_size (next_chunk( A ))
*(size_t*)(b1) = (size_t*)(b1-0x10);//FD->bk != P || BK->fd != P
*(size_t*)(b1+0x8) =(size_t*) (b1-0x10);

),free Q后就能够overlap B,这样做的原因是更灵活,因为能控制B的所有信息;house_of_einherjar是Q被nullbyte,然后在P中构造一个假的freed的chunk(p->fd = p p->bk = p;chunksize( P ) != prev_size (next_chunk( P )),prev_size(Q)需恰好覆盖到假chunk的头部,free(Q)即可获得 假chunk+Q的空闲chunk。

以上是比较保守的做法,how2heap里的就相对大胆,在栈里伪造一个假的freed的chunk,把prev_size(Q)改为nullbyte了的块的地址-栈中假chunk的地址,free(Q)获得了在栈上malloc块的机会。
| house_of_einherjar.c | Exploiting a single null byte overflow to trick malloc into returning a controlled pointer | < 2.26 | Seccon 2016-tinypad |

house_of_orange

在题目没有free的情况下,通过malloc(大于top chunk size的值)以将top放入unsortedbin中来获得一个freed的chunk。
how2heap的16.04可跑,18.04跑不起来,看的ctfwiki,和IOFILE结合的部分还没怎么看懂
| house_of_orange.c | Exploiting the Top Chunk (Wilderness) in order to gain arbitrary code execution | < 2.26 | Hitcon 2016 houseoforange |

House of Rabbit

前提条件:

1
2
1. 可以修改 fastbin 的 fd 指针或 size 
2. 可以触发 malloc consolidate(merge top 或 malloc big chunk 等等)

效果:把块放入修改后的size对应的small bin里

House of Roman

fastbin attack 和 Unsortbin attack 结合的一个小 trick。
fastbin attack用于在malloc hook附近分配堆块
Unsortbin attack用于在malloc hook处填上unsorted bin header的address

tcache_dup

tcache的doublefree,看起来什么都没检查(能放进tcache即可),只要有一个地址可以free两次就可。
| tcache_dup.c | Tricking malloc into returning an already-allocated heap pointer by abusing the tcache freelist. | 2.26 - 2.28 | |

tcache_poisoning

只要改掉放进了tcache的块的fd,就可以获取任意地址的内存

  • 和fastbin attack的区别在于分配时 tcache 不和块所在 bin 的 idx 进行比较

| tcache_poisoning.c | Tricking malloc into returning a completely arbitrary pointer by abusing the tcache freelist. | > 2.25 | |

tcache_house_of_spirit

得先初始化内存malloc(1);
需构造chuck的size <=0x410即在tcache范围内
The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n”);

  • 和fastbin attack的区别在于nextchunk的size 需要合法,tcache没这个要求

| tcache_house_of_spirit.c | Frees a fake chunk to get malloc to return a nearly-arbitrary pointer. | > 2.25 | |