非栈上格式化字符串的利用方法
字数 1663 2025-08-29 22:41:24

非栈上格式化字符串漏洞利用技术详解

1. 前言

本文详细讲解非栈上格式化字符串漏洞的利用方法,以第六届强网拟态线下赛的格式化字符串Pwn题为例。重点介绍两个关键技术点:多级指针链利用和高位截断技术,并探讨格式化字符串利用中的一些限制条件。

2. 题目分析

2.1 题目基本情况

  • 程序提供一个栈地址
  • 给予一次非栈上格式化字符串的利用机会
  • 程序直接使用exit退出,无法直接控制main函数的返回地址

2.2 面临的挑战

  1. 格式化字符串缓冲区不在栈上,无法直接在栈中写入目标地址进行修改
  2. 只有一次利用机会,需要精心构造payload

3. 关键技术点

3.1 多级指针链利用

当格式化字符串不在栈上时,可以通过修改栈上现有的多级指针链(二重/三重指针)来间接控制目标内存。

3.1.1 指针链的发现

通过gdb调试,在call printf指令处查看栈内容,可以发现栈中存在多级指针链:

0x7ffde9b11bd8 -> 0x7ffde9b11cb8 -> 0x7ffde9b12317

3.1.2 利用方法

  1. 修改指针链中的地址,使其指向目标位置
  2. 通过修改后的指针链间接写入目标值

3.1.3 具体步骤

  1. 找到栈上的多级指针链
  2. 计算各指针在printf参数中的偏移量(如0x7ffde9b11bd8偏移为11,0x7ffde9b11cb8偏移为39)
  3. 修改指针链使其指向目标地址(如printf的返回地址0x7ffde9b11ba8)

3.2 高位截断技术

当前期输出字符数已超过后期需求值时,利用0x10000溢出特性实现单字节精确写入。

3.2.1 技术原理

  • 使用%n系列格式化符(%hn%hhn)进行写入
  • 利用整数溢出特性:0x10000 + x ≡ x (mod 256)
  • 当已输出字符数大于目标值时,可以构造0x10000 + x的输出

3.2.2 应用场景

  • 需要精确控制单字节写入时
  • 前期已经输出了大量字符,无法直接使用小数值时

3.2.3 示例

payload = b'%' + str(0x10023).encode() + b'c%39$hhn'

这个payload会写入0x23到目标地址,因为0x10023 ≡ 0x23 (mod 256)

4. 重要限制条件

4.1 同一条指针链上不能连续使用$符号

4.1.1 问题描述

不能对同一条指针链(如0x7ffde9b11bd8 -> 0x7ffde9b11cb8 -> 0x7ffde9b12317)连续使用两次$符号进行修改。

4.1.2 原因分析

  • $符号的实现采用预处理机制
  • printf会分析完整的format参数,找到最大的%x$n,然后从栈上提取相应信息到args_value
  • 第一次修改成功的是栈中的数据,但args_value仍是原来的值
  • 第二次修改时,仍使用旧的args_value中的地址

4.1.3 解决方案

  • 避免在同一条指针链上连续使用$符号
  • 如果需要多次修改,应使用不同的指针链或分阶段修改

5. 完整利用流程

5.1 利用步骤

  1. 修改指针链使其指向printf的返回地址

    • 使用%n修改0x7ffde9b11cb8中存储的地址
    • 将0x7ffde9b12317改为0x7ffde9b11ba8(修改两个字节)
  2. 修改printf的返回地址

    • 使用高位截断技术精确写入单字节
    • 构造payload如%0x23c%39$hhn
  3. 实现多次利用

    • 通过修改后的指针链多次使用格式化字符串漏洞
  4. 最终利用

    • 在printf返回地址下方的栈空间逐步填入one_gadget
    • 最后将printf的返回地址修改为ret指令地址
    • 从而成功调用one_gadget

5.2 EXP示例

# 修改指针链使其指向printf返回地址
payload1 = b'%' + str(0x1ba8).encode() + b'c%11$hn'
# 使用高位截断技术修改返回地址
payload2 = b'%' + str(0x23).encode() + b'c%39$hhn'
# 后续利用...

6. 相关题目推荐

  • [LitCTF 2025]onlyone:与强网拟态题目几乎相同的利用方式

