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.