The program writes outside the bounds of allocated memory, which could corrupt data, crash the program, or lead to the execution of malicious code.
Buffer overflow is probably the best known form of software security vulnerability. Most software developers know what a buffer overflow vulnerability is, but buffer overflow attacks against both legacy and newly-developed applications are still quite common. Part of the problem is due to the wide variety of ways buffer overflows can occur, and part is due to the error-prone techniques often used to prevent them.
In a classic buffer overflow exploit, the attacker sends data to a program, which it stores in an undersized stack buffer. The result is that information on the call stack is overwritten, including the function's return pointer. The data sets the value of the return pointer so that when the function returns, it transfers control to malicious code contained in the attacker's data.
Although this type of stack buffer overflow is still common on some platforms and in some development communities, there are a variety of other types of buffer overflow, including heap buffer overflows and off-by-one errors among others. There are a number of excellent books that provide detailed information on how buffer overflow attacks work, including Building Secure Software [1], Writing Secure Code [2], and The Shellcoder's Handbook [3].
At the code level, buffer overflow vulnerabilities usually involve the violation of a programmer's assumptions. Many memory manipulation functions in C and C++ do not perform bounds checking and can easily overwrite the allocated bounds of the buffers they operate upon. Even bounded functions, such as strncpy()
, can cause vulnerabilities when used incorrectly. The combination of memory manipulation and mistaken assumptions about the size or makeup of a piece of data is the root cause of most buffer overflows.
Buffer overflow vulnerabilities typically occur in code that:
- Relies on external data to control its behavior.
- Depends upon properties of the data that are enforced outside of the immediate scope of the code.
- Is so complex that a programmer cannot accurately predict its behavior.
The following examples demonstrate all three of the scenarios.
Example 1: This is an example of the second scenario in which the code depends on properties of the data that are not verified locally. In this example a function named lccopy()
takes a string as its argument and returns a heap-allocated copy of the string with all uppercase letters converted to lowercase. The function performs no bounds checking on its input because it expects str
to always be smaller than BUFSIZE
. If an attacker bypasses checks in the code that calls lccopy()
, or if a change in that code makes the assumption about the size of str
untrue, then lccopy()
will overflow buf
with the unbounded call to strcpy()
.
char *lccopy(const char *str) {
char buf[BUFSIZE];
char *p;
strcpy(buf, str);
for (p = buf; *p; p++) {
if (isupper(*p)) {
*p = tolower(*p);
}
}
return strdup(buf);
}
gets()
function to read an arbitrary amount of data into a stack buffer. Because there is no way to limit the amount of data read by this function, the safety of the code depends on the user to always enter fewer than BUFSIZE
characters.
...
char buf[BUFSIZE];
gets(buf);
...
gets()
function in C++ by using the >>
operator to read input into a char[]
string.
...
char buf[BUFSIZE];
cin >> (buf);
...
memcpy()
. This function accepts a destination buffer, a source buffer, and the number of bytes to copy. The input buffer is filled by a bounded call to read()
, but the user specifies the number of bytes that memcpy()
copies.
...
char buf[64], in[MAX_SIZE];
printf("Enter buffer contents:\n");
read(0, in, MAX_SIZE-1);
printf("Bytes to copy:\n");
scanf("%d", &bytes);
memcpy(buf, in, bytes);
...
png_crc_read()
. However, immediately before it tests length, the code performs a check on png_ptr->mode
, and if this check fails a warning is issued and processing continues. Because length
is tested in an else if
block, length
would not be tested if the first check fails, and is used blindly in the call to png_crc_read()
, potentially allowing a stack buffer overflow.
if (!(png_ptr->mode & PNG_HAVE_PLTE)) {
/* Should be an error, but we can cope with it */
png_warning(png_ptr, "Missing PLTE before tRNS");
}
else if (length > (png_uint_32)png_ptr->num_palette) {
png_warning(png_ptr, "Incorrect tRNS chunk length");
png_crc_finish(png_ptr, length);
return;
}
...
png_crc_read(png_ptr, readbuf, (png_size_t)length);
getUserInfo()
function takes a username specified as a multibyte string and a pointer to a structure for user information, and populates the structure with information about the user. Since Windows authentication uses Unicode for usernames, the username
argument is first converted from a multibyte string to a Unicode string. This function then incorrectly passes the size of unicodeUser
in bytes rather than characters. The call to MultiByteToWideChar()
may therefore write up to (UNLEN+1)*sizeof(WCHAR)
wide characters, or(UNLEN+1)*sizeof(WCHAR)*sizeof(WCHAR)
bytes, to the unicodeUser
array, which has only (UNLEN+1)*sizeof(WCHAR)
bytes allocated. If the username
string contains more than UNLEN
characters, the call to MultiByteToWideChar()
will overflow the buffer unicodeUser
.
void getUserInfo(char *username, struct _USER_INFO_2 info){
WCHAR unicodeUser[UNLEN+1];
MultiByteToWideChar(CP_ACP, 0, username, -1,
unicodeUser, sizeof(unicodeUser));
NetUserGetInfo(NULL, unicodeUser, 2, (LPBYTE *)&info);
}
[1] Standards Mapping - OWASP Top 10 2004 - (OWASP 2004) A5 Buffer Overflow
[2] Standards Mapping - Security Technical Implementation Guide Version 3 - (STIG 3) APP3510 CAT I, APP3590.1 CAT I
[3] Standards Mapping - Web Application Security Consortium 24 + 2 - (WASC 24 + 2) Buffer Overflow
[4] J. Viega, G. McGraw Building Secure Software Addison-Wesley
[5] Standards Mapping - Common Weakness Enumeration - (CWE) CWE ID 787, CWE ID 120, CWE ID 129, CWE ID 131
[6] Standards Mapping - Payment Card Industry Data Security Standard Version 1.2 - (PCI 1.2) Requirement 6.3.1.1
[7] Standards Mapping - Payment Card Industry Data Security Standard Version 2.0 - (PCI 2.0) Requirement 6.5.2
[8] Standards Mapping - Payment Card Industry Data Security Standard Version 1.1 - (PCI 1.1) Requirement 6.5.5
[9] Standards Mapping - SANS Top 25 2009 - (SANS 2009) Risky Resource Management - CWE ID 119
[10] Standards Mapping - SANS Top 25 2010 - (SANS 2010) Risky Resource Management - CWE ID 120, Risky Resource Management - CWE ID 129, Risky Resource Management - CWE ID 131
[11] J. Koziol et al. The Shellcoder's Handbook: Discovering and Exploiting Security Holes John Wiley & Sons
[12] M. Howard, D. LeBlanc Writing Secure Code, Second Edition Microsoft Press