If a vulnerability exists in the operating system and applications you run, you can be sure that an attacker will find a way to exploit it. The only sure-fire way to eliminate such a threat is to fix the vulnerability in the codebase. But until a security patch is released, your systems are at the mercy of being exploited. Many of us accept this status quo.
Fortunately, there is a better option, and it's called rotational Moving Target Defense (rMTD).
rMTD is the process of making an existing vulnerability difficult to exploit. This can be achieved through a variety of different techniques that are either static – built in during the compilation of the application, referred as Compile Time Application Self Protection (CASP) – or dynamically enforced during runtime, referred to as Runtime Application Self Protection (RASP). CASP and RASP are not mutually exclusive and can be combined.
CASP modifies the application's generated assembly code during compilation in such a way that no two compilations generate the same assembly instruction set. Hackers rely on a known assembly layout from a generated static compilation in order to craft their attack. Once they've built their attack, they can target systems with the same binaries. They leverage the static nature of the compiled application or operating system to hijack systems.
This is analogous to a thief getting a copy of the same safe you have and having the time to figure out how to crack it. The only difference is that in the case of a hacker, it's a lot easier to get their hands on a copy of the software than a safe, and the vulnerability is known and published. With CASP, the same safe (i.e., application) looks very different under the hood (i.e., binary layout).
RASP takes the approach of creating entropy while the application runs. The binary layout is dynamically changed every time the application runs. The attacker can no longer make any assumptions of the layout of the application’s stack and addresses of functions and libraries. This is analogous to switching out the engine and reconfiguring the components of a car while simultaneously driving down the highway.
CASP and RASP are achieved through a variety of techniques. Let's take a look.
1. Assembly code modification: Substitute the library's original assembly instruction set with a different but logically equivalent instruction set. The Application Binary Interface (ABI) is the calling convention to interface with the binary. The modification of the assembly instruction set does not impact the ABI. Applications interfacing with this binary remains the same.
Attackers examine the layout of the assembly instruction set of a binary to craft their attacks. Their tool of choice, ADA, allows them to examine the compiled code, which produces assembly instructions, to search for code flaws (i.e., buffer overflows, underflow, integer overflow, double free calls, etc.). Once a flaw is discovered, they rely on the assembly layout of the binary to leverage surrounding assembly instructions to craft their attack. By modifying the assembly code unbeknown to the attacker, the attack fails because the assembly instruction layout of the binary was changed.
2. Randomization of the start address of every function within each library: Every library or module exposes functions that applications can call. By default, the start address of each function is fixed during compilation.
Attackers can examine the compiled binary and easily determine the address of each function without needing to examine the source code. This information is then used to craft an attack. When the stack is hijacked, the attack code can specify the address of the library the attacker wants to call to start a shell, for example.
3. Data structure protection: The memory layout of data structures in binaries is randomized during compilation and the data fields are dynamically encrypted at runtime to prevent them from being overwritten or read.
Data structures often contain buffers such as string buffers or arrays. These are targets for performing a buffer overflow attack. In order to successfully hijack the stack using a buffer defined in a data structure, the attacker must rely on the position of the buffer within the data structure. By randomizing the layout of the data structure during compilation, the attacker can no longer rely on determining the location of the buffer within the data structure.
1. Dynamic library address randomization: At runtime, the address of libraries is randomized. All references to a library are updated to reflect the new address. This measure makes it more difficult for attackers to call an external library from within the application they hijacked the process.
2. Dynamic function address randomization: The address of each function defined in a library is given a static address during compilation. In addition to randomizing the address of libraries, the address of each function is also dynamically changed during execution when an application calls it.
3. Dynamic stack canary: Instead of a static stack canary, which is a fixed random value that can be easily circumvented, the canary is randomized dynamically over time. By dynamically randomizing the stack canary, the attacker doesn't have enough time to guess it before it rotates.
4. Shadow stack: A shadow stack is a copy of the stack generated during compilation, where only the return addresses are stored in this shadow stack. The return addresses are encrypted in the shadow stack so they cannot be tampered by an attacker. Before the application reads the return address from the stack to jump back to the previous execution state, the return address is encrypted and compared against the copy in the shadow stack. If they match, execution jumps to the next instruction pointed to by the return address. If they do not match, an attacker modified the return address and an application fault is raised, causing the application to crash and an event to be logged.
5. PLT scrambling: The Procedure Linkage Table (PLT) is a lookup table for where to locate the address of functions exposed by other libraries. Instead of a static lookup table, the address of the functions of external libraries are randomly stored in the PLT during process execution.
This prevents attackers from calling a function to a known library, such as libc, to spawn a terminal, for example. By dynamically changing the location (i.e., address) of the functions defined in external libraries, the attacker can no longer perform a return-to-plt attack by discovering the address of an external function, such as the function, strcpy(), from the libc library in the PLT in order to call it.
rMTD is the next frontier in defending against zero-day attacks and making our computers and systems more resilient against hackers. As this technology matures and gains industrywide adoption, it could become as ubiquitous as antivirus software.