Based in San Francisco Bay Area, Securesql is a blog by John Menerick. His insights dissect complex systems, offering a masterclass in cyber guardianship through expert analysis and cutting-edge protective strategies.

Memory Safety Code Review

Memory related vulnerabilities are very dangerous. 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:

  1. Buffer Copy without Checking Size of Input — CWE 120

  2. Incorrect Calculation of Buffer Size — CWE 131

  3. Uncontrolled Format String — CWE 134

  4. 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 a and 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!”.

1.png

If the user enters the string AAAAA (5 As) for variable b the following will happen.

2.png

Variable 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 a.

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.

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.

There exists several functions that allow the program to limit the size of the value read into a buffer: fgetssnprintfstrncpystrncmp.

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?

3.png

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.

4.png

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

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?

5.png

If you spotted the error in top.cpp you are correct.

Memory Injection?

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?

6.png

If you identified top.cpp as the code that allows Format String Injection you were correct. If the user includes %d or %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.

Compiler Flags

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

QuarkChain code review

PII Code Review