Relative write primitive

The relative write primitive does not have a formal Common Weakness Enumeration (CWE) Weakness ID, making it a bit harder to find great resources that adequately cover this primitive. Nonetheless, this is still a pretty powerful primitive and, under the right conditions, can lead to pretty reliable exploits.

Definition

A relative write primitive is a condition in which the attacker can control sensitive program information via a memory corruption vulnerability from a relative location to the targeted sensitive information. Basically, the attacker can expect that their target memory location for corruption is at a specific offset when providing data to the program. Given this definition, both stack buffer overflows and heap buffer overflows can qualify as relative write primitives.

In a stack buffer overflow, the attacker can expect that the saved frame pointer and return address of a program stack frame will always be in the same location. Thus, when they've determined the offset of the buffer they're overflowing relative to their targeted write location, they can use their relative write primitive to reliably corrupt a stack frame, providing the attacker control over process execution. A good example of reliable exploitation that uses a relative write primitive is the funge-and-games CTF challenge from Cyberstakes 2017. The challenge allows us to provide a Befunge program to the vulnerable binary, simulating operations that would be executed by a Befunge interpreter. The vulnerability lies in the fact that no bounds checking is conducted when the Befunge interpreter references its internal stack data structures, allowing an attacker to conduct out-of-bounds reads and writes. The attacker can use this to reliably corrupt the return address of the program's stack frames using a relative write primitive.

Likewise, a heap buffer overflow can be used by an attacker to achieve similar objectives. If the attacker is aware that sensitive program information is contained within the heap chunk that they are able to overflow into, they can use this intra-chunk heap overflow, or relative write primitive, to reliably corrupt the sensitive information contained within the current heap chunk. This bug class and its reliability is discussed further in this Google Project Zero article.

House of Roman

The House of Roman is a great example of using relative overwrites within the heap to gain code execution. This technique doesn't require a specific heap memory corruption vulnerability to exist, it could be either a heap overflow or use-after-free (UAF). The attacker just needs to be able to edit a couple of pointers contained within the user data and metadata of heap chunks , using heap feng shui to adequately groom the heap for deterministic relative writes.

The attacker grooms the heap to obtain a favorable state and then uses their relative write primitive to obtain a fastbin chunk that overlaps the __malloc_hook function pointer in glibc. This is possible because the attacker abuses the nature of the unsortedbin to acquire glibc pointers in the fd and bk pointers of a chunk, and then edits the last 4 bits of these pointers to point to a fake chunk that overlaps the __malloc_hook.

The attacker is also able to get this fake chunk linked into the fastbin by editing a preceding free chunk that is also in the fastbin, modifying the last 4 bits of the chunk's fd pointer to link the chunk that contains the address of the __malloc_hook in to the fastbin. This is possible because, as we mentioned earlier, the attacker uses heap feng shui to land target chunks at specific offests. In this case, the chunk with glibc pointers is at an offset of 0x100 from the preceding chunk mentioned.

The attacker conducts an unsortedbin attack to overwrite the __malloc_hook with an address of main_arena. Again, only a couple of bytes are required to be overwritten within the bk pointer of the unsortedbin chunk in order to point the bk to __malloc_hook. Once the attacker writes a main_arena address to the __malloc_hook, the attacker uses the previously mentioned fastbin chunk that overlaps the __malloc_hook to edit the last couple of bytes in the written information, overwriting the __malloc_hook with the address of system() or a one_gadget.

Of note, due the amount of randomness introduced by ASLR, this technique is only really successful if the attacker is able to brute force the location of glibc data structures. If the program crashes after each attempt, the likelihood of this technique working is pretty low. Despite all of this, the House of Roman is a good technique to demonstrate the power of relative write primitives and how they can be chained to gain code execution.

References

  1. https://googleprojectzero.blogspot.com/2015/06/what-is-good-memory-corruption.html
  2. https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_roman.c