Software vulnerabilities have been a major cause of computer security incidents since the advent of multiuser and networked computing. Most of these software vulnerabilities can be traced back to a few mistakes that programmers make over and over again.
Even though many papers and books attempt to teach programmers how to program more securely, the problem persists and will most likely continue to be a major problem in the foreseeable future.
This paper focuses on a specific subclass of software vulnerabilities, implementation errors in C and C++, as well as the countermeasures that have been proposed and developed to deal with these vulnerabilities. More specifically, implementation errors that allow an attacker to break memory safety and execute foreign code are addressed in this survey.
Several preventive and defensive countermeasures have been proposed to com- bat exploitation of common implementation errors, and this paper examines many of these. We also describe several ways in which some of the proposed counter- measures can be circumvented.
The paper focuses on run-time countermeasures: only countermeasures that have some effect at run-time are in scope. This includes countermeasures that perform additional run-time checks, or harden the C/C++ run-time environment. It excludes purely static countermeasures, for instance those that try to detect vulnerabilities using static analysis or program verification. It also excludes testing approaches such as fuzzers. These areas are also very active research areas and deserve their own survey.
Although some countermeasures examined here protect against the more general case of buffer overflows, this paper focuses on protection against attacks that specifically attempt to execute code an application would not execute in normal circumstances. Such attacks subvert the control flow of the application either to injected code or to existing code which is then executed in a different context.
Given the large number of runtime countermeasures that have been proposed to deal with such attacks, and given the wide variety in techniques used in the design of these countermeasures, it is hard for an outsider of the research field itself to get a good understanding of existing solutions.
This paper aims to provide such an understanding to software engineers and computer scientists without specific security expertise, by providing a structured classification and evaluation framework. At the top level, we classify existing countermeasures based on the main technique they use to address the problem.
We will focus on languages that are similar to C, i.e., languages that stay as close to C and C++ as possible. These are mostly referred to as safe dialects of C. Programs written in these dialects generally have some restrictions in terms of memory management: the programmer no longer has explicit control over the dynamic memory allocator.
Bounds checkers perform bounds checks on array and pointer operations and detect when the program tries to perform an out of bounds operation and take action accordingly.Probabilistic countermeasures make use of randomness to make exploitation of vul- nerabilities harder.
Separators and replicators of information exist in two types: the first type will try to replicate valuable control-flow data or will separate this data from regular data. Replication can be used to verify the original value, while separation prevents an attacker from overwriting the separated data because it is no longer adjacent to the vulnerable object.
The second type relies on replication only, but replicates processes with some diversity introduced. If the processes act differently for the same input, then an attack has been detected. VMM-based countermeasures make use of properties of the virtual memory man- ager to build countermeasures.
Execution monitors observe specific security-relevant events (like system calls) and perform specific actions based on what is monitored. Some monitors will try to limit the damage of a successful attack on a vulnerability on the underlying system by limiting the actions a program can perform.
Others will detect if a program is exhibiting unexpected behavior and will provide alerts if this occurs. The first type of runtime monitor is called a sandbox, while the second type of monitoring is called anomaly detection. Hardened libraries replace library functions with versions that perform extra checks to ensure that the parameters are correct.
Runtime taint trackers will instrument the program to mark input as tainted. If such tainted data is later used in the program where untainted data is expected or is used to modify a trusted memory location (like a return address), then a fault is generated.
In this paper we present an overview of the most commonly exploited vulnerabilities that lead to code injection attacks. More importantly however, we also presented a survey of the many countermeasures that exist to protect against these vulnerabilities together with a framework for evaluating and classifying them.
We describe the many techniques used to build countermeasures and discussed some of their advantages and disadvantages. We also assigned specific properties to each of the countermeasures that allow the reader to evaluate which countermeasures could be most useful in a given context.
Although we tried to be as complete as possible when discussing the different countermeasures that exist, it can never be entirely complete. Countermeasure design is an active field, so this paper can only provide a snapshot of the current state of the field with respect to specific countermeasures. However, we believe we have provided a strong framework that can be applied to future countermeasures to further evaluate and classify these new countermeasures.
To read this external content in full, download the complete paper from the author archives online at Yves Younan's online archive .