Secure Exam Browser
Writeup for Secure Exam Browser (Rev) - K17 CTF (2025) π
Description
I love academic misconduct
Solution
We'll follow the usual reversing methodology; basic file checks, static analysis, dynamic analysis.
Basic File Checks
64-bit binary, not stripped (easier to reverse).
file seb
seb: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c07f6718ca003c40c5170f23910830a91e90633e, for GNU/Linux 3.2.0, not strippedRunning the program, it asks us for a password. Apparently they changed it from universal default πΈ
./seb
Welcome to Secure Exam Browser
Enter Password: meow
ERR: another process is running!ltrace and strace don't give a quick win, over to ghidra.
Static Analysis
I started by renaming all the variables in the main function. I still do this out of habit (methodology), but honestly it's easier and faster to use ChatGPT. From the syntax, I can see the application was C++ and while the ghidra output cannot be compiled directly, ChatGPT can re-create it nicely.
main.cpp
decode_flag.cpp
It looks like there's a lot going on in the decode_flag function but hey, did you notice this line in main? (this is the [manually] refactored/commented pseudocode from ghidra)
Dynamic Analysis
Why bother reversing that whole function, when we can just set a breakpoint after it? However, PIE is enabled on this binary - we don't yet know the address to set the breakpoint at, only the offset (0x5106). We could run GDB and break on the main function (or first instruction) and then check the address of the instruction we want to break at.
It's been too long since I did rev/pwn but I could have sworn there was a better way to do this in pwndbg (the GDB plugin). I spend a few minutes asking ChatGPT but it seems incapable of reading the docs (???), so I have to do it myself! I quickly find what I was looking for π
Perfect. We can use breakrva (or brva) to set a breakpoint at the desired offset. First run the program, then hit ctrl + C to pause execution.
I try to enter the password but we never reach the breakpoint.
That pesky error again! I didn't see that string in the two functions, let's find it: search -> search for strings
Check the references to string.
We quickly find the problematic exit call.
Binary Patching
The function doesn't seem relevant to the challenge, but is preventing us from proceeding. What if we just patch the binary? The function starts at offset 0x3e67, let's patch that instruction.
Just right click -> patch instruction and change the code to return on entry.
The pseudocode for the op() function now looks like this.
Save the binary and go to file -> export program and save as Original File with Export User Byte Modifications checked.
Finally, it is checking the password! Let's open the patched binary in pwndbg and return to where we were earlier.
Hmmm, it still never triggers. Let's move the breakpoint a bit earlier (0x50dc)
Run the program and this time when we enter the password, GDB hits a breakpoint and we have our flag π
Flag: K17{i_heard_that_it's_impossible_to_re_c++!}
Last updated