非栈上格式化字符串的利用方法
字数 1663 2025-08-29 22:41:24
非栈上格式化字符串漏洞利用技术详解
1. 前言
本文详细讲解非栈上格式化字符串漏洞的利用方法,以第六届强网拟态线下赛的格式化字符串Pwn题为例。重点介绍两个关键技术点:多级指针链利用和高位截断技术,并探讨格式化字符串利用中的一些限制条件。
2. 题目分析
2.1 题目基本情况
- 程序提供一个栈地址
- 给予一次非栈上格式化字符串的利用机会
- 程序直接使用exit退出,无法直接控制main函数的返回地址
2.2 面临的挑战
- 格式化字符串缓冲区不在栈上,无法直接在栈中写入目标地址进行修改
- 只有一次利用机会,需要精心构造payload
3. 关键技术点
3.1 多级指针链利用
当格式化字符串不在栈上时,可以通过修改栈上现有的多级指针链(二重/三重指针)来间接控制目标内存。
3.1.1 指针链的发现
通过gdb调试,在call printf指令处查看栈内容,可以发现栈中存在多级指针链:
0x7ffde9b11bd8 -> 0x7ffde9b11cb8 -> 0x7ffde9b12317
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 = 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 利用步骤
-
修改指针链使其指向printf的返回地址
- 使用
%n修改0x7ffde9b11cb8中存储的地址 - 将0x7ffde9b12317改为0x7ffde9b11ba8(修改两个字节)
- 使用
-
修改printf的返回地址
- 使用高位截断技术精确写入单字节
- 构造payload如
%0x23c%39$hhn
-
实现多次利用
- 通过修改后的指针链多次使用格式化字符串漏洞
-
最终利用
- 在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. 参考资源
- 一次有趣的格式化字符串漏洞利用 | ZIKH26's Blog
- 格式化字符串漏洞利用 - WJH's Blog
8. 总结
非栈上格式化字符串漏洞利用的关键在于:
- 发现并利用栈上现有的多级指针链
- 掌握高位截断技术实现精确写入
- 理解并规避同一条指针链上不能连续使用$符号的限制
- 通过精心构造的payload实现有限条件下的完整利用