CTF/ECW/Pwn Hacking Gameboy (Shellboy)

These last two weeks I had the opportunity to participate in the qualifying phase of the CTF ECW (for European Cyber ​​Cup). In this first write-up I tackle the resolution of a pwn challenge which I found very nice and in which we will have to exploit a game on gameboy.

You can download an archive containing the files provided here (source code + compiled program).

Code analysis

This is a game in which you play a character locked in a maze, and the resolution of which seems clearly impossible. You can program a sequence of instructions (go up, down, left, right) by pressing A then pressing one of the 4 arrows. The list of instructions appears at the top. You can select an instruction using the right or left arrows and modify the number of times it will be executed using the up and down arrows. You can also select two instructions. This has the effect of swapping their places if they are of different types (eg: going left and going right). If the two instructions are the same, this merges them.

Reading the code, we see that even if we manage to reach the center box, the flag will not be displayed.

The bug

The flaw is found in the move_inst function (inst_list.c). Indeed, it allows us to make a call to remove_inst , which allows us to decrement the inst_count variable by 1, without refreshing the list_empty variable . We can, by creating then selecting the same item twice, set inst_count to 0 while having list_empty to False.

With the configuration that we obtained thanks to move_inst , we can try to delete the item that we have already deleted (with the double select). As list_empty is at Falseremove_inst will be called a second time on the same item.

remove_inst decrements by 1 again our variable inst_count , making it go from 0 has -1 (signed), or 0xFF= 255 (unsigned).

If we do the test live in the Gameboy emulator we get something interesting: a bug !

We now have access to 255 bytes of memory that we can “read” and especially that we can rewrite. Enough to start the exploitation phase.

Operation

The objective is to read the flag. We are therefore trying to execute shellcode. We debug the program using BGB (a Gameboy emulator with a debugger). By playing a little with the program and inspecting the memory, we understand where the different variables are located.

  • In purple: the array of pointers to the move functions inst_funcs [4]
  • In yellowinst_rpt
  • In blueinst_ids
  • In redinst_count
  • In cyaninst_cursor_pos

We can arb-write from inst_rpt . Since we want to execute shellcode, we need to find a way to control the PC code pointer register. The idea is to add a pointer to the inst_funcs array and call this new element during the simulation. To do this we write the address of the memory area in which we will write our shellcode (in my case I chose 0xc0c0) has 0xc0bb. We could have written it to 0xc0ba, but we must leave this location empty to keep a valid instruction repetition (the one that will call our shellcode).

If we create an instruction with code 5, then it is the code indicated by the pointer located at 0xc0cb=0xc0b1+ 5*2 which will be called during the simulation. We therefore write a 1 to 0xc0b9 (first element of the inst_rpt array ) and a 5 to 0xc0c9 (1st element of inst_ids array ).

Shellcode design

The objective is to display the flag, therefore to make a call to bnprintf with the address of the flag as an argument. As the creator of the chall is a good guy (hooray), he makes our task easier (just a little bit) by telling us the address of the flag (0x6fa).

By opening the program in Ghidra with the ghidraboy extension , we find an interesting place to jump to display the flag without crashing the program.

We also note the opcodes associated with the instructions that interest us and we construct our shellcode. I added an addition to the SP register so that it points to the right place when returning to 0x0ed8.

ADD SP,4
LD DE,0x6fa
LD HL,0x0eca 21ca0e
JP HL e9

0xe8 0x04
0x11 0xfa 0x06
0x21 0xca 0x0e
0xe9

The flag

To summarize our memory should be organized like this:

  • 1 (repeat call to instr_funcs[5] ]
  • 0 (padding)]
  • [0xc0 0xc0(address of our shellcode)]
  • 0 // 3 times (padding) ]
  • [shellcode]
  • 5 (index of instr_funcs called 1 time)]
  • 0 // 15 times (padding)]

We use the python script provided (or not) to enter our combination, we click on start and boom! the flag appears (yay).


Posted

in

,

by

Tags: