Security Code Review 101 — Memory
How to employ secure coding practices in C/C++ code and prevent memory vulnerabilities such as Buffer Overflow
This article is part of a series. The previous article describes strategies for preventing Injection flaws. You can access it here: https://medium.com/@paul_io/security-code-review-101-parameterized-statements-df95c264364a
Problems with Memory
Memory related vulnerabilities are very dangerous. They are often classified as 0-days. 0-days are vulnerabilities in common software leveraged by malware and viruses. Essential operating system components and programs such as system services, browsers, crypto libraries and document readers are written in C/C++ and are particularly exposed to these types of flaws.
There are many flavours of memory vulnerabilities but we will focus on the most common mistakes:
- Buffer Copy without Checking Size of Input — CWE 120
- Incorrect Calculation of Buffer Size — CWE 131
- Uncontrolled Format String — CWE 134
- Off by One — CWE 193
How do we know they are the most common? All of these mistakes are documented by MITRE with the top 3 being included in the MITRE Sans Top 25.
They all lead to arbitrary access of memory outside the intended boundaries. This is also known as Overflow.
Memory Overflow Explained
Imagine the program uses two variables
b. The contents and length of variable
b are controlled by the user. The variable memory locations are represented in the simplistic example below. Each variable is intended to store 3 characters. At first variable
a contains the string “Hi!”.
If the user enters the string
AAAAA (5 As) for variable
b the following will happen.
b only had 3 locations reserved for its value plus one for the string terminator
\0 . By entering 5 characters the user is now writing into space reserved for variable
Variables are pointers to memory locations. The value of
b will now become AAAAAi! while the value of
a will become Ai!. If the program outputs the value of
b then the attacker will be able to know part of the value of
a which is known as a memory leak.
If the user enters a sufficiently long value, they will hit the instruction area and alter the program code.
Memory SafeR Functions
Overflow can be prevented by controlling the number of characters read into the buffer. This is done using memory safe functions. It is important to note that simply using these functions will not prevent overflow. They also must be used correctly.
The image below lists several functions that allow the program to limit the size of the value read into a buffer:
If the BUFF_SIZE argument is larger than the size of the buffer, overflow will still occur.
In the example below we have two different code snippets that both read a password from the standard input. Can you spot the one that allows Buffer Overflow because it does not check the size of the input?
If you identified
bottom.cpp as the vulnerable code you were correct. The top example, is making use of
fgets and it restricts the number of characters to 9.
Incorrect Calculation of Buffer Size
Some of our keen readers may have noticed that if the size of
userPass is less than 9, then overflow will still occur.
This is an example of Incorrect Calculation of Buffer Size. Defining constants for the size argument rather than using numerals and paying close attention during code review to the code checking boundaries, can prevent this type of flaw. Let’s take a look at the example below and see if we can spot the vulnerable code.
If you identified
bottom.cpp as the vulnerable code you were correct. The top example, is making use of the constant
BUFFER_SIZE to ensure consistency. Besides being a safer option the code is also easier to maintain if the constant value must be modified in the future. Many secure coding practices have other benefits besides security.
Off-by-one is another variation of buffer size flaw. This type of programming mistake is introduced when employing comparison operators. A simple extra equal sign, for example using
<= instead of
< can lead to the program crashing. Let’s take a look at an example. Can you spot the vulnerable code?
If you spotted the error in
top.cpp you are correct.
Format String Injection is a type of vulnerability caused by concatenating or using user input in a format parameter. Code that logs user values using functions such as printf is particularly exposed to this type of vulnerability.
Take for example the snippets below. Can you spot which of the two snippets allows the user to control the format string?
If you identified
top.cpp as the code that allows Format String Injection you were correct. If the user includes
%p in the password, printf will take the next value following the format string and include it in the display. This can lead to information disclosure or program crashes.
By the way there are a couple of bonus insecure practices in both code snippets :) . Did you spot them? One is the use of
printf, a dangerous function as mentioned earlier in the article. It’s also a bad practice to display the user password.
This is not really a code review item but is worth mentioning because is a countermeasure that can be applied at build time to prevent the exploitation of memory flaws.
Compiler flags enable operating system defences such as ASLR in Windows or PIE/SSP in Linux. They tell the operating system to employ countermeasures such as randomizing memory, which is making it hard for attackers to insert arbitrary code.
Even with compiler flags in place attackers can still crash the program so the main effect of compiler flags is reducing the impact of the attack. The best defence is to prevent the flaws in the code, from the start, by employing the best practices discussed in this article.
To sum it all up…
- Safer functions allow limiting the number of bytes read into the buffer
- Even with safe functions special attention should be paid to size specified
- Use constants to prevent mistakes
- Careful with the <= operator
- Do not allow user input in format strings
The next article in the series will discuss strategies for securing data. This is a very important subject, especially in the light of recent privacy breaches and the introduction of the GDPR European legislation in 2018 which mandates security measures for personal data. Stay tuned :)