hxp 2022 - browser_insanity (pwn)
Last week, I participated in hxp 2022, an esteemed CTF event, where I successfully tackled the pwn challenge “browser_insanity.” This captivating exploit required reading arbitrary files from the KolibriOS OS filesystem, pushing me to dive deep into KalibriOS browser vulnerabilities and hone my reverse engineering skills.
Ever wanted to hack a tiny OS written in x86-32 assembly and C--? Me neither but it’s hxp CTF 2022.
Give us the URL, the user in the KolibriOS VM will visit it. You need to get the flag from
The source code you could get from: https://repo.or.cz/kolibrios.git/tree/7fc85957a89671d27f48181d15e386cd83ee7f1a
The browser is at
programs/cmm/browser in the source tree. It relies on a couple of different libraries (e.g.
programs/develop/libraries), grep around.
KolibriOS has its own debugger,
DEBUG, available on the desktop. It may come in useful.
The kernel ABI is at
For building random pieces:
INCLUDE=path_to_header.inc fasm -m 1000000 -s debug.s file.asm file.out
So basically we are provided with an unmodified KalibriOS image that is run by qemu-system.
enter_challenge.py is a proxy between us and KolibriOS - it will take URL from us and pass it to the WebView browser inside KolibriOS. The first idea is to create a page that will be visited and will allow the exploitation of some vulnerability.
To do a bit of reconnaissance, I started the VM using slightly modified
run_vm.sh script which loads the
kolibri.img and looked around. After booting, we see:
There is also a built-in debugger:
We can load programs into it using
load command, so in order to debug webview we can run
load /sys/network/webview. After loading a program, we see 32bit assembly :) I’ve experimented with it for a little, and it looks like all memory is executable, if we had control over
ebp, we could execute arbitrary code. Sounds good.
The WebView browser is written in C-- language which is similar to C, so reading code was not a problem. The browser has many dependency libraries. During the CTF I was thinking about making a simple fuzzer which will test the browser code and look for crashes, but first I’ve decided to look around for a bit longer, and I’ve found an interesting line of code in one of the functions responsible for setting styles for html elements:
Notice line number 13. There is unrestricted
strcpy, so we can easily utilize that to overwrite some memory.
#redirect field is a member of the following global (?) struct:
The logic populating
tag.value is located in
programs\cmm\browser\TWB\parse_tag.h. Unfortunately there is call to
tag.value won’t contain whitespaces and upper letters.
Let’s create a simple
index.html page with a super long
meta element with
http-equiv tag set to
content tag starting with
url and containing a lot of characters (which are truncated on the snippet below):
Now we need to host the page somewhere, run the WebView under the debugger and visit the page.
Page fault! Now we need to somehow check what happened and where. To do so, it would be good to set breakpoint before call to faulty
strcpy. I found the binary in
/sys/network but I couldn’t disassemble it. After a little bit of googling I figured out that it is packed by tool called
kpack, so to unpack it I used kunpack, and then I was able to load it into disassembler (binary does not have symbols). After a while I had the address when binary is calling our
strcpy - 0x6a8b, so we can easily put the breakpoint there (no ASLR here!) and inspect where data is being copied.
edi points to our
esi points data to be copied. Notice that
esp is not that far from
edi, so maybe we can somehow overflow the stack? Crafting another
.html file with a
content value set to
58610*'A' should overflow the return address from
We have it - we are controlling, so now when code does a
strcpy it jumps to
0x41414141. You can immediately think about smuggling a malicious code in our tag, but unfortunately it cannot contain a null byte, because
strcpy will stop reading it further and even if the payload would be null-less, tag value is “lowerized” (
strlwr) - dead end?
Not really, we can utilize the html comments; they shouldn’t be mutated in any way. Here is the code responsible for parsing them:
so we can easily put our payload in the comment, but we need to find out where it will be located in memory. We can craft a simple web page and put a breakpoint in the function above. Let’s put breakpoint on address
0x3A43, so we can check out where our comment is.
As we can see,
esi register points to our data, so we want to jump to
1\xc5K), so the
K will be turned into
k, which means that the code will jump to
0x31c56b. We can handle that by putting a bunch of
nops in our payload. We also want to load a file into memory, so we have to use some syscalls to do it. Quick look at the docs reveals needed syscall:
Code for crafting the payload has the following form (it is also available on my GitHub):
WebView under the debugger and stepping a little over the code confirms that the exploit works and flag was loaded into memory.
Now it is time to exfiltrate the flag. We know that the VM has access to the internet, so we can make HTTP request with a flag as a parameter. I’ve found a useful function which can do work for us:
Actually, we will use only
http.get function. Quick check in IDA reveals that address where we need to jump is:
0x1137c, so we just need to create the url for HTTP request. The Final exploit generator looks like this:
Let’s run the browser and put breakpoint before
And after continuing, we see that something hit the web server :)
This was the first time I exploited a custom OS, and it was a great experience. I feel like the WebView browser is a damn buggy, and there are multiple ways to get code execution. Overall, it was a good challenge; I enjoyed solving it.