Zj_W1nd's BLOG

How2Heap总集篇导演剪辑剧场版

2024/12/13

~~摆了,~~可以参考:

Basic:

UAF

free后指针不置空可以再次使用。

Overflow

溢出,这个一般用于篡改size或prevsize域比较多,能溢出fd和bk也行。

Off By One

只能溢出1字节。可以篡改下一个chunk的PREV_INUSE位造成合并等。

Double Free

多次释放同一堆块。接着通过分配可以获得指向同一堆块的多个不同指针。可以类型混淆,也可以获取UAF。

  • 对于fastbin chunk,PREV_INUSE永远为1,因此double free会有奇效。同时只检查第一块是否double free。

Overlap

重叠。本质上也是借助上面几个漏洞修改size(向前重叠)或者prevsize(向后重叠)域然后释放。这样就有一个chunk内部的一个额外指针了。(未必)更加细节的介绍
house of Rabbit也是差不多

Leak

Unsorted Bin Leak

unsortedbin中的头部chunk的fd会指向main_arena+88的位置。只有一个chunk的话fd和bk都会指向其中。

tcache leak

用tcache可以泄露堆的起始地址。

其他

类似的放入bin中然后打出内容的思想都可以。

hook

在2.34之前hook没有移除的情况下我们一直可以通过修改mallochook,freehook,reallochook等函数来劫持控制流。一般来说是将chunk分配到这里然后改成one_gadget。比如House_of_Roman中针对2.23的malloc_hook-0x23地方的fastbinchunk等等

