Return-Oriented Programming is a security exploit technique used by attackers to execute code on their target system. By obtaining control of the call stack, the attacker can control the flow of existing trusted software running on the computer and manipulate it to their own ends. New research published this month has demonstrated how SPECTRE style vulnerabilities can be leveraged with modern ROP techniques to create a new and powerful class of attack called BlindSide.
Stack Buffer Overflow Attacks
The call stack is a data structure that controls the execution flow of a computer program. It tracks the calling of subroutines and the exact position in the code where execution resumes after each subroutine completes. A stack buffer overflow occurs when data is written to the stack which is longer than the length of the memory space allocated to the buffer – the result is that the adjacent memory space is overwritten. Since that memory space will form part of the contiguous call stack, the contents of the stack has been changed which will result either in the program crashing (because the new contents make no sense) or the program flow changing because the new data can be interpreted as valid stack entries.
A number of technologies have been introduced to counter the threat of stack buffer overflow attacks, including:
- Stack Canaries (placing a random integer value at a specific point on the call stack which is regularly validated – if the stack if overwritten this value will change allowing the program to self-terminate safely)
- Data Execution Prevention (prevents the execution of code in memory spaces which should only contain data – like the call stack)
In response to these mitigations, new attack techniques were developed – the most powerful of which is Return-Oriented Programming.
The first example of ROP was published in 1997 and focused on abusing the libc library which is present in most compiled C programs. Since the structure of the libc library is well known, it is possible to identify the memory address of every subroutine and craft a call stack buffer overflow that does not corrupt the call stack (causing a crash) but simply replaces the memory pointer with one that will invoke a libc subroutine (or chain of subroutines) selected by the attacker which then performs a function helpful to the attack.
This type of abuse of standard libraries was made much harder with the introduction of Address Space Layout Randomisation (ASLR) in 2002. ASLR randomly arranges the address space positions of key data areas of a process so an attacker does not know which addresses to inject into the call stack in order to abuse libc or other known software libraries including the system Kernel. The introduction of ASLR and changes to many system libraries like libc that made it harder to abuse their functions spurred new research and in 2007 the Return-Oriented Programming technique was first demonstrated.
The Intel x86 architecture is dense – it has many different instructions and so any random set of bytes is likely to be interpretable as a valid sequence of x86 instructions. It is possible, therefore, to search through a software binary file looking for specific machine code instructions (opcodes) that perform a useful function – the most interesting being the return instruction (0xC3) and then back-track to see if the preceding bytes form valid instructions that can be used. These small snippets of instructions ending with a return instruction are known as ‘gadgets.’ Given a sufficiently large quantity of code (such as libc) there will be enough gadgets discovered to allow the creation of a Turing complete instruction set – meaning any desired function or program flow can be achieved. Tools such as ROPgadget automate the search for gadgets in software binaries. Since the gadgets are discovered in a known binary (such as certain Linux distribution) there is some protection provided by ASLR in that the attacker needs to do some work in order to identify the memory location of the library which contains the gadgets they wish to exploit. However, once a single gadget or other identifiable data item has been located in memory the location of all the others can be inferred as ASLR does not alter the contents of software in memory – only the starting location for each library.
Blind Return-Oriented Programming attacks
Blind Return-Oriented Programming develops the ROP approach to allow software to be attacked sight unseen – without the need for the attacker to first harvest gadgets from their own copy of the software binary. This extends the scope of ROP attacks to include proprietary closed-binary systems. The limitation is that the targeted software will crash many times during the attack and so it will only be effective against services which are configured to auto-restart (and the repeated crashing does not draw the attention of systems staff to investigate and identify the attack while it is underway).
Researchers at Stanford University in 2014 were able to remotely start a shell process on nginx + MySQL servers (with stack canaries and ASLR defences in place) in 20 minutes using a BROP exploit they developed called Braille. Essentially, they repeatedly corrupted the return address of an exploitable function, forcing the program to return to every possible address in search of a libc target, while the server recovered from crashes by spawning a new process each time.
What is the BlindSide attack?
In September 2020 a team of European researchers published the results of their work attempting to exploit Spectre style speculative execution vulnerabilities present in the micro-architecture of modern CPUs combined with a BROP style attack. By leveraging the speculative execution capabilities of modern CPU, the attackers are able to abandon calls that would otherwise result in a crash and so successfully use BROP techniques to identify useful gadgets to exploit without the disruptive and attention-grabbing crashes. As a result, they were able to target components that are not crash-resistance – such as the Linux kernel – and successfully retrieve the root password hash from memory. The team posted a video of their proof of concept code running on a Linux device and obtaining root access.
The abstract of the BlindSide paper summarises the results succinctly:
To defeat ASLR or more advanced fine-grained and leakage-resistant code randomization schemes, modern software exploits rely on information disclosure to locate gadgets inside the victim’s code. In the absence of such info-leak vulnerabilities, attackers can still hack blind and derandomize the address space by repeatedly probing the victim’s memory while observing crash side effects, but doing so is only feasible for crash-resistant programs. However, high-value targets such as the Linux kernel are not crash-resistant. Moreover, the anomalously large number of crashes is often easily detectable.
In this paper, we show that the Spectre era enables an attacker armed with a single memory corruption vulnerability to hack blind without triggering any crashes. Using speculative execution for crash suppression allows the elevation of basic memory write vulnerabilities into powerful speculative probing primitives that leak through microarchitectural side effects. Such primitives can repeatedly probe victim memory and break strong randomization schemes without crashes and bypass all deployed mitigations against Spectre-like attacks.
Mitigations for BlindSide style attacks will need to be prepared by both hardware (CPU) and software (Operating System) vendors – there is nothing the Security Manager can do today in order to mitigate against these kinds of attacks. However, there is a useful lesson here as noted by the BlindSide researchers: BROP style attacks have been well studied for many years and it is assumed they are understood – but by chaining or combining BROP attacks with the Spectre vulnerabilities new avenues of attack become available.
The attack surface of your network is always changing – even if you make no deliberate changes yourself. Every software patch changes the attack surface of your servers and each new research paper reveals previously hidden possible vulnerabilities. So even for networks with low levels of change, it is important to maintain a regular tempo of testing and checking (through vulnerability scans and penetration tests) to ensure that new discoveries and vulnerabilities do not compromise systems you assumed are still secure today because they were shown to be secure six months ago.