C语言 sprintf 函数的缓冲区溢出?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4282281/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:12:43  来源:igfitidea点击:

sprintf function's buffer overflow?

cprintfoverflowfortify-source

提问by remainn

{     
    char buf[8];
    sprintf(buf,"AAAA%3s","XXXXXXXX");
    printf("%s\n",buf);
}

what will happen?

会发生什么?

The buffer have 8 characters space and only 3 free characters left, however, "XXXXXXXX" is 8 characters long.

缓冲区有 8 个字符空间,只剩下 3 个可用字符,但是,“XXXXXXXX”有 8 个字符长。

I take a test with Visual Studion 2008 on Windows 7. As a result, the program printed:AAAXXXXXXX, and a run-time error happened.

我在 Windows 7 上用 Visual Studion 2008 测试,结果程序打印:AAAXXXXXXX,出现运行时错误。

回答by dennycrane

It makes a lot of sense to consider what happens in your and, more importantly, similar, cases. As other posters have noted, it invokes UB. That's probably true. However, the world does not stop simply because someone did not define what exactly should happen next. And what physicallyhappens next, may well be a major security hole.

考虑在您的案例中发生的情况,更重要的是,类似的案例中会发生什么是很有意义的。正如其他海报所指出的那样,它调用了 UB。这大概是真的。然而,世界不会仅仅因为有人没有定义接下来应该发生什么而停止。接下来发生的物理事件很可能是一个主要的安全漏洞

If your string XXX...comes from uncontrolled sources, you are very close to generating a buffer overflow vulnerability.

如果您的字符串XXX...来自不受控制的来源,您就非常接近产生缓冲区溢出漏洞。

(1) Your stack typically "grows" backwards, i.e. the smaller the addresses, the more the stack is filled.

(1) 您的堆栈通常向后“增长”,即地址越小,堆栈填充的越多。

(2) Strings expect the characters belonging to that string to be stored so that character n+1 is stored aftercharacter n.

(2) 字符串期望存储属于该字符串的字符,以便字符 n+1 存储字符 n 之后。

(3) When you call a function, the return address, i.e. the address of the instruction that is to be executed after the function returns, is pushed to the stack (among other things, typically).

(3) 当你调用一个函数时,返回地址,即函数返回后要执行的指令的地址,被压入堆栈(通常包括其他内容)。

Now consider a stack frame of your function.

现在考虑您的函数的堆栈框架。

|----------------|
| buf [size 8]   |
|----------------|
| (func args)    |
|----------------|
| (other stuff)  |
|----------------|
| return address |
|----------------|

By finding out what exactly the offset between bufand the return address on the stack is, a malicious user may manipulate input to your application in a way that the XXX...string contains an address of the attacker's choosing at just the point where the uncontrolled sprintffunction will overwrite the return address on the stack. (NB: Better use snprintfif it's available to you). Thereby the attacker mounted a buffer overflowattack. He might use something like the NOP sled techniqueto have your application start a shellfor him. If you were writing an application that ran under a privileged user account, you'd just have provided an attacker with a first-grade entry to your costumer's system, an ACEhole, if you will.

通过找出buf堆栈上的偏移量和返回地址之间的确切偏移量,恶意用户可能会以XXX...字符串包含攻击者选择的地址的方式操纵您的应用程序的输入,而此时不受控制的sprintf函数将覆盖堆栈上的返回地址。(注意:snprintf如果您可以使用,请更好地使用)。从而攻击者发起了缓冲区溢出攻击。他可能会使用类似NOP sled 技术的方法让您的应用程序为他启动一个shell。如果您正在编写一个在特权用户帐户下运行的应用程序,那么您只需为攻击者提供一个访问您客户系统的一级入口,即ACE洞,如果你愿意的话。

Update

更新

The run-time error you experience may well be due to an overwritten return address. Since you filled it with, basically, gargabe, the address the CPU jumped to did probably contain byte sequences that, interpreted as program text, cause an invalid memory access (or the address itself was already bad).