7. 参考资源

  1. 一次有趣的格式化字符串漏洞利用 | ZIKH26's Blog
  2. 格式化字符串漏洞利用 - WJH's Blog

8. 总结

非栈上格式化字符串漏洞利用的关键在于:

  1. 发现并利用栈上现有的多级指针链
  2. 掌握高位截断技术实现精确写入
  3. 理解并规避同一条指针链上不能连续使用$符号的限制
  4. 通过精心构造的payload实现有限条件下的完整利用
非栈上格式化字符串漏洞利用技术详解 1. 前言 本文详细讲解非栈上格式化字符串漏洞的利用方法,以第六届强网拟态线下赛的格式化字符串Pwn题为例。重点介绍两个关键技术点:多级指针链利用和高位截断技术,并探讨格式化字符串利用中的一些限制条件。 2. 题目分析 2.1 题目基本情况 程序提供一个栈地址 给予一次非栈上格式化字符串的利用机会 程序直接使用exit退出,无法直接控制main函数的返回地址 2.2 面临的挑战 格式化字符串缓冲区不在栈上,无法直接在栈中写入目标地址进行修改 只有一次利用机会,需要精心构造payload 3. 关键技术点 3.1 多级指针链利用 当格式化字符串不在栈上时,可以通过修改栈上现有的多级指针链(二重/三重指针)来间接控制目标内存。 3.1.1 指针链的发现 通过gdb调试,在 call printf 指令处查看栈内容,可以发现栈中存在多级指针链: 3.1.2 利用方法 修改指针链中的地址,使其指向目标位置 通过修改后的指针链间接写入目标值 3.1.3 具体步骤 找到栈上的多级指针链 计算各指针在printf参数中的偏移量(如0x7ffde9b11bd8偏移为11,0x7ffde9b11cb8偏移为39) 修改指针链使其指向目标地址(如printf的返回地址0x7ffde9b11ba8) 3.2 高位截断技术 当前期输出字符数已超过后期需求值时,利用0x10000溢出特性实现单字节精确写入。 3.2.1 技术原理 使用 %n 系列格式化符( %hn 、 %hhn )进行写入 利用整数溢出特性:0x10000 + x ≡ x (mod 256) 当已输出字符数大于目标值时,可以构造0x10000 + x的输出 3.2.2 应用场景 需要精确控制单字节写入时 前期已经输出了大量字符,无法直接使用小数值时 3.2.3 示例 这个payload会写入0x23到目标地址,因为0x10023 ≡ 0x23 (mod 256) 4. 重要限制条件 4.1 同一条指针链上不能连续使用$符号 4.1.1 问题描述 不能对同一条指针链(如0x7ffde9b11bd8 -> 0x7ffde9b11cb8 -> 0x7ffde9b12317)连续使用两次$符号进行修改。 4.1.2 原因分析 $ 符号的实现采用预处理机制 printf会分析完整的format参数,找到最大的 %x$n ,然后从栈上提取相应信息到 args_value 第一次修改成功的是栈中的数据,但 args_value 仍是原来的值 第二次修改时,仍使用旧的 args_value 中的地址 4.1.3 解决方案 避免在同一条指针链上连续使用 $ 符号 如果需要多次修改,应使用不同的指针链或分阶段修改 5. 完整利用流程 5.1 利用步骤 修改指针链使其指向printf的返回地址 使用 %n 修改0x7ffde9b11cb8中存储的地址 将0x7ffde9b12317改为0x7ffde9b11ba8(修改两个字节) 修改printf的返回地址 使用高位截断技术精确写入单字节 构造payload如 %0x23c%39$hhn 实现多次利用 通过修改后的指针链多次使用格式化字符串漏洞 最终利用 在printf返回地址下方的栈空间逐步填入one_ gadget 最后将printf的返回地址修改为ret指令地址 从而成功调用one_ gadget 5.2 EXP示例 6. 相关题目推荐 [ LitCTF 2025 ]onlyone:与强网拟态题目几乎相同的利用方式 7. 参考资源 一次有趣的格式化字符串漏洞利用 | ZIKH26's Blog 格式化字符串漏洞利用 - WJH's Blog 8. 总结 非栈上格式化字符串漏洞利用的关键在于: 发现并利用栈上现有的多级指针链 掌握高位截断技术实现精确写入 理解并规避同一条指针链上不能连续使用$符号的限制 通过精心构造的payload实现有限条件下的完整利用