然而实际利用中很多时候我们会发现所有的one_gadget都不满足的情况,这种时候该怎么办?下面是一些思路

  1. 利用realloc_hook。观察__libc_realloc的代码可以发现,前面push了很多参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:00000000000846C0 ; __unwind {
.text:00000000000846C0 push r15 ; Alternative name is '__libc_realloc'
.text:00000000000846C2 push r14
.text:00000000000846C4 push r13
.text:00000000000846C6 push r12
.text:00000000000846C8 mov r13, rsi
.text:00000000000846CB push rbp
.text:00000000000846CC push rbx
.text:00000000000846CD mov rbx, rdi
.text:00000000000846D0 sub rsp, 38h
.text:00000000000846D4 mov rax, cs:__realloc_hook_ptr
.text:00000000000846DB mov rax, [rax]
test rax, rax
jnz loc_848E8 ;这里call rax

realloc调整栈帧og触发,或者利用其他环境触发malloc包括doublefree异常–strdup等

realloc的意思是,我们在malloc_hook处填上realloc的地址,而在realloc_hook处填上one_gadget的地址,利用realloc调用前的这段push参数对栈帧进行微调。

一些常识:

1
2
#define NBINS             128
#define NSMALLBINS 64

tcache最多64个,每个最大连接7个chunk。下标就是按malloc_align分,以64位为例,0-24大小的对应idx0,25-40对应idx1,41-56对应idx2依次类推每个+16。然后32位就是0-12,13-20这样。

1
DEFAULT_MXFAST             64 (for 32bit), 128 (for 64bit)

Glibc 2.23

无tcache,检查较少,很经典

arena attack(House of Mind & House of Gods)

实战利用价值小,看个乐

House of mind

伪造arena,blog没写,是一种通过伪造arena从而将释放的chunk连入假arena的过程,利用比较抽象而且最后似乎还没有任意写。

House of Gods

利用arena结构体中的数据和错位实现在main_arena中构建一个chunk进行写入,然后篡改arena的next指针将堆变成我们自己的arena来劫持整个堆。具体参见How2Heap3,虽然非常复杂没什么用但是可以看看这个堆风水的过程,非常的神必,有助于理解main_arena的结构:

  1. 泄露unsorted bin 头的地址,malloc一个smallchunk然后直接读。通过free+malloc(intm)启动遍历过程,将位图置位。这个时候我们让bin[0]作为伪造的size域存在。此时它是0x200.

  2. 首先将smallchunk放回unsortedbin中。用上面的leak在main_arena内寻址。利用UAF修改smallchunk的bk,在unsortedbin里面链接上这个伪造chunk。

  3. 利用malloc_state的特性,修改fastbin chunk的bk字段然后将其释放,控制其在malloc_state的开头,接着作为一个伪造的bk字段存在,链接到INTM。巧妙至极。

    这时候的unsorted bin:head->smallchunk->binmap[last](伪造的)->main_arena的开头(由于next指针开始就是指向本身的)->fast40->INTM

  4. 将binmap chunk作为一个“chunk”取出。得到了写入main_arena(即便是部分)的权限!开始为触发reused_arena()的条件铺路。

  5. 接着UAF改INTM的bk,让INTM后面再连接上narenas字段。然后取出INTM触发unsortedbin attack,将narenas写为一个大数。同时用binmap chunk,改掉system_mem字段。

  6. 用binmap chunk将arena的next指针随便写成你想分配的地址

  7. 连续调用两次malloc(0xffffffffffffffbf + 1),arena直接全部hijack掉。

  8. 随便操。

利用topchunk的一些特性达成我们的目的。House of Force可以实现任意地址分配,House of Orange可以实现不调用free情况下的一次释放。

House of Force

How2Heap3
将top chunk的size域改到极大,然后通过分配evil_size的chunk将topchunk推进到我们想分配的地址,随后下次分配就会从top中切出我们指定地址的chunk。

  • 其中top的size一般写成-1,evil_size的计算如下

new_top = old_top + nb
nb = new_top - old_top
req + 2sizeof(long) = new_top - old_top
req = new_top - old_top - 2sizeof(long)
req = dest - 2sizeof(long) - old_top - 2sizeof(long)
req = dest - old_top - 4*sizeof(long)

House of Orange

How2Heap4
神中神,适用于没有free的情况。通过先改小top chunk的size域然后申请一个较大的chunk来触发sysmalloc中的brk扩展,就会将旧的top放进unsortedbin中。

  • top的size需要页对齐,动调确定,一般改为0xfe1然后申请0x1000刚好。

  • 新申请的会放在新的内存页。

  • 【glibc 2.23】借助unsorted bin attack改写IO_LIST_ALL为unsorted(av)后将old top放入smallbins[4](改写size为0x61)从而实现用伪造chunk链接至IO_list_all触发fsop。

Fastbin(normal, house of spirit, House of Rabbit)

(未必)更加细节的介绍

Normal fast bin attack:

将一个fastbin分配在我们想要的位置。通过劫持已在fastbin中的一个chunk的fd指针来做到这一点。

  • 会检查size域,需要提前伪装。

  • 分配在malloc_hook或free_hook的话经常配合0x7f的字节错位来伪造size域

  • UAF和Double free,fastbin在2.23中只检查头部是否double free从而我们可以用dup来实现Write after free

House of Spirit

伪造fastbin的chunk。通过free放入bin。需要注意:

  • 边界0x10对齐

  • size域大小合适,满足fastbin要求

  • next的地址不能是top,而且size要正常(不用必须是fastbin)

House of Rabbit

参考
针对consolidate不严格检查size的fastbin attack. 要能修改size域并且有触发consolidate的条件

  • 修改size,然后触发consolidate变成overlap。

  • 修改fd然后触发consolidate将fake_chunk放入bin中变成合法chunk(注意伪造size)

Unsorted Bin Attack

参考
利用Unsorted Bin取出chunk时候的这个特性(双向链表,bk遍历):

1
2
3
bck = victim->bk;
...
bck->fd = unsorted_chunks (av);

控制住unsorted bin中的下一个会取出的chunk的bk域,我们就能将一个大数(unsorted_chunks(av))写入我们想要的地址,由于是fd偏移,所以将chunk中的bk写成targetaddr-16即可。可以用于篡改循环次数或GLOBAL_MAX_FAST等。

House of Lore(Small bin attack)

参考
smallbin attack。一个UAF改一下smallchunk的fd就是任意地址fakechunk分配。只要能绕过smallbin分配唯一的检查就行

1
2
3
4
5
if (__glibc_unlikely (bck->fd != victim))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}

Large Bin Attack

参考
利用large bin的指针将指定地址的内容写成一个合法的large chunk的地址。将已经在largebin中的chunk的bk或bk_nextsize篡改为target-16、-32即可实现在下一次victim放入largebin时触发将victim写入target的效果。

  • 从 unsorted bin 中来的 large chunk 要紧跟在攻击过的large chunk后面,也就是要连在一个bin里,大小也要相邻。

  • 可以篡改io_list_all进行fsop

  • 可以帮助tcache smash unlink?

