格式化字符串漏洞

参考:
利用 - CTF Wiki

原理

格式化字符串函数

可以接受可变数量的参数,

将第一个参数作为格式化字符串,根据其来解析之后的参数。
通俗来讲,格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串。

几乎所有的C/C++程序都会利用格式化字符串函数来输出信息、调试程序、处理字符串
一般来说,格式化字符串在利用的时候主要分为三个部分:

  • 格式化字符串函数
  • 格式化字符串
  • 后续参数,可选

example:

1
printf("Color %s, Number %d, Float %4.2f", "red", 123456, 3.14)

在进入printf函数之前的栈布局:

1
2
3
4
5
6
7
[stack bottom / high address]
...
3.14
123456
addr of "red"
addr of format string: Color %s...
[stack top / low address]

在进入printf之后,函数首先获取第一个参数,一个一个读取其字符会遇到两种情况

  • 当前字符不是%,直接输出到响应标准输出
  • 当前字符是%,继续读取下一个字符
    • 如果下一个字符是%,输出%
    • 如果没字符,报错,也可能仍运行输出%【gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)】
    • 如果有其它字符则根据字符,获取相应的参数,对其进行解析并输出

但是如果我们写成了这个样子:

1
printf("Color %s, Number %d, Float $4.2f");

此时我们可以发现我们并没有提供参数,那么程序会如何运行呢?
程序仍然会运行,而且会将栈上存储格式化字符串地址的高地址方向的三块相应大小的内存区域分别解析为

  1. 解析其地址(内存中的值)对应的字符串
  2. 解析其内容(内存中的值)对应的整型值
  3. 解析其内容(内存中的值)对应的浮点值

对于2,3来说倒也无妨,
但是对于1来说,若内存块中的值是一个不可访问的地址,比如0,那么程序就会因此而崩溃。

如何利用

在上一部分,我们已经看到了格式化字符串漏洞的两个利用手段

  • 使程序崩溃,因为栈上方(高地址/栈底)的内存处存到的地址大概率不可访问,利用%s
  • 查看进程内容,利用%d%f输出栈上的内容

利用手段之泄漏内存

获取我们所想要的内容

  • 泄漏栈内存
    • 获取某个变量的值 %d
    • 获取某个变量对应地址的内存 %s
  • 泄漏任意地址内存
    • 利用GOT表得到libc函数地址,进而获取libc,进而获取其它libc函数地址
    • 盲打,dump整个程序,获取有用的信息。
1
2
3
%n$x
%<位置>$<格式>
%5$x

%n 表示要访问第n个参数(位置参数,整数)(输出函数的第n+1个参数)
$x 指定以16进制格式输出该参数的值
%5$x 表示以十六进制输出第5个参数(输出函数的第6个参数)的值

[!tip] Sum up

  1. 利用%x来获取对应栈的内存,但建议使用%p,可以不用考虑位数的区别
  2. 利用%s来获取变量所对应地址的内容,只不过有零截断
  3. 利用%order$x来获取指定参数的值,利用%order$s来获取指定参数对应地址的内容

泄漏任意地址内存

控制我们所要泄漏的变量的地址

获取某个指定地址addr的内容

1
addr%k$s

格式符

格式符 用途
%d 十进制有符号整数
%u 十进制无符号整数
%x 十六进制
%o 八进制
%f 浮点数
%e 科学计数法
%c 单个字符
%p 指针地址,支持ascii或转义字符(\n)
%s 字符串,需以\0结尾,否则可能越界
%n 写入已输出的字符数

【PWN · 格式化字符串|劫持fini_array|劫持got表】[CISCN 2019西南]PWN1-CSDN博客

本文在一定程度上参考了各种推文、wiki


格式化字符串漏洞
http://example.com/2025/02/26/格式化字符串漏洞/
作者
yvyvSunlight
发布于
2025年2月26日
许可协议