您遇到的运行时错误很可能是由于返回地址被覆盖。因为你基本上用 gargabe 填充它,所以 CPU 跳转到的地址可能包含字节序列,这些字节序列被解释为程序文本,导致无效的内存访问(或者地址本身已经坏了)。

It should be noted that some compilers can help against these kinds of errors. GCC, for example, has the -fstack-protector. I'm not familiar with how good those features are.

应该注意的是,一些编译器可以帮助解决这些类型的错误。例如,GCC 具有-fstack-protector. 我不熟悉这些功能有多好。

回答by In silico

The function sprintf()will write past the array as it writes in the string, and therefore invokes undefined behavior. Looking at your code, it'll probably write over the first few bytes of whatever happens to be next on the stack, or cause a runtime error, but that behavior is not guaranteed.

该函数sprintf()将在写入字符串时越过数组写入,因此调用未定义的行为。查看您的代码,它可能会覆盖堆栈中接下来发生的任何内容的前几个字节,或者导致运行时错误,但不能保证该行为。

Undefined behavior quite literally means anything can happen.That means your code may do nothing wrong, cause a runtime error, or cause your computer to explode, win the lottery, make unicorns appear in your backyard, raise Hitler from the dead or assassinate the President of the United States. Please don't do this.

未定义行为的字面意思是任何事情都可能发生这意味着您的代码可能没有任何错误、导致运行时错误或导致您的计算机爆炸、中彩票、让独角兽出现在您的后院、使希特勒复活或暗杀美国总统。请不要这样做。

Always ensure that your character buffer has enough space to hold whatever you are sprintf()-ing into it plus an extra character for the null terminator. In general, don't try to mess around with memory spaces that are not yours.

始终确保您的字符缓冲区有足够的空间来容纳您正在sprintf()输入的任何内容以及用于空终止符的额外字符。一般来说,不要试图弄乱不属于你的内存空间。

回答by JonVD

Instead of using this method you should try using the snprintf() method as describedhere. This method performs essentially the same function but it allows you to control the number of characters explictly, preventing undefined behaviour (this is a good thing)

您应该尝试使用此处描述的 snprintf() 方法,而不是使用此方法。此方法执行基本相同的功能,但它允许您显式控制字符数,防止未定义的行为(这是一件好事)

snprintf is guaranteed not to write more than size bytes into str, so use of it can help avoid the risk of a buffer overflow Wiki

snprintf 保证不会向 str 写入超过 size 个字节,因此使用它可以帮助避免缓冲区溢出Wiki的风险

回答by R.. GitHub STOP HELPING ICE

You have a bug/typo in your format string. Instead of "AAAA%3s"it should be "AAAA%.3s". Field [minimum] width and field precision are very different. The former sets the minimumnumber of bytes the field will expand to fill. The latter (for strings) sets the maximumnumber of bytes that will be output; additional bytes of the string are neither inspected nor copied to the output.

您的格式字符串中有错误/错别字。而不是"AAAA%3s"它应该"AAAA%.3s"。字段 [最小] 宽度和字段精度有很大不同。前者设置字段将扩展以填充的最小字节数。后者(对于字符串)设置将输出的最大字节数;字符串的附加字节既不检查也不复制到输出。

回答by ArBR

The sprintf()function facilitates unbounded copying of text, in turn leaving the buffer susceptible to overflow attack. A buffer overflow occurs when a process attemps to store more data than the boundaries allow in the fixe-length buffer.

的sprintf()函数有利于文字的无限复制,继而离开缓存容易溢出攻击。当进程试图存储比固定长度缓冲区中允许的边界更多的数据时,就会发生缓冲区溢出。

After discovering overflow vulnerability, attackers will observe how the call obtains its user input and it is routed through the function call. Attackers can then write an exploit, which makes the software do things it would not do normally. This can range from simply crashing the machine to injecting code so that the attacker can gain remote access to the machine.