COMBO FISTS (House of Roman, House of Storm)

House of Roman(组合拳,getshell思路很常用而且允许爆破, Fastbin atk + Unsorted bin atk)

参考
通过分配一个malloc_hook附近的chunk(确切的说是在malloc_hook-0x23处获取一个fastbinchunk)写入one_gadget。
这个思路是非常好的,只要是2.23并且任意地址分配chunk就可以用来getshell。有libc地址最好,没有也没关系借助unsorted bin attack+爆破来实现。
原poc的流程如下:

  • 拿一个0x60的fastbin chunk 1(实际大小0x70)

  • 分配一个不用的chunk,大小0x80,实际大小0x90,此时相对堆基址的偏移来到了0x100,这灵性的一步是为了待会我们改一个字节就能修改指针指向的chunk

  • 分配一个0x80的chunk 2,等会放入unsorted bin,chunk2的地址此时是0x100对齐的

  • 分配一个0x60的fastbin chunk 3,该chunk的偏移地址来到了0x190

  • 释放chunk2,放入unsorted bin,此时其fd和bk存有main_arena+0x68

  • 取0x60大小的chunk4(fake_libc_chunk),会从chunk2中进行分割,带有main_arena地址

  • 读取取出的chunk4,泄露main_arena地址然后计算偏移获取malloc_hook

  • 释放chunk3,放入fastbin

  • 释放chunk1,接入fastbin链表头,其fd指向chunk3(0x190)

  • 模拟一个UAF,写chunk1的fd一字节0x00将其fd指向0x100的chunk2(chunk 4)

  • 写chunk4,修改低位地址令其fd指向目标地址

  • 最后将fastbin中的chunk1和3取出,再次取就取到了malloc_hook-0x23处的chunk

House of Storm (unsorted bin atk + largebin atk)

BLOG没有写懒得补了写在这里,该技术实现任意地址chunk分配
2.26-2.28 需要tcache填满,再往后该漏洞不可用这个技术最关键的一步是largebin attack将fake_chunk地址写上一个堆地址,而且非常巧妙的利用了一个字节错位将其作为了size。下面是POC的流程

  • 准备一个unsortedbin堆块一个largebin堆块,其中unsortedbin中的堆块要比largebin中的大 (还要防止top合并)

  • 将unsortedbin的地址进行一下处理,算出一个移位偏移量。

  • 将unsorted bin的chunk bk写为fake_chunk,将large chunk的bk写成fake_chunk+8

  • 将large chunk的bk_nextsize写成fake chunk-0x18-shift_amount,shift是我们先前的偏移

  • 利用一个large_bin attack将unsorted chunk的地址写入一个恰当的位置正好伪造出fakechunk的size

  • 申请相应大小(堆地址高8位?)即可获得fake_chunk

House of Einhejar——off by null

参考
仅需off by one就能工作。控制住当前chunk的末8字节(prev_size写成evil_size,差值)然后溢出修改掉下一个chunk的PREV_INUSE位。然后通过free下一个chunk触发合并后,下次分配就能从指定地址取chunk了(prev_size错误合并,overlap)。

  • 由于off by one,最好溢出的下个chunk的size是0x100对齐的,便于改写

  • fakechunk的size要和我们修改的prev_size一致

利用Unlink实现overlap或指定地址写(确切地说是将target写成&target-0x18)。但是这个POC中利用所需要的前置条件较多…需要同时修改被unlink的chunk的fd和bk以及下一chunk的prevsize和PREV_INUSE位,要求太高了。

glibc 2.27

这个版本首次引入了tcache
下面会着重涉及tcache相关的攻击,同时大部分上面的攻击技巧都要和tcache打交道,比如先填满7个。

Fastbin

同上,只是每次要先填满对应大小的7个tcache

tcache

参考链接

Same as fastbin

和fastbin一样只不过更简单(没有了fastbin szie检查)。包括double free,house of spirit,tcache poison(对应fastbin attack)等。

在有tcache的情况下替代unsorted bin attack实现任意地址写一个libc地址。同时还能构造fake chunk(任意地址chunk分配)。

  • tcache空两个,拿到两个small bin和一个small bin的UAF,fake_chunk的bk写入target

  • 修改后释放的smallbin块的bk为fake_chunk地址,calloc触发写入攻击,target被改同时tcache头部会链入fake_chunk

  • 下次malloc会返回fake_chunk

