June 26, 2020

Security Error C

Here is a list of three classic security errors in C I saw during my carrier.

At first, C is a great language: performant, perfect for memory management, and perfect for embedded systems... However, C is an old programming language (The first version appeared in 1972!) and it was created at a period where the security matter wasn't common.

Today, hackers won't hesitate to exploit some classic patterns in order to inject commands or retrieve sensible data! During this post, I'll feature you several attacks with an example of a security error/exploit in C.

Please, note that all those histories don't concern the C language directly (or even the example I'll show)!

Let's see together how a simple inattention can trigger terrible consequences...

1 - Variable overflow error - A classic C Security error.

Concerning this first security error in C, it is a case study. The disaster of fly 501 (Ariane 5). A simple overflow error cost about 370 million dollars and is still responsible of several bug:

On June 4th 1996, "Ariane 5" rocket was launched by the European Space Agency. Unfortunately, this flight finished in an explosion just 37 seconds after its lift-off. How did this happen?

Ariane 5 Explosion GIF | Gfycat

Let's identify this error: This is a Software bug in the "Inertial Reference System" (IRS). In order to gain time, a module used by Ariane 4 had been reexploited in Ariane 5 without testing. However, Ariane 5 used a different environment. It is a simple conversion from 64 bit floating variable to a 16 bit signed integer value which triggered this explosion.

Let's see this kind of error in C with the following code:

#include <stdio.h>

short operationAddOverflow(short a, short b)
{
    return (a+b);
}

int operationAdd(short a, short b)
{
    return (a+b);
}

int main()
{
    short A = 32767;
    short B = 1;
    
    printf("With SHORT: %d + %d = %d\n", A, B, operationAddOverflow(A, B));
    printf("With INT: %d + %d = %d\n", A, B, operationAdd(A, B));
    return 0;
}

A build with GCC does not generate any warning or error. However, things become complicated when the code runs:

With SHORT: 32767 + 1 = -32768
With INT: 32767 + 1 = 32768

What happened here? 32767+ 1 is not supposed to be equal to -32768!

This is a variable overflow case. Indeed, a "short" variable is contained in 16bits, it can be equal to the range [−32,767, +32,767]. As "32,767 + 1" is superior to +32,767, we obtained this case of variable overflow. If I supposed that the MSB (Most Significant Bit) is a signed bit, let's see the binary operation!

   0111 1111 1111 1111
+ 0000 0000 0000 0001
= 1000 0000 0000 0000


Here the "MSB" switched from "0" to "1". This simple modification changes the positive value to a negative one. Of course, the bit manipulation depends on your system.

To fix this issue, don't hesitate to return a value with a type which contains more bits. For instance, you can apply those conversions:

  • char + char ==> short
  • short + short ==> int
  • int + int ==> long long
  • etc...

2 - Buffer Overflow - The vicious security error in C.

In 1988, the Unix utility "finder" was regularly used to know remotely the timestamp of a user connection. However, despite the developers, it helped the first computer worms to be distributed via the Internet: The Morris worm:

  • With "finder" exploit, Morris was able to control the network access.
  • With "sendmail" exploit, the Morris worm was able to send file to a remote system.

At first, the Morris worm wasn't developed to damage computers. However, due to an implementation error in the code, it became dangerous.
It was able to infect computers more than one time. Due to this error, it was able to create new processes. Those processes slowed down the targeted computers (until to be unusable).

Morris Worm on Wikipedia

Now, the source code of Morris Worm is sleeping at the Computer History Museum in a floppy disk:

The Morris Worm Tumblr_muzml3EGPK1rpiyaso1_r1_400

To illustrate the buffer overflow error, let's focus on the "finder" utility. It is a buffer overflow security error.

Now, let's see this kind of error in C:

#include <stdio.h>
#include <string.h>

void myFunction(char *stringReceived)
{
    char str[13];
    char strCorrupted[28] = {0};

    printf("strCorrupted = %s\n", strCorrupted);

    // No check against the buffer overflow
    strcpy(str, stringReceived);

    printf("strCorrupted = %s\n", strCorrupted);
}

int main()
{
    myFunction("Hey it is me? Who am I ? The corrupted!");
    return 0;
}

Here, a buffer of 12 characters had been created (str = "Hello World!" plus null termination (/0)).

At first, let's build this application: No Error/Warning
Secondly, let's run:

strCorrupted =
strCorrupted = Who am I ? The corrupted!

The output looks strange, what happened here? I didn't modify the value of strCorrupted!

You got it, it is a classic case of "stack overflow". I modified the value of "strCorrupted" without any manipulation concerning this buffer directly.

To illustrate this, let's see the stack of "myFunction".
It looks like "str" and strCorrupted" are organized like this:

BEFORE STRCPY:


However, my call of"strcpy" is not protected.
As result, the command has no idea where the copy should stop. Then, the operation gave this:

AFTER STRCPY:

It is one of the most vicious security error in C. The buffer overflow attack can be simpler than we can imagine. Currently, there are still much Softwares hacked by this technique.

For this example, it was a simple string modification.
However, hackers don't hesitate to inject shell commands (or others!). It can be exploited by a simple user action or a simple file modification (with too many characters!).

I can fix my code easily! Compared to "strcpy", "snprintf" can secure the buffer writing process:

#include <stdio.h>
#include <string.h>

void myFunction(char *stringReceived)
{
    char str[13];
    char strCorrupted[28] = {0};

    printf("strCorrupted = %s\n", strCorrupted);

    snprintf(str, 13, "%s", stringReceived);

    printf("strCorrupted = %s\n", strCorrupted);
}

int main()
{
    myFunction("Hey it is me? Who am I ? The corrupted!");
    return 0;
}

Yeah! No more buffer overflow!

3 - Sensible data hard-coded - No protection

In 2010, a malicious computer worm had been discovered by VirusBlokAda: Stuxnet.

Today, analyses says that Stuxnet was active since 2005. This worm focused his attack on the Iranian nuclear central.
But, despite that only Iran had been targeted, the worm started to spread through multiple countries such as India and the United States. This uncontrolled spreading is certainly due to an error in the code.

Currently, it is frequently supposed that Stuxnet is the first cyber-weapon developed by a country.

This worm had been designed to attack the "Siemens supervisory control" and data acquisition (SCADA) systems.
You see, Stuxnet is an extremely complicated attack which exploited several breaches. Although, among all those breaches, it used a classic exploit based on hard-coded sensible data.

Now, let's see this kind of security error in C:

#include <stdio.h>
#include <string.h>

const int SIZE_OF_BUFFER= 5;

int main()
{
    const char* superSecretPassword = "1234";
    char passwordTyped[SIZE_OF_BUFFER];

    printf("Please, type the password (%d characters):\n", SIZE_OF_BUFFER-1);
    fgets(passwordTyped, SIZE_OF_BUFFER, stdin);

    if (strncmp(passwordTyped, superSecretPassword, SIZE_OF_BUFFER) == 0)
    {
        printf("Access granted!\n");
    }
    else
    {
        printf("Access refused\n");
    }

    return 0;
}

At first, it seems that the code looks good: No overflow, no strange buffer manipulation...
The issue is on a single declaration: The "superSecretPassword" is hard-coded.

Those kinds of variables are easy to retrieve without knowledge of the source code.
Let's see how I can obtain the "superSecretPassword" (and even modify it)!

Let's read the executable file with "readelf":

 readelf -x .rodata a.out 

Hex dump of section '.rodata':
  0x00002000 01000200 00000000 05000000 31323334 ............1234
  0x00002010 00000000 00000000 506c6561 73652c20 ........Please, 
  0x00002020 74797065 20746865 20706173 73776f72 type the passwor
  0x00002030 64202825 64206368 61726163 74657273 d (%d characters
  0x00002040 293a0a00 41636365 73732067 72616e74 ):..Access grant
  0x00002050 65642100 41636365 73732072 65667573 ed!.Access refus
  0x00002060 656400                              ed.

Hey, I got a strange isolated number here: 1234.
Indeed, it is my "superSecretPassword". Now, let's open the binary with a classic file editor. As I didn't use any encryption system, I can modify my password directly in the binary:

The protection of sensible data is a touchy subject. My best recommendation here is:

  1. At first, to protect those data in a separate file. This solution is not perfect but gives minimum protection.
  2. Secondly, to improve the protection, you can encrypt this file.

What is in common in those C security errors?

All those attacks had been created thanks to code error - Bad variable management, sensible data not protected, pointer not correctly managed...

Is it the fault of the developer? I don't think... There could have multiple reason:

  • I could be due to pressure - To develop code "quick and dirty" (in Software, dirty means "slow"!).
  • Lack of code review.
  • Reuse of incompatible system.
  • Important turnover.
  • Libraries not updated.
  • ...

Of course, in a code sample, as I featured, the security issues are easy to detect!
However, who can detect one of those errors in a source code with 100,000 lines? If the code is not well written, it would be really complicated, even for a tiger team.

Don't hesitate to create code easily understandable, review with colleagues, and ask for pair programming.
A simple security error can break a reputation and trigger companies in complete turmoil!

Don't hesitate to continue the improvement of your code (The book "Clean Code" illustrates perfectly this example: https://grapeprogrammer.com/category/review/book/).

The security matter is an important subject for the industry.
I'll certainly show to you more security error examples in C in another post!

About the author 

Axel Fortun

​Developer specialized in Linux environment and embedded systems.
​Knowledge in multiple languages as C/C++, Java, Python​ and AngularJs.
​Working as Software developer since 2014 with a beginning in the car industry​ and then in ​medical systems.