Alles CTF 2020 Writeups




[Rev] Flag Service Revolution

Given a boot.dol file, a Nintendo GameCube file. Opened it with the dolphin-emu.
Then searched how we can disassemble the .dol files and found this https://mkwii.com/showthread.php?tid=1193 used ghidra

This is a stripped and statically linked binary, it's hard to trace the functions.
Searched for strings appearing on app -> `Cross References` - `main function FUN_8003d4c4`.

The decompiled code of ghidra is too messy with the stripped func names, var names.
Anyway gone through the code and renamed `variables` and the `functions` based on arguments, codeflow, values etc.. by tracing to make it convinient.

Finally modified decompiled code :
void FUN_8003d4c4(void)
{
int counter;
uint key_code;
int iVar1;
undefined4 extraout_r4;
undefined4 *flag_status;
int iVar2;
int iVar3;
int iVar4;
undefined total_display_string [100];
undefined Array_8 [8];
short local_18c;
short local_18a;
ushort local_16a;
undefined4 s_no_flag_0-4;
undefined4 s_no_flag_4-4;
undefined4 s_no_flag_8-4;
undefined4 s_no_flag_12-4;
undefined Array_36 [36];
undefined4 s_no_buttons_0-4;
undefined4 s_no_buttons_4-4;
undefined4 s_no_buttons_8-4;
undefined4 s_no_buttons_12-4;
undefined4 s_no_buttons_16-4;
undefined4 s_no_buttons_20-4;
undefined2 s_no_buttons_24-2;
char s_no_buttons_26-1;
undefined4 local_111;
undefined4 local_10d;
undefined4 local_109;
undefined4 local_105;
undefined4 local_101;
undefined2 local_fd;
undefined local_fb;
undefined Frame [26];
undefined2 local_de;
undefined Array_40 [40];
char local_a0;
char local_9f;
char local_9e;
char local_9d;
char local_9c;
undefined local_9b;
undefined local_9a;
undefined local_99;
undefined local_98;
undefined local_97;
char local_96;
undefined local_95;
undefined local_94;
undefined local_93;
undefined local_92;
undefined local_91;
undefined local_90;
undefined local_8f;
undefined local_8e;
undefined local_8d;
undefined local_8c;
undefined local_8b;
undefined local_8a;
undefined local_89;
char local_88;
char local_87;
char local_86;
char local_85;
undefined local_84;
undefined local_83;
char local_82;
undefined local_81;
char local_80;
undefined local_7f;
char local_7e;
undefined local_7d;
undefined local_7c;
undefined4 s_service_revolution_0-4;
undefined4 s_service_revolution_4-4;
undefined4 s_service_revolution_8-4;
undefined4 s_service_revolution_12-4;
undefined4 s_service_revolution_16-4;
undefined2 s_service_revolution_20-2;
undefined4 s_welcome_0-4;
undefined4 s_welcome_4-4;
undefined4 s_great_0-4;
undefined2 s_great_4-2;
undefined4 s_flag_0-4;
char s_to_4-1;
undefined4 s_our_0-4;
undefined2 s_to_0-2;
char s_to_2-1;
FUN_8003cf48();
FUN_8003dac4();
FUN_8003ed74();
FUN_8009e1e8(0);
FUN_8009846c(extraout_r4);
Create_Frame(Frame,&DAT_800b9840,0xf1088,0x18);
s_to_0-2 = s_to_803900a8._0_2_;
s_great_4-2 = s_Great_803900a0._4_2_;
s_flag_0-4 = s_Flag_803900ac._0_4_;
s_service_revolution_4-4 = s_{Service_Revolution}!_803900b4._4_4_;
s_service_revolution_8-4 = s_{Service_Revolution}!_803900b4._8_4_;
s_to_2-1 = s_to_803900a8[2];
s_to_4-1 = s_Flag_803900ac[4];
s_great_0-4 = s_Great_803900a0._0_4_;
s_service_revolution_0-4 = s_{Service_Revolution}!_803900b4._0_4_;
s_service_revolution_12-4 = s_{Service_Revolution}!_803900b4._12_4_;
s_service_revolution_16-4 = s_{Service_Revolution}!_803900b4._16_4_;
s_service_revolution_20-2 = s_{Service_Revolution}!_803900b4._20_2_;
s_welcome_0-4 = s_Welcome_80390098._0_4_;
s_welcome_4-4 = s_Welcome_80390098._4_4_;
s_our_0-4 = 0x6f757200;
s_no_buttons_12-4 = s_No_buttons_pressed_yet_..._800a7fc0._12_4_;
s_no_buttons_16-4 = s_No_buttons_pressed_yet_..._800a7fc0._16_4_;
s_no_buttons_20-4 = s_No_buttons_pressed_yet_..._800a7fc0._20_4_;
s_no_buttons_24-2 = s_No_buttons_pressed_yet_..._800a7fc0._24_2_;
s_no_buttons_26-1 = s_No_buttons_pressed_yet_..._800a7fc0[26];
s_no_flag_0-4 = s_No_flag_yet_..._800a7ff4._0_4_;
local_de = 0x111;
s_no_buttons_0-4 = s_No_buttons_pressed_yet_..._800a7fc0._0_4_;
s_no_buttons_4-4 = s_No_buttons_pressed_yet_..._800a7fc0._4_4_;
s_no_buttons_8-4 = s_No_buttons_pressed_yet_..._800a7fc0._8_4_;
s_no_flag_4-4 = s_No_flag_yet_..._800a7ff4._4_4_;
s_no_flag_8-4 = s_No_flag_yet_..._800a7ff4._8_4_;
s_no_flag_12-4 = s_No_flag_yet_..._800a7ff4._12_4_;
local_fb = 0;
local_111 = 0;
local_10d = 0;
local_109 = 0;
local_105 = 0;
local_101 = 0;
local_fd = 0;
FUN_800971b4(Array_36,0,0x22);
counter = Display_png(Array_40,Array_8,PNG_801aa8e0,300,200);
if (counter == 0) {
return;
}
iVar3 = 0xf;
flag_status = &s_no_flag_0-4;
counter = 0;
FUN_80040594(Array_8,0x4c,0x3e);
iVar2 = 0;
iVar4 = 0x14;
FUN_800406e4((double)DAT_803900cc,Array_8);
local_16a = 0;
FUN_8003eb1c();
do {
iVar1 = FUN_8003eb74();
if (iVar1 == 0) {
FUN_8003eba0(&audio_file.mp4,0x1b70d8);
}
if (DAT_8044b998 != 0) {
FUN_8003eb54();
FUN_80041db0(Array_40);
FUN_80040458();
}
FUN_800568f4();
if (DAT_8044b958 == 0) {
iVar4 = iVar4 + ((uint)(DAT_8044b954 == 0) - 1);
if (DAT_8044b950 == 0) goto LAB_8003d84c;
LAB_8003d74c:
iVar3 = iVar3 + 1;
}
else {
iVar4 = iVar4 + 1;
if (DAT_8044b950 != 0) goto LAB_8003d74c;
LAB_8003d84c:
iVar3 = iVar3 + ((uint)(DAT_8044b94c == 0) - 1);
}
Combine_strings(total_display_string,s_%s_%s_%s_%s_%s%s_80390084,&s_welcome_0-4,&s_to_0-2,
&s_our_0-4,&s_great_0-4,&s_flag_0-4,&s_service_revolution_0-4);
Display_text(Frame,iVar4,iVar3,total_display_string);
key_code = Read_keyPress(0);
if (key_code != 0) {
Check_button(key_code,&s_no_buttons_0-4);
if (counter == 9) {
if ((key_code & 0x800) != 0) {
flag_status = (undefined4 *)&local_a0;
local_8e = s_welcome_4-4._2_1_;
local_90 = s_service_revolution_12-4._0_1_;
}
LAB_8003d8e4:
if (counter == 0) {
LAB_8003d8ec:
counter = 0;
if ((key_code & 8) != 0) {
counter = 1;
local_80 = (char)s_great_0-4;
local_7e = s_service_revolution_0-4._1_1_ + ' ';
local_81 = s_welcome_0-4._2_1_;
local_a0 = (char)s_great_0-4 + -0x20;
local_83 = s_service_revolution_8-4._0_1_;
}
}
}
else {
if (counter == 8) {
if ((key_code & 0x800) == 0) goto LAB_8003d8ec;
local_93 = s_great_0-4._2_1_;
counter = 9;
local_98 = s_service_revolution_16-4._0_1_;
}
else {
if (counter == 7) {
if ((key_code & 0x400) == 0) goto LAB_8003d8ec;
counter = 8;
local_85 = (char)s_great_0-4;
local_82 = s_flag_0-4._0_1_ + ' ';
local_92 = s_service_revolution_8-4._0_1_;
local_8d = s_service_revolution_16-4._2_1_;
}
else {
if (counter == 6) {
if ((key_code & 0x400) == 0) goto LAB_8003d8ec;
counter = 7;
local_7d = (undefined)s_service_revolution_16-4;
local_9f = s_flag_0-4._1_1_ + -0x20;
local_9c = s_service_revolution_0-4._1_1_;
local_95 = (undefined)s_to_0-2;
}
else {
if (counter == 5) {
if ((key_code & 0x100) == 0) goto LAB_8003d8ec;
counter = 6;
local_7f = (undefined)s_flag_0-4;
local_9d = (char)s_service_revolution_4-4 + -0x20;
local_7c = 0;
local_91 = s_great_4-2._0_1_;
}
else {
if (counter == 4) {
if ((key_code & 0x200) == 0) goto LAB_8003d8ec;
counter = 5;
local_96 = s_flag_0-4._1_1_;
local_88 = s_great_0-4._0_1_ + ' ';
local_8b = s_welcome_0-4._1_1_;
local_97 = s_service_revolution_8-4._0_1_;
}
else {
if (counter == 3) {
if ((key_code & 0x100) == 0) goto LAB_8003d8ec;
local_8a = s_great_0-4._1_1_;
counter = 4;
local_8f = s_service_revolution_8-4._0_1_;
local_9b = s_service_revolution_0-4._0_1_;
local_89 = local_8f;
}
else {
if (counter == 2) {
if ((key_code & 0x200) == 0) goto LAB_8003d8ec;
counter = 3;
local_94 = (undefined)s_service_revolution_8-4;
local_87 = s_service_revolution_8-4._1_1_ + ' ';
local_86 = (char)s_service_revolution_4-4;
local_99 = s_service_revolution_4-4._1_1_;
}
else {
if (counter != 1) goto LAB_8003d8e4;
if ((key_code & 4) == 0) goto LAB_8003d8ec;
counter = 2;
local_84 = s_to_0-2._0_1_;
local_9e = s_service_revolution_12-4._1_1_ + -0x20;
local_8c = (undefined)s_service_revolution_12-4;
local_9a = s_welcome_0-4._0_1_;
}
}
}
}
}
}
}
}
}
iVar2 = iVar2 + 1;
key_code = (uint)local_16a;
if ((uint)(iVar2 * -0x33333333) < 0x33333334) {
key_code = key_code + 1 & 0xffff;
if (key_code == 0x1b) {
key_code = 0;
local_16a = 0;
}
else {
local_16a = (ushort)key_code;
}
}
FUN_80040630(Array_8,(int)local_18c,(int)local_18a,key_code);
Display_text(Frame,0xf,100,&s_no_buttons_0-4);
Display_text(Frame,0xf,0x82,flag_status);
FUN_80040270();
} while( true );
}