House of botcake

double free实现任意堆块分配。

  • 用0x110的堆块填满tcache,然后再释放两个相邻的0x110堆块(先a后b)放入unsorted bin并导致合并

  • 取出一个0x110的chunk(从tcache),然后再次释放上一步中的a块,a块此时被放入tcache,这里形成了一个重叠

  • 分配一个0x130的堆块,这会从unsorted bin中合并好的块切割,而这时候我们取得的这个chunk可以拿来修改tcache头部的a块

  • 修改fd后malloc两次即可

glibc 2.34+

Glibc 2.31 House of Pig

House of Kiwi

House of Apple

一种思想。在glibc 2.34+的版本中已经没有了malloc_hook这种函数让我们一次就能getshell。堆利用的思想更多的集中到了FSOP上。

v1

v2

参考How2Heap实战——强网杯2024babyheap的板子用一次largebin attack将io_list_all劫持,然后将vtable换成wide版本来让内核从wvtable找函数调用从而规避检查,*(wdata+0xe0)+0x68就是能劫持控制流的地方。

值得注意的是,在我们劫持的时候,rdi中存放的是fp的地址(也就是我们劫持到的堆地址,借助一些gadget以及setcontext这种我们能够控制几乎所有的寄存器,只要堆上布局合理。
在setcontext最后会将[rdx+0xa8]的内容push到栈上然后ret,这也是我们ROP链路的开始。

v3

House of Banana

攻击_rtld_global的一种方法。劫持rtld_global到可写堆块后通过伪造程序基地址或者finiarray的办法,让程序在调用exit()退出的时候执行我们想要执行的函数。
rtld_global的第一字段是linkmap结构体的地址,无论是伪造linkmap还是篡改本来的,再或者改掉程序基地址,我们最终是要让

House of Emma

tlor_list

Libc_GOT

io_obstack

House of Cat

House of Water(glibc 2.39 newest)

CATALOG
  1. 1. Basic:
    1. 1.1. UAF
    2. 1.2. Overflow
    3. 1.3. Off By One
    4. 1.4. Double Free
    5. 1.5. Overlap
    6. 1.6. Leak
      1. 1.6.1. Unsorted Bin Leak
      2. 1.6.2. tcache leak
      3. 1.6.3. 其他
    7. 1.7. hook
    8. 1.8. 一些常识:
  2. 2. Glibc 2.23
    1. 2.1. arena attack(House of Mind & House of Gods)
      1. 2.1.1. House of mind
      2. 2.1.2. House of Gods
    2. 2.2. Top chunk related attack(House of Force & House of Orange)
      1. 2.2.1. House of Force
      2. 2.2.2. House of Orange
    3. 2.3. Fastbin(normal, house of spirit, House of Rabbit)
      1. 2.3.1. Normal fast bin attack:
      2. 2.3.2. House of Spirit
      3. 2.3.3. House of Rabbit
    4. 2.4. Unsorted Bin Attack
    5. 2.5. House of Lore(Small bin attack)
    6. 2.6. Large Bin Attack
    7. 2.7. COMBO FISTS (House of Roman, House of Storm)
      1. 2.7.1. House of Roman(组合拳,getshell思路很常用而且允许爆破, Fastbin atk + Unsorted bin atk)
      2. 2.7.2. House of Storm (unsorted bin atk + largebin atk)
    8. 2.8. House of Einhejar——off by null
    9. 2.9. Unsafe Unlink
  3. 3. glibc 2.27
    1. 3.1. Fastbin
    2. 3.2. tcache
      1. 3.2.1. Same as fastbin
      2. 3.2.2. tcache stashing unlink attack
      3. 3.2.3. House of botcake
  4. 4. glibc 2.34+
    1. 4.1. Glibc 2.31 House of Pig
    2. 4.2. House of Kiwi
    3. 4.3. House of Apple
      1. 4.3.1. v1
      2. 4.3.2. v2
      3. 4.3.3. v3
    4. 4.4. House of Banana
    5. 4.5. House of Emma
    6. 4.6. tlor_list
    7. 4.7. Libc_GOT
    8. 4.8. io_obstack
    9. 4.9. House of Cat
    10. 4.10. House of Water(glibc 2.39 newest)