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 [code=”c” inline=”true”]sbrk()[/code]) 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 [code lang=”c” inline=”true”]“kernel/misc/elf.c”[/code]. 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 [code lang=”c” inline=”true”]memcpy()[/code] 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 [code lang=”c” inline=”true”]kernel/misc/elf.c[/code]. 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:
[code lang=”c”]memcpy((void *)(shdr->sh_addr), (void *)((uintptr_t)header +shdr->sh_offset), shdr->sh_size);[/code]
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:
[code lang=”c”]Elf32_Header * header = (Elf32_Header *)malloc(file->length + 100);[/code]
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 [code lang=”c” inline=”true”]sys_setuid()[/code] 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 [code lang=”c” inline=”true”]sys_setuid();[/code] follows:
In theory it should therefore be possible to overwrite the JNE instruction at [code lang=”c” inline=”true”]0x0010f103[/code] and evade the privilege check. By crafting an ELF executable with a section header address of [code lang=”c” inline=”true”]0x0010f103[/code] ([code lang=”c” inline=”true”]sys_setuid[/code] 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 [code lang=”c” inline=”true”]0x7F[/code] 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.
This blog post was written by @hackerfantastic