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.