M0lecon CTF 2023 NoRegVM Writeup
For some reason, I’ve found VM exploitation challenges to be quite interesting recently.
I managed to solve a challenge called NoRegVM from M0lecon CTF 2023. I also tried to solve a reversing challenge involving the same binary, but I couldn’t find the final solution. But that’s for another blog post.
Step 1: Reversing
This program lets you provide the code and memory that a VM is initialized with.
The VM can perform 13 different functions including reading and writing to the standard input/output.
This VM does not use registers, so all the operands used for an operation should be immediate values or memory addresses. Data can be copied to and from the memory using the pop_in
and pop_out
instructions in the VM.
Step 2: The Bug(s)
I found three bugs in this challenge and I eventually used all of them for the final exploit.
The first bug is a buffer overflow vulnerability in the pop_out
function.
The loop in pop_out
can be executed as many times as we want. This would eventually lead to an out-of-bounds write of the output
buffer.
And that leads us to the second bug which is a format string vulnerability in the write_buf
function.
Looking at the pseudo-code generated by IDA, this vulnerability is not apparent.
However, if you look at the actual assembly code, we see that the format specifier used by printf
is a buffer in the data section of the binary.
And it so happens that this FMT_STR
buffer lies just after the output
buffer.
Therefore, by overflowing the output
buffer using the pop_out
function, we can trigger a format string vulnerability in the write_buf
function. This can be used to leak whatever data we need.
I ended up leaking the heap, canary, stack, binary and libc base addresses.
The final bug is in the len
function.
In this function again, we can execute the loop as many times as we want. This eventually leads to an out-of-bounds write of the stack buffer of size 200;
Step 3: The Exploit
So the plan was clear,
- Overflow `output` buffer to overwrite `FMT_STR`
- Leak all the pointers
- Overflow stack buffer in `len` and get PC control
- ????
- Profit
A slightly annoying part about this challenge is that the loop in the len
function will stop at the first instance of an int value 0. However, only the lower 8 bits from each memory address is copied into the stack buffer. Therefore, if the value at a memory address was 0x0100, the check for 0 would pass while only the last byte (i.e NULL) would be appended to the stack buffer.
In order to do this, I modified the program code to generate this 0x100 value (by performing 0x10 * 0x10) and writing it to the indices where I needed NULL bytes.
The final exploit uses the leaks in a simple ROP chain to call system("/bin/sh")
I had initially written this exploit by hard-coding the opcodes and operand values. And it was very hard to read for someone who didn’t understand my plan.
So, after the CTF, I tried re-writing the exploit to use my assembler. And it worked with minimal adjustments! Even I was surprised.
The final exploit is available here: Exploit
And since the assembler will keep getting updated, its better to store it somewhere like GitHub.
Enjoy Reading This Article?
Here are some more articles you might like to read next: