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.
Description
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 /hd0/1/flag.txt
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 kernel/trunk/docs/sysfuncs.txt
.
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.
Environment
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.
Vulnerability
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 strlwr
and strrtrim
in get_next_param
, so tag.value
won’t contain whitespaces and upper letters.
Exploitation
Checking if bug is exploitable
Let’s create a simple index.html
page with a super long meta
element with http-equiv
tag set to refresh
and 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 #redirect
field, 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 url
+58610*'A'
should overflow the return address from strcpy
.
We have it - we are controlling, so now when code does a ret
from 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?
Crafting the exploit
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 0x31c54b
(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 nop
s 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):
|
|
Now launching 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:
|
|
Running the exploit locally
Let’s run the browser and put breakpoint before http.get
call.
And after continuing, we see that something hit the web server :)
Running the exploit on a remote
hxp{wHy_h4cK_Chr0m3_wh3n_y0u_c4n_hAcK_BROWSER}
Summary
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.