Friday, 29 May 2015

PonyOS is a hobby Unix-like operating system that uses it’s own kernel, built from
scratch. This makes it a great research target for exploring software
exploitation concepts. The OS is actually a variant of Toarus written by Kevin Lange and this post is relevant to both variants, however as the author prefers
ponies we will stick to referring to it as PonyOS. I decided to attack the OS
by reviewing some of the kernel code as a limited user-space environment is
provided with very limited user separation and no real attack surface. Auditing the PonyOS source code identified a number of
vulnerabilities such as a lack of bounds checking in memory allocators (e.g. in
sbrk()) to a lack of checking permissions on the file system (e.g. https://github.com/HackerFantastic/Public/blob/master/exploits/rarity.c)!
Here is one of the more interesting vulnerabilities and the steps taken to
exploit it.

PonyOS supports the
Extensible Linking Format (ELF) file format for executable programs and the
kernel contains an ELF loader responsible for mapping a binary from disk into
memory and executing it. PonyOS performs this functionality when ELF magic
bytes are read from a binary, the ELF loader can be found in “kernel/misc/elf.c”.
PonyOS has a very simple ELF loader that performs no checks on where it
attempts to load an ELF binary. It was discovered that by abusing the ELF
loader we could influence a memcpy() when mapping an ELF binary from disk to
memory simply by providing an ELF section header with an address pointing to
anywhere we like. The vulnerable code can be seen on line 70 of kernel/misc/elf.c.
The ELF section header is completely user controlled meaning sh_addr, sh_offset
and sh_size can all be influenced by the user. The vulnerable code is shown below:

The ELF header is
malloc()’d into the kernel heap on attempts to execute an ELF binary, as can be
seen on line 17 of the same file:

Elf32_Header * header =
(Elf32_Header *)malloc(file->length + 100);

By abusing the ELF
section headers we are able to write arbitrary data anywhere we like in memory,
with kernel privileges. Now we need to write something, somewhere to elevate
our privileges from “local” to “root”. PonyOS contains a sys_setuid() system call
however it is disabled for any user other than root. This seemed like a good
target to over-write as simply disabling the root check would provide a simple
privilege escalation attack within the syscall table. We also wouldn’t need to
find our current_process and modify any internal kernel structures. A
disassembled output of sys_setuid(); follows:

In theory it should therefore be possible
to overwrite the JNE instruction at 0x0010f103 and evade the privilege check. By
crafting an ELF executable with a section header address of 0x0010f103
(sys_setuid location within our kernel image) it is possible to write two NOP
bytes into the syscall, allowing any other process on the system to obtain root
by calling our modified function. The syscall ISR is 0x7F on PonyOS and the
syscall number is 0x18. We wrote a simple ELF binary generator, rainbowdash.c,
which when run will output a malformed ELF binary that abuses the write
anywhere primitive to perform this patch. You can download our exploit for
educational purposes here.

The screen shot below shows an example of
the exploit being run and the “setuid” code being executed which simply spawns
a shell after executing the patched syscall.