Use after free
The programmers definition of “gone but not forgotten”
This lesson is available in C++
What is use after free?
Use after free (UAF) is a type of bug that can be exploited in programming languages that are not memory-safe. In C++, a developer is responsible for allocating (and deallocating) memory on either the stack or the heap. While memory on the stack is statically allocated, memory on the heap can be dynamically allocated and deallocated by using keywords and functions such as
UAF bugs occur when a program continues to access a memory location after the memory has been freed or deallocated. This can lead to unexpected behavior, crashes or even security vulnerabilities such as remote code execution or privilege escalation.
UAF bugs can be particularly difficult to detect and fix because they often do not result in visible errors right away. A vulnerable application can continue to function just fine in certain scenarios, making it seem like there is no bug. Meanwhile, data could get corrupted, or worse, attackers could leak or purposely alter data.
About this lesson
In this lesson, you will learn how use after free (UAF) bugs works and how to protect your applications against them. We will begin by exploiting a UAF vulnerability in a simple ticketing application. Then we will analyze the vulnerable code and explore some options for remediation and prevention.
UAF in the wild
Even the most popular projects and programs have UAF bugs. For example, take a look at the vulnerabilities that are disclosed in the ‘torvalds/linux’ project. You will notice some use after free CVEs.
Let's look at a website for the upcoming rock band named “The Cyber Junkies”. The band has a very simple ticket shop where their fans can order tickets to their next festival. There is no signup or payment required. It is just an open-to-all ticket ordering service where tickets get reserved.
Testing for UAF
- STEP 1
- STEP 2
- STEP 3
- STEP 4
- STEP 5
- STEP 6
Setting the stage
The goal is to order a ticket from their shop. But sometimes, we accidentally find exploits and in this case, a UAF bug.
Let's break down what happened in the story above. You had no intentions of exploiting a UAF bug, but by accident, you managed to exploit one to gain free festival access.
It turns out that when you hit the back button in your browser, you never started a new ticket order. You canceled your order and when the application deallocated the order details, you confirmed it again.
The backend of the ticket shop, in a very simplified way, looks something like this:
As you can see, the ticket flow consists of three parts (order, confirm, cancel). All three parts share the same pointer for the order price. In the start logic, the memory at the pointer location is assigned with the $25 price. In the confirm logic, the data at that address is read to retrieve the ticket price, and then in the cancel logic, the memory of at that location is released again.
Releasing the memory does not remove or invalidate the pointer. In this case, the pointer
order_price is still pointing to that specific memory address, even though the memory at that address is already released and could have been used by a different program.
By using an incorrect order flow, we can free the memory for the
order_price pointer, and then still use that same pointer in our ticket confirmation.
Let’s walk through these steps together:
- Start an order by requesting
- The order_price contains the address of the memory where
- Cancel the order by requesting
- The memory where
25was stored is now released back with the
- Confirm the order by requesting
- This backend still thinks that the memory located at the address stored in the order_price variable is managed by us, but in reality, that memory has already been assigned to a different component, program or part of the system
- At this point, when the backend retrieves the data stored at that location, it could be anything
Impacts of use after free
The impact of a UAF bug can range from a best-case scenario where the application crashes, to a worst-case scenario where an attacker is able to achieve remote code execution (RCE).
The most common types of UAF impacts are information leaks (technical information, as well as user information), crashes, privilege escalation, authentication bypass, data corruption, and remote code execution.
Depending on factors such as where the application is running, who is using it, and how the pointer value is being processed influences the overall impact. In general, UAF bugs lead to so-called “undefined behavior”.
A good way to prevent, and at the same time get notified of, UAF bugs is by setting pointers to NULL (
nullptr) when you free the memory they point to. Trying to dereference a null pointer will lead to a crash, which is better than having a silent UAF bug that leads to data corruption and other undefined behavior.
Want to learn more about null pointer dereferences? Checkout our C++ null deference module next!
Another way to prevent UAF bugs is by using smart pointers in C++. This way, the developer does not have to manually allocate and free memory
Knowing how these bugs can be prevented, let’s dive into the vulnerable code from the scenario, and remediate the UAF bug that gave James a free ticket.
The following code has some minor tweaks added to ensure that the
order_price pointer is handled correctly:
As you will notice, the confirm and cancel code have both gotten an extra
if statement that utilizes a so-called “early return” where the code returns early if a condition isn’t met.
In this case, we check if the
order_price is NULL, and if so, we let the user know that first an order has to be started before it can be confirmed or canceled.
Additionally, the cancel function also sets the
order_price pointer back to NULL after the memory has been freed.
confirm code now checks if the pointer is safe to use, before actually reading the data it points to, the vulnerability is remediated.
A better overall solution in this scenario would have been to store the price in a session, database or a non-pointer value. But because this is not always possible, we leave the code as is, and set the focus on the
order_price handling instead of the bigger picture.
To learn more about Use After Free bugs, check out some other great content:
- The MITRE CWE page for the use after free vulnerability class has more information, references and examples.
- OWASP has a page on UAF bugs which provides a description, consequences and examples (the same examples as the MITRE CWE page).
- Microsoft has a detailed page about smart pointers in C++ here.