By looking the game window, assumed that `No flag yet ...` is the status message.
Looked for it in code, checked if that status message changes anywhere and found line 179
flag_status = (undefined4 *)&local_a0;
C
From the declaration part, found that `local_a0 is a char array upto local_7c`.
From the code found that these values are get initiated based on some conditions, and finally that message goes to status message.
That constructed message is most likely a flag. Boom :)

It just modifying that array using the strings used previously in code
Written a python script based on those modifications. Got the flag :)

wlc = b"Welcome"
grt = b"Great"
to = b"to"
our = b"our"
flg = b"Flag"
srv = b"{Service_Revolution}!"
local = [0 for x in range(37)]
local[0xa0-0x7c] = grt[3] - 0x20
local[0x9f-0x7c] = flg[1] - 0x20
local[0x9e-0x7c] = srv[13]- 0x20
local[0x9d-0x7c] = srv[7] - 0x20
local[0x9c-0x7c] = srv[1]
local[0x9b-0x7c] = srv[0]
local[0x9a-0x7c] = wlc[0]
local[0x99-0x7c] = srv[5]
local[0x98-0x7c] = srv[16]
local[0x97-0x7c] = srv[8]
local[0x96-0x7c] = flg[1]
local[0x95-0x7c] = to[1]
local[0x94-0x7c] = srv[11]
local[0x93-0x7c] = grt[2]
local[0x92-0x7c] = srv[8]
local[0x91-0x7c] = grt[4]
local[0x90-0x7c] = srv[12]
local[0x8f-0x7c] = srv[8]
local[0x8e-0x7c] = wlc[6]
local[0x8d-0x7c] = srv[18]
local[0x8c-0x7c] = srv[15]
local[0x8b-0x7c] = wlc[1]
local[0x8a-0x7c] = grt[1]
local[0x89-0x7c] = srv[8]
local[0x88-0x7c] = grt[0] + ord(' ')
local[0x87-0x7c] = srv[9] + ord(' ')
local[0x86-0x7c] = srv[7]
local[0x85-0x7c] = grt[3]
local[0x84-0x7c] = to[0]
local[0x83-0x7c] = srv[8]
local[0x82-0x7c] = flg[0] + ord(' ')
local[0x81-0x7c] = wlc[2]
local[0x80-0x7c] = grt[3]
local[0x7f-0x7c] = flg[3]
local[0x7e-0x7c] = srv[1] + ord(' ')
local[0x7d-0x7c] = srv[19]
local[0x7c-0x7c] = 0
final = local[1:][::-1]
Str = ''.join(chr(x) for x in final)
print(Str)
Flag : ALLES{Wii_love_to_enter_great_flags}

