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.

References

  1. https://cwe.mitre.org/data/definitions/123.html
  2. https://cwe.mitre.org/data/definitions/134.html
  3. https://cwe.mitre.org/data/definitions/787.html
  4. House of Force
  5. https://cwe.mitre.org/data/definitions/416.html
  6. Fastbin Dup
  7. Safe list unlinking
  8. https://cwe.mitre.org/data/definitions/590.html