I think , therefore I am

first

只要能掌握printf第一个函数控制权,就能达到相当强大的威力

先看类型
%d:用于格式化有符号十进制整数。
%u:用于格式化无符号十进制整数。
%x、%X:分别用于格式化无符号十六进制整数,%x 输出小写字母,%X 输出大写字母。
%o:用于格式化无符号八进制整数。
%f:用于格式化浮点数。
%c:用于格式化字符。
%s:用于格式化字符串。
%e、%E:用于格式化浮点数,以科学计数法表示,%e 输出小写字母 e,%E 输出大写字母 E。
%g、%G:根据值的大小,自动选择使用 %f 或 %e(%g 输出小写字母 e,%G 输出大写字母 E)。
%%:用于输出百分号

到目前笔者用到的只有这三个:

%p 用于输出指针的值(地址)。在格式化字符串漏洞中,利用 %p 来泄露程序中的内存地址
举个例子:
#include <stdio.h>
int main() {
int value = 42;
int *ptr = &value;
// 输出指针的值
printf(“Pointer value: %p\n”, (void *)ptr);
return 0;
}
在这个例子中,%p 会输出指针 ptr 的值,即变量 value 在内存中的地址

%s在格式化字符串漏洞中可以被滥用用来泄露栈上的数据,即栈上的地址所对应的参数
还是上个例子,%s输出的即是变量value

%n 用于在格式化字符串的输出中写入已经输出字符的数量到一个指定的整数变量中。
0x1234567,%9n$s 即是将4赋到printf的第9个变量中,因为0x12345678是四个字节,n写入4字节,hn2字节,hhn1字节。

接下来看例题:

程序向用户使用read函数读入了一个buf,然后使用printf函数直接打印了出来,也就是说,我们掌握了printf第一个参数的控制权,那么这里就存在一个格式化字符串漏洞。再观察程序,x的原赋值为3(点一下x进去看就好了,这里就不截图了),我们只要把x改为4,程序就开始系统调用,执行/bin/sh
gdb调试一下,通过计算我们写入的数据距离ebp44个字节,再加上返回地址的四个字节,就是48个字节,但这里要注意,就是printf的第一个参数并不是格式化字符串的第一个参数,举例:0x12345678,%s%n,在这里0x12345678是printf的第一个参数,而%s是格式化字符串的第一个参数,所以还要减去0x1234567所占用的四个字节(两个数字占用一字节),48-4=44,因为这是32位的程序,所以一个变量占4个字节,44/4=11,也就是

再提一下为什么要用ebp减去我们输入参数的地址,因为在栈中,ebp上方便是父函数的返回地址,我们通过将x(要修改的变量)的地址填入这个返回地址,程序便跳转到x的地址,再通过%n便可以修改x的值
接下来撰写payload,这里使用的地址是在ida中查询到的x的地址

second