Solution - 2

But i want to complete this game in it's way :). skip;(next-chall, menu)
In previous part in the urge of getting flag i have left those conditions.
The conditions are checking the key strokes, 🤔.
        if (counter == 8) {
          if ((key_code & 0x800) == 0) goto LAB_8003d8ec;
          local_93 = s_great_0-4._2_1_;
          counter = 9;
          local_98 = s_service_revolution_16-4._0_1_;
        }
        else {
          if (counter == 7) {
            if ((key_code & 0x400) == 0) goto LAB_8003d8ec;
            counter = 8;
            local_85 = (char)s_great_0-4;
            local_82 = s_flag_0-4._0_1_ + ' ';
            local_92 = s_service_revolution_8-4._0_1_;
            local_8d = s_service_revolution_16-4._2_1_;
          }
JavaScript
Biseds the key_code check, the counter is increment, which makes leads it to next check.
Game wants us to click 10 keys (based on counter checks 0-9), in a sequence.
Key-code sequence : `0x8, 0x4, 0x200, 0x100, 0x200, 0x100, 0x400, 0x400, 0x800, 0x800`

Need to find the keys for those codes, Luckily it gives the key press status on game.
By tracing the status message variable, found a function for updating that message.
Used that function to extracted key-code, status message pairs.
 0x8      =  A = ML // Mouse Left Click
 0x4      =  B =MR  // Mouse Right Click
 0x800    =  UP (up arrow key)
 0x400    =  DOWN (down arrow key)
 0x200    =  RIGHT (right arrow key)
 0x100    =  LEFT (left arrow key)
 0x1000   =  PLUS
 0x10     =  MINUS
PHP
Surprisigly, on Mouse clicks the game is displaying A, B. So ML(Mouse Left) = A ; MR(Mouse Right) = B.
Finally: Key-Sequence = `ML, MR, RIGHT, LEFT, RIGHT, LEFT, DOWN, DOWN, UP, UP`.

Here we go :)



