Windows系统中的“格式字符串漏洞”究竟是什么,它是如何工作的,以及如何防范它?
答案 0 :(得分:4)
最简单的格式字符串攻击是:
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__)
来做同样的事情。
答案 1 :(得分:2)
在这个伪代码中,用户输入一些要打印的字符,如“hello”
string s=getUserInput();
write(s)
按预期工作。但由于write可以格式化字符串,例如
int i=getUnits();
write("%02d units",i);
输出:“03单位”。如果用户在第一个位置写了“%02d”...如果堆栈上没有参数,那么将获取其他内容。那是什么,如果这是一个问题取决于程序。
一个简单的解决方法是告诉程序输出一个字符串:
write("%s",s);
或使用另一种不尝试格式化字符串的方法:
output(s);
一个link到维基百科的更多信息。