堆溢出之Unlink漏洞

1. unlink(脱链)原理

Unlink(脱链) 是Linux堆管理器(ptmalloc)中的一个核心内部宏。它的唯一目的就是将一个已经处于空闲状态的堆块(Chunk)从它所在的双向循环链表(如Unsorted Bin、Small Bin等)中完整地移除

脱链操作通常发生在两个场景:

  • 1. 堆块合并:当一个堆块被释放(free)时,如果它物理相邻的前后堆块也是空闲的,系统需要把前后堆块“拉出来”和它合并成一个大堆块;
  • 2. 分配内存:当malloc()函数找到了一个合适的空闲堆块准备分配给用户时,需要把它从空闲链表中取出来;

1.1 unlink的指针操作

在没有引入安全检查的时代(经典Unsafe Unlink),Unlink的底层逻辑就是标准的双向链表节点删除

假设我们要将堆块P从链表中脱链,P的结构中包含两个核心指针:

  • P->fd:指向链表中的前一个空闲堆块;
  • P->bk:指向链表中的后一个空闲堆块;

脱链的本质是让P的前驱节点和后继节点直接相连,从而跳过P,核心代码如下:

1. P->fd->bk = P->bk;  // 让前一个节点的后向指针,指向后一个节点
2. P->bk->fd = P->fd;  // 让后一个节点的前向指针,指向前一个节点

结构体成员的赋值本质上是向某个地址写入数据。P->fd的值是一个内存地址(记为Addr_A)。P->fd->bk 相当于 *(Addr_A + bk_offset);也就是说第一行代码P->fd->bk = P->bk等同于*(P->fd + bk_offset) = P->bk;

如果攻击者能通过溢出控制P->fd和P->bk的值,就能实现 “向任意地址(P->fd + 偏移)写入任意数据(P->bk)” 的能力。

1.2 Safe Unlink校验机制

现代glibc引入了严格的双向链表完整性检查(Safe Unlink)。在执行指针修改前,系统会强行检查以下条件:

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
  malloc_printerr ("corrupted double-linked list");

校验程序会根据P的指针验证链表的合法性:

  • 1. 找到P指向的前一个节点FD,检查FD的后向指针bk是否指回P;
  • 2. 找到P指向的后一个节点BK,检查BK的后向指针fd是否指回P;

如果攻击者随意篡改了P->fd和P->bk,这个相互指向的闭环就会破裂,系统会立即触发SIGABRT崩溃,从而阻止了任意地址写。

2. 漏洞利用

2.1 利用条件

要成功触发并利用Unlink漏洞,通常需要满足以下条件:

  • 1. 存在堆溢出:能够修改相邻空闲块的头部信息(Prev_Size, Size, FD, BK);
  • 2. 伪造Chunk:在内存中构造一个符合堆管理器规则的“假”块(Fake Chunk);
  • 3. 绕过保护机制:
    • Safe Unlink:现代glibc引入了FD->bk == P && BK->fd == P的校验;
    • 绕过方式:攻击者通常需要将FD设置为&P – 3*size_t,将BK设置为&P – 2*size_t,使校验通过,最终效果是让指针P指向它自己附近的地址;

2.2 相关漏洞

Unlink漏洞的本质都是利用堆溢出等手段篡改双向链表指针,在脱链时引发内存破坏

2.2.1 Unsafe Unlink(经典任意写漏洞)

这是最原始的Unlink漏洞,存在于没有安全检查的早期glibc(< 2.3.2)中:

  • 漏洞原理:利用堆溢出修改一个空闲Chunk的fd和bk指针。当触发free导致合并脱链时,直接执行P->fd->bk = P->bk;
  • 后果:造成任意地址写漏洞,即可以将任意数据写入任意内存;

2.2.2 Safe Unlink Bypass

glibc引入了双向指针完整性检查(FD->bk == P && BK->fd == P)。安全研究员随之发明了绕过技术,也就是现代版的Unlink漏洞利用:

  • 核心原理:在已知一个全局指针(或栈指针)指向该堆块的前提下,在堆内伪造一个Fake Chunk,将其fd和bk设置为基于该全局指针地址计算出的特定偏移;
  • 后果:成功绕过双向检查,并使得该全局指针被改写为指向它自己附近的地址。随后,攻击者通过向该指针写入数据,可实现对全局变量区(如GOT表、其他指针)的任意修改;

2.2.3 Off-By-One引发的Unlink漏洞

