Dec 3, 2024
4 mins read
Ever since Solar Designer abused unsafe unlinking to pwn the Netscape browser back in 2000, heap-based vulnerabilities have shown themselves to be one of the most consistently exploitable forms of memory corruption. Over the years, the maintainers of the GNU C Library have introduced multiple defenses to try and break that streak of consistency… but are they enough to stop a determined attacker?
In this multi-part series, I’ll cover multiple different techniques to get leakless RCE on the latest version of GLIBC. This post will set the stage– what defenses have to be bypassed nowadays?
This series will assume you have some experience with heap exploitation. Completing Part 1 of Max Kemper’s HeapLab or pwn.college’s Dynamic Allocator Exploitation course (costs nothing!) should be enough to understand this post and the ones following it.
Glibc performs a wide range of security checks on the heap to try and prevent an attacker from doing anything funny– going by malloc_printerr
, about 50 in total.
Some well-known ones include:
key
field used to try and prevent double-free attacks.free()
d, the next chunk in-memory unsets a bit on itself to say ’the chunk before me is free’. This bit is checked before returning a freed chunk to the user.
free()
’d chunk is also written to the next chunk in-memory, and this is again checked before returning the chunk.fd
and bk
pointers are checked to see if their bk
/ fd
pointers point to that chunk.Easily the most infamous defense against memory corruption, ASLR randomizes where the heap, stack, mmap()
and potentially even the program itself are loaded into memory. This means an attacker with the ability to write anywhere in memory still has to learn where to write in memory, generally done by getting the program to accidentally leak out memory addresses. But since this series is about leakless exploitation, we’re going to have to do some pretty tricky stuff to bypass this.
Implemented in GLIBC 2.32, this defense aims to harden the two most ’exploitable’ bins on the heap, the tcache and fastbin. It boils down to “we can obfuscate some data x
by using &x
as an XOR key, since ASLR is a thing”. In code, it looks like this:
/*
POS is the address of the field being protected
PTR is the data being protected
*/
void * PROTECT_PTR(void * pos, void * ptr) {
return (pos >> 12) ^ ptr;
}
void * REVEAL_PTR(void * ptr){
return PROTECT_PTR(&ptr, ptr);
}
/*
here is how glibc actually defines it
https://elixir.bootlin.com/glibc/glibc-2.40/source/malloc/malloc.c#L329
*/
#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)
This function generates an XOR key out of thin air by abusing the randomness of ASLR. pos
, the key, is shifted 12 bits right as those 12 are unaffected by ASLR.
Moving on, this pair of functions is used to obfuscate the fd
fields of the two singly-linked bins– the tcache and the fastbins. We’ll see later that this protection isn’t perfect, but you can see why leakless exploitation is so hard nowadays: poisoning the fd
field of a tcache/fastbin chunk now requires a heap leak.
Let’s say we managed to bypass all the previous protections– we have knowledge of the program’s memory layout, and can write anywhere we want to in memory. We still have one problem: what can we overwrite to hijack execution?
This would’ve been an easy question to answer a few years ago, but the maintainers of glibc have been consistently patching out well-known techniques to get RCE with an arbitrary write. Some of those patches include:
This isn’t to say that getting RCE from an arbitrary write is impossible nowadays, it’s just usually a lot more complicated.
In short, leakless heap exploitation is VERY difficult. There are TONS of more problems our attack has to get around, and even bigger ones like Intel CET or PAC are soon-to-come…
but I never said it was impossible! Part 1 of this series should be out at the same time this releases, going over the first leakless heap exploit we’ll be looking at– the House of Water.
Sharing is caring!