14

There are many security risks coming from having close contact to the hardware as opposed to using well-tested and proved APIs from high level programming languages. It is much easier to cause a buffer overflow in C than in a language such as Java.

What are the risks or vulnerabilities (e.g. buffer overflows) that every C programmer should be aware of (I.E. vulnerabilities relevant to C programmers)? What problems could these lead to? How to avoid them, and what are common mistakes causing these to occur in programs?

Anto
  • 11,157
  • 13
  • 67
  • 103
  • What about this list: https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project What more is needed than this? – S.Lott Apr 27 '11 at 17:36
  • 2
    @S.Lott: It seems to be very much about security issues in web development. There seems to be more resources on that in general than what I am actually asking for, it seems. – Anto Apr 27 '11 at 17:37
  • @Anto: Please **update** the question to distinguish between all the resources on security and the security you're asking about. – S.Lott Apr 27 '11 at 20:50
  • @S.Lott: I'm not sure what you mean. I ask for security which is of importance to most C programmers, that is, things like buffer overflows and other things which are possible in C. – Anto Apr 27 '11 at 20:53
  • @Anto: "There seems to be more resources on that [web security?] in general than what I am actually asking for" Seems to say that you're asking about some security that isn't web security. True? If so, please **update** the question to explain what you're looking for. False? Then you **are** asking about web security, in which case, why isn't the OWASP list mentioned in your question? – S.Lott Apr 27 '11 at 20:55
  • @S.Lott: I'm asking for security of relevance to C programmers. That does *not* include web security as far as I am concerned. – Anto Apr 27 '11 at 21:02
  • @Anto: Please **update** the question to distinguish between security and the security you're asking about. Please make sure that the question is complete and clear. – S.Lott Apr 27 '11 at 21:11
  • Realistically, you _cannot_ avoid security vulnerabilities in C, it was never designed to be secure and predictable. If security is a priority, do not use (standard) C. There are some restricted variants of C which can be formally checked, that could be an alternative. – sleske Jan 12 '16 at 09:47

4 Answers4

13

Buffer overflows are a big one. Nothing in C is range-checked by default, so it's very easy to overwrite a buffer. There's a standard library function, gets(), that cannot be stopped from overflowing the buffer, and should almost never be used.

There are some implementation-level techniques to hinder exploitation, such as scrambling heap blocks, but that won't stop buffer overflows in local buffers, which can often do interesting things like change the address a function will return to.

There is no good general solution in C. Many library functions have versions that will limit the amount they will write. although calculating that can be clumsy. There's software that can detect heap buffer overflows in test, as long as the appropriate test is run, and stack overflow will often show up as a crash in testing. Other than that, it's a matter of careful coding and code review.

A related issue is the problem of writing into a buffer too small by one character, forgetting that a C string that's n characters long requires n+1 characters in memory, because of the '\0' terminator. If the attacker can manage to store a string without the terminator, any C function expecting a string will continue processing until it hits a zero byte, which could result in copying or outputting more information than is desired (or hitting protected memory for a DOS attack). The solution, again, is awareness, care, and code reviews.

There's another risk with the printf() family. If you ever write char * str; ... printf(str);, you're setting yourself up for problems if str contains a '%' when printed. The %n format directive allows printf() to write into memory. The solution is printf("%s", str); or puts(str);. (Also, use the C99 snprintf() instead of sprintf().)

Using unsigned integers, particularly as loop indexes, can cause problems. If you assign a small negative value to an unsigned, you get a large positive value. That can undermine things like processing only N instances of something, or in limited functions like strncpy(). Examine all unsigned integers. You might want to avoid unsigned short, since a large value in one of those will convert to a large positive value in an int.

Don't forget that a character constant, in C, is actually an int. Writing something like char c; while((c = getchar()) != EOF) ... can easily fail, since EOF won't be representable in a char.

There's a lot more characteristic C mistakes I can think of, but these could cause security problems.