触发Unlink攻击的边界漏洞类型:

  • 核心原理:程序在向堆块写入数据时边界判断错误,恰好溢出了一个字节:
    • Off-by-One:溢出的字节可以控制,直接修改下一个相邻Chunk的size字段;
    • Off-by-Null:溢出的字节只能是\x00(单字节空截断)。这会强制清空下一个相邻Chunk的PREV_INUSE(P位),使系统误以为前一个块是空闲的;
  • 后果:配合在前一个块中伪造的Fake Chunk,当free后面的块时,系统会触发向前合并,从而强制触发Safe Unlink Bypass流程;

2.2.4 House Of系列

随着glibc的更新,针对常规Bin的Unlink检查越来越严,所以就出现了Top Chunk的Unlink合并行为

  • House of Einherjar原理:利用Off-by-Null漏洞篡改下一个非空闲块的prev_size和P位,使其指向一个在别处(如栈上、全局变量区)伪造的Fake Chunk。当释放该块时,系统会错误地将当前的Top Chunk与远处的Fake Chunk进行Unlink并合并;
  • 后果Top Chunk被强制转移到任意指定位置。当下一次调用malloc时,程序会直接在攻击者指定的地址(如栈或全局区)分配出堆块,彻底劫持内存;

2.2.5 Large Bin Attack(现代Unlink衍生漏洞)

在最新的glibc版本中,Small Bin的Unlink检查近乎完美,但Large Bin的结构更为复杂,除了fd/bk外还引入了用于加速排序的fd_nextsize/bk_nextsize双向链表:

  • 核心原理:在Large Bin插入或取出的过程中,系统需要对nextsize链表进行脱链和链接操作。由于glibc早期对nextsize链表的校验存在疏漏,攻击者可以通过堆溢出篡改Large Bin中的bk_nextsize;
  • 后果:在特定的malloc触发脱链分配时,可以实现往任意地址写入一个堆地址。这在绕过ASLR(内存地址随机化)或改写__free_hook / stderr等全局受限指针时非常致命;

3. 漏洞实例

3.1 CVE-2015-0235:Glibc “GHOST” 漏洞

CVE-2015-0235(代号GHOST,幽灵漏洞)是2015年轰动全球的Linux系统级高危漏洞。它存在于Linux核心基础库glibc中,由于堆缓冲区溢出间接引发了内存破坏,允许攻击者在不掌握系统账号的前提下,进行远程代码执行(RCE)

3.1.1 漏洞成因

漏洞的核心位于glibc的__nss_hostname_digits_dots()函数中,该函数在内部被gethostbyname()gethostbyname2()等著名的域名解析函数调用。

当程序尝试解析一个域名时,该函数会在堆(Heap)上分配一块缓冲区来存储解析结果。然而,glibc开发者在计算需要分配的内存大小时,没有把域名自身的长度完全算进去(即漏算了一个指针的大小)。当用户输入的域名或IP地址过长时,系统计算缓冲区大小出现严重失误。

3.1.2 漏洞攻击过程

攻击路径如下:

  • 1. 攻击者向目标服务器(如邮件服务器)发送一个精心构造的、超长的、全数字与点号组成的恶意域名(如123.123.123);
  • 2. 服务器调用gethostbyname()解析该域名;
  • 3. 函数根据错误公式分配了一个较小的堆块;
  • 4. 随后,函数在向该堆块拷贝域名数据时,发生了堆溢出(Heap Overflow),恶意数据直接冲出了当前堆块的边界;

3.1.3 漏洞与unlink的关系

溢出发生后,它并没有立刻导致程序崩溃,而是精准地覆盖了物理相邻的下一个空闲堆块的控制头(Chunk Header)

  • 篡改指针:溢出数据改写了下一个空闲堆块的size字段以及双向链表指针(fd和bk);
  • 触发脱链:当gethostbyname()运行完毕,或者服务器清理临时变量调用free()释放当前内存时,堆管理器检测到相邻的堆块是空闲的,于是尝试将它们进行合并
  • 内存劫持:为了合并,系统必须把那个被篡改的空闲堆块从Unsorted Bin等双向链表中“拉出来”,从而触发了Unlink操作
  • 任意写:Unlink脱链直接变成了任意地址写,攻击者借此将关键的函数指针(或GOT表)修改为自己的恶意Shellcode地址,从而夺取服务器权限;
免责声明:

1. 一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。

2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。

3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
觉得有帮助可以赞赏本文哦~万分感谢!
文章:堆溢出之Unlink漏洞
作者:沛旗
链接:https://www.peiqiblog.com/article/11607/
版权声明::本博客站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议
转载请注明文章地址及作者哦~
暂无评论

发送评论(禁止发表一切违反法律法规的敏感言论) 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