A buffer overflow is a runtime error that occurs when a program writes beyond the boundaries of a buffer or array, causing it to overwrite adjacent memory. This type of bug doesn't always present itself in every execution of the program; it usually emerges under specific conditions, like unusual user input.
Example Scenario:The following code performs a simple password check and is vulnerable to a buffer overflow attack:
#include
#include
#include
const char *PASSWORD = "azedev";
int main()
{
char input[8];
char password[8];
std::sscanf(PASSWORD, "%s", password);
std::cout << "Password: ";
std::cin >> input;
if (std::strncmp(password, input, 8) == 0)
std::cout << "Access granted\n";
else
std::cout << "Access denied\n";
return 0;
}
The code promts the user to enter a password and compares it to the stored password. If they match the user is granted access.
This is what happens when you run the program:
Password: azedev
Access granted
Entering the correct password grants us access - as expected. Entering an incorrect password denies access:
Password: 123
Access denied
So far so good, the code is working as intended. However here comes the exploit:
Password: firewallfirewall
Access granted
Unexpectedly we entered something very different from the correct password - yet were still granted access.
The reason to why this happens is because we sucessfully executed a buffer overflow attack against the program. In order to understand how this happened let's add a couple of debug lines to the code:
std::cout << "Address of input: " << &input << "\n";
std::cout << "Address of password: " << &password << "\n";
std::cout << "Input: " << input << "\n";
std::cout << "Password: " << password << "\n";
Then we re-run the program, first with proper input:
Password: azedev
Address of input: 0x7ffdf027f988
Address of password: 0x7ffdf027f990
Input: azedev
Password: azedev
Access granted
There's nothing unusual about the output.
Let's try again with improper input:
Password: 123
Address of input: 0x7ffc75a2c298
Address of password: 0x7ffc75a2c2a0
Input: 123
Password: azedev
Access denied
Still nothing unusual about the output.
But notice what happens when we enter the third password "firewallfirewall"
Password: firewallfirewall
Address of input: 0x7ffd4288bd18
Address of password: 0x7ffd4288bd20
Input: firewallfirewall
Password: firewall
Access granted
As you can see the password is no longer "azedev" - it now contains "firewall"
In our example, we use two 8-byte arrays: password holds the application password, and input captures the user input. In this instance, the GCC compiler places password eight bytes after input (0x7ffd4288bd20 - 0x7ffd4288bd18 = 8), meaning the arrays are adjacent in memory. Keep in mind that different compilers might result in varying memory layouts.
For passwords shorter than eight characters, the memory layout appears as follows:
But for our special password "firewallfirewall" it looks like this:
Here, the null terminator \0 is written beyond the end of the password array, potentially overriding other data on the stack.
This issue arises because std::cin does not enforce bounds checking. It continues reading input from the console until it encounters a newline, which occurs when the user presses Enter, without verifying that the buffer is adequately sized for the input.
Since we only compare the first eight characters of both password and input using std::strncmp to prevent reading beyond the end of either array, a match is mistakenly found where it shouldn't be.
To avoid buffer overflow, use std::string for user input, as it dynamically manages buffer size. This prevents overflow issues and enhances security. Ensure comparisons stay within bounds with functions like std::strncmp.
Like this:
#include
#include
#include
#include
const char *PASSWORD = "azedev";
int main()
{
std::string input;
char password[8];
std::sscanf(PASSWORD, "%s", password);
std::cout << "Password: ";
std::cin >> input;
if (std::strncmp(password, input.c_str(), 8) == 0)
std::cout << "Access granted\n";
else
std::cout << "Access denied\n";
return 0;
}
10-09-2024 by azedev
This blog post is intended for educational purposes only.