{
char buf[8];
sprintf(buf,"AAAA%3s","XXXXXXXX");
printf("%s\n",buf);
}
将会发生什么?
缓冲区有8个字符空格,只剩下3个空闲字符,但“XXXXXXXX”长度为8个字符。
我在Windows 7上使用Visual Studion 2008进行测试。结果,打印的程序:AAAXXXXXXX,发生了运行时错误。
答案 0 :(得分:10)
考虑在你的,更重要的是,类似的案例中发生的事情是很有意义的。正如其他海报所指出的那样,它会调用UB。这可能是真的。然而,世界并不仅仅因为有人没有明确接下来应该发生什么而停止。接下来会发生什么物理,可能是主要安全漏洞。
如果您的字符串XXX...
来自不受控制的来源,那么您就非常接近于生成缓冲区溢出漏洞。
(1)你的堆栈通常向后“增长”,即地址越小,堆栈就越多。
(2)字符串期望存储属于该字符串的字符,以便在字符n之后存储字符n + 1。
(3)当你调用一个函数时,返回地址,即函数返回后要执行的指令的地址,被推送到堆栈(通常是其中之一)。
现在考虑你的函数的堆栈帧。
|----------------|
| buf [size 8] |
|----------------|
| (func args) |
|----------------|
| (other stuff) |
|----------------|
| return address |
|----------------|
通过找出buf
与堆栈上的返回地址之间的确切偏移是什么,恶意用户可以以XXX...
字符串包含地址的方式操作对应用程序的输入。攻击者只选择不受控制的sprintf
函数将覆盖堆栈上的返回地址。 (注意:如果可以使用,请更好地使用snprintf
)。从而攻击者发起buffer overflow攻击。他可能会使用类似NOP sled technique的内容让您的应用程序为他启动shell。如果您正在编写一个在特权用户帐户下运行的应用程序,那么您只需向攻击者提供一个一级入口到您的客户系统,ACE一个洞,如果您愿意的话。
您遇到的运行时错误可能是由于覆盖了返回地址。因为基本上用gargabe填充它,CPU跳转的地址可能包含字节序列,解释为程序文本,导致无效的内存访问(或者地址本身已经坏了)。
应该注意的是,一些编译器可以帮助解决这些类型的错误。例如,GCC有-fstack-protector
。我不熟悉这些功能有多好。
答案 1 :(得分:8)
函数sprintf()
将在字符串中写入时写入数组,因此会调用未定义的行为。查看您的代码,它可能会写入堆栈上接下来发生的任何事件的前几个字节,或者导致运行时错误,但不保证该行为。
未定义的行为在字面上意味着anything can happen。这意味着您的代码可能没有任何错误,导致运行时错误,或导致您的计算机爆炸,赢得彩票,使独角兽出现在你的后院,从死里复活希特勒或暗杀美国总统。请不要这样做。
始终确保您的字符缓冲区有足够的空间来容纳您sprintf()
的任何内容 - 加上空终止符的额外字符。一般来说,不要试图乱用不属于你的内存空间。
答案 2 :(得分:6)
您应该尝试在此处使用snprintf()方法described,而不是使用此方法。此方法执行基本相同的功能,但它允许您明确地控制字符数,防止未定义的行为(这是一件好事)
snprintf保证不写 超过str的字节大小,所以使用 它可以帮助避免风险 缓冲区溢出 Wiki
答案 3 :(得分:5)
您的格式字符串中有错误/拼写错误。而不是"AAAA%3s"
它应该是"AAAA%.3s"
。场[最小]宽度和场精度非常不同。前者设置字段将扩展为填充的最小字节数。后者(对于字符串)设置最大将输出的字节数;字符串的其他字节既不会被检查也不会被复制到输出中。
答案 4 :(得分:1)
“In silico”是完全正确的,但可能是因为计算机内核比以前更聪明,它不会让你写入char buf[4];
之后的内容并且会杀死你的程序并发出分段故障信号。
这很好,因为如果下一段内存是非常重要,那么它将保持安全,而不是让您的计算机崩溃。
并且正如他所说从不这样做。
答案 5 :(得分:1)
sprintf()函数有助于无限制地复制文本,从而使缓冲区容易受到溢出攻击。当进程尝试存储的数据超过了fixe-length缓冲区中允许的边界时,就会发生缓冲区溢出。
在发现溢出漏洞后,攻击者将观察呼叫如何获取其用户输入,并通过函数调用进行路由。然后攻击者可以写一个exploit,这会让软件做一些不能正常做的事情。这可以从简单地崩溃机器到注入代码,以便攻击者可以远程访问机器。
如果使用不当,C中的许多功能会导致错误。一些功能提供了替代解决方案:
Avoid prefer
sprintf snprintf
vsprintf vsnprintf
strcat strlcat
strcpy strlcpy
strncat strlcat
strncpy strlcpy
来源:ECSP-Secure Programmer。
答案 6 :(得分:0)
会发生什么? ...
{ char buf[8]; sprintf(buf,"AAAA%3s","XXXXXXXX"); printf("%s\n",buf); }
在Windows上,您应该使用sprintf_s
。代码应该通过审计失败,因此不应该将其投入生产。有关参考,请参阅Microsoft的Writing Secure Code (Developer Best Practices)。特别是,请参阅第5章。
在Linux上,如果编译器和平台提供FORTIFY_SOURCE,那么上面的代码应该调用abort()
。许多现代Linux平台都支持它,所以我期待它。
FORTIFY_SOURCE使用高风险函数的“更安全”变体,例如memcpy
,strcpy
和sprintf
。当编译器可以推导出目标缓冲区大小时,编译器会使用更安全的变体。如果副本超过目标缓冲区大小,则程序将调用abort()
。
要禁用FORTIFY_SOURCE进行测试,您应该使用-U_FORTIFY_SOURCE
或-D_FORTIFY_SOURCE=0
编译该程序。
要解决@ prng关于可移植性的评论,strcpy_s
,printf_s
,sprintf_s
和朋友是标准C.请参阅ISO/IEC TR 24731-1。
如果Linux和glibc上缺少的功能是一个问题,那么你可以通过预处理器宏来消除由于glibc瘫痪造成的差异。无论Linux和glibc做什么,代码都不符合Windows平台的最低标准。