keyboard_arrow_up

title: Writeup ESAIP CTF 2023 - Super Oiram Party
date: May 29, 2023
tags: ESAIP_CTF_2023 writeups reverse


Writeup ESAIP CTF 2023 - Super Oiram Party

Description:

You are still in the Nantendo secret room, and you came across a strange file in one of the computers.
You need to dig into this file a little bit, you might find something interesting.

**Author: Ooggle**"

Files:

super_oiram_party.pbp



🔍 Recon

Let's download the file and run some commands:

$ file super_oiram_party.pbp 
super_oiram_party.pbp: data

$ binwalk super_oiram_party.pbp 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
448           0x1C0           PNG image, 80 x 80, 8-bit/color RGBA, non-interlaced
889           0x379           Zlib compressed data, compressed
6433          0x1921          PNG image, 480 x 272, 8-bit/color RGBA, non-interlaced
6874          0x1ADA          Zlib compressed data, compressed
55202         0xD7A2          ELF, 32-bit LSB MIPS-II executable, MIPS, version 1 (SYSV)
108816        0x1A910         bix header, header size: 64 bytes, header CRC: 0x82801F00, created: 2004-01-22 20:09:36, image size: 170139136 bytes, Data Address: 0x60502140, Entry Point: 0x200, data CRC: 0x86801900, CPU: Blackfin, compression type: none, image name: ""'()

Ok, I now know that it contains a MIPS executable file, and by searching for "pbp file" on Internet it says that it is either a psx (PS1) or a PSP game file. I will try to run this file on both consoles emulators (ePSXe and PPSSPP).

And it run on PPSSPP! Little gameplay footage ahead:
oiram_party

And when I click on start, the following screen appears:
start_pressed

By now, it's still not clear what we have to do, and I want to reverse the code statically and not bother doing it with the PPSSPP debuger I know nothing about for now.

By googling how to extract code from pbp files, I came across PBP Unpacker, a windows only tool to extract all the content of a pbp file into separate ones.

After installing it using wine (because I'm on Linux), I loaded my pbp file into PBP Unpacker:
pbp_unpacker

It match with the output binwalk gave me earlier, so DATA.PSP must be the MIPS executable.


📟 Reversing the binary

By opening DATA.PSP in Ghidra, it detect it as a MIPS 32bit binary, so we have our code!

ghidra_import

First of all, we can check for strings we know: "You shall not know my secret." and "(pspsdk is really amazing!!)" and... it's stored in clear in the binary and used in a big function:
ghidra_2

The majority of the function code is in a big while(true) I can see a lot of if statments before the code can reach the function with the "Well done!" parameter. This is probably the main game loop:

from https://gameprogrammingpatterns.com/game-loop.html

I assume the other ifs are here to handle the controls but some magic numbers are used to defined the keys. Also, pressing a button seems to be filling a buffer called local_154 that is then used to make checks when pressing start button.

Ok, let's rename variables now that we have a better understanding of the code:
ghidra_3

It's better but we still have no idea what those magic numbers are refering to!


📄 Exploring pspsdk source code

The game give the information that it's using pspsdk as a development toolkit, and luckily it's open source: https://github.com/pspdev/pspsdk.

By digging in some tutorials and the source code, we found all the button declarations and replace them in Ghidra.

enum PspCtrlButtons
{
    /** Select button. */
    PSP_CTRL_SELECT     = 0x000001,
    /** Start button. */
    PSP_CTRL_START      = 0x000008,
    /** Up D-Pad button. */
    PSP_CTRL_UP         = 0x000010,
    /** Right D-Pad button. */
    PSP_CTRL_RIGHT      = 0x000020,
    /** Down D-Pad button. */
    PSP_CTRL_DOWN       = 0x000040,
    /** Left D-Pad button. */
    PSP_CTRL_LEFT       = 0x000080,
    /** Left trigger. */
    PSP_CTRL_LTRIGGER   = 0x000100,
    /** Right trigger. */
    PSP_CTRL_RTRIGGER   = 0x000200,
    /** Triangle button. */
    PSP_CTRL_TRIANGLE   = 0x001000,
    /** Circle button. */
    PSP_CTRL_CIRCLE     = 0x002000,
    /** Cross button. */
    PSP_CTRL_CROSS      = 0x004000,
    /** Square button. */
    PSP_CTRL_SQUARE     = 0x008000,
    /** Home button. In user mode this bit is set if the exit dialog is visible. */
    PSP_CTRL_HOME       = 0x010000,
    /** Hold button. */
    PSP_CTRL_HOLD       = 0x020000,
    /** Music Note button. */
    PSP_CTRL_NOTE       = 0x800000,
    /** Screen button. */
    PSP_CTRL_SCREEN     = 0x400000,
    /** Volume up button. */
    PSP_CTRL_VOLUP      = 0x100000,
    /** Volume down button. */
    PSP_CTRL_VOLDOWN    = 0x200000,
    /** Wlan switch up. */
    PSP_CTRL_WLAN_UP    = 0x040000,
    /** Remote hold position. */
    PSP_CTRL_REMOTE     = 0x080000, 
    /** Disc present. */
    PSP_CTRL_DISC       = 0x1000000,
    /** Memory stick present. */
    PSP_CTRL_MS         = 0x2000000,
};


🚩 Getting the flag

Now let's sum up all the ifs we have when start is pressed:

if(buffer[6] == buffer[5]) // 5 = PSP_CTRL_LEFT
if(buffer[6] == 0x80) // 6 = PSP_CTRL_LEFT
if(buffer[6] << 1 == buffer[2]) // 2 = 0x100 = PSP_CTRL_LTRIGGER
if(buffer[7] == 0x20) // 7 = PSP_CTRL_RIGHT
if(buffer[0] == buffer[4]) // 4 = PSP_CTRL_UP
if(buffer[0] == 0x10) // 0 = PSP_CTRL_UP
if((buffer[1] >> 7 == 0) && ((buffer[1] & 0x40U) != 0)) // 1 = PSP_CTRL_DOWN
if(buffer[3] >> 0xf != 0) goto LAB_08901340;
if(((buffer[3] & 0x4000U) == 0) || (!bVar2)) goto LAB_08901340; // 3 = PSP_CTRL_CROSS

You can see in the disassembly that the first 8 inputs are recorded, and are stored into buffer. Now that we have all our inputs, we must follow them then press start in the right order: up, down, L, cross, up, left, left, right, start.

Here is a gif of the flag:
flag

flag: ECTF{up_down_L_cross_up_left_left_right}