Arbitrary write primitives
An arbitrary write primitive, formally known as a Write-what-where condition, is a condition where the attacker has the ability to write an arbitrary value to an arbitrary location. An attacker can implement this primitive by abusing an already existing vulnerability. Here are some examples:
Use of Externally-Controlled Format String
This isn't really an attacker implemented arbitrary write primitive as format
string vulnerabilities alone can allow attackers to write to arbitrary locations
, given the right circumstances. Essentially, the attacker is able to provide
a format string argument to the vulnerable program and this argument will be
used by functions like printf
, sprintf
, etc.
The issue here is that the format string identifiers n
, hn
, and hhn
allow
the attacker to write the number of bytes written so far by the printf
call
into an integer pointer parameter - n
is an integer, hn
is a short, and
hhn
is a byte (char). The attacker can craft a format string to place their
write address(es) onto the stack, use format string identifiers like c
and
x
to write an arbitrary number of bytes to stdout
, and then use n
, hn
,
or hhn
with an offset (for example $7hn
where 7
is the location of the
target address on the stack) to write arbitrary values to any writeable
location in program memory.
Out-of-bounds Write
As demonstrated in the House of Force,
a heap buffer overflow can be used by an attacker to implement an arbitrary
write primitive. The attacker leverages the heap buffer overflow to overwrite
the value of the top_chunk
and tricks malloc into relocating the top chunk to
a location directly above the attacker's desired arbitrary write target.
Finally, the attacker coerces the program to execute another malloc
call,
creating a chunk directly overlapping the arbitrary write target. From here,
the attacker can now modify the user data of the chunk, writing arbitrary data
to the arbitrary write target.
Use-after-free (UAF)
As we discussed in the Fastbin Dup, an attacker
can leverage a UAF to corrupt the heap metadata of a free chunk that has been
linked into the fastbin. The attacker overwrites the victim chunk's fd
pointer to point to a fake chunk that overlap's the attacker's desired
arbitrary write location. The attacker coerces the program to empty the fastbin
, eventually allowing the attacker to allocate a chunk that overlaps their
arbitrary write target. From here, the attacker can now modify the user data
of the chunk, writing arbitrary data to the arbitrary write target.
A UAF can also be leveraged to gain an arbitrary write by abusing malloc()
's
unlink()
ing behavior. If the attacker has the ability to modify a chunk that
has been recently free()
d and linked into the unsortedbin, the attacker can
corrupt the heap metadata of the victim unsortedbin chunk, specifically its
fd
and bk
pointers. The attacker overwrites the fd
pointer of the victim
chunk to point to a fake chunk that overlaps the attacker's arbitrary write
location, and overwites the bk
of the victim chunk to contain the arbitrary
data to be written to the arbitrary location. If the attacker can coerce
malloc()
to consolidate the victim unsortedbin chunk with another free
chunk, the corrupted bk
will be written to the fake chunk overlapping the
attacker's arbitrary write target. A couple of details regarding this method
have been left out, including mitigations that have been applied to prevent
this as well as how to gain code execution using this technique. More detail
is provided in the
Safe list unlinking discussion.
Free of Memory not on the Heap
This one is pretty interesting. Essentially, a free()
call gets pointed to a
chunk that's not on the heap and, if nothing fails free()
's chunk checks,
the location in memory that was just free()
d gets linked into one of the
arena's bins. I say this is particularly interesting because there could be
some scenario in which chunk pointers are contained in some data structure
located on either the stack or in global data, such as .data
or .bss
. If
the attacker were able to corrupt one of these pointers before it gets
free()
d, the attacker could coerce free()
into linking an arbitrary
location in memory into a bin. The attacker could then coerce the program to
call malloc()
enough times until the attacker acquires a pointer to a chunk
overlapping their arbitrary write target. From here, the attacker could modify
the fake chunk's user data, overwriting their arbitrary write target.