[Rev] prehistoric_mario

Given a prehistoric-mario.apk file, a Game simmilar to mario. given a map (100 x 100), with 11 question tiles, which changes there color on hit. (green-> red-> blue-> yellow-> green -> ..)

Decompiled the Apk and gone through the source code. (MyPlatformer.java)
checkFlag function drabbed the attention,
private void checkFlag() {
    MessageDigest messageDigest;
    int intValue;
    byte[] bArr = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    TiledMapTileLayer tiledMapTileLayer = (TiledMapTileLayer) this.map.getLayers().get("questionmarks");
    int i = 0;
    int i2 = 0;
    while (i < 100) {
        int i3 = i2;
        for (int i4 = 0; i4 < 100; i4++) {
            TiledMapTileLayer.Cell cell = tiledMapTileLayer.getCell(i, i4);
            if (!(cell == null || !cell.getTile().getProperties().containsKey("questionmarkType") || (intValue = ((Integer) cell.getTile().getProperties().get("questionmarkType")).intValue()) == 1337)) {
                bArr[i3] = (byte) intValue;
                i3++;
            }
        }
        i++;
        i2 = i3;
    }
    try {
        messageDigest = MessageDigest.getInstance("SHA-256");
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        messageDigest = null;
    }
    messageDigest.update(bArr);
    messageDigest.update("P4ssw0rdS4lt".getBytes());
    if (toHex(messageDigest.digest()).equals("024800ace2ec394e6af68baa46e81dfbea93f0f6730610560c66ee9748d91420")) {
        try {
            // Use that messageDigest to decrypt the flag map
JavaScript
It's grabbing `questionmarkType` property values of 11 tiles(based on length of bArr), might be from those 11 questionMark tiles.
Constructing a string with those values and checking its sha256 with some hash.
Note : the code is skipping tile with 1337 value, there might be 12th questionMark Tile some where in map.

Now we have to check for where 'questionmarkType' values are modified and where this checkFlag is called.
Surprisingly both are at same place.
TiledMapTileLayer tiledMapTileLayer = (TiledMapTileLayer) this.map.getLayers().get("questionmarks");
TiledMapTileLayer.Cell cell2 = tiledMapTileLayer.getCell((int) next.x, (int) next.y);
if (cell2.getTile().getProperties().containsKey("questionmarkType")) {
    int intValue = ((Integer) cell2.getTile().getProperties().get("questionmarkType")).intValue();
    if (intValue == 1337) {
        new Array();
        checkFlag();
    } else {
        if (intValue == 0) {
            intValue = 21;
        } else if (intValue == 21) {
            intValue = 97;
        } else if (intValue == 97) {
            intValue = 37;
        } else if (intValue == 37) {
            intValue = 0;
        }
        try {
            new TiledMapTileLayer.Cell();
            cell2.setTile(this.map.getTileSets().getTile(this.questionMarkTileMapping.get(Integer.valueOf(intValue)).intValue()));
            tiledMapTileLayer.setCell((int) next.x, (int) next.y, cell2);
        } catch (Exception unused) {
        }
    }
    z = true;
}
JavaScript
This is the code for hitting questionMark tiles event.

Things to notice:
  1. we need to find the tile with 1337 value, and hit it to call check Flag
  2. The questionmarkType value is changing on hit (4 values), same like color.
1. Tile with 1337 value
Extracted the apk, to get (map.tmx) in assets. Used pytmx and loaded (map.tmx).
And found there is 12 tile(with 1337 value as expected) on [15, 90] grid. which is below the map. it's impossible to reach there.

2. questionmarkType values
Each color denotes each value.
0  = GREEN
21 = RED
97 = BLUE
37 = YELLOW
JavaScript
We need to set the colors of 11 question Mark tiles in a correct manner, then call checkFlag.
11 tiles, 4 colors ; Total possibilites = 4**11 = 4194304. Nothing for a brite force :)

Python code for getting Key (colors):
#/usr/bin/python3
from hashlib import sha256
def check_Hash(Num):
hh = sha256()
hh.update(Num.to_bytes(11, 'big'))
hh.update(b'P4ssw0rdS4lt')
if hh.hexdigest() == '024800ace2ec394e6af68baa46e81dfbea93f0f6730610560c66ee9748d91420':
print("[+] Foun the Key !..."+' '*10)
return Num
def to_num_array(Hex):
Num = 0
for i in range(11):
Num = Num * 256 + Pos[ Hex % 4 ]
Hex = Hex >> 2
return Num
Pos = [0, 21, 97, 37]
Color = {
0 : 'GREEN',
21 : 'RED',
97 : 'BLUE',
37 : 'YELLOW'
}
for i in range(4**11):
num = to_num_array(i)
print(num, end='\r')
fin = check_Hash(num)
if fin:
break
tmp = fin.to_bytes(11, 'big')
for i in tmp:
print(Color[i], end=", ")
print('')
Color-Key : RED, GREEN, BLUE, YELLOW, RED, YELLOW, YELLOW, BLUE, BLUE, YELLOW, RED
Note: We need to set those colors from left -> right manner.

Now the only challenge with which we left is calling checkFlag
There might be many ways like patching apk, patching map, dynamic debugging.
I choose calling the function[checkFlag] using objection tool
The script i used :
console.log("Script loaded successfully ");
function callSecretFun() {
Java.perform(function () {
Java.choose("com.alles.platformer.MyPlatformer", {
onMatch: function (instance) {
console.log("[+] Found instance: " + instance);
console.log("Result of secret func: " + instance.checkFlag());
},
onComplete: function () {
console.log("[+] Completed")
}
});
});
}
callSecretFun();
view raw inject..js hosted with ❤ by GitHub

Expliotation:
1. Solved the color of questionMark tiles as Color-Key (above), by hand.
2. Then injected that script using objection, then dumping the map_flag.tmx file

Now we need the see the dumped flag map.
I have unpacked it , and changed the map.tmx with this map_flag.tmx and packed it using apktool.
Then installed, opened. Everything is same as before (walls, questionMarkstiles, structure) :(

Again came to pytmx, loaded map_flag.tmx. to check if ther are any hidden layers etc.
Found that there are many(100+) questionMark tiles than normal, something is fishy
Extracted there cordinates, and drawn them with python for rough figure
Those 11 tiles on the upper half are similar to normal map.
Means those extra questionMarks are for drawing the flag , flag is below the map. It's time to jump 👀.
Boom :) Flag : ALLES{1TS_A_DINO}


Thanks for reading !...

Comments

Post a Comment

Popular posts from this blog

Square CTF 2019 Writeup's

K3RN3L CTF 2021 Rev - `Recurso & Rasm ` writeups

Confidence CTF 2020 `Cat web` challenge writeup