格式化输出函数
%[parameter][flags][width][.precision][length]type
其中只有type是必选的
- parameter是一个POSIX扩展,用于指定某个参数,例如%3$p表示输出后面的第三参数
- flags用来调整输出和打印的符号、空白、、小数点等
- width用来指定输出字符的最小个数
- .precision用来指定打印符号个数、小数位数或者有效数字个数
- length用来指定参数的大小
相关type
%d:整形
%u:无符号整形
%x:十六进制
%s:字符串
%c:字符
格式化字符串漏洞
原理
x86结构下,格式化字符串的参数是通过栈传递的
根据_cdecl调用约定,将参数从右向左压入栈中,进入printf之后,从第一个参数开始一次读取一个字符,如果不是%则字符被直接复制到输出,否则读取相应的参数并解析输出
如果程序有输入并且有格式化字符串的时候我们可以获取栈中指定的数据
漏洞利用
读取栈上数据
可以输入%08x~%08x~%08x作为格式化字符串取出栈中的三个数据输出
也可以使用%<arg#>$<format>输出栈上相对于格式化字符串指定位置的参数
任意地址内存泄漏
可以使用%s泄露出参数所指向内存的数据,程序会将它作为一个ASCII字符串处理,知道遇到一个空字符
可以写入一个地址,然后写入%<改地址相对格式化字符串在栈上的偏移>$s就可以输出该地址指向的内容了
覆盖栈数据
%n将当前已经成功写入流或者缓冲区的字符个数存储到由参数指定的整数中
x28xcdxffxff%08x%08x%012d%13$n
先解析%13$n找到地址0xffffcd28将其数据修改为0x00000020
任意地址内存覆盖
"AA%15$nA"+"x28xcdxffxff"
开头的AA占两个字节,将地址复制为2
%15$n占5个字节,指向第15个参数,后面的A占一个字节,一共8字节
然后输入要覆盖的地址
%hhn:单字节
%hn:双字节
%n:4字节
%ln:8字节
%lln:16字节