Windows 系统中的“格式字符串漏洞”到底是什么,它是如何工作的,我该如何防范?
2 回答
最简单的格式字符串攻击是这样的:
char buffer[128];
gets(buffer);
printf(buffer);
那里也存在缓冲区溢出漏洞,但重点是:您正在将不受信任的数据(来自用户)传递给printf
使用该参数作为格式字符串的(或其表亲之一)。
也就是说:如果用户输入“%s”,你就有了信息泄露漏洞,因为printf
会将用户输入视为格式字符串,并尝试将堆栈中的下一个内容作为字符串打印。就好像你的代码说printf("%s");
. 由于您没有将任何其他参数传递给printf
,因此它将显示任意内容。
如果用户键入“%n”,则可能会受到特权提升攻击(至少是拒绝服务攻击),因为 %n 格式字符串会导致printf
将目前打印的字符数写入下一个位置在堆栈上。因为你没有给它一个放置这个值的地方,它会写到任意的地方。
printf
这一切都很糟糕,这也是您在使用和堂兄弟时应该非常小心的原因之一。
你应该做的是:
printf("%s", buffer);
这意味着用户的输入永远不会被视为格式字符串,因此您可以免受特定攻击向量的影响。
在 Visual C++ 中,您可以使用__Format_string
注释告诉它验证printf
. %n
默认情况下是不允许的。在 GCC 中,您可以__attribute__(__printf__)
用于同样的事情。
在这个伪代码中,用户输入一些要打印的字符,比如“hello”
string s=getUserInput();
write(s)
这按预期工作。但是由于 write 可以格式化字符串,例如
int i=getUnits();
write("%02d units",i);
输出:“03 个单位”。如果用户首先写了“%02d”怎么办......因为堆栈上没有参数,所以会获取其他东西。那是什么,以及这是否是一个问题取决于程序。
一个简单的解决方法是告诉程序输出一个字符串:
write("%s",s);
或使用另一种不尝试格式化字符串的方法:
output(s);
包含更多信息的维基百科链接。