在发现溢出漏洞后,攻击者将观察调用如何获取其用户输入并通过函数调用进行路由。然后攻击者可以编写一个漏洞利用程序,这使得软件可以做通常不会做的事情。这可以从简单地使机器崩溃到注入代码,以便攻击者可以远程访问机器。

Many functins in C lead to erros if not properly used. Some functions provide alternative solutions:

如果使用不当,C 中的许多 functin 会导致错误。一些函数提供了替代解决方案:

Avoid      prefer
sprintf    snprintf
vsprintf   vsnprintf
strcat     strlcat
strcpy     strlcpy
strncat    strlcat
strncpy    strlcpy

Source: ECSP-Secure Programmer.

来源:ECSP-安全程序员。

回答by Youarefunny

"In silico" is quite right, but likely because computer kernels are much smarter then they used to be, it won't let you write into what comes after char buf[4];and will kill your program and issue a segmentation fault signal.

“In silico”是非常正确的,但可能是因为计算机内核比以前聪明得多,它不会让您写入后面的内容,char buf[4];并且会杀死您的程序并发出分段错误信号。

This is nice because if the next piece of memory is something reallyimportant, it will be kept safe instead of crashing your computer.

这很好,因为如果下一块内存非常重要,它将被安全保存而不是使您的计算机崩溃。

And also as he said NEVERdo this.

而且正如他所说,永远不要这样做。

回答by jww

what will happen? ...

{     
    char buf[8];
    sprintf(buf,"AAAA%3s","XXXXXXXX");
    printf("%s\n",buf);
}

会发生什么?...

{     
    char buf[8];
    sprintf(buf,"AAAA%3s","XXXXXXXX");
    printf("%s\n",buf);
}


On Windows, you are supposed to use sprintf_s. The code should fail an audit, so it should not make it into production. For reference, see Microsoft's Writing Secure Code (Developer Best Practices). In particular, see Chapter 5.

在 Windows 上,您应该使用sprintf_s. 代码应该未通过审核,因此不应将其投入生产。有关参考,请参阅 Microsoft 的编写安全代码(开发人员最佳实践)。具体参见第 5 章。



On Linux, if the compiler and platform provides FORTIFY_SOURCE, then the code above should result in a call to abort(). Many modern Linux platforms support it, so I would expect it.

在 Linux 上,如果编译器和平台提供 FORTIFY_SOURCE,那么上面的代码应该会调用abort(). 许多现代 Linux 平台都支持它,所以我期待它。

FORTIFY_SOURCE uses "safer" variants of high risk functions like memcpy, strcpyand sprintf. The compiler uses the safer variants when it can deduce the destination buffer size. If the copy would exceed the destination buffer size, then the program calls abort().

FORTIFY_SOURCE 使用高风险函数的“更安全”变体,例如memcpystrcpysprintf。当编译器可以推导出目标缓冲区大小时,它会使用更安全的变体。如果副本将超过目标缓冲区大小,则程序调用abort().

To disable FORTIFY_SOURCE for testing, you should compile the program with -U_FORTIFY_SOURCEor -D_FORTIFY_SOURCE=0.

要禁用 FORTIFY_SOURCE 进行测试,您应该使用-U_FORTIFY_SOURCE或编译程序-D_FORTIFY_SOURCE=0



To address @prng's comment regarding portability, strcpy_s, printf_s, sprintf_sand friends are standard C. See ISO/IEC TR 24731-1.

为了解决@prng 关于可移植性的评论strcpy_sprintf_ssprintf_s和 朋友是标准 C。请参阅ISO/IEC TR 24731-1

If the missing functionality on Linux and glibc is a problem, then you can abstract away the differences due to a crippled glibc with preprocessor macros. Regardless of what Linux and glibc does, the code does not meet minimum standards on the Windows platform.

如果 Linux 和 glibc 上缺少的功能是一个问题,那么您可以抽象出由于带有预处理器宏的残缺的 glibc 而造成的差异。不管 Linux 和 glibc 做什么,代码都不符合 Windows 平台上的最低标准。