David Thornley
  • 20,238
  • 2
  • 55
  • 82
  • There's no need to use `printf("%s", str)` for a naked string when `puts(str)` will do the same job. – Blrfl Apr 27 '11 at 20:22
  • @Blrfl but `puts` appends a newline character while `printf` does not. –  Oct 28 '11 at 19:05
  • Could also do `fputs(str, stdout)`, which doesn't. – Blrfl Oct 28 '11 at 19:15
  • As to integer overflow: Using signed ints is no solution, as overflowing them will cause UB. The only (painful) solution is to either formally prove that you will never overflow, or check at runtime (but check correctly, which is also tricky without overflowing in the check). – sleske Jan 12 '16 at 09:44
  • @DavidThornley: C11 & C++14 standard removed gets() function from standard library due to its dangerousness. – Destructor Jan 28 '16 at 14:30
  • @sleske GCC has awesome buitins that do the job for you ! – kiwixz Mar 27 '16 at 11:19
  • Well, the remedy for the buffer-overflowing problems is simple: Allocate your buffers to fit. Never assume that some size will never be exceeded. This is also made easy by glibc functions like `asprintf()` which also allocate their result buffer to fit. With that, 99.9% of your buffer errors are gone. – cmaster - reinstate monica Aug 26 '16 at 06:29
  • @kiwixz Can you tell me some of the gcc builtin functions to mitigate these vulnerabilities? – strikersps Feb 20 '20 at 20:21
  • @strikersps I was probably referencing what you can find in [gcc docs](https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html), but you may also be interested by [this flags](https://stackoverflow.com/a/3679149/3494492). – kiwixz Feb 21 '20 at 00:26
5

Some of the C-specific risks include: buffer overflows, formatting string attacks and integer overflows.

Nemanja Trifunovic
  • 6,815
  • 1
  • 26
  • 34
  • 1
    There's nothing C-specific about buffer overflows - any language with pointers can have this. Integer overflows apply to just about any language, and can easily happen in managed code too. – Steve Apr 27 '11 at 17:55
  • 1
    @Steve, its not really pointers that cause that problem but how the language doesn't enforce array bounds. – Doug T. Apr 27 '11 at 18:06
  • 2
    @Steve the question was not asking about stuff that only concerns C, but something that C programmers should be aware of. – AttackingHobo Apr 27 '11 at 18:16
  • 1
    @Steve: C is unusually susceptible to buffer overflows, partly because of the lack of support for range-checking, and the number of library functions that will happily overflow buffers for you. – David Thornley Apr 27 '11 at 18:17
  • I understand the question is asking about C in particular, but I think it is worth clarifying in case the answer is read out of context that these risks are more general. In particular developers of managed code are (IMHO) far too complacent about security, and integer overflows in particular affect most languages. – Steve Apr 27 '11 at 18:45
  • @Steve: How can you have an integer overflow in i.e. Lisp? – Nemanja Trifunovic Apr 27 '11 at 19:13
4

Here is an easy to miss risk that can cause problems which will take hours to fix.

Consider the following code, which will compile with no problems.

if(lpstr_current_state = CONST_EMERGENCY_STATE_HOLY_CRAP)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}

When you check to see if lpstr_current_state is in CONST_EMERGENCY_STATE_HOLY_CRAP you're actually assigning. It is better to always put the constant variable on the left. When you put the constant on the left, then the compiler will fail because you can't assign a value to a variable.

if(CONST_EMERGENCY_STATE_HOLY_CRAP = lpstr_current_state)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}

Then you can easily say to yourself, "Holy crap, that could have been bad", while fixing the code to read ...

if(CONST_EMERGENCY_STATE_HOLY_CRAP == lpstr_current_state)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}
Kristofer Hoch
  • 408
  • 2
  • 6
0

There is just one security risk: The fact that there are people outside who will do their best to catch any vulnerability in your software and to exploit it for their own gain. Everything else follows from there.

So when you think "nobody in their right mind would ... ", then you need to think immediately "except someone who wants to hack into other people's computers would do exactly that".

The biggest consequence is that whenever you react to events outside (for example, by processing data delivered from the outside), you must assume that this data was under control of your worst enemy.

gnasher729
  • 42,090
  • 4
  • 59
  • 119
  • While I would agree with paragraph two and three, putting all the blame on the attacker is a bit thick in my eyes. It always takes two for a successful attack: A programmer who screws up, and an attacker who catches the programmer in the act. However, the security vulnerability is there *before* the attacker can exploit it. And for that, the programmer is to be blamed. – cmaster - reinstate monica Aug 26 '16 at 06:35