sprintf function's buffer overflow?
Asked Answered
P

7

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

What will happen?

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

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

Putupon answered 26/11, 2010 at 3:11 Comment(0)
R
16

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 physically happens next, may well be a major security hole.

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

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

(2) Strings expect the characters belonging to that string to be stored so that character n+1 is stored after character 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).

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 buf and 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 sprintf function will overwrite the return address on the stack. (NB: Better use snprintf if it's available to you). Thereby the attacker mounted a buffer overflow attack. He might use something like the NOP sled technique to have your application start a shell for 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 ACE hole, if you will.

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).

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.

Rebound answered 26/11, 2010 at 3:38 Comment(2)
"GCC, for example, has the -fstack-protector. I'm not familiar with how good those features are." — you should never have to find out :PTrine
-fstack-protector is good, but information leakage from somewhere else in the code can allow a bypass of any stack canary. Stack canary brute forcing can also be a viable source of bypass in some situationsMember
C
13

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.

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.

Crispa answered 26/11, 2010 at 3:13 Comment(0)
H
8

Instead of using this method you should try using the snprintf() method as described here. 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 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

Hilel answered 26/11, 2010 at 3:19 Comment(0)
L
5

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 minimum number of bytes the field will expand to fill. The latter (for strings) sets the maximum number of bytes that will be output; additional bytes of the string are neither inspected nor copied to the output.

Loxodrome answered 26/11, 2010 at 3:29 Comment(0)
D
5

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.

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:

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

Source: ECSP-Secure Programmer.

Dorthydortmund answered 26/11, 2010 at 4:11 Comment(0)
P
1

"In silico" is quite right, but likely because computer kernels are much smarter than 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.

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

And also as he said NEVER do this.

Photogrammetry answered 26/11, 2010 at 3:21 Comment(5)
Really? What if the next thing on the stack is a writable variable? Why would the kernel prevent you from writing to that? Are are you claiming that the kernel knows where the programming language's variables start and end?Fairground
The kernel has no notion of where a buffer on the stack starts or ends. See my post on buffer overflow exploits.Rebound
@EJP that is an interesting point. I do not know what will happen in that case. And yes, the kernel does know a bit about what processes are allowed to write to what areas of memory. If you really want to know try it. Try to write to beyond the buffer buf[10] = 10 In my case I get a segfault every time. (on windows I get "This program has encountered an error and need to quit" Then it asks me if I want to send an error report.Photogrammetry
That doesn't prove anything unless there was another variable in the next position. Was there? And how did the operating system know where the variable ended? or was it just the last thing in the stack?Fairground
I have no idea about the memory structure and set-up but it seems too consistent to be a coincidence. II have tried it on different programs on different times. I may be wrong and if so I am sorry for my mistake.Photogrammetry
M
-1

what will happen? ...

{     
    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.


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.

FORTIFY_SOURCE uses "safer" variants of high risk functions like memcpy, strcpy and 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().

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


To address @prng's comment regarding portability, strcpy_s, printf_s, sprintf_s and friends are standard C. See 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.

Marchellemarcher answered 12/9, 2014 at 13:31 Comment(3)
-1 "On Windows, you are supposed to use sprintf_s." ... and lose all hopes of portability? The single biggest advantage of C versus many other languages is its portability!Marko
ok @jww; -1 revoked. Anyway there differences between the description in MSDN and the description in the (draft) Standard.Marko
Way late here, but besides the gratuitous "crippled gcc", Microsoft's implementation of the OPTIONAL Annex K is still not portable: "Microsoft Visual Studio implements an early version of the APIs. However, the implementation is incomplete and conforms neither to C11 nor to the original TR 24731-1. ... As a result of the numerous deviations from the specification the Microsoft implementation cannot be considered conforming or portable." It's almost as if Microsoft implemented vendor lock-in here....Innominate

© 2022 - 2024 — McMap. All rights reserved.