Escalating privileges

We've covered how an arbitrary write primitive can be used to escalate privileges or execute arbitrary code in a lot of these different discussions, the Houses covered in our heap exploitation section, for example. In all of these examples, once the attacker has all the information they need from previous sensitive information disclosures to build their final payload, these techniques usually conduct one final, important write to gain control of the vulnerable process in order to execute arbitrary code. In this section we'll list some interesting targets that attackers write to to gain control of the process. This list will not be all-encompassing as I'm sure there are plenty of other candidates that can be used to hijack the process, these are just ones that are more popular within the community.

Global Offset Table

The GOT is a common location for attackers to target given they have an arbitrary write primitive. Whenever a program calls functions from linked libraries, the GOT will be used by the program to either resolve the symbol or call the symbol stored there. Attackers will commonly overwrite a GOT entry with the symbol of a different function they want the program to call, usually ensuring that the next couple of call operations conducted by the program include the now corrupted GOT entry. While this is a common target for arbitrary writes to gain arbitrary code execution, keep in mind that full RELRO mitigates this tactic, resolving all entries within the GOT and setting it to read-only before the program starts. [2]

Partial RELRO

In order to complete our knowledge about the Relocation Read-Only (RELRO) protection mechanism, Partial RELRO is a security mitigation where the Global Offset Table is relocated to reside above the .bss section. There existed previous methods of attacking the GOT where attackers would utilize a buffer overflow of a global variable contained in the .bss section to corrupt the GOT - for some reason the .bss used to reside above the GOT in ELF programs.

Compilers now enforce Partial RELRO by default, placing the GOT above the .bss in order to mitigate this attack surface. [7]

initial@glibc

This write target is pretty cool. The initial data structure in glibc is a read/writeable segment in memory that is used to contain/register sensitive information that glibc tracks for the current process. For an attacker, the more interesting information they would most likely go after are the function pointers registered by atexit(). My understanding of this data structure and its use to gain control of the process is derived from 0x00 CTF 2017's challenge, "Left". [3] In the Left challenge, the program gives the attacker a glibc leak, one arbitrary read and one arbitrary write and then calls exit immediately after. The attacker must use this arbitrary read to leak the mangled pointer of _dl_fini stored in the initial section of glibc. _dl_fini is a function pointer that is always registered in the initial section of glibc, and all of the function pointers stored in initial are unmangled and executed when exit is called. The attacker uses the exposed, mangled pointer of _dl_fini to derive the secret stored in Thread Local Storage (TLS) and then mangles a one_gadget address in the correct manner to forge a valid initial entry. The attacker then uses a single arbitrary write to overwrite the _dl_fini pointer in initial. Once the program calls exit, the one_gadget entry in initial will be executed, allowing the attacker to gain an interactive shell.

_IO_list_all@glibc

This is an interesting one as well, and its usefulness is showcased in The House of Orange. In The House of Orange, the attacker uses the write primitive provided by an Unsortedbin Attack to write a main_arena address to the _IO_list_all symbol in glibc. This symbol keeps track of all the open _IO_FILE structures for the process, and the attacker uses this to kick off some File Stream Oriented Programming to gain control of the process for arbitrary code execution. [5] The attacker forces the process to experience a SIGABRT rasied by checks in malloc() which leads to glibc attempting to call the overflow function of all _IO_FILE structures present in the linked list of _IO_list_all. Because _IO_list_all now points to the main_arena, it attempts to treat the main_arena as an _IO_FILE structure and attempts to execute the overflow function registered in its vtable. This obviously fails but, instead of failing completely, glibc just moves onto the next _IO_FILE structure pointed to by main_arena's chain attribute (since glibc assumes main_arena is an _IO_FILE structure at this point). It just so happens that this chain attribute points to the smallbin, a location where the attacker controls the data. Using this, the attacker ensures that a fake, but valid, _IO_FILE structure resides in this location and fools glibc into calling overflow from its forged vtable. The attacker ensures that overflow just ends up being a pointer to system@glibc, and the argument being passed to this call is the current _IO_FILE struct. The attacker ensures that "/bin/sh" resides at the first word of bytes in the _IO_FILE struct, thus fooling the process into executing system("/bin/sh").

__malloc_hook@glibc and __free_hook@glibc

And, finally, we have our usual suspects, the __malloc_hook and the __free_hook. These are kinda the easy button of arbitrary write targets within glibc if we know that our target program is making calls to malloc and free in the future. As discussed in The House of Force, the __malloc_hook and the __free_hook are symbols in glibc that were originally implemented to allow programmers the ability to register functions that will be executed when calls malloc() or free() are made. The thought process was to allow programmers the ability to gather memory usage statistics or to install their own versions of these functions, etc.

Attackers use these hooks to redirect program execution with just one arbitrary write, and they usually overwrite these locations with the address of a one_gadget or the symbol of system(). It's not outside the realm of possibility, however, that they could use these hooks to pivot to the location of a ROP chain which could possibly lead to the execution of more complex shellcode.

Honorable mention: _dl_open_hook@glibc

I won't go into too much detail with this one seeing as we've covered so many other options already, but I recently just learned about this technique which surprisingly doesn't have a lot of coverage on the internet. In POCORGTFO 2018 [6], an author covers what they call the "House of Fun", a frontlink attack in malloc() that people assumed was mitigated but actually went unpatched until glibc 2.30. With the arbitrary write that they gain through this technique, the attackers target _dl_open_hook which is a hook where programmers can register a function pointer to be called when dlopen() is called by the program. A little known fact is that malloc_printerr when causes a SIGABRT and the program attempts to dump the memory mapping of the process, it needs to call dlopen on libgcc_s.so.1 in order to access the __backtrace function. With this in mind, the attackers write a one_gadget to _dl_open_hook->dlopen_mode and force malloc() to encounter corrupted heap metadata, causing it to call malloc_printerr which leads to dlopen() which leads to the program executing sys_execve("/bin/sh").

Conclusion

As we can see, viable write targets to gain arbitrary code execution within a process are aplenty. It's up to the attacker to ensure they understand what conditions need to be present in order to get reliable code execution - there's also some creativity required.

References

  1. The House of Force
  2. https://ctf101.org/binary-exploitation/relocation-read-only/
  3. https://ctftime.org/writeup/8385
  4. The House of Orange
  5. https://ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/fsop/
  6. https://www.alchemistowl.org/pocorgtfo/pocorgtfo18.pdf
  7. https://ctf101.org/binary-exploitation/relocation-read-only/