Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

understanding the dangers of sprintf(...)

Tags:

c++

printf

OWASP says:

"C library functions such as strcpy (), strcat (), sprintf () and vsprintf () operate on null terminated strings and perform no bounds checking."

sprintf writes formatted data to string int sprintf ( char * str, const char * format, ... );

Example:

sprintf(str, "%s", message); // assume declaration and 
                             // initialization of variables

If I understand OWASP's comment, then the dangers of using sprintf are that

1) if message's length > str's length, there's a buffer overflow

and

2) if message does not null-terminate with \0, then message could get copied into str beyond the memory address of message, causing a buffer overflow

Please confirm/deny. Thanks

like image 607
Kevin Meredith Avatar asked Sep 07 '10 21:09

Kevin Meredith


People also ask

Is Snprintf safer than sprintf?

Snprintf is more secure and if the string number overruns the characters, the string is protected in the buffer even if the format is different. It works with n characters and nth location and hence the location of null character is not considered at all. Allocation of null character memory is preserved in sprintf.

What is the purpose of a sprintf?

The sprintf() function writes a formatted string to a variable. The arg1, arg2, ++ parameters will be inserted at percent (%) signs in the main string. This function works "step-by-step".

What is the difference between sprintf and Snprintf?

Both will give the result you want, but snprintf is more generic, and will protect your string from overruns no matter the format string given. In addition, because snprintf (or sprintf for that matter) adds a final \0 , you should make the string buffer one byte bigger, char buff[MAXLEN + 1] .

Does sprintf add null terminator?

In other words, sprintf and snprintf always append the null byte, but printf does not, so your example isn't applicable.


4 Answers

You're correct on both problems, though they're really both the same problem (which is accessing data beyond the boundaries of an array).

A solution to your first problem is to instead use std::snprintf, which accepts a buffer size as an argument.

A solution to your second problem is to give a maximum length argument to snprintf. For example:

char buffer[128];

std::snprintf(buffer, sizeof(buffer), "This is a %.4s\n", "testGARBAGE DATA");

// std::strcmp(buffer, "This is a test\n") == 0

If you want to store the entire string (e.g. in the case sizeof(buffer) is too small), run snprintf twice:

int length = std::snprintf(nullptr, 0, "This is a %.4s\n", "testGARBAGE DATA");

++length;           // +1 for null terminator
char *buffer = new char[length];

std::snprintf(buffer, length, "This is a %.4s\n", "testGARBAGE DATA");

(You can probably fit this into a function using va or variadic templates.)

like image 92
strager Avatar answered Oct 21 '22 02:10

strager


Both of your assertions are correct.

There's an additional problem not mentioned. There is no type checking on the parameters. If you mismatch the format string and the parameters, undefined and undesirable behavior could result. For example:

char buf[1024] = {0};
float f = 42.0f;
sprintf(buf, "%s", f);  // `f` isn't a string.  the sun may explode here

This can be particularly nasty to debug.

All of the above lead many C++ developers to the conclusion that you should never use sprintf and its brethren. Indeed, there are facilities you can use to avoid all of the above problems. One, streams, is built right in to the language:

#include <sstream>
#include <string>

// ...

float f = 42.0f;

stringstream ss;
ss << f;
string s = ss.str();

...and another popular choice for those who, like me, still prefer to use sprintf comes from the boost Format libraries:

#include <string>
#include <boost\format.hpp>

// ...

float f = 42.0f;
string s = (boost::format("%1%") %f).str();

Should you adopt the "never use sprintf" mantra? Decide for yourself. There's usually a best tool for the job and depending on what you're doing, sprintf just might be it.

like image 43
John Dibling Avatar answered Oct 21 '22 01:10

John Dibling


Yes, it is mostly a matter of buffer overflows. However, those are quite serious business nowdays, since buffer overflows are the prime attack vector used by system crackers to circumvent software or system security. If you expose something like this to user input, there's a very good chance you are handing the keys to your program (or even your computer itself) to the crackers.

From OWASP's perspective, let's pretend we are writing a web server, and we use sprintf to parse the input that a browser passes us.

Now let's suppose someone malicious out there passes our web browser a string far larger than will fit in the buffer we chose. His extra data will instead overwrite nearby data. If he makes it large enough, some of his data will get copied over the webserver's instructions rather than its data. Now he can get our webserver to execute his code.

like image 4
T.E.D. Avatar answered Oct 21 '22 01:10

T.E.D.


Your 2 numbered conclusions are correct, but incomplete.

There is an additional risk:

char* format = 0;
char buf[128];
sprintf(buf, format, "hello");

Here, format is not NULL-terminated. sprintf() doesn't check that either.

like image 3
MSalters Avatar answered Oct 21 '22 01:10

MSalters