简介

格式化字符串函数:格式化字符串函数就是将计算机内存中表示的数据转化为我们人类可读的字符串格式

函数基本介绍
printf输出到 stdout
fprintf输出到指定 FILE 流
vprintf根据参数列表格式化输出到stdout
vfprintf根据参数列表格式化输出到指定 FILE 流
sprintf输出到字符串
snprintf输出指定字节数到字符串
vsprintf根据参数列表格式化输出指定字节到字符串
setproctitle设置argv
syslog输出日志
err、verr、warn、vwarn等

用 print() 为例,它们的第一个参数就是格式化字符串:“Color %s.Number %d.Float %4.2f”,然后printf函数会根据这个格式化字符串来解析

%d - 十进制-输出十进制整数 %s - 字符串-从内存中读取字符串 %x - 十六进制-输出十六进制 %c - 字符-输出字符 %p - 指针-指针地址 %n - 到目前为止所写的字符数

参数如下: %[parameter][flags][field width][.precision][length]type

  • parameter:n$用来获取格式化字符串中的指定参数,比如%3$p,就是获取第三个参数
  • flags:可以为0个或多个,可以是这些字符:+,空格,-,#,0
  • field width:给出显示数值的最小宽度
  • precision:常指明输出的最大长度
  • length指出浮点型参数或整型参数的长度,可以是以下字符
    • hh:输出1byte
    • h:输出2byte
    • l:输出4byte
    • ll:输出8byte
    • 在格式化字符串漏洞利用中我们通常使用hhh,也就是一次性写1字节和一次性写2字节
  • type,也被称为转换说明,可以是如下字符
    • d/i:有符号整型,int
    • u:无符号整型,unsigned int
    • x/X:十六进制unsigned int,x使用小写字母输出,X使用大写字母输出
    • s:输出null结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节
    • c:把int输出转为unsigned char类型输出,格式化漏洞利用中通常使用其输出大量字节
    • p:void*型,输出对应变量的值
    • n:不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量

程序崩溃

攻击方式最简单,只需要输入一段%s就可以 %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s

对于每一个%sprintf()都会从栈上取一个数字,把数字视为地址,然后打印出该地址指向的内存内容,由于不可能获取每一个数字都是地址,所以数字对应的内容可能不存在,或者该地址被保护,那么就会使程序崩溃

泄露内存

# include <stdio.h>

int main(){
    char s[100];
    int a =1,b=0x22222222,c=-1;
    scanf("%s",s);
    printf("%08x.%08x.%08x.%s\n",a,b,c,s);
    printf(s);
    return 0;
}

编译一下:gcc -m32 -fno-stack-protector -no-pie -o fsl 1.c