格式化字符串漏洞小总结(上)
字数 1305 2025-08-24 16:48:16
格式化字符串漏洞详解(上)
前言
格式化字符串漏洞是二进制安全领域中的一个重要漏洞类型,本文将从理论层面详细解析格式化字符串漏洞的原理和利用方法。
格式化字符串基础
格式化字符串的基本格式:
%[parameter][flags][field width][.precision][length]type
关键参数解析
- parameter:
n$,获取格式化字符串中的指定参数 - field width:输出的最小宽度
- precision:输出的最大长度
- length:输出的长度
hh:1-byteh:2-bytel:4-bytell:8-byte
重要type类型
d/i:有符号整数u:无符号整数x/X:16进制unsigned int(x小写,X大写)s:输出以null结尾的字符串c:把int参数转为unsigned char输出p:输出指针值n:不输出字符,但把已成功输出的字符个数写入对应的整型指针参数所指变量%:'%'字面值
漏洞原理
格式化字符串函数根据格式化字符串进行解析,参数个数由格式化字符串控制。当格式化字符串中的格式化字符多于实际提供的参数时,程序会从栈上读取额外的值进行解析,导致信息泄露或内存覆盖。
漏洞利用方法
内存数据泄露
栈上数据泄露
- 使用
%order$p或%order$x获取指定参数对应栈的内存值 - 使用
%order$s获取指定变量对应地址的内容(遇到\x00会截断)
任意地址内存泄露
需要将目标地址写入栈中,然后通过格式化字符串引用该地址。
确定偏移
32位程序:
- 参数全部在栈上传递
- 偏移计算直接对应栈位置
64位程序:
- 前6个参数通过寄存器传递(rdi, rsi, rdx, rcx, r8, r9)
- 第7个参数开始才在栈上
- 偏移计算需要考虑寄存器参数
写入地址到栈
32位格式:
<address>%<order>$s
改进版(添加标记):
<address>@@%<order>$s@@
64位格式:
- 地址不能放在开头(高位为0)
- 需要处理字节对齐问题
- 示例:
payload = '@@%11$s@@'.ljust(0x28,'a') + p64(0x000000601028)
小技巧
-
泄露libc基址:
- 通过
__libc_start_main + x的返回地址 - 示例:
payload = '%21$p'.ljust(0x8,'a') libc_base = int(io.recv(12),16) - 240 - libc.symbols['__libc_start_main'] - 通过
-
泄露栈地址:
- 利用栈上接近ESP的数据
- 多层函数调用时留下的EBP值
内存覆盖
任意地址覆盖
使用%n转换指示符,将已输出的字符数写入指定地址。
基本格式:
...[overwrite addr]....%[overwrite offset]$n
格式化字符串写入大小控制
%n:写入4字节%hn:写入2字节%hhn:写入1字节
常用%hn和%hhn来精确控制写入值。
单次printf多次写入
示例格式:
%xc%offset1$hn %yc%offset2$hn address address+2
写入值控制脚本
单字节写入:
def fmt_byte(prev, val, idx, byte=1):
result = ""
if prev < val:
result += "%" + str(val - prev) + "c"
elif prev == val:
result += ''
else:
result += "%" + str(256**byte - prev + val) + "c"
result += "%" + str(idx) + "$hhn"
return result
双字节写入:
def fmt_short(prev, val, idx, byte=2):
result = ""
if prev < val:
result += "%" + str(val - prev) + "c"
elif prev == val:
result += ''
else:
result += "%" + str(256**byte - prev + val) + "c"
result += "%" + str(idx) + "$hn"
return result
pwntools fmtstr模块
pwnlib.fmtstr.fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')
参数说明:
offset:控制的第一个格式化程序的偏移量writes:格式为{addr: value}的字典numbwritten:已由printf函数写入的字节数write_size:'byte'、'short'或'int'
总结
本文详细介绍了格式化字符串漏洞的理论基础,包括:
- 格式化字符串的基本语法和关键参数
- 漏洞原理和利用方法
- 内存泄露技术(栈数据和任意地址)
- 内存覆盖技术(包括精确控制写入值)
- 实用脚本和工具
下篇将重点介绍实际CTF题目中的利用技巧和高级利用方法。