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.