Access of Resource Using Incompatible Type ('Type Confusion')
Definition in a New Window
Weakness ID: 843 (Weakness Base)
Status: Incomplete
Description
Description Summary
The program allocates or initializes a resource such as a pointer, object, or variable using one type, but it later accesses that resource using a type that is incompatible with the original type.
Extended Description
When the program accesses the resource using an incompatible type, this could trigger logical errors because the resource does not have expected properties. In languages without memory safety, such as C and C++, type confusion can lead to out-of-bounds memory access.
While this weakness is frequently associated with unions when parsing data with many different embedded object types in C, it can be present in any application that can interpret the same variable or memory location in multiple ways.
This weakness is not unique to C and C++. For example, errors in PHP applications can be triggered by providing array parameters when scalars are expected, or vice versa. Languages such as Perl, which perform automatic conversion of a variable of one type when it is accessed as if it were another type, can also contain these issues.
Alternate Terms
Object Type Confusion
Time of Introduction
Implementation
Applicable Platforms
Languages
C
C++
Language-independent
Type-unsafe Languages
Demonstrative Examples
Example 1
The following code uses a union to support the representation of
different types of messages. It formats messages differently, depending on
their type.
(Bad Code)
Example
Language: C
#define NAME_TYPE 1
#define ID_TYPE 2
struct MessageBuffer
{
int msgType;
union {
char *name;
int nameID;
};
};
int main (int argc, char **argv) {
struct MessageBuffer buf;
char *defaultMessage = "Hello World";
buf.msgType = NAME_TYPE;
buf.name = defaultMessage;
printf("Pointer of buf.name is %p\n", buf.name);
/* This particular value for nameID is used to make the
code architecture-independent. If coming from untrusted input,
it could be any value. */
buf.nameID = (int)(defaultMessage + 1);
printf("Pointer of buf.name is now %p\n", buf.name);
if (buf.msgType == NAME_TYPE) {
printf("Message: %s\n", buf.name);
}
else {
printf("Message: Use ID %d\n", buf.nameID);
}
}
The code intends to process the message as a NAME_TYPE, and sets the
default message to "Hello World." However, since both buf.name and
buf.nameID are part of the same union, they can act as aliases for the
same memory location, depending on memory layout after
compilation.
As a result, modification of buf.nameID - an int - can effectively
modify the pointer that is stored in buf.name - a string.
Execution of the program might generate output such as:
Pointer of name is 10830
Pointer of name is now 10831
Message: ello World
Notice how the pointer for buf.name was changed, even though buf.name
was not explicitly modified.
In this case, the first "H" character of the message is omitted.
However, if an attacker is able to fully control the value of
buf.nameID, then buf.name could contain an arbitrary pointer, leading to
out-of-bounds reads or writes.
Example 2
The following PHP code accepts a value, adds 5, and prints the
sum.
Example
Language: PHP
$value = $_GET['value'];
$sum = $value + 5;
echo "value parameter is '$value'<p>";
echo "SUM is $sum";
When called with the following query string:
value=123
the program calculates the sum and prints out:
SUM is 128
However, the attacker could supply a query string such as:
value[]=123
The "[]" array syntax causes $value to be treated as an array type,
which then generates a fatal error when calculating $sum:
Fatal error: Unsupported operand types in program.php on line
2
Example 3
The following Perl code is intended to look up the privileges for
user ID's between 0 and 3, by performing an access of the
$UserPrivilegeArray reference. It is expected that only userID 3 is an admin
(since this is listed in the third element of the array).
(Bad Code)
Example
Language: Perl
my $UserPrivilegeArray = ["user", "user", "admin", "user"];
In this case, the programmer intended to use
"$UserPrivilegeArray->{$userID}" to access the proper position in the
array. But because the subscript was omitted, the "user" string was
compared to the scalar representation of the $UserPrivilegeArray
reference, which might be of the form "ARRAY(0x229e8)" or
similar.
Since the logic also "fails open" (CWE-636), the result of this bug is that all users are assigned administrator privileges.
While this is a forced example, it demonstrates how type confusion can
have security consequences, even in memory-safe languages.
Improperly-parsed file containing records of
different types leads to code execution when a memory location is
interpreted as a different object than
intended.
Type confusion weaknesses have received some attention by applied
researchers and major software vendors for C and C++ code. Some
publicly-reported vulnerabilities probably have type confusion as a
root-cause weakness, but these may be described as "memory corruption"
instead. This weakness seems likely to gain prominence in upcoming
years.
For other languages, there are very few public reports of type confusion
weaknesses. These are probably under-studied. Since many programs rely
directly or indirectly on loose typing, a potential "type confusion"
behavior might be intentional, possibly requiring more manual
analysis.
The program accesses or uses a pointer that has not been initialized.
Extended Description
If the pointer contains an uninitialized value, then the value might not point to a valid memory location. This could cause the program to read from or write to unexpected memory locations, leading to a denial of service. If the uninitialized pointer is used as a function call, then arbitrary functions could be invoked. If an attacker can influence the portion of uninitialized memory that is contained in the pointer, this weakness could be leveraged to execute code or perform other attacks.
Depending on memory layout, associated memory management behaviors, and program operation, the attacker might be able to influence the contents of the uninitialized pointer, thus gaining more fine-grained control of the memory location to be accessed.
Terminology Notes
Many weaknesses related to pointer dereferences fall under the general
term of "memory corruption" or "memory safety." As of September 2010, there
is no commonly-used terminology that covers the lower-level variants.
Common Consequences
Scope
Effect
Confidentiality
Technical Impact: Read memory
If the uninitialized pointer is used in a read operation, an attacker
might be able to read sensitive portions of memory.
Availability
Technical Impact: DoS: crash / exit /
restart
If the uninitialized pointer references a memory location that is not
accessible to the program, or points to a location that is "malformed"
(such as NULL) or larger than expected by a read or write operation,
then a crash may occur.
Integrity
Confidentiality
Availability
Technical Impact: Execute unauthorized code or
commands
If the uninitialized pointer is used in a function call, or points to
unexpected data in a write operation, then code execution may be
possible.
Step-based manipulation: invocation of debugging
function before the primary initialization function leads to access of an
uninitialized pointer and code execution.
Under-studied and probably under-reported as of September 2010. This
weakness has been reported in high-visibility software, but applied
vulnerability researchers have only been investigating it since
approximately 2008, and there are only a few public reports. Few reports
identify weaknesses at such a low level, which makes it more difficult to
find and study real-world code examples.
Maintenance Notes
There are close relationships between incorrect pointer dereferences and
other weaknesses related to buffer operations. There may not be sufficient
community agreement regarding these relationships. Further study is needed
to determine when these relationships are chains, composites,
perspective/layering, or other types of relationships. As of September 2010,
most of the relationships are being captured as chains.
The code uses an operator for assignment when the intention was to perform a comparison.
Extended Description
In many languages the compare statement is very close in appearance to the assignment statement and are often confused. This bug is generally the result of a typo and usually causes obvious problems with program execution. If the comparison is in an if statement, the if statement will usually evaluate the value of the right-hand side of the predicate.
Time of Introduction
Implementation
Applicable Platforms
Languages
C
C++
Java
.NET
Common Consequences
Scope
Effect
Other
Technical Impact: Alter execution
logic
Likelihood of Exploit
Low
Demonstrative Examples
Example 1
The following C/C++ and C# examples attempt to validate an int input
parameter against the integer value 100. However, the expression to be
evaluated in the if statement uses the assignment operator "=" rather than
the comparison operator "==". The result of using the assignment operator
instead of the comparison operator causes the int variable to be reassigned
locally and the expression in the if statement will always evaluate to the
value on the right hand side of the expression. This will result in the
input value not being properly validated, which can cause unexpected
results.
(Bad Code)
Example Languages: C and C#
int isValid(int value) {
if (value=100) {
printf("Value is valid\n");
return(1);
}
printf("Value is not valid\n");
return(0);
}
(Bad Code)
Example
Language: C#
bool isValid(int value) {
if (value=100) {
Console.WriteLine("Value is valid.");
return true;
}
Console.WriteLine("Value is not valid.");
return false;
}
Example 2
In this example, we show how assigning instead of comparing can impact code when values are being passed by reference instead of by value. Consider a scenario in which a string is being processed from user input. Assume the string has already been formatted such that different user inputs are concatenated with the colon character. When the processString function is called, the test for the colon character will result in an insertion of the colon character instead, adding new input separators. Since the string was passed by reference, the data sentinels will be inserted in the original string (CWE-464), and further processing of the inputs will be altered, possibly malformed..
(Bad Code)
Example
Language: C
void processString (char *str) {
int i;
for(i=0; i<strlen(str); i++) {
if (isalnum(str[i])){
processChar(str[i]);
}
else if (str[i] = ':') {
movingToNewInput();}
}
}
}
Example 3
The following Java example attempts to perform some processing based
on the boolean value of the input parameter. However, the expression to be
evaluated in the if statement uses the assignment operator "=" rather than
the comparison operator "==". As with the previous examples, the variable
will be reassigned locally and the expression in the if statement will
evaluate to true and unintended processing may occur.
(Bad Code)
Example
Language: Java
public void checkValid(boolean isValid) {
if (isValid = true) {
System.out.println("Performing processing");
doSomethingImportant();
}
else {
System.out.println("Not Valid, do not perform
processing");
return;
}
}
While most Java compilers will catch the use of an assignment operator
when a comparison operator is required, for boolean variables in Java
the use of the assignment operator within an expression is allowed. If
possible, try to avoid using comparison operators on boolean variables
in java. Instead, let the values of the variables stand for themselves,
as in the following code.
(Good Code)
Example
Language: Java
public void checkValid(boolean isValid) {
if (isValid) {
System.out.println("Performing processing");
doSomethingImportant();
}
else {
System.out.println("Not Valid, do not perform
processing");
return;
}
}
Alternatively, to test for false, just use the boolean NOT
operator.
(Good Code)
Example
Language: Java
public void checkValid(boolean isValid) {
if (!isValid) {
System.out.println("Not Valid, do not perform
processing");
return;
}
System.out.println("Performing processing");
doSomethingImportant();
}
Example 4
(Bad Code)
Example
Language: C
void called(int foo){
if (foo=1) printf("foo\n");
}
int main() {
called(2);
return 0;
}
Potential Mitigations
Pre-design: Through Build: Many IDEs and static analysis products will
detect this problem.
Phase: Implementation
Place constants on the left. If one attempts to assign a constant with
a variable, the compiler will of course produce an error.
The software performs authentication based on the name of a resource being accessed, or the name of the actor performing the access, but it does not properly check all possible names for that resource or actor.
Bypass of authentication for files using "\"
(backslash) or "%5C" (encoded backslash).
Potential Mitigations
Phase: Architecture and Design
Avoid making decisions based on names of resources (e.g. files) if
those resources can have alternate names.
Phase: Implementation
Strategy: Input Validation
Assume all input is malicious. Use an "accept known good" input
validation strategy, i.e., use a whitelist of acceptable inputs that
strictly conform to specifications. Reject any input that does not
strictly conform to specifications, or transform it into something that
does.
When performing input validation, consider all potentially relevant
properties, including length, type of input, the full range of
acceptable values, missing or extra inputs, syntax, consistency across
related fields, and conformance to business rules. As an example of
business rule logic, "boat" may be syntactically valid because it only
contains alphanumeric characters, but it is not valid if the input is
only expected to contain colors such as "red" or "blue."
Do not rely exclusively on looking for malicious or malformed inputs
(i.e., do not rely on a blacklist). A blacklist is likely to miss at
least one undesirable input, especially if the code's environment
changes. This can give attackers enough room to bypass the intended
validation. However, blacklists can be useful for detecting potential
attacks or determining which inputs are so malformed that they should be
rejected outright.
Phase: Architecture and Design
Use and specify a strong output encoding (such as ISO 8859-1 or UTF
8).
Phase: Implementation
Strategy: Input Validation
Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180). Make sure that the application does not decode the same input twice (CWE-174). Such errors could be used to bypass whitelist validation schemes by introducing dangerous inputs after they have been checked.
The software uses a sequential operation to read or write a buffer, but it uses an incorrect length value that causes it to access memory that is outside of the bounds of the buffer.
Extended Description
When the length value exceeds the size of the destination, a buffer overflow could occur.
Time of Introduction
Implementation
Applicable Platforms
Languages
C: (Often)
C++: (Often)
Assembly
Common Consequences
Scope
Effect
Integrity
Confidentiality
Availability
Technical Impact: Execute unauthorized code or
commands
Buffer overflows often can be used to execute arbitrary code, which is
usually outside the scope of a program's implicit security policy. This
can often be used to subvert any other security service.
Buffer overflows generally lead to crashes. Other attacks leading to
lack of availability are possible, including putting the program into an
infinite loop.
Likelihood of Exploit
Medium to High
Detection Methods
Automated Static Analysis
This weakness can often be detected using automated static analysis
tools. Many modern tools use data flow analysis or constraint-based
techniques to minimize the number of false positives.
Automated static analysis generally does not account for environmental
considerations when reporting out-of-bounds memory operations. This can
make it difficult for users to determine which warnings should be
investigated first. For example, an analysis tool might report buffer
overflows that originate from command line arguments in a program that
is not expected to run with setuid or other special privileges.
Effectiveness: High
Detection techniques for buffer-related errors are more mature than
for most other weakness types.
Automated Dynamic Analysis
This weakness can be detected using dynamic tools and techniques that
interact with the software using large test suites with many diverse
inputs, such as fuzz testing (fuzzing), robustness testing, and fault
injection. The software's operation may slow down, but it should not
become unstable, crash, or generate incorrect results.
Effectiveness: Moderate
Without visibility into the code, black box methods may not be able to
sufficiently distinguish this weakness from others, requiring manual
methods to diagnose the underlying problem.
Manual Analysis
Manual analysis can be useful for finding this weakness, but it might
not achieve desired code coverage within limited time constraints. This
becomes difficult for weaknesses that must be considered for all inputs,
since the attack surface can be too large.
Demonstrative Examples
Example 1
This example takes an IP address from a user, verifies that it is
well formed and then looks up the hostname and copies it into a
buffer.
(Bad Code)
Example
Language: C
void host_lookup(char *user_supplied_addr){
struct hostent *hp;
in_addr_t *addr;
char hostname[64];
in_addr_t inet_addr(const char *cp);
/*routine that ensures user_supplied_addr is in the right
format for conversion */
validate_addr_form(user_supplied_addr);
addr = inet_addr(user_supplied_addr);
hp = gethostbyaddr( addr, sizeof(struct in_addr),
AF_INET);
strcpy(hostname, hp->h_name);
}
This function allocates a buffer of 64 bytes to store the hostname
under the assumption that the maximum length value of hostname is 64
bytes, however there is no guarantee that the hostname will not be
larger than 64 bytes. If an attacker specifies an address which resolves
to a very large hostname, then we may overwrite sensitive data or even
relinquish control flow to the attacker.
Note that this example also contains an unchecked return value (CWE-252) that can lead to a NULL pointer dereference (CWE-476).
Example 2
In the following example, the source character string is copied to
the dest character string using the method strncpy.
(Bad Code)
Example Languages: C and C++
...
char source[21] = "the character string";
char dest[12];
strncpy(dest, source, sizeof(source)-1);
...
However, in the call to strncpy the source character string is used
within the sizeof call to determine the number of characters to copy.
This will create a buffer overflow as the size of the source character
string is greater than the dest character string. The dest character
string should be used within the sizeof call to ensure that the correct
number of characters are copied, as shown below.
(Good Code)
Example Languages: C and C++
...
char source[21] = "the character string";
char dest[12];
strncpy(dest, source, sizeof(dest)-1);
...
Example 3
In this example, the method outputFilenameToLog outputs a filename
to a log file. The method arguments include a pointer to a character string
containing the file name and an integer for the number of characters in the
string. The filename is copied to a buffer where the buffer size is set to a
maximum size for inputs to the log file. The method then calls another
method to save the contents of the buffer to the log file.
(Bad Code)
Example Languages: C and C++
#define LOG_INPUT_SIZE 40
// saves the file name to a log file
int outputFilenameToLog(char *filename, int length) {
int success;
// buffer with size set to maximum size for input to log
file
char buf[LOG_INPUT_SIZE];
// copy filename to buffer
strncpy(buf, filename, length);
// save to log file
success = saveToLogFile(buf);
return success;
}
However, in this case the string copy method, strncpy, mistakenly uses
the length method argument to determine the number of characters to copy
rather than using the size of the local character string, buf. This can
lead to a buffer overflow if the number of characters contained in
character string pointed to by filename is larger then the number of
characters allowed for the local character string. The string copy
method should use the buf character string within a sizeof call to
ensure that only characters up to the size of the buf array are copied
to avoid a buffer overflow, as shown below.
Language interpreter API function doesn't validate
length argument, leading to information
exposure
Potential Mitigations
Phase: Requirements
Strategy: Language Selection
Use a language that does not allow this weakness to occur or provides
constructs that make this weakness easier to avoid.
For example, many languages that perform their own memory management,
such as Java and Perl, are not subject to buffer overflows. Other
languages, such as Ada and C#, typically provide overflow protection,
but the protection can be disabled by the programmer.
Be wary that a language's interface to native code may still be
subject to overflows, even if the language itself is theoretically safe.
Phase: Architecture and Design
Strategy: Libraries or Frameworks
Use a vetted library or framework that does not allow this weakness to
occur or provides constructs that make this weakness easier to
avoid.
Examples include the Safe C String Library (SafeStr) by Messier and
Viega, and the Strsafe.h library from Microsoft. These libraries provide
safer versions of overflow-prone string-handling functions.
This is not a complete solution, since many buffer overflows are not
related to strings.
Phase: Build and Compilation
Strategy: Compilation or Build Hardening
Run or compile your software using features or extensions that
automatically provide a protection mechanism that mitigates or
eliminates buffer overflows.
For example, certain compilers and extensions provide automatic buffer
overflow detection mechanisms that are built into the compiled code.
Examples include the Microsoft Visual Studio /GS flag, Fedora/Red Hat
FORTIFY_SOURCE GCC flag, StackGuard, and ProPolice.
Effectiveness: Defense in Depth
This is not necessarily a complete solution, since these mechanisms
can only detect certain types of overflows. In addition, an attack could
still cause a denial of service, since the typical response is to exit
the application.
Phase: Implementation
Consider adhering to the following rules when allocating and managing
an application's memory:
Double check that your buffer is as large as you specify.
When using functions that accept a number of bytes to copy, such
as strncpy(), be aware that if the destination buffer size is equal
to the source buffer size, it may not NULL-terminate the
string.
Check buffer boundaries if accessing the buffer in a loop and make
sure you are not in danger of writing past the allocated
space.
If necessary, truncate all input strings to a reasonable length
before passing them to the copy and concatenation functions.
Phase: Architecture and Design
For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
Phase: Operation
Strategy: Environment Hardening
Use a feature like Address Space Layout Randomization (ASLR).
Effectiveness: Defense in Depth
This is not a complete solution. However, it forces the attacker to
guess an unknown value that changes every program execution. In
addition, an attack could still cause a denial of service, since the
typical response is to exit the application.
Phase: Operation
Strategy: Environment Hardening
Use a CPU and operating system that offers Data Execution Protection
(NX) or its equivalent.
Effectiveness: Defense in Depth
This is not a complete solution, since buffer overflows could be used
to overwrite nearby variables to modify the software's state in
dangerous ways. In addition, it cannot be used in cases in which
self-modifying code is required. Finally, an attack could still cause a
denial of service, since the typical response is to exit the
application.
Phases: Architecture and Design; Operation
Strategy: Environment Hardening
Run your code using the lowest privileges that are required to
accomplish the necessary tasks. If possible, create isolated accounts
with limited privileges that are only used for a single task. That way,
a successful attack will not immediately give the attacker access to the
rest of the software or its environment. For example, database
applications rarely need to run as the database administrator,
especially in day-to-day operations.
Phases: Architecture and Design; Operation
Strategy: Sandbox or Jail
Run your code in a "jail" or similar sandbox environment that enforces
strict boundaries between the process and the operating system. This may
effectively restrict which files can be accessed in a particular
directory or which commands can be executed by your software.
OS-level examples include the Unix chroot jail, AppArmor, and SELinux.
In general, managed code may provide some protection. For example,
java.io.FilePermission in the Java SecurityManager allows you to specify
restrictions on file operations.
This may not be a feasible solution, and it only limits the impact to
the operating system; the rest of your application may still be subject
to compromise.
Be careful to avoid CWE-243 and other weaknesses related to jails.
Effectiveness: Limited
The effectiveness of this mitigation depends on the prevention
capabilities of the specific sandbox or jail being used and might only
help to reduce the scope of an attack, such as restricting the attacker
to certain system calls or limiting the portion of the file system that
can be accessed.
Weakness Ordinalities
Ordinality
Description
Resultant
(where
the weakness is typically related to the presence of some other
weaknesses)
Primary
(where
the weakness exists independent of other weaknesses)
Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
Definition in a New Window
Weakness ID: 120 (Weakness Base)
Status: Incomplete
Description
Description Summary
The program copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
Extended Description
A buffer overflow condition exists when a program attempts to put more data in a buffer than it can hold, or when a program attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the "classic" case in which the program copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections.
Alternate Terms
buffer overrun:
Some prominent vendors and researchers use the term "buffer overrun,"
but most people use "buffer overflow."
Unbounded Transfer
Terminology Notes
Many issues that are now called "buffer overflows" are substantively
different than the "classic" overflow, including entirely different bug
types that rely on overflow exploit techniques, such as integer signedness
errors, integer overflows, and format string bugs. This imprecise
terminology can make it difficult to determine which variant is being
reported.
Time of Introduction
Implementation
Applicable Platforms
Languages
C
C++
Assembly
Common Consequences
Scope
Effect
Integrity
Confidentiality
Availability
Technical Impact: Execute unauthorized code or
commands
Buffer overflows often can be used to execute arbitrary code, which is
usually outside the scope of a program's implicit security policy. This
can often be used to subvert any other security service.
Buffer overflows generally lead to crashes. Other attacks leading to
lack of availability are possible, including putting the program into an
infinite loop.
Likelihood of Exploit
High to Very High
Detection Methods
Automated Static Analysis
This weakness can often be detected using automated static analysis
tools. Many modern tools use data flow analysis or constraint-based
techniques to minimize the number of false positives.
Automated static analysis generally does not account for environmental
considerations when reporting out-of-bounds memory operations. This can
make it difficult for users to determine which warnings should be
investigated first. For example, an analysis tool might report buffer
overflows that originate from command line arguments in a program that
is not expected to run with setuid or other special privileges.
Effectiveness: High
Detection techniques for buffer-related errors are more mature than
for most other weakness types.
Automated Dynamic Analysis
This weakness can be detected using dynamic tools and techniques that
interact with the software using large test suites with many diverse
inputs, such as fuzz testing (fuzzing), robustness testing, and fault
injection. The software's operation may slow down, but it should not
become unstable, crash, or generate incorrect results.
Manual Analysis
Manual analysis can be useful for finding this weakness, but it might
not achieve desired code coverage within limited time constraints. This
becomes difficult for weaknesses that must be considered for all inputs,
since the attack surface can be too large.
Demonstrative Examples
Example 1
The following code asks the user to enter their last name and then
attempts to store the value entered in the last_name array.
(Bad Code)
Example
Language: C
char last_name[20];
printf ("Enter your last name: ");
scanf ("%s", last_name);
The problem with the code above is that it does not restrict or limit
the size of the name entered by the user. If the user enters
"Very_very_long_last_name" which is 24 characters long, then a buffer
overflow will occur since the array can only hold 20 characters total.
Example 2
The following code attempts to create a local copy of a buffer to
perform some manipulations to the data.
(Bad Code)
Example
Language: C
void manipulate_string(char* string){
char buf[24];
strcpy(buf, string);
...
}
However, the programmer does not ensure that the size of the data
pointed to by string will fit in the local buffer and blindly copies the
data with the potentially dangerous strcpy() function. This may result
in a buffer overflow condition if an attacker can influence the contents
of the string parameter.
Example 3
The excerpt below calls the gets() function in C, which is
inherently unsafe.
(Bad Code)
Example
Language: C
char buf[24];
printf("Please enter your name and press
<Enter>\n");
gets(buf);
...
}
However, the programmer uses the function gets() which is inherently
unsafe because it blindly copies all input from STDIN to the buffer
without restricting how much is copied. This allows the user to provide
a string that is larger than the buffer size, resulting in an overflow
condition.
Example 4
In the following example, a server accepts connections from a client
and processes the client request. After accepting a client connection, the
program will obtain client information using the gethostbyaddr method, copy
the hostname of the client that connected to a local variable and output the
hostname of the client to a log file.
(Bad Code)
Example Languages: C and C++
...
struct hostent *clienthp;
char hostname[MAX_LEN];
// create server socket, bind to server address and listen on
socket
...
// accept client connections and process requests
int count = 0;
for (count = 0; count < MAX_CONNECTIONS; count++) {
int clientlen = sizeof(struct sockaddr_in);
int clientsocket = accept(serversocket, (struct sockaddr
*)&clientaddr, &clientlen);
logOutput("Accepted client connection from host ",
hostname);
// process client request
...
close(clientsocket);
}
}
close(serversocket);
...
However, the hostname of the client that connected may be longer than
the allocated size for the local hostname variable. This will result in
a buffer overflow when copying the client hostname to the local variable
using the strcpy method.
By replacing a valid cookie value with an
extremely long string of characters, an attacker may overflow the
application's buffers.
Potential Mitigations
Phase: Requirements
Strategy: Language Selection
Use a language that does not allow this weakness to occur or provides
constructs that make this weakness easier to avoid.
For example, many languages that perform their own memory management,
such as Java and Perl, are not subject to buffer overflows. Other
languages, such as Ada and C#, typically provide overflow protection,
but the protection can be disabled by the programmer.
Be wary that a language's interface to native code may still be
subject to overflows, even if the language itself is theoretically safe.
Phase: Architecture and Design
Strategy: Libraries or Frameworks
Use a vetted library or framework that does not allow this weakness to
occur or provides constructs that make this weakness easier to
avoid.
Examples include the Safe C String Library (SafeStr) by Messier and Viega [R.120.4], and the Strsafe.h library from Microsoft [R.120.3]. These libraries provide safer versions of overflow-prone string-handling functions.
This is not a complete solution, since many buffer overflows are not
related to strings.
Phase: Build and Compilation
Strategy: Compilation or Build Hardening
Run or compile your software using features or extensions that
automatically provide a protection mechanism that mitigates or
eliminates buffer overflows.
For example, certain compilers and extensions provide automatic buffer
overflow detection mechanisms that are built into the compiled code.
Examples include the Microsoft Visual Studio /GS flag, Fedora/Red Hat
FORTIFY_SOURCE GCC flag, StackGuard, and ProPolice.
Effectiveness: Defense in Depth
This is not necessarily a complete solution, since these mechanisms
can only detect certain types of overflows. In addition, an attack could
still cause a denial of service, since the typical response is to exit
the application.
Phase: Implementation
Consider adhering to the following rules when allocating and managing
an application's memory:
Double check that your buffer is as large as you specify.
When using functions that accept a number of bytes to copy, such
as strncpy(), be aware that if the destination buffer size is equal
to the source buffer size, it may not NULL-terminate the
string.
Check buffer boundaries if accessing the buffer in a loop and make
sure you are not in danger of writing past the allocated
space.
If necessary, truncate all input strings to a reasonable length
before passing them to the copy and concatenation functions.
Phase: Implementation
Strategy: Input Validation
Assume all input is malicious. Use an "accept known good" input
validation strategy, i.e., use a whitelist of acceptable inputs that
strictly conform to specifications. Reject any input that does not
strictly conform to specifications, or transform it into something that
does. Do not rely exclusively on looking for malicious or malformed
inputs (i.e., do not rely on a blacklist). However, blacklists can be
useful for detecting potential attacks or determining which inputs are
so malformed that they should be rejected outright.
When performing input validation, consider all potentially relevant
properties, including length, type of input, the full range of
acceptable values, missing or extra inputs, syntax, consistency across
related fields, and conformance to business rules. As an example of
business rule logic, "boat" may be syntactically valid because it only
contains alphanumeric characters, but it is not valid if you are
expecting colors such as "red" or "blue."
Phase: Architecture and Design
For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
Phase: Operation
Strategy: Environment Hardening
Use a feature like Address Space Layout Randomization (ASLR). [R.120.5] [R.120.7]
Effectiveness: Defense in Depth
This is not a complete solution. However, it forces the attacker to
guess an unknown value that changes every program execution. In
addition, an attack could still cause a denial of service, since the
typical response is to exit the application.
Phase: Operation
Strategy: Environment Hardening
Use a CPU and operating system that offers Data Execution Protection (NX) or its equivalent. [R.120.7] [R.120.9]
Effectiveness: Defense in Depth
This is not a complete solution, since buffer overflows could be used
to overwrite nearby variables to modify the software's state in
dangerous ways. In addition, it cannot be used in cases in which
self-modifying code is required. Finally, an attack could still cause a
denial of service, since the typical response is to exit the
application.
Phases: Build and Compilation; Operation
Most mitigating technologies at the compiler or OS level to date
address only a subset of buffer overflow problems and rarely provide
complete protection against even that subset. It is good practice to
implement strategies to increase the workload of an attacker, such as
leaving the attacker to guess an unknown value that changes every
program execution.
Phase: Implementation
Replace unbounded copy functions with analogous functions that support
length arguments, such as strcpy with strncpy. Create these if they are
not available.
Effectiveness: Moderate
This approach is still susceptible to calculation errors, including issues such as off-by-one errors (CWE-193) and incorrectly calculating buffer lengths (CWE-131).
Phase: Architecture and Design
Strategy: Enforcement by Conversion
When the set of acceptable objects, such as filenames or URLs, is
limited or known, create a mapping from a set of fixed input values
(such as numeric IDs) to the actual filenames or URLs, and reject all
other inputs.
Phases: Architecture and Design; Operation
Strategy: Environment Hardening
Run your code using the lowest privileges that are required to accomplish the necessary tasks [R.120.10]. If possible, create isolated accounts with limited privileges that are only used for a single task. That way, a successful attack will not immediately give the attacker access to the rest of the software or its environment. For example, database applications rarely need to run as the database administrator, especially in day-to-day operations.
Phases: Architecture and Design; Operation
Strategy: Sandbox or Jail
Run your code in a "jail" or similar sandbox environment that enforces
strict boundaries between the process and the operating system. This may
effectively restrict which files can be accessed in a particular
directory or which commands can be executed by your software.
OS-level examples include the Unix chroot jail, AppArmor, and SELinux.
In general, managed code may provide some protection. For example,
java.io.FilePermission in the Java SecurityManager allows you to specify
restrictions on file operations.
This may not be a feasible solution, and it only limits the impact to
the operating system; the rest of your application may still be subject
to compromise.
Be careful to avoid CWE-243 and other weaknesses related to jails.
Effectiveness: Limited
The effectiveness of this mitigation depends on the prevention
capabilities of the specific sandbox or jail being used and might only
help to reduce the scope of an attack, such as restricting the attacker
to certain system calls or limiting the portion of the file system that
can be accessed.
Weakness Ordinalities
Ordinality
Description
Resultant
(where
the weakness is typically related to the presence of some other
weaknesses)
Primary
(where
the weakness exists independent of other weaknesses)
At the code level, stack-based and heap-based overflows do not differ
significantly, so there usually is not a need to distinguish them. From the
attacker perspective, they can be quite different, since different
techniques are required to exploit them.
Affected Resources
Memory
Functional Areas
Memory Management
Causal Nature
Explicit
Taxonomy Mappings
Mapped Taxonomy Name
Node ID
Fit
Mapped Node Name
PLOVER
Unbounded Transfer ('classic overflow')
7 Pernicious Kingdoms
Buffer Overflow
CLASP
Buffer overflow
OWASP Top Ten 2004
A1
CWE_More_Specific
Unvalidated Input
OWASP Top Ten 2004
A5
CWE_More_Specific
Buffer Overflows
CERT C Secure Coding
STR35-C
Do not copy data from an unbounded source to a fixed-length
array
WASC
7
Buffer Overflow
CERT C++ Secure Coding
STR35-CPP
Do not copy data from an unbounded source to a fixed-length
array
A weakness where the code path includes a Buffer Write Operation such
that:
1. the expected size of the buffer is greater than the actual size of
the buffer where expected size is equal to the sum of the size of the
data item and the position in the buffer
Where Buffer Write Operation is a statement that writes a data item of a
certain size into a buffer at a certain position and at a certain
index
References
[R.120.1] [REF-11] M. Howard and
D. LeBlanc. "Writing Secure Code". Chapter 5, "Public Enemy #1: The Buffer Overrun" Page
127. 2nd Edition. Microsoft. 2002.
[R.120.2] [REF-17] Michael Howard, David LeBlanc
and John Viega. "24 Deadly Sins of Software Security". "Sin 5: Buffer Overruns." Page 89. McGraw-Hill. 2010.
The software reads from a buffer using buffer access mechanisms such as indexes or pointers that reference memory locations after the targeted buffer.
Extended Description
This typically occurs when the pointer or its index is incremented to a position beyond the bounds of the buffer or when pointer arithmetic results in a position outside of the valid memory location to name a few. This may result in exposure of sensitive information or possibly a crash.
Time of Introduction
Implementation
Applicable Platforms
Languages
C
C++
Common Consequences
Scope
Effect
Confidentiality
Technical Impact: Read memory
Demonstrative Examples
Example 1
In the following C/C++ example the method processMessageFromSocket()
will get a message from a socket, placed into a buffer, and will parse the
contents of the buffer into a structure that contains the message length and
the message body. A for loop is used to copy the message body into a local
character string which will be passed to another method for
processing.
(Bad Code)
Example Languages: C and C++
int processMessageFromSocket(int socket) {
int success;
char buffer[BUFFER_SIZE];
char message[MESSAGE_SIZE];
// get message from socket and store into buffer
//Ignoring possibliity that buffer >
BUFFER_SIZE
if (getMessage(socket, buffer, BUFFER_SIZE) > 0) {
// place contents of the buffer into message
structure
ExMessage *msg = recastBuffer(buffer);
// copy message body into string for
processing
int index;
for (index = 0; index < msg->msgLength; index++)
{
message[index] = msg->msgBody[index];
}
message[index] = '\0';
// process message
success = processMessage(message);
}
return success;
}
However, the message length variable from the structure is used as the condition for ending the for loop without validating that the message length variable accurately reflects the length of message body. This can result in a buffer over read by reading from memory beyond the bounds of the buffer if the message length variable indicates a length that is longer than the size of a message body (CWE-130).
Weakness Ordinalities
Ordinality
Description
Primary
(where
the weakness exists independent of other weaknesses)
The software writes to a buffer using an index or pointer that references a memory location prior to the beginning of the buffer.
Extended Description
This typically occurs when a pointer or its index is decremented to a position before the buffer, when pointer arithmetic results in a position before the beginning of the valid memory location, or when a negative index is used.
Alternate Terms
buffer underrun:
Some prominent vendors and researchers use the term "buffer underrun". "Buffer underflow" is more commonly used, although both terms are also sometimes used to describe a buffer under-read (CWE-127).
Out of bounds memory access will very likely result in the corruption
of relevant memory, and perhaps instructions, possibly leading to a
crash.
Integrity
Confidentiality
Availability
Access Control
Other
Technical Impact: Execute unauthorized code or
commands; Modify memory; Bypass protection
mechanism; Other
If the corrupted memory can be effectively controlled, it may be
possible to execute arbitrary code. If the corrupted memory is data
rather than instructions, the system will continue to function with
improper changes, possibly in violation of an implicit or explicit
policy. The consequences would only be limited by how the affected data
is used, such as an adjacent memory location that is used to specify
whether the user has special privileges.
Access Control
Other
Technical Impact: Bypass protection
mechanism; Other
When the consequence is arbitrary code execution, this can often be
used to subvert any other security service.
Likelihood of Exploit
Medium
Demonstrative Examples
Example 1
In the following C/C++ example, a utility function is used to trim
trailing whitespace from a character string. The function copies the input
string to a local character string and uses a while statement to remove the
trailing whitespace by moving backward through the string and overwriting
whitespace with a NUL character.
(Bad Code)
Example Languages: C and C++
char* trimTrailingWhitespace(char *strMessage, int length)
{
char *retMessage;
char *message = malloc(sizeof(char)*(length+1));
// copy input string to a temporary string
char message[length+1];
int index;
for (index = 0; index < length; index++) {
message[index] = strMessage[index];
}
message[index] = '\0';
// trim trailing whitespace
int len = index-1;
while (isspace(message[len])) {
message[len] = '\0';
len--;
}
// return string without trailing whitespace
retMessage = message;
return retMessage;
}
However, this function can cause a buffer underwrite if the input
character string contains all whitespace. On some systems the while
statement will move backwards past the beginning of a character string
and will call the isspace() function on an address outside of the bounds
of the local buffer.
Example 2
The following is an example of code that may result in a buffer
underwrite, if find() returns a negative value to indicate that ch is not
found in srcBuf:
Buffer underflow from an all-whitespace string,
which causes a counter to be decremented before the buffer while looking for
a non-whitespace character.
This could be resultant from several errors, including a bad offset or an array index that decrements before the beginning of the buffer (see CWE-129).
Research Gaps
Much attention has been paid to buffer overflows, but "underflows"
sometimes exist in products that are relatively free of overflows, so it is
likely that this variant has been under-studied.
Causal Nature
Explicit
Taxonomy Mappings
Mapped Taxonomy Name
Node ID
Fit
Mapped Node Name
PLOVER
UNDER - Boundary beginning violation ('buffer
underflow'?)
Cleansing, Canonicalization, and Comparison Errors
Definition in a New Window
Category ID: 171 (Category)
Status: Draft
Description
Description Summary
Weaknesses in this category are related to improper handling of data within protection mechanisms that attempt to perform neutralization for untrusted data.
Applicable Platforms
Languages
Language-independent
Potential Mitigations
Avoid making decisions based on names of resources (e.g. files) if
those resources can have alternate names.
Assume all input is malicious. Use an appropriate combination of black
lists and white lists to ensure only valid, expected and appropriate
input is processed by the system. For example, valid input may be in the
form of an absolute pathname(s). You can also limit pathnames to exist
on selected drives, have the format specified to include only separator
characters (forward or backward slashes) and alphanumeric characters,
and follow a naming convention such as having a maximum of 32 characters
followed by a '.' and ending with specified extensions.
Canonicalize the name to match that of the file system's
representation of the name. This can sometimes be achieved with an
available API (e.g. in Win32 the GetFullPathName function).
The software is composed of a server that relies on the client to implement a mechanism that is intended to protect the server.
Extended Description
When the server relies on protection mechanisms placed on the client side, an attacker can modify the client-side behavior to bypass the protection mechanisms resulting in potentially unexpected interactions between the client and server. The consequences will vary, depending on what the mechanisms are trying to protect.
Client-side validation checks can be easily bypassed, allowing
malformed or unexpected input to pass into the application, potentially
as trusted data. This may lead to unexpected states, behaviors and
possibly a resulting crash.
Access Control
Technical Impact: Bypass protection
mechanism; Gain privileges / assume
identity
Client-side checks for authentication can be easily bypassed, allowing
clients to escalate their access levels and perform unintended
actions.
Likelihood of Exploit
Medium
Enabling Factors for Exploitation
Consider a product that consists of two or more processes or nodes that
must interact closely, such as a client/server model. If the product uses
protection schemes in the client in order to defend from attacks against the
server, and the server does not use the same schemes, then an attacker could
modify the client in a way that bypasses those schemes. This is a
fundamental design flaw that is primary to many weaknesses.
Demonstrative Examples
Example 1
This example contains client-side code that checks if the user
authenticated successfully before sending a command. The server-side code
performs the authentication in one step, and executes the command in a
separate step.
CLIENT-SIDE (client.pl)
(Good Code)
Example
Language: Perl
$server = "server.example.com";
$username = AskForUserName();
$password = AskForPassword();
$address = AskForAddress();
$sock = OpenSocket($server, 1234);
writeSocket($sock, "AUTH $username $password\n");
$resp = readSocket($sock);
if ($resp eq "success") {
# username/pass is valid, go ahead and update the
info!
writeSocket($sock, "FAILURE -- address is
malformed\n");
}
}
The server accepts 2 commands, "AUTH" which authenticates the user,
and "CHANGE-ADDRESS" which updates the address field for the username.
The client performs the authentication and only sends a CHANGE-ADDRESS
for that user if the authentication succeeds. Because the client has
already performed the authentication, the server assumes that the
username in the CHANGE-ADDRESS is the same as the authenticated user. An
attacker could modify the client by removing the code that sends the
"AUTH" command and simply executing the CHANGE-ADDRESS.
client allows server to modify client's
configuration and overwrite arbitrary files.
Potential Mitigations
Phase: Architecture and Design
For any security checks that are performed on the client side, ensure
that these checks are duplicated on the server side. Attackers can
bypass the client-side checks by modifying values after the checks have
been performed, or by changing the client to remove the client-side
checks entirely. Then, these modified values would be submitted to the
server.
Even though client-side checks provide minimal benefits with respect
to server-side security, they are still useful. First, they can support
intrusion detection. If the server receives input that should have been
rejected by the client, then it may be an indication of an attack.
Second, client-side error-checking can provide helpful feedback to the
user about the expectations for valid input. Third, there may be a
reduction in server-side processing time for accidental input errors,
although this is typically a small savings.
Phase: Architecture and Design
If some degree of trust is required between the two entities, then use
integrity checking and strong authentication to ensure that the inputs
are coming from a trusted source. Design the product so that this trust
is managed in a centralized fashion, especially if there are complex or
numerous communication channels, in order to reduce the risks that the
implementer will mistakenly omit a check in a single code path.
Phase: Testing
Use dynamic tools and techniques that interact with the software using
large test suites with many diverse inputs, such as fuzz testing
(fuzzing), robustness testing, and fault injection. The software's
operation may slow down, but it should not become unstable, crash, or
generate incorrect results.
Phase: Testing
Use tools and techniques that require manual (human) analysis, such as
penetration testing, threat modeling, and interactive tools that allow
the tester to record and modify an active session. These may be more
effective than strictly automated techniques. This is especially the
case with weaknesses that are related to design and business
rules.
Weakness Ordinalities
Ordinality
Description
Primary
(where
the weakness exists independent of other weaknesses)
Server-side enforcement of client-side security is conceptually likely to
occur, but some architectures might have these strong dependencies as part
of legitimate behavior, such as thin clients.
The code contains a class with sensitive data, but the class is cloneable. The data can then be accessed by cloning the class.
Extended Description
Cloneable classes are effectively open classes, since data cannot be hidden in them. Classes that do not explicitly deny cloning can be cloned by any other class without running the constructor.
Time of Introduction
Implementation
Applicable Platforms
Languages
C++
Java
.NET
Common Consequences
Scope
Effect
Access Control
Technical Impact: Bypass protection
mechanism
A class that can be cloned can be produced without executing the
constructor. This is dangerous since the constructor may perform
security-related checks. By allowing the object to be cloned, those
checks may be bypassed.
Likelihood of Exploit
Medium
Demonstrative Examples
Example 1
(Bad Code)
Example
Language: Java
public class CloneClient {
public CloneClient() //throws
java.lang.CloneNotSupportedException {
Teacher t1 = new Teacher("guddu","22,nagar road");
//...
// Do some stuff to remove the teacher.
Teacher t2 = (Teacher)t1.clone();
System.out.println(t2.name);
}
public static void main(String args[]) {
new CloneClient();
}
}
class Teacher implements Cloneable {
public Object clone() {
try {
return super.clone();
}
catch (java.lang.CloneNotSupportedException e) {
throw new RuntimeException(e.toString());
}
}
public String name;
public String clas;
public Teacher(String name,String clas) {
this.name = name;
this.clas = clas;
}
}
Potential Mitigations
Phase: Implementation
Make classes uncloneable by defining a clone function like:
(Mitigation Code)
Example
Language: Java
public final void clone() throws
java.lang.CloneNotSupportedException {
throw new java.lang.CloneNotSupportedException();
}
Phase: Implementation
If you do make your classes clonable, ensure that your clone method is
final and throw super.clone().
chain: HTTP server protects against ".." but allows "." variants such as "////./../.../". If the server removes "/.." sequences, the result would collapse into an unsafe value "////../" (CWE-182).
XSS protection mechanism strips a <script>
sequence that is nested in another <script>
sequence.
Potential Mitigations
Avoid making decisions based on names of resources (e.g. files) if
those resources can have alternate names.
Phase: Implementation
Strategy: Input Validation
Assume all input is malicious. Use an "accept known good" input
validation strategy, i.e., use a whitelist of acceptable inputs that
strictly conform to specifications. Reject any input that does not
strictly conform to specifications, or transform it into something that
does.
When performing input validation, consider all potentially relevant
properties, including length, type of input, the full range of
acceptable values, missing or extra inputs, syntax, consistency across
related fields, and conformance to business rules. As an example of
business rule logic, "boat" may be syntactically valid because it only
contains alphanumeric characters, but it is not valid if the input is
only expected to contain colors such as "red" or "blue."
Do not rely exclusively on looking for malicious or malformed inputs
(i.e., do not rely on a blacklist). A blacklist is likely to miss at
least one undesirable input, especially if the code's environment
changes. This can give attackers enough room to bypass the intended
validation. However, blacklists can be useful for detecting potential
attacks or determining which inputs are so malformed that they should be
rejected outright.
Use and specify a strong output encoding (such as ISO 8859-1 or UTF
8).
Phase: Implementation
Strategy: Input Validation
Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180). Make sure that the application does not decode the same input twice (CWE-174). Such errors could be used to bypass whitelist validation schemes by introducing dangerous inputs after they have been checked.
Canonicalize the name to match that of the file system's
representation of the name. This can sometimes be achieved with an
available API (e.g. in Win32 the GetFullPathName function).
Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')
Definition in a New Window
Weakness ID: 362 (Weakness Class)
Status: Draft
Description
Description Summary
The program contains a code sequence that can run concurrently with other code, and the code sequence requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence that is operating concurrently.
Extended Description
This can have security implications when the expected synchronization is in security-critical code, such as recording whether a user is authenticated or modifying important state information that should not be influenced by an outsider.
A race condition occurs within concurrent environments, and is effectively a property of a code sequence. Depending on the context, a code sequence may be in the form of a function call, a small number of instructions, a series of program invocations, etc.
A race condition violates these properties, which are closely related:
Exclusivity - the code sequence is given exclusive access to the shared resource, i.e., no other code sequence can modify properties of the shared resource before the original sequence has completed execution.
Atomicity - the code sequence is behaviorally atomic, i.e., no other thread or process can concurrently execute the same sequence of instructions (or a subset) against the same resource.
A race condition exists when an "interfering code sequence" can still access the shared resource, violating exclusivity. Programmers may assume that certain code sequences execute too quickly to be affected by an interfering code sequence; when they are not, this violates atomicity. For example, the single "x++" statement may appear atomic at the code layer, but it is actually non-atomic at the instruction layer, since it involves a read (the original value of x), followed by a computation (x+1), followed by a write (save the result to x).
The interfering code sequence could be "trusted" or "untrusted." A trusted interfering code sequence occurs within the program; it cannot be modified by the attacker, and it can only be invoked indirectly. An untrusted interfering code sequence can be authored directly by the attacker, and typically it is external to the vulnerable program.
Time of Introduction
Architecture and Design
Implementation
Applicable Platforms
Languages
C: (Sometimes)
C++: (Sometimes)
Java: (Sometimes)
Language-independent
Architectural Paradigms
Concurrent Systems Operating on Shared Resources: (Often)
When a race condition makes it possible to bypass a resource cleanup routine or trigger multiple initialization routines, it may lead to resource exhaustion (CWE-400).
When a race condition allows multiple control flows to access a
resource simultaneously, it might lead the program(s) into unexpected
states, possibly resulting in a crash.
Confidentiality
Integrity
Technical Impact: Read files or
directories; Read application
data
When a race condition is combined with predictable resource names and loose permissions, it may be possible for an attacker to overwrite or access confidential data (CWE-59).
Likelihood of Exploit
Medium
Detection Methods
Black Box
Black box methods may be able to identify evidence of race conditions
via methods such as multiple simultaneous connections, which may cause
the software to become instable or crash. However, race conditions with
very narrow timing windows would not be detectable.
White Box
Common idioms are detectable in white box analysis, such as time-of-check-time-of-use (TOCTOU) file operations (CWE-367), or double-checked locking (CWE-609).
Automated Dynamic Analysis
This weakness can be detected using dynamic tools and techniques that
interact with the software using large test suites with many diverse
inputs, such as fuzz testing (fuzzing), robustness testing, and fault
injection. The software's operation may slow down, but it should not
become unstable, crash, or generate incorrect results.
Race conditions may be detected with a stress-test by calling the
software simultaneously from a large number of threads or processes, and
look for evidence of any unexpected behavior.
Insert breakpoints or delays in between relevant code statements to
artificially expand the race window so that it will be easier to
detect.
Effectiveness: Moderate
Demonstrative Examples
Example 1
This code could be used in an e-commerce application that supports
transfers between accounts. It takes the total amount of the transfer, sends
it to the new account, and deducts the amount from the original
account.
(Bad Code)
Example
Language: Perl
$transfer_amount = GetTransferAmount();
$balance = GetBalanceFromDatabase();
if ($transfer_amount < 0) {
FatalError("Bad Transfer Amount");
}
$newbalance = $balance - $transfer_amount;
if (($balance - $transfer_amount) < 0) {
FatalError("Insufficient Funds");
}
SendNewBalanceToDatabase($newbalance);
NotifyUser("Transfer of $transfer_amount succeeded.");
NotifyUser("New balance: $newbalance");
A race condition could occur between the calls to
GetBalanceFromDatabase() and SendNewBalanceToDatabase().
Suppose the balance is initially 100.00. An attack could be
constructed as follows:
(Attack)
Example
Language: PseudoCode
The attacker makes two simultaneous calls of the program, CALLER-1
and CALLER-2. Both callers are for the same user account.
CALLER-1 (the attacker) is associated with PROGRAM-1 (the instance
that handles CALLER-1). CALLER-2 is associated with
PROGRAM-2.
CALLER-1 makes a transfer request of 80.00.
PROGRAM-1 calls GetBalanceFromDatabase and sets $balance to
100.00
PROGRAM-1 calculates $newbalance as 20.00, then calls
SendNewBalanceToDatabase().
Due to high server load, the PROGRAM-1 call to
SendNewBalanceToDatabase() encounters a delay.
CALLER-2 makes a transfer request of 1.00.
PROGRAM-2 calls GetBalanceFromDatabase() and sets $balance to
100.00. This happens because the previous PROGRAM-1 request was not
processed yet.
PROGRAM-2 determines the new balance as 99.00.
After the initial delay, PROGRAM-1 commits its balance to the
database, setting it to 20.00.
PROGRAM-2 sends a request to update the database, setting the
balance to 99.00
At this stage, the attacker should have a balance of 19.00 (due to
81.00 worth of transfers), but the balance is 99.00, as recorded in the
database.
To prevent this weakness, the programmer has several options,
including using a lock to prevent multiple simultaneous requests to the
web application, or using a synchronization mechanism that includes all
the code between GetBalanceFromDatabase() and
SendNewBalanceToDatabase().
Example 2
The following function attempts to acquire a lock in order to
perform operations on a shared resource.
(Bad Code)
Example
Language: C
void f(pthread_mutex_t *mutex) {
pthread_mutex_lock(mutex);
/* access shared resource */
pthread_mutex_unlock(mutex);
}
However, the code does not check the value returned by
pthread_mutex_lock() for errors. If pthread_mutex_lock() cannot acquire
the mutex for any reason, the function may introduce a race condition
into the program and result in undefined behavior.
In order to avoid data races, correctly written programs must check
the result of thread synchronization functions and appropriately handle
all errors, either by attempting to recover from them or reporting it to
higher levels.
chain: time-of-check time-of-use (TOCTOU) race
condition in program allows bypass of protection mechanism that was designed
to prevent symlink attacks.
chain: time-of-check time-of-use (TOCTOU) race
condition in program allows bypass of protection mechanism that was designed
to prevent symlink attacks.
chain: race condition might allow resource to be
released before operating on it, leading to NULL dereference
Potential Mitigations
Phase: Architecture and Design
In languages that support it, use synchronization primitives. Only
wrap these around critical code to minimize the impact on
performance.
Phase: Architecture and Design
Use thread-safe capabilities such as the data access abstraction in
Spring.
Phase: Architecture and Design
Minimize the usage of shared resources in order to remove as much
complexity as possible from the control flow and to reduce the
likelihood of unexpected conditions occurring.
Additionally, this will minimize the amount of synchronization necessary and may even help to reduce the likelihood of a denial of service where an attacker may be able to repeatedly trigger a critical section (CWE-400).
Phase: Implementation
When using multithreading and operating on shared variables, only use
thread-safe functions.
Phase: Implementation
Use atomic operations on shared variables. Be wary of innocent-looking
constructs such as "x++". This may appear atomic at the code layer, but
it is actually non-atomic at the instruction layer, since it involves a
read, followed by a computation, followed by a write.
Phase: Implementation
Use a mutex if available, but be sure to avoid related weaknesses such as CWE-412.
Phase: Implementation
Avoid double-checked locking (CWE-609) and other implementation errors that arise when trying to avoid the overhead of synchronization.
Phase: Implementation
Disable interrupts or signals over critical parts of the code, but
also make sure that the code does not go into a large or infinite
loop.
Phase: Implementation
Use the volatile type modifier for critical variables to avoid
unexpected compiler optimization or reordering. This does not
necessarily solve the synchronization problem, but it can help.
Phases: Architecture and Design; Operation
Strategy: Environment Hardening
Run your code using the lowest privileges that are required to
accomplish the necessary tasks. If possible, create isolated accounts
with limited privileges that are only used for a single task. That way,
a successful attack will not immediately give the attacker access to the
rest of the software or its environment. For example, database
applications rarely need to run as the database administrator,
especially in day-to-day operations.
Race conditions in web applications are under-studied and probably
under-reported. However, in 2008 there has been growing interest in this
area.
Much of the focus of race condition research has been in Time-of-check Time-of-use (TOCTOU) variants (CWE-367), but many race conditions are related to synchronization problems that do not necessarily require a time-of-check.
Taxonomy Mappings
Mapped Taxonomy Name
Node ID
Fit
Mapped Node Name
PLOVER
Race Conditions
CERT C Secure Coding
FIO31-C
Do not simultaneously open the same file multiple
times
CERT Java Secure Coding
VNA03-J
Do not assume that a group of calls to independently atomic
methods is atomic
CERT C++ Secure Coding
FIO31-CPP
Do not simultaneously open the same file multiple
times
The relationship between race conditions and synchronization problems (CWE-662) needs to be further developed. They are not necessarily two perspectives of the same core concept, since synchronization is only one technique for avoiding race conditions, and synchronization can be used for other purposes besides race condition prevention.
The wrong "handler" is assigned to process an object.
Extended Description
An example of deploying the wrong handler would be calling a servlet to reveal source code of a .JSP file, or automatically "determining" type of the object even if it is contradictory to an explicitly specified type.
Time of Introduction
Implementation
Applicable Platforms
Languages
All
Common Consequences
Scope
Effect
Integrity
Other
Technical Impact: Varies by context; Unexpected state
The software detects a specific error, but takes no actions to handle the error.
Time of Introduction
Architecture and Design
Implementation
Applicable Platforms
Languages
All
Common Consequences
Scope
Effect
Integrity
Other
Technical Impact: Varies by context; Unexpected state; Alter execution
logic
Likelihood of Exploit
Medium
Demonstrative Examples
Example 1
The following example attempts to allocate memory for a character.
After the call to malloc, an if statement is used to check whether the
malloc function failed.
(Bad Code)
Example
Language: C
foo=malloc(sizeof(char)); //the next line checks to see if malloc
failed
if (foo==NULL) {
//We do nothing so we just ignore the error.
}
The conditional successfully detects a NULL return value from malloc
indicating a failure, however it does not do anything to handle the
problem. Unhandled errors may have unexpected results and may cause the
program to crash or terminate.
Instead, the if block should contain statements that either attempt to
fix the problem or notify the user that an error has occurred and
continue processing or perform some cleanup and gracefully terminate the
program. The following example notifies the user that the malloc
function did not allocate the required memory resources and returns an
error code.
(Good Code)
Example
Language: C
foo=malloc(sizeof(char)); //the next line checks to see if malloc
failed
if (foo==NULL) {
printf("Malloc failed to allocate memory resources");
return -1;
}
Example 2
In the following C++ example the method readFile() will read the
file whose name is provided in the input parameter and will return the
contents of the file in char string. The method calls open() and read() may
result in errors if the file does not exist or does not contain any data to
read. These errors will be thrown when the is_open() method and good()
method indicate errors opening or reading the file. However, these errors
are not handled within the catch statement. Catch statements that do not
perform any processing will have unexpected results. In this case an empty
char string will be returned, and the file will not be properly
closed.
(Bad Code)
Example
Language: C++
char* readfile (char *filename) {
try {
// open input file
ifstream infile;
infile.open(filename);
if (!infile.is_open()) {
throw "Unable to open file " + filename;
}
// get length of file
infile.seekg (0, ios::end);
int length = infile.tellg();
infile.seekg (0, ios::beg);
// allocate memory
char *buffer = new char [length];
// read data from file
infile.read (buffer,length);
if (!infile.good()) {
throw "Unable to read from file " + filename;
}
infile.close();
return buffer;
}
catch (...) {
/* bug: insert code to handle this later */
}
}
The catch statement should contain statements that either attempt to
fix the problem or notify the user that an error has occurred and
continue processing or perform some cleanup and gracefully terminate the
program. The following C++ example contains two catch statements. The
first of these will catch a specific error thrown within the try block,
and the second catch statement will catch all other errors from within
the catch block. Both catch statements will notify the user that an
error has occurred, close the file, and rethrow to the block that called
the readFile() method for further handling or possible termination of
the program.
(Good Code)
Example
Language: C++
char* readFile (char *filename) {
try {
// open input file
ifstream infile;
infile.open(filename);
if (!infile.is_open()) {
throw "Unable to open file " + filename;
}
// get length of file
infile.seekg (0, ios::end);
int length = infile.tellg();
infile.seekg (0, ios::beg);
// allocate memory
char *buffer = new char [length];
// read data from file
infile.read (buffer,length);
if (!infile.good()) {
throw "Unable to read from file " + filename;
}
infile.close();
return buffer;
}
catch (char *str) {
printf("Error: %s \n", str);
infile.close();
throw str;
}
catch (...) {
printf("Error occurred trying to read from file
\n");
infile.close();
throw;
}
}
Example 3
In the following Java example the method readFile will read the file
whose name is provided in the input parameter and will return the contents
of the file in a String object. The constructor of the FileReader object and
the read method call may throw exceptions and therefore must be within a
try/catch block. While the catch statement in this example will catch thrown
exceptions in order for the method to compile, no processing is performed to
handle the thrown exceptions. Catch statements that do not perform any
processing will have unexpected results. In this case, this will result in
the return of a null String.
(Bad Code)
Example
Language: Java
public String readFile(String filename) {
String retString = null;
try {
// initialize File and FileReader objects
File file = new File(filename);
FileReader fr = new FileReader(file);
// initialize character buffer
long fLen = file.length();
char[] cBuf = new char[(int) fLen];
// read data from file
int iRead = fr.read(cBuf, 0, (int) fLen);
// close file
fr.close();
retString = new String(cBuf);
} catch (Exception ex) {
/* do nothing, but catch so it'll compile... */
}
return retString;
}
The catch statement should contain statements that either attempt to
fix the problem, notify the user that an exception has been raised and
continue processing, or perform some cleanup and gracefully terminate
the program. The following Java example contains three catch statements.
The first of these will catch the FileNotFoundException that may be
thrown by the FileReader constructor called within the try/catch block.
The second catch statement will catch the IOException that may be thrown
by the read method called within the try/catch block. The third catch
statement will catch all other exceptions thrown within the try block.
For all catch statements the user is notified that the exception has
been thrown and the exception is rethrown to the block that called the
readFile() method for further processing or possible termination of the
program. Note that with Java it is usually good practice to use the
getMessage() method of the exception class to provide more information
to the user about the exception raised.
(Good Code)
Example
Language: Java
public String readFile(String filename) throws
FileNotFoundException, IOException, Exception {
System.err.println("Error: IOException reading the input
file.\n" + ex.getMessage() );
throw new IOException(ex);
} catch (Exception ex) {
System.err.println("Error: Exception reading the input
file.\n" + ex.getMessage() );
throw new Exception(ex);
}
return retString;
}
Potential Mitigations
Phase: Implementation
Properly handle each exception. This is the recommended solution.
Ensure that all exceptions are handled in such a way that you can be
sure of the state of your system at any given moment.
Phase: Implementation
If a function returns an error, it is important to either fix the
problem and try again, alert the user that an error has happened and let
the program continue, or alert the user and close and cleanup the
program.
Phase: Testing
Subject the software to extensive testing to discover some of the
possible instances of where/how errors or return values are not handled.
Consider testing techniques such as ad hoc, equivalence partitioning,
robustness and fault tolerance, mutation, and fuzzing.
The web application does not adequately enforce appropriate authorization on all restricted URLs, scripts, or files.
Extended Description
Web applications susceptible to direct request attacks often make the false assumption that such resources can only be reached through a given navigation path and so only apply authorization at certain points in the path.
Alternate Terms
forced browsing:
The "forced browsing" term could be misinterpreted to include
weaknesses such as CSRF or XSS, so its use is discouraged.
Time of Introduction
Architecture and Design
Implementation
Operation
Applicable Platforms
Languages
Language-independent
Common Consequences
Scope
Effect
Confidentiality
Integrity
Availability
Access Control
Technical Impact: Read application
data; Modify application
data; Execute unauthorized code or
commands; Gain privileges / assume
identity
Demonstrative Examples
Example 1
If forced browsing is possible, an attacker may be able to directly
access a sensitive page by entering a URL similar to the
following.
Overlaps Modification of Assumed-Immutable Data (MAID), authorization
errors, container errors; often primary to other weaknesses such as XSS and
SQL injection.
Theoretical Notes
"Forced browsing" is a step-based manipulation involving the omission of
one or more steps, whose order is assumed to be immutable. The application
does not verify that the first step was performed successfully before the
second step. The consequence is typically "authentication bypass" or "path
disclosure," although it can be primary to all kinds of weaknesses,
especially in languages such as PHP, which allow external modification of
assumed-immutable variables.
The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
Extended Description
When a program calls free() twice with the same argument, the program's memory management data structures become corrupted. This corruption can cause the program to crash or, in some circumstances, cause two later calls to malloc() to return the same pointer. If malloc() returns the same value twice and the program later gives the attacker control over the data that is written into this doubly-allocated memory, the program becomes vulnerable to a buffer overflow attack.
Alternate Terms
Double-free
Time of Introduction
Architecture and Design
Implementation
Applicable Platforms
Languages
C
C++
Common Consequences
Scope
Effect
Integrity
Confidentiality
Availability
Technical Impact: Execute unauthorized code or
commands
Doubly freeing memory may result in a write-what-where condition,
allowing an attacker to execute arbitrary code.
Likelihood of Exploit
Low to Medium
Demonstrative Examples
Example 1
The following code shows a simple example of a double free
vulnerability.
(Bad Code)
Example
Language: C
char* ptr = (char*)malloc (SIZE);
...
if (abrt) {
free(ptr);
}
...
free(ptr);
Double free vulnerabilities have two common (and sometimes
overlapping) causes:
Error conditions and other exceptional circumstances
Confusion over which part of the program is responsible for
freeing the memory
Although some double free vulnerabilities are not much more
complicated than the previous example, most are spread out across
hundreds of lines of code or even different files. Programmers seem
particularly susceptible to freeing global variables more than
once.
Example 2
While contrived, this code should be exploitable on Linux
distributions which do not ship with heap-chunk check summing turned
on.
Choose a language that provides automatic memory management.
Phase: Implementation
Ensure that each allocation is freed only once. After freeing a chunk,
set the pointer to NULL to ensure the pointer cannot be freed again. In
complicated error conditions, be sure that clean-up routines respect the
state of allocation properly. If the language is object oriented, ensure
that object destructors delete each chunk of memory only once.
Phase: Implementation
Use a static analysis tool to find double free instances.
This is usually resultant from another weakness, such as an unhandled
error or race condition between threads. It could also be primary to
weaknesses such as buffer overflows.
Affected Resources
Memory
Taxonomy Mappings
Mapped Taxonomy Name
Node ID
Fit
Mapped Node Name
PLOVER
DFREE - Double-Free Vulnerability
7 Pernicious Kingdoms
Double Free
CLASP
Doubly freeing memory
CERT C Secure Coding
MEM00-C
Allocate and free memory in the same module, at the same level
of abstraction
CERT C Secure Coding
MEM01-C
Store a new value in pointers immediately after
free()
CERT C Secure Coding
MEM31-C
Free dynamically allocated memory exactly
once
CERT C++ Secure Coding
MEM01-CPP
Store a valid value in pointers immediately after
deallocation
CERT C++ Secure Coding
MEM31-CPP
Free dynamically allocated memory exactly
once
White Box Definitions
A weakness where code path has:
1. start statement that relinquishes a dynamically allocated memory
resource
2. end statement that relinquishes the dynamically allocated memory
resource
Maintenance Notes
It could be argued that Double Free would be most appropriately located as
a child of "Use after Free", but "Use" and "Release" are considered to be
distinct operations within vulnerability theory, therefore this is more
accurately "Release of a Resource after Expiration or Release", which
doesn't exist yet.
The program uses double-checked locking to access a resource without the overhead of explicit synchronization, but the locking is insufficient.
Extended Description
Double-checked locking refers to the situation where a programmer checks to see if a resource has been initialized, grabs a lock, checks again to see if the resource has been initialized, and then performs the initialization if it has not occurred yet. This should not be done, as is not guaranteed to work in all languages and on all architectures. In summary, other threads may not be operating inside the synchronous block and are not guaranteed to see the operations execute in the same order as they would appear inside the synchronous block.
Time of Introduction
Implementation
Applicable Platforms
Languages
Java
Common Consequences
Scope
Effect
Integrity
Other
Technical Impact: Modify application
data; Alter execution
logic
Demonstrative Examples
Example 1
It may seem that the following bit of code achieves thread safety
while avoiding unnecessary synchronization...
(Bad Code)
Example
Language: Java
if (helper == null) {
synchronized (this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
The programmer wants to guarantee that only one Helper() object is
ever allocated, but does not want to pay the cost of synchronization
every time this code is called.
Let's say helper is not initialized. Then, thread A comes along, sees
that helper==null, and enters the synchronized block and begins to
execute:
(Bad Code)
helper = new Helper();
If a second thread, thread B, takes over in the middle of this call
and helper has not finished running the constructor, then thread B may
make calls on helper while its fields hold incorrect values.
Potential Mitigations
While double-checked locking can be achieved in some languages, it is
inherently flawed in Java before 1.5, and cannot be achieved without
compromising platform independence. Before Java 1.5, only use of the
synchronized keyword is known to work. Beginning in Java 1.5, use of the
"volatile" keyword allows double-checked locking to work successfully,
although there is some debate as to whether it achieves sufficient
performance gains. See references.
The product downloads source code or an executable from a remote location and executes the code without sufficiently verifying the origin and integrity of the code.
Extended Description
An attacker can execute malicious code by compromising the host server, performing DNS spoofing, or modifying the code in transit.
Time of Introduction
Architecture and Design
Implementation
Applicable Platforms
Languages
Language-independent
Common Consequences
Scope
Effect
Integrity
Availability
Confidentiality
Other
Technical Impact: Execute unauthorized code or
commands; Alter execution
logic; Other
Executing untrusted code could compromise the control flow of the
program. The untrusted code could execute attacker-controlled commands,
read or modify sensitive resources, or prevent the software from
functioning correctly for legitimate users.
Likelihood of Exploit
Medium
Detection Methods
Manual Analysis
This weakness can be detected using tools and techniques that require
manual (human) analysis, such as penetration testing, threat modeling,
and interactive tools that allow the tester to record and modify an
active session.
Specifically, manual static analysis is typically required to find the
behavior that triggers the download of code, and to determine whether
integrity-checking methods are in use.
These may be more effective than strictly automated techniques. This
is especially the case with weaknesses that are related to design and
business rules.
Black Box
Use monitoring tools that examine the software's process as it
interacts with the operating system and the network. This technique is
useful in cases when source code is unavailable, if the software was not
developed by you, or if you want to verify that the build phase did not
introduce any new weaknesses. Examples include debuggers that directly
attach to the running process; system-call tracing utilities such as
truss (Solaris) and strace (Linux); system activity monitors such as
FileMon, RegMon, Process Monitor, and other Sysinternals utilities
(Windows); and sniffers and protocol analyzers that monitor network
traffic.
Attach the monitor to the process and also sniff the network
connection. Trigger features related to product updates or plugin
installation, which is likely to force a code download. Monitor when
files are downloaded and separately executed, or if they are otherwise
read back into the process. Look for evidence of cryptographic library
calls that use integrity checking.
Demonstrative Examples
Example 1
This example loads an external class from a local
subdirectory.
(Bad Code)
Example
Language: Java
URL[] classURLs= new URL[]{
new URL("file:subdir/")
};
URLClassLoader loader = new URLClassLoader(classURLs);
Class loadedClass = Class.forName("loadMe", true, loader);
This code does not ensure that the class loaded is the intended one,
for example by verifying the class's checksum. An attacker may be able
to modify the class file to execute malicious code.
Example 2
This code includes an external script to get database credentials,
then authenticates a user against the database, allowing access to the
application.
(Bad Code)
Example
Language: PHP
//assume the password is already encrypted, avoiding
CWE-312
//dbInfo.php makes $dbhost, $dbuser, $dbpass, $dbname
available
mysql_connect($dbhost, $dbuser, $dbpass) or die ('Error
connecting to mysql');
mysql_select_db($dbname);
$query = 'Select * from users where username='.$username.'
And password='.$password;
$result = mysql_query($query);
if(mysql_numrows($result) == 1){
mysql_close();
return true;
}
else{
mysql_close();
return false;
}
}
This code does not verify that the external domain accessed is the
intended one. An attacker may somehow cause the external domain name to
resolve to an attack server, which would provide the information for a
false database. The attacker may then steal the usernames and encrypted
passwords from real user login attempts, or simply