This April at Hackito Ergo Sum in Paris and Immunity's Infiltrate in Miami, Dan Rosenberg and I presented on a technique to exploit grsecurity/PaX-hardened Linux kernels. Read on for a brief overview of our presentation and a link to the full slides and PoC code.
The Stackjacking Technique
In our slides, we presented a technique to exploit a grsecurity/PaX-hardened Linux kernel (eg. GRKERNSEC_HIGH) given the existence of two exploitation primitives:
- an arbitrary kernel write; and
- a kernel stack memory disclosure
To be clear, this attack vector is completely unnecessary when exploiting a vanilla Linux kernel, since an arbitrary write is more than sufficient to get root, given the vast amount of useful targeting information Linux gives out via /proc, etc. Likewise, the kernel stack memory disclosure is also unnecessary on vanilla, since there are much easier ways of getting this information. However, due to GRKERNSEC_HIDESYM (which aims to remove all known sources of info leakage), PAX_KERNEXEC (which makes global data structures with known locations read-only), and other mitigation features of grsecurity/PaX, effective exploitation is orders of magnitude harder than a vanilla kernel and took a few interesting twists.
Our technique can be broken down into three distinct stages:
- Stack self-discovery: We observed that kernel stack memory disclosures can leak sensitive addresses to userspace. In particular, if we can leak a pointer TO the kernel stack that resides ON the kernel stack, we can calculate the base of our own process' kernel stack: kstack_base = leaked_addr & \~(THREAD_SIZE-1). We call this technique stack self-discovery.
- Stack groping: If our end goal is to read the address of our process' cred structure and use our write to modify it and escalate privileges, we need to turn our kleak+kwrite into an arbitrary read. We discovered two such techniques to do this: (1) the Rosengrope technique that modifies addr_limit in thread_info metadata stored at the base of the kstack to allow arbitrary reads from kernel space to userspace; and (2) the Obergrope technique that manipulates saved registers within a kernel stack frame that are later popped and used as the source address for copy_to_user()/put_user() operations.
- Stack jacking: After constructing our arbitrary read from a kleak+kwrite, we read the task_struct address out of thread_info at the base of the kstack and then read the cred struct address out of task_struct. Armed with the address of our process' credential structure and an arbitrary write, we modified our uids/gids/caps to escalate privileges.
For the full details, please see the presentation materials and PoC code:
If you haven't yet read spender's response to our presentation, I recommend doing so. While I'll refrain from commenting on the political aspects of his post, I'll happily comment on the technical aspects. The fixes that spender and pipacs have released have mitigated the particular exploit vectors we used to perform the stack groping stage of our attack against the grsec/PaX kernel:
- The thread_info struct has been moved out from the base of the kernel stack preventing the Rosengrope technique from being able to write KERNEL_DS into the addr_limit member.
- The RANDKSTACK feature, now available on both i386 and amd64, frustrates the Obergrope technique as the randomization of the kernel stack pointer on each system call makes writing into a particular offset in the stack frame unreliable.
- USERCOPY has been enhanced to protect certain SLUB caches from being read/written to/from with the userspace accessor funtions. This prevents our groping technique from being able to easily read task_struct contents back out to userspace. Thanks to pipacs for pointing this out!
Props to spender and pipacs for cranking out those fixes as well as a number of other enhancements. While the latest grsecurity patch effectively prevents the current vectors we discovered and presented in our talks at HES and Infiltrate, there are several loose ends I need to investigate to ensure the fixes address other potential exploitation vectors.
More on that later...