If you'd like to try yourself before reading the CTF write-up you can download the file here: i-am-in-control.zip.
Opening the bin file in a hex editor reveals it’s a SEGA ROM! (SEGGGGG-AAAAA).
Looks like we’re thwarting SHODAN again.
Functionality-wise you can move the cursor (red dot) around the screen with the arrow keys. Pressing the buttons on the controller (A, B, C, Start) (for my emulator they’re mapped to Z, X, C, V) cycles through 4 messages. The other buttons (if they’re supported) don’t do anything.
Ok, I know nothing about the Motorola 68k processor, but it looks like the ASM is in AT&T syntax. I adapted code from here to create a loader for Ghidra that could handle SEGA roms: https://github.com/lab313ru/ghidra_sega_ldr (thanks Vladimir Kononovich!).
Now that I've got good looking disassembly and now we’re ready to start working on it.
There’s a small hint I think in one of the messages about sprite coordinates that SHODAN gives us. Since we have a cursor, I suppose that’s a good place to start. My intuition says we need to move the cursor to some sprite coordinates and maybe press some keys or something. If I was doing this to solve for speed I would check the sprite sheets for a potential flag, but that seems like no fun at all. Let’s solve it completely without changing the running code at all.
The cursor coordinates x and y are stored in memory (short) at address 0xA, and 0xC respectively. Let’s find references to those locations.
First interesting function besides an init_cursor() is a function that checks cursor_y and cursor_x.
Unfortunately this function doesn’t break, we’ll need to find what calls this function first.
Ok, so we need to fail the if check to get this function to be called. We need to now know what references address DAT_00ff0008.
Getting slightly side-tracked I found the bitmask for keypresses at DAT_00ff0006.
The low bits are the d-pad, the high bits are the A,B,C,Start buttons.
To get to this interesting function we have to not pass this test:
if (((DAT_00ff0008 & 0x10) == 0) || (unaff_D7w = 0, (DAT_00ff0008 & 0x20) == 0))
We call our check_cursor() function if we press the B and C buttons at the same time (There are other ways to get this called, you can press ABC at once, ABC and Start at once, but the easiest way is just to press B (0x10) and C (0x20) (shown above).
Here’s the function that sets DAT_00ff0008.
Small note: the function which calls this is basically a wrapper. I say basically because it sets a level 7 interrupt and the supervisor bit, this should trigger a IRQL7 interrupt in the vector table, but the handler for it is just a rte. I’m not familiar enough with the Motorola 68K processor or disassembly to know if this was common in sega games or not, but it did stick out a bit.
One damn problem I’m having is finding the location of that memory at 0xA10003 (which is loaded in the A0 register before calling this function)…
After searching for about 30 minutes, I found this, it’s my only hint at the moment on what’s at that memory location.
Clearly the data is coming from our input, I just wish I could see it live in this debugger I’ve chosen. I checked all of the (3) registered interrupt functions and there’s nothing interesting there. I’m calling it here and moving onto the function that checks our cursor location since we already know how to call the function check_cursor().
Threw together a quick python script to print out the valid pixel coordinates.
And sure enough, we get the flag if we press BC at the same time at either of these coordinates!
Brilliant challenge. I have no idea how difficult it was writing an even simple Sega program might have been but that was an excellent challenge! Tons of fun. Moving on to the next challenge.