0%

格式化字符串漏洞

关于printf格式化字符串漏洞

关于printf—可变参数函数的参数寻找

  由printf函数的可变参数,很容易得出,printf是一个stdcall的函数,由调用者维护栈帧平衡,那在printf中寻找其参数原理如下:

  • printf第一个参数在调用printf时必定位于栈顶;
  • printf根据其第一个参数,即字符串指针,对其指向的缓冲区进行扫描,每当扫描到一个%,就根据其后续字符判断是否为格式化控制字符;
  • 根据每一个格式化控制字符在字符串缓冲区后进行参数读取;

printf格式化控制字符

  • %d 用于读取10进制数值
  • %x 用于读取16进制数值
  • %p 用于输出16进制数值
  • %s 用于读取字符串值
  • %n 将对应的栈内容当指针,向其地址写入已打印的字符数目

格式化字符串漏洞原理

  由上述printf函数的寻参过程printf无法知道栈上是否放置了正确数量的变量,如果没有足够的变量可供操作,而指针按正常情况下递增,就会产生越界访问。如果在第一个参数,字符串缓冲区中加入大量的控制字符,便可以轻松突破其参数限制,而打印出栈上的数据,以此便可以实现栈上的任意位置读和写。

"%x"-可以十六进制打印出指针后的数值;
"%n"-将前面的所有字符串写入栈的对应位置内容指向的内存

stack_overflow
这两个控制字符便是实现任意位置读写的关键。同时,printf还有一个有意思的符号:$,可以用于控制符中直接跳过指定数量的栈区域,如%{n}$x即会打印出栈上第n个参数的内容;%{n}$n会直接跳跃到第n个栈区,将其作为地址写入对应内存。

 如果字符串为%4$p,则会打印出0x41414141,而如果字符串为%4$n,则会将0x41414141当作一个地址,向其中写入已打印的字符数。而此内容位于buff中,可由我们自己构造,也就意味着,如果想向某个地址写入内容,则通过特殊构造,将buff对应位置写入目标地址,即可实现任意地址写。

 对于写任意内容的话,由于%n写入的是打印的字符数,也就是说,如果字符串为%1234x%4$n,则会向第四个栈区对应内存位置写入1234,

至此,得以实现任意地址的读和任意地址写任意值。

常规题目解法

见博客hitcon-training lab7

复杂变体

见博客hitcon-training lab9

更多的情况参考ctf-wiki fmtstr