Tool releases: ksymhunter and kstructhunter

Thursday, September 8, 2011

I'm releasing a couple tools I use internally for Linux kernel exploit development: ksymhunter and kstructhunter. They're probably only useful for like ten people on the planet, but oh well, enjoy!


Kernel symbols are definitely a useful resource when writing Linux kernel exploits. Whether you're looking for particular structures in kernel memory or pulling the old commit_creds technique for convenient privilege escalation, having access to a kernel symbol table can be incredibly useful for an exploit writer.

So why is /proc/kallsyms still world-readable in many distributions, giving unfettered access to the symbol table to unprivileged users? Well, it shouldn't be, but that's only a small part of the problem since kernel symbols can be discovered from a variety of sources on the target system. For example, kernel symbols are often available in:

  • /proc/kallsyms
  • stored in /boot, /usr/src/linux, /lib/modules
  • vmlinux stored in /boot, /usr/src/linux, /lib/modules, /usr/lib/debug
  • vmlinuz stored in /boot, /usr/src/linux/arch/x86/boot

ksymhunter is a tool I wrote designed to expose kernel symbols in these locations on a running system. As you can see in the source code, ksymhunter enumerates a large number of common kernel symbol sources in an attempt to resolve whatever symbol you pass on the command line:

$ ./ksymhunter prepare_kernel_cred
[+] trying to resolve prepare_kernel_cred...
[+] resolved prepare_kernel_cred using /boot/
[+] resolved prepare_kernel_cred to 0xffffffff81061060

So why doesn't your distribution protect /boot, /usr/src, /lib/modules, and other paths to make them only readable by a privileged root user? Well, they certainly should (Ubuntu has done some of this in its Natty release thanks to Kees Cook), but that's not the end of the story.

Binary distributions suffer from an inherent problem when they distribute the same stock binary kernel to all of their users. So while your distribution may successfully block access to all the kernel symbol sources on your machine from an attacker, the attacker can easily download and run the same distribution and stock binary kernel image that is running on your system, thereby discovering the symbol addresses on his box and then using them to exploit your kernel.

Taking it a step further, an attacker could simply crawl all the public kernel rpms/debs/etc provided by Linux distributions, parse symbol tables out of the kernel images, index them by distribution/version, and make them accessible via a lookup API. In fact, that is exactly what ksymhunter's remote lookup functionality does. If all of the local sources of kernel symbols fail, it will query out to a network service to look up the correct symbol address based on the provided kernel version.

Really, ksymhunter is just a tool to prove that a more proper solution is necessary to sufficiently hide kernel symbols from an attacker. One such proposal is tore-using the existing relocation support in the Linux kernel to load the kernel image at a random offset. However, the leak of a single symbol address via dmesg, /proc, or other sources would allow the attacker to determine the offset and "re-base" the desired kernel symbol addresses. A more thorough solution would be in-kernel ASLR, but that is a non-trivial task.

You can download ksymhunter on GitHub.


kstructhunter is a tool that can help save a boatload of time when doing SLAB/SLUB/SLOB overflows, use-after-free exploits, or other techniques that rely on knowing where particular structures will be allocated in kernel memory.

The Linux kernel's SLAB memory allocator groups similarly-sized allocations into general kmalloc caches. For example, there is a kmalloc-64 cache for any allocations greater than 32 bytes, but less than or equal to 64. There are general kmalloc caches for allocation sizes of 8, 16, 32, 64, 96, 128, 192, 256, 512, 1024, 2048, 4096, and 8192 bytes. There are also specialized caches for some frequently allocated structures, but those are of lesser importance for exploitation.

Normally, if you're writing a SLUB overflow for example, you'll want to find a structure that can be allocated within the same kmalloc cache as the structure you're planning on overflowing. That way, if you're able to achieve adjacent allocations of those structures, you can overflow into an object of your choosing, potentially overwriting sensitive data or function pointers that may lead to privilege escalation.

Finding a structure that is appropriate for a particular SLUB overflow or UAF vulnerability can be a real pain though, digging through header files and calculating structure sizes for promising structures that may be allocated in a particular kmalloc cache.

kstructhunter simply automates this tedious task! Given a particular target structure, kstructhunter will calculate which kmalloc cache it is allocated in and output every other structure that is also allocated in that kmalloc cache. For example, if I want to figure out what other structures may be co-located in a kmalloc cache with sctp_ssnmap:

$ python sctp_ssnmap
struct sctp_ssnmap is size 40
struct sctp_ssnmap is allocated in kmalloc-64
other structs allocated in kmalloc-64:

In reality, kstructhunter is really just a simple python script that operates on the output of a tool called pahole. pahole does all the black magic by reading the DWARF debugging information from a kernel object file compiled with CONFIG_DEBUG_INFO (aka "gcc -g"). kstructhunter comes bundled with the pahole output for the stock Ubuntu Natty kernel (both 32-bit and 64-bit versions), but can optionally take a -f option to supply your own pahole data file (generated via "pahole -s object_file").

Future versions of kstructhunter may further prune down the lists of structures, by determining which structures are actually kmalloc'ed, if they're kmalloc'ed in code paths reachable by an unprivileged process, and if they contain sensitive pointers that may be useful to overwrite.

You can downloadkstructhunter on GitHub.

Copyright © 2021 - Jon Oberheide