0x01. 前置栈知识

通过前面的函数调用细节以及栈中数据的分布情况,我们可以发现,局部变量是在栈中挨个排放的,如果这些局部变量中有数组之类的缓冲区,并且程序存在数组越界的问题,那么越界的数组就有可能破坏栈中相邻变量的值,进而破坏EBP的值、返回地址等重要数据。

![[Pasted image 20260325082234.png]]

0x02. 格式化字符串漏洞原理

首先,什么是格式化字符串呢,举个简单的例子:

1
printf("CTF(Capture the flag) is DEAD!");

执行该函数后将返回字符串:CTF(Capture the flag) is DEAD!
printf函数的第一个参数就是格式化字符串,它来告诉程序将以什么格式输出。
printf()函数的一般形式为:printf("format" , 输出列表),我们对format比较关心,它的结构是:% {标志}{输出最小宽度}{.精度}{长度}类型,其中跟格式化字符串漏洞有关系的主要有以下绩点:

  1. 输出最小宽度:用十进制整数来表示输出的最少位数。若实际位数多于定义的宽度,则按实际位数输出,若实际位数少于定义的宽度,则补以空格或0。
  2. 类型:
    Text
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    %c : 输出字符,配上%n可用于向指定地址写数据

    %d:输出十进制整数,配上%n可用于向指定地址写数据

    %x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数据,%i$lx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。

    %p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。

    %s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。

    %n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100×10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而&$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可通过%$hn或%$hhn来适时调整。

    %n是通过格式化字符串漏洞改变程序流程的关键方式,而其他格式化字符串参数可用于读取信息或配合%n写数据。

Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

占位符 含义

%d 以十进制形式输出整数
%u 以十进制形式输出无符号整数
%x 以十六进制形式输出整数(小写字母)
%X 以十六进制形式输出整数(大写字母)
%o 以八进制形式输出整数
%f 以浮点数形式输出实数
%e 以指数形式输出实数
%g 自动选择%f或者%e输出实数
%c 输出单个字符
%s 输出字符串
%p 输出指针的地址
%n 将已经输出的字符数写入参数