Introduction
What are binary protections? How do they prevent us from exploiting binaries? Today, we'll delve into these binary protections, exploring their purpose and how they function.
What are binary protections?
Of course, let's start from the beginning. Binary protections, also known as security mechanisms or hardening techniques, are essential components embedded by the compiler into binary executables to mitigate various types of security vulnerabilities and exploitation techniques. These protections serve as safeguards against common attack vectors such as buffer overflows, format string vulnerabilities, and arbitrary code execution. Today, we'll discuss some of them and explain why they're important.
No eXecute (NX) / Data Execution Prevention (DEP)
The NX (No eXecute) protection, also known as DEP (Data Execution Prevention) protection, prevents certain memory regions from being executed as instructions. For example, in the context of a Stack-Based Buffer Overflow exploit, attackers typically insert shellcode onto the stack. By manipulating the return pointer, they aim to redirect the program's flow to the shellcode on the stack, executing it as malicious instructions. However, the NX protection prevents the stack from being executable, effectively thwarting attempts to run such malicious instructions. Even if an attacker successfully controls the return pointer, they would be unable to execute the injected shellcode due to this protection.
This protection is enabled by default by the compiler, unless you explicitly specify to make the stack executable. It's unlikely to encounter binaries without this protection in the wild.
Relocation Read-Only (RELRO)
This protection involves granting Read Only Permission to certain sections of memory. There are two ways to apply this protection: one is Partial RELRO, and the other is Full RELRO.
Partial RELRO: Partial RELRO, the default setting in GCC, provides a foundational level of protection against certain types of attacks targeting the Global Offset Table (GOT). In binaries configured with Partial RELRO, only certain portions of the GOT are made read-only, while the remaining parts retain their original permissions.
This setting ensures that critical entries in the GOT, such as those corresponding to essential system functions, are protected from tampering. By forcing the GOT to precede the Block Started by Symbol (BSS) in memory layout, Partial RELRO mitigates the risk of buffer overflows on global variables overwriting critical GOT entries. This adjustment ensures that even if an attacker manages to exploit a buffer overflow vulnerability, they cannot manipulate the GOT entries to redirect program execution flow to malicious functions.
Full RELRO: Full RELRO goes beyond Partial RELRO by rendering the entire Global Offset Table (GOT) section read-only. In doing so, it effectively prevents any modification to GOT entries at runtime. This comprehensive protection strategy ensures the mitigation of a potential GOT overwrite attack, wherein an attacker attempts to manipulate GOT addresses to redirect program execution flow to malicious functions instead of the intended ones.
Full RELRO isn't enabled by default in compilers because it can significantly prolong program startup times. This delay stems from the necessity to resolve all symbols before the program commences. In large programs with numerous symbols requiring linkage, this delay could result in a noticeable slowdown during startup.
ASLR (Address Space Layout Randomization)
Address Space Layout Randomization (ASLR) is a security measure that randomizes the memory locations where programs, shared libraries, the stack, and the heap are stored. By doing so, it becomes more challenging for attackers to exploit vulnerabilities in a service. Since the exact locations of critical memory regions such as the stack, heap, or libc are unpredictable and vary between program launches, attackers can't rely on consistent memory layouts to execute their attacks. ASLR serves as a partially effective defense mechanism, making it harder for attackers to jump to sensitive areas like libc without first obtaining a memory leak.
While ASLR is indeed a valuable defense against memory corruption vulnerabilities, its effectiveness can indeed vary depending on the system and binary architecture. For example, on 32-bit systems, memory addresses are limited in size. This limitation can lead to collisions and repetitions, which might enable an attacker to perform a brute force attack, given a base libc address. Consequently, this reduces the randomness of memory layout randomization, potentially making it easier for attackers to predict and exploit vulnerabilities despite the presence of ASLR. Therefore, while ASLR does provide significant protection, its effectiveness may indeed be constrained in certain architectural contexts.
Position Independent Executable (PIE)
PIE, which stands for "Position Independent Executable", is a protection technique used in modern operating systems to enhance the security of executable binaries. When a binary is marked as PIE, its code, data, and shared libraries are loaded into random memory addresses each time the program is executed. This makes it difficult for attackers to predict memory addresses since they cannot rely on predefined static addresses.
PIE protection is particularly effective against buffer overflow attacks and code injection attacks, as it makes it harder for attackers to calculate or guess specific memory addresses to target their attacks.
Modern operating systems such as Linux, macOS, and Windows support the execution of binaries with PIE protection. When compiling a program with PIE enabled, the linker and dynamic loader ensure that the program is loaded into a random memory location each time it is run.
Control Flow Integrity (CFI)
Control Flow Integrity (CFI) is a security mechanism designed to prevent attackers from hijacking a program's control flow and executing malicious code. It works by verifying that the program's control flow follows a valid path, as defined during compilation or runtime. This prevents attackers from diverting the program's execution to unauthorized code locations or injecting malicious instructions into the control flow. CFI helps to mitigate a wide range of code-reuse attacks, including return-oriented programming (ROP) and jump-oriented programming (JOP), by enforcing strict constraints on the program's control flow transitions.
Stack Smashing Protection -> Stack Canaries
Stack canaries are a straightforward protection mechanism. Initially, they insert a random value onto the stack. Before the ret instruction, the program verifies if the initial and final values match. If they differ, program execution halts. This safeguard was developed to thwart buffer overflow exploits. When attempting to overwrite the stack, altering this value triggers the protection mechanism. Despite its effectiveness in preventing buffer overflow vulnerabilities, it can be circumvented through a Format String Vulnerability. This vulnerability allows an attacker to extract the value during runtime and position it correctly when manipulating the stack, preserving the original value and bypassing the protection.
Conclusion
In summary, binary protections are important security tools built into software to prevent different kinds of attacks. These protections, like ASLR, NX, PIE, RELRO, and stack canaries work together to make software safer by stopping unauthorized access, code running, and memory tampering. While no one protection is perfect, using a mix of them makes it much harder for attackers to break into software and helps keep it secure. In coming articles, we'll demostrate how to bypass some of these protections to carry out buffer overflows attacks.
References
https://ctf101.org/binary-exploitation/relocation-read-only/
https://ctf101.org/binary-exploitation/no-execute/
https://ir0nstone.gitbook.io/notes/types/stack/pie
https://en.wikipedia.org/wiki/Control-flow_integrity
https://www.redhat.com/en/blog/position-independent-executables-pie
https://ir0nstone.gitbook.io/notes/types/stack/relro
https://en.wikipedia.org/wiki/Buffer_overflow_protection
https://ir0nstone.gitbook.io/notes/types/stack/aslr
https://ir0nstone.gitbook.io/notes/types/stack/no-execute
https://ctf101.org/binary-exploitation/stack-canaries/