格式化字符串是一种特殊的字符串,常见于printf(),scanf()函数等。 常见的格式化字符串如%d会按照某一种规则读取某一个地方的数据,然后与字符串进行某种方式的交互。所以,当格式化字符串可控时,就有了操作空间。

形式

在题目中,醒目的形式如

printf(buf)

基本可以确认为存在格式化字符串字符串漏洞。同时,也可以确认格式化字符串是否可以修改,比如在data段,也可以达到同样的效果。

原理

由于调用约定的不同,64位程序与32位程序的形式不同,但相似。如题目,如将buf的值传入为%p%p%p,函数期待形如

printf(buf,loc0,loc1,loc2)

的形式,而从对应调用约定中读取信息,但是函数却没有传入信息,就能进行利用。 同时,还可以使用%n$p,如%12$p来显式指定参数的编号

利用

格式化字符串漏洞的利用主要有两个方向,一个是泄露,一个是写入。

泄露

泄露主要利用的方式有%s, %p几种。其中使用%p主要是为了泄露地址,而%s主要是为了泄露地址中的值。

地址泄露

目标是泄露调用约定范围内存在的地址,如上地址,方便后续利用。其中buf的值如下

"%n$p"

其中n的值的获取方法可以通过动态调试查看,自己通过调用约定算,还有打印一连串%p试出来,整体上较为简单。

值泄露

这个方向需要利用%s参数会解引用地址然后输出直到遇到终止条件的特性(多是/x00)。整体泄露的方式与puts的字符串泄露相似。 这种方式的自由度很高。如果我们可以控制,我们甚至可以获取任何地址的值。 方式:

"%n$s"

写入

%n非常神奇,可以使一个本应只是输出函数的printf()拥有了写入能力。其作用方式为将该参数前所成功打印的字符的总数写入到其对应的参数(视作一个指针)所指向的位置。原生写4字节,加入一个h减半,如%hn写入2字节,%hhn写入1字节。 它的使用同样非常自由。若能写入栈,则可以任意修改任意地址的值。

快速填充 可以使用%nc,如%20c,在不足20个字符时,其会自动填充空格,而空格也是算有效输入的。所以,可以通过%nc%n$n来快捷写入(前后n可以不同)。

32位下的实例:往地址0x12345678的低位写入0x17:

"%23c%9$hhn__\x78\x56\x34\x12"

注意在32位下,参数是4字节为单位的读取,故输入的地址要以4字节对齐。 以及地址要求小端序