Square CTF 2017
The Turing Agent
The hamsters have triumphed! We found an open Github repository! It looks like some sort of game; perhaps they’re planning to trick our humans into playing it. We need to know if there’s any data hidden in the game that might harm our humans. Note: You'll need to prepend "flag-" to the flag.
It's the most interesting and the most difficult (for me) challenge in this CTF. We are given with several files:
file we see that this is Game Boy ROM image.
$ file mission.gb mission.gb: Game Boy ROM image (Rev.01) [ROM ONLY], ROM: 256Kbit
After several hours of setting up this debugger, finally, I came to this:
Yay! It's finally started correctly under debugging mode. So, there is one very important instruction that requires the breakpoint —
0x351E address. When the
halt instruction is called, the Gameboy stops executing instructions until an interrupt occurs. For us it means that all changes on the screen produced after this instruction. So, it's the convenient place to set the breakpoint.
The task itself
Now, we can continue executing until task itself is show:
So, after this text, the program waits for our 16 symbols input. We can input the next symbols:
- box button
- arrow button
These are all Gameboy's controllers. So, after entering a sequence of symbols, the binary have to compare them with the correct sequence. It's obvious. Of course, it's not, because the binary can comapre hashes of sequences, but after completing the challenge I can say, that only sequences are compared.
Anyway, let's input something:
Now, you should be very careful, because there is one important moment. Look closely at
0xC0C0 addresses. At the
0xC0A0 address placed our inputted string where each byte is a symbol from the input, corresponding to the next table:
|Symbol||Byte (in printable)|
And now look at the
0xC0C0 address. Each byte then transformed into another, corresponding to the yet another table:
I understand that it's hard to see in the first time, but if you try it several times, you'll definitely notice it.
Time for reversing
You can ask me now "And what should we do after this?". It's time to see the disassembled binary. Of course, we can see it in
bgb, but it's very inconvenient for me. Because of that, I'll use IDA Pro.
When IDA runs, it asks about processor architecture. Choose
Zilog 80 [z80]:
After this, you'll be faced with ROM and RAM parameters. I didn't change anything because IDA handled herself with this work.
Let's examine the list of strings placed in this binary. We can find very interesting one:
Actually, this is where we want to get. Okay, let's see a little above and find any xrefs:
Oops, IDA can't disassemble these bytes. But don't be sad because of it. We still have the xref! This
byte_2AA9 is called from the next function:
Now, we want to set the breakpoint at the
0x338C address and see if this address is reachable from the code without any modifications. Let's run it.
[Omitted screen of the reached instruction. Sorry, I'm lazy. You will see it on the next screen, I promise!]
And yes, it's reachable. We are stepping in this function and see disassembled instructions (those IDA couldn't disassembly)
After several instructions below, we can see interesting
and (hl) at the
Little information about
and in z80
and val — this is template for
and instruction. It ands
A register with
val and then puts obtained value in the
Brackets in the z80 assembly is equal to brackets in x86 assembly — getting value under the address. So, our instruction takes value from
hl address ands it with
A register and put the value back into
A register. Cool
Now let's set the breakpoint at this address and see what exactly this instruction ands.
A = 0x2 and
(hl) = 0x1. Doesn't it look very similiar? Yes,
(hl) includes our transformed bytes from the input. But it's too early to make conclusions. Let's continue execution until new
Yes, in the
(hl) lays our transformed bytes one by one. One more loop:
Don't be confused because of unchangeable
0x1 in the
(hl). Recall that we inputted the sequence
🡆🡆🡆🡆🡆🡅🡅🡅🡅.... See at the screen with this input above. So, transformed value of the
🡆 is exactly
Okay, what do we have so far? This
and ands bytes of transformed input and some bytes in
A register. Bytes in
A register correspond to the last table. It means, that our input checking takes place exactly in this instruction!
Next actions are obvious: Get all bytes from
A register and input exactly this symbols. In the end, we will get the next:
- In bytes:
0x8 0x2 0x10 0x4 0x20 0x10 0x4 0x8 0x8 0x10 0x4 0x1 0x2 0x1 0x20 0x8
- In symbols:
And when we enter the last symbol we will get the flag!