For this task we are provided with an iOS ARM listing, our goal is to craft a database file that will be accepted by the server.

First we start crafting the header of the file, and for this the hint that was released later in the competition was really useful:

typedef struct
{
        uint32_t magic;
        uint32_t version;
        uint16_t num_cols;
        uint16_t num_rows;
} header_t;

Let’s look at the asm code, we can realize that var_18 contains our input file. So we need a magic byte, and we can find the check for it here:

__text:00000B20 MOV R0,** #0x4F4C4F57** __text:00000B28 LDR R1, [SP,#0x2C+var_18] __text:00000B2A LDR R1, [R1] __text:00000B2C CMP R1, R0

Thus “574F4C4F”=“WOLO” needs to be at the beginning of our file (remember we are in a little endian architecture). Now let’s look for the version number:

__text:00000B3A LDR R0, [SP,#0x2C+var_18] __text:00000B3C LDR R0, [R0,#4] __text:00000B3E CMP R0, #1

At offset #4 we need to put a 1 (0100 0000 because we are in a little endian world :P). Then the code checks for the number of rows at offset #0xA and later for the number of cols at offset #8. Note that the number of rows needs to be between 4 and 0x1000 because of the following check:

__text:00000B4C LDR R0, [SP,#0x2C+var_18] __text:00000B4E LDRH R0, [R0,#0xA] __text:00000B50 CMP R0, #4 […] __text:00000B5E LDR R0, [SP,#0x2C+var_18] __text:00000B60 LDRH R0, [R0,#0xA] __text:00000B62 CMP.W R0, #0x1000

While the number of cols needs to be between 4 and 0x10:

__text:00000B72 LDR R0, [SP,#0x2C+var_18] __text:00000B74 LDRH R0, [R0,#8] __text:00000B76 CMP R0, #4 […] __text:00000B84 LDR R0, [SP,#0x2C+var_18] __text:00000B86 LDRH R0, [R0,#8] __text:00000B88 CMP R0, #0x10 

Later we can see that the code calculates the length of the column definition which is 0x11 bytes * n_cols and checks each column type with the function “_col_size”. We know that a col_t is 17 bytes from the hint:

typedef struct
{
        uint8_t type;
        char name[16];
} col_t;

__text:00000B98 LDRH R0, [R0,#8] __text:00000B9A MOVS R1, #0x11 __text:00000BA0 MULS R0, R1

A similar check is done for rows.

So what we need to do now is to write the column definition, followed by the actual content of each row. As we know the structure of col_t this is an easy job, however we need to know which columns and with which content are expected by the “_check_login” function. We can go through it and find out that these are the needed information to save in the db:

USERNAME: captainfalcon
PASSWORD: fc03329505475dd4be51627cc7f0b1f1
ADMIN: probably we want this set to 1
ISAWESOME: also this should probably set to 1

We can also know the type of each column from the code:

USERNAME: __text:00000D16 CMP R0, #5 -> 5 means 16 bytes string
PASSWORD: __text:00000D7A CMP R0, #6 -> 6 means 32 bytes string
ADMIN: __text:00000DDE CMP R0, #0 -> 0 means 8 bit integer
ISAWESOME: __text:00000E34 CMP R0, #0 -> 0 means 8 bit integer

Now we just need to sum everything up. Let’s start with the column definition

"\x05" + "USERNAME\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x06" + "PASSWORD\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00" + "ADMIN\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00" + "ISAWESOME\x00\x00\x00\x00\x00\x00\x00"

Then we just need to add the actual row data. Note that the format requires a minimum of 4 rows! So we can just replicate the row with our “captainfalcon” information for 4 times.

The final result is available here. By sending the file with a python script provided by the organizers we get the flag: flag{Small Group of Helpless Villages? Call in the Trebuchets.}

I really enjoyed this challenge, it allowed me to go back to some ARM assembly (and some Age of Empires atmosphere too) :) Thanks to the CSAW organizers for this great CTF!

fox