Ser Szwajcarski 🧀 was a baby pwn challenge from hxp 38C3 CTF.
While I “took part” in the CTF with my good friend Josh under the team name “frfrmode”, I wasn’t able to do much as I was travelling at the time and had never seen this challenge. Josh, on the other hand, claimed third solve for this challenge while it was live. Shortly after the CTF ended, he suggested I try it myself…
[10:58 PM] Josh: also although it’s not particularly hard as far as these things go, Ser Szwajcarski 🧀 was pretty cool and i recommend giving it a go if it sounds interesting
…and so I did.
The Challenge
The challenge has an almost vanilla x86_64 image of ToarouOS v2.2.0 – a cool hobby OS developed from scratch by K Lange – running in a QEMU VM. The changes to the image can be found in build.sh:
| |
In words, the passwords for the root, local and guest users are replaced with random values and flag.txt is added to the root directory (/) with permissions such that only the root user can read it.
The goal is to use a shell that is exposed by the challenge to leak the contents of /flag.txt. The catch is that the shell runs as the local user.
Vague Possibilities
ToarouOS might feel familiar to someone that uses Linux. Maybe some of this is in part due to it supporting POSIX somewhat (at least enough to port programs per its README). For example, it has file system permissions like Linux does and even has syscalls such as open, read and write that share the same arguments and flags. It also uses the same executable format (ELF).
Down in user-space, it even has programs like vim bim, cat and sudo. Now, while the local user is in “sudoers”, the flag can’t be leaked by simply running sudo cat /flag.txt, as the local user’s password was randomized. Though, the existence of setuid binaries such as sudo is promising as it opens the opportunity for privilege escalation solely through user-space, if such binaries can be compromised.
Alternatively, the flag can be leaked by trying to find vulnerabilities within kernel-space components and code that can be triggered through user-space (eg. file systems, networking, syscalls, etc.).
The Exploit
The latter was what I went with as it felt cooler and more interesting. After skimming through kernel-space code for some time, I landed on the ELF loader, where I discovered a vulnerability.
Note: To understand this vulnerability, it suffices to know that one of the things an ELF loader does is load applicable segments/sections (eg. code, data, etc.) from the executable file into memory based on the ELF headers.
Arbitrary Write
In kernel/misc/elf64.c#L322-L335, I spotted the following:
| |
Here, file is the executable file that is being executed, and read_fs is a function that reads from the provided file into memory. The arguments to read_fs in order are the file, file offset, read size and destination buffer.
Based on this, we can see that the above snippet of code is unsafe, as:
- The program header (
phdr) is read straight from the executable file in L323-L324 without any validation being done. read_fsin L332 is blindly using values from thephdras the read length, size and destination buffer.
Since this code runs in kernel mode, we can craft an executable file that copies any data from itself to any writable memory region – basically an arbitrary write – as long as we have a program header with the right values. What are the right values?
p_typeneeds to be set toPT_LOADso it does the copy.p_offsetneeds to be set to the file offset of the data we want to copy from the file.p_fileszneeds to be set to the size of the data we want to copy.p_vaddrneeds to point to the destination we want to write to.p_memszneeds to be set to 0 to prevent side-effects such as the loop in L326-329 or L333-335 from running.
Kernel Code Execution
Since there is no Kernel Address Space Layout Randomization (KASLR), the arbitrary write can easily be used to hijack kernel code execution. One such way to hijack kernel code execution is by overwriting some function pointer that kernel-space uses and then making the kernel call it. For this purpose, I found the printf_output function pointer used by the kernel printf function:
| |
Which, in turn, is used in the sys_sysfunc syscall (that’s callable from user-space) when fn is TOARU_SYS_FUNC_SYNC:
| |
So, if we construct an executable such that the ELF Loader overwrites the printf_output to somewhere in our own executable at load time and then call the SYS_SYSFUNC syscall at run-time, we can run our code in kernel mode.
Privilege Escalation
While there are many ways to go now that we have kernel code execution, I chose to elevate the privileges of our executable process by changing the user it’s running as to root. This allows our user-space process to read the flag when it previously couldn’t due to running as the local user.
For an example of how to do so, we need look no further than the ELF loader code itself, as it needs to change users for setuid binaries:
| |
Proof of Concept
Note: The full script containing all the constants and helper functions to generate the ELF can be found here. I used the excellent pwntools library to create the ELF.
For the user-space payload, we need to call the SYS_SYSFUNC syscall with TOARU_SYS_FUNC_SYNC first so that the kernel runs printf, and by extension, our kernel payload. Then we can read the flag file like we would for a normal file read:
| |
For the kernel payload which runs when printf_output is called, we simply need to patch this_core->current_process->user to root’s user ID (0).
Since this_core is accessible via gsbase and current_process is the first element in the struct, there’s thankfully not much to do here:
| |
To generate an ELF containing these payloads, we can pack them together and use pwntools’ nifty make_elf function:
| |
Thankfully, the ELF that pwntools generates runs without hitches on ToaruOS. Otherwise, we might have had to craft the ELF by hand, which doesn’t sound fun.
To make the ELF loader overwrite printf_output to point to our kernel_payload, we need to forge a program header in the ELF. Thankfully, this isn’t too much work as we can patch the PT_GNU_STACK program header in the generated ELF, because ToaruOS doesn’t understand nor need it to run our executable:
| |
Finally, we can upload the resulting solver binary somewhere and connect to the provided shell to capture the flag:

The End
ToaruOS was a really cool OS, and I thoroughly enjoyed hacking through it! Despite this being a baby challenge, I learned a lot about OS development and x86_64 in doing it. Though none of that would be apparent from this write-up, as most of what I learned ended up being unnecessary for the exploit I found 🙃
Funnily enough, I later learned that Josh had independently discovered the same vulnerability earlier, though he didn’t exploit it. He also found another cool one that involves a physical use-after-free which he did exploit and write about!