Writeup : 1ot wreck-it CTF

Diberikan sebuah file raw binary. Pada awalnya kami tidak mengetahui file apa ini sebenarnya. Kami lakukan strings dan mendapatkan string yang menarik.

  % strings 1ot.bin
__FMAP__
FLASH
BIOS
FMAP
COREBOOT
LARCHIVE
8cbfs master header
ORBC1112
LARCHIVE
dfallback/romstage
--->snip<---
PWVS
LARCHIVE
Master Header Locator
fallback/romstage
4.10-657-gbfb0c2d-dirty
Emulation
QEMU x86 i440fx/piix4

Setelah googling dengan berbagai variasi keyword yang dihasilkan dari string. Kami mendapatkan informasi bahwa file ini merupakan file bios coreboot yang bisa dijalankan lewat qemu.

  % qemu-system-x86_64 -bios ./1ot.bin -nographic
qemu-system-x86_64: warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
Password : testtest

Nope..!

Selanjutnya kami mencari cara untuk mendapatkan kode program yang ada didalam file coreboot tersebut. Dengan sedikit googling “coreboot file structure” kami mendapatkan referensi ini : https://www.coreboot.org/CBFS#Components. Kami mengekstrak komponen-komponen yang ada didalam file coreboot dengan bantuan cbfstool (https://github.com/michaelforney/coreboot/tree/master/util/cbfstool)
Dengan memahami struktur dari file coreboot kami mencoba untuk mengekstrak payload yang ada didalam file coreboot dengan menggunakan cbfstool. Kenapa payload? karena menurut informasi payload bisa saja mengandung executable code. (percobaan ini dilakukan juga karena kami sebelumnya mencoba mengekstrak bootlock dan stage tapi tidak menemukan apa-apa).

± % ./cbfstool 1ot.bin print
1ot.bin: 4096 kB, bootblocksize 4, romsize 4194304, offset 0x200
Alignment: 64 bytes

Name                           Offset     Type         Size
cbfs master header             0x0        unknown      32
fallback/romstage              0x80       stage        11388
fallback/ramstage              0x2d80     stage        44959
config                         0xdd80     raw          485
revision                       0xdfc0     raw          674
cmos_layout.bin                0xe2c0     unknown      548
fallback/postcar               0xe540     stage        9192
fallback/dsdt.aml              0x10980    raw          4021
fallback/payload               0x11980    payload      21566
(empty)                        0x16e00    null         4083608
bootblock                      0x3fbdc0   unknown      16384

Dari hasil print kita menemukan payload berada pada komponen yang bernama fallback/payload. Kita coba ekstrak.

± % ./cbfstool 1ot.bin extract fallback/payload 1ot_payload
1ot.bin: 4096 kB, bootblocksize 4, romsize 4194304, offset 0x200
Alignment: 64 bytes

Found file fallback/payload at 0x11980, type payload, size 21566
Warning: only 'raw' files are safe to extract.
Successfully dumped the file.

Didapatkan file 1ot_payload yang berisi payload. File tersebut masih berisi struktur dari payload (bisa dibaca disini https://www.coreboot.org/CBFS#Payloads). Dibawah ini merupakan isi dari file payload tsb.

± % xxd 1ot_payload | head -n 2
00000000: 434f 4445 0000 0001 0000 0038 0000 0000  CODE.......8....
00000010: 0400 0000 0000 5406 0003 1760 454e 5452  ......T....`ENTR

Dengan mengacu pada struktur payload (dari link yang kami berikan diatas), Payload ini mengandung executable code (bisa dilihat dari 4 byte pertama yaitu 0x45444F43). Isinya telah dicompress dengan lzma (dilihat dari 4 byte kedua yang berisi 0x1), dan datanya berada pada offset 0x38 (didapat dari 4 byte ke-3). Lalu kita extract data pada offset ke 0x38 dan decompress dengan lzma.

± % dd if=1ot_payload ibs=$((0x38)) skip=1 | lzma -dc > 1ot
384+1 records in
42+1 records out
21510 bytes (22 kB, 21 KiB) copied, 0,000214651 s, 100 MB/s

Didapatkanlah file 1ot

  ± % file 1ot
1ot: SYMMETRY i386 executable (0 @ 0) not stripped

Kami tidak tau itu file executable apa, bukan elf, tapi kami membukanya melalui ghidra. Kami lakukan search string “Password” didapatkan informasi menarik.

        00007e53  50 61 73      ds         "Password : "
                  73 77 6f 
                  72 64 20 
        00007e5f  25            ??         25h    %
        00007e60  63            ??         63h    c
        00007e61  00            ??         00h
        00007e62  0a 0a 42      ds         "\n\nBetul, 100! itu flagnya ;)"
                  65 74 75 
                  6c 2c 20 
        00007e7f  0a            ??         0Ah
        00007e80  0a            ??         0Ah
        00007e81  4e            ??         4Eh    N
        00007e82  6f            ??         6Fh    o
        00007e83  70            ??         70h    p
        00007e84  65            ??         65h    e
        00007e85  2e            ??         2Eh    .

Tadinya kami mau melakukan xref terhadapt string password. Sepertinya ghidra gagal melakukannya karena setelah dilihat seharusnya program memiliki base address 0x4000000 tetapi ghidra meloadnya pada base address 0. Kami tahu bahwa base address seharusnya 0x4000000 dari fungsi yang kami decompile secara acak hasil contohnya seperti ini.

void FUN_00004aaf(void)

{
  FUN_00004a98((uint *)0x400bc38); // Dilihat dari parameter yang berupa address seharusnya base address adalah 0x4000000
  return;
}

Dengan base address 0x4000000 seharusnya juga string Password berada pada alamat 0x4007e53. Kami melakukan search memory di ghidra terhadap nilai 0x4007e53 untuk mencari penggunaan alamat tersebut. Didapatkan 1 alamat pada 0x2585 yang berisi instruksi push nilai tsb.

        00002585  68 53 7e      PUSH       0x4007e53
                  00 04
        0000258a  e8 61 17      CALL       FUN_00003cf0                                     undefined FUN_00003cf0(int param
                  00 00

Lakukan decompile pada area tersebut. Didapatkan kode yang sepertinya melakukan pengecekan pada password.

/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

void FUN_00002571(undefined1 param_1)

{
  byte bVar1;
  bool bVar2;
  int iVar3;
  uint uVar4;
  int iVar5;
  int local_80;
  int len;
  int i;
  byte *pbVar6;
  byte *pbVar7;
  byte j;
  byte local_53 [59];
  undefined1 *puStack24;
  
  j = 0;
  puStack24 = &param_1;
  FUN_00003cf0(0x4007e53);
  len = 0x33;
  pbVar6 = &DAT_04007e20;
  pbVar7 = local_53;
  while (len != 0) {
    len = len + -1;
    *pbVar7 = *pbVar6;
    pbVar6 = pbVar6 + (uint)j * -2 + 1;
    pbVar7 = pbVar7 + (uint)j * -2 + 1;
  }
  iVar3 = FUN_00002f62(0x1e0);
  bVar2 = false;
  i = 0;
  local_80 = iVar3;
  while( true ) {
    uVar4 = FUN_00003e63(local_80);
    *(undefined *)(iVar3 + i) = (char)uVar4;
    iVar5 = FUN_00004aaf();
    bVar1 = local_53[i];
    local_80 = FUN_00003cf0(0x4007e5f);
    if ((*(char *)(iVar3 + i) == '\r') || (i == 0x32)) break;
    if (((uVar4 & 0xff ^ iVar5 % 0x100) != (uint)bVar1) || (0x15 < i)) {
      bVar2 = true;
    }
    i = i + 1;
  }
  if (bVar2) {
    local_80 = 0x4007e7f;
  }
  else {
    local_80 = 0x4007e62;
  }
  FUN_00003cf0(local_80);
  FUN_000000a0();
  local_80 = FUN_00002661();
  FUN_00003da4(local_80);
  FUN_00002b04();
  FUN_00002571((char)_DAT_0400c6e4);
  return;
}

Kurang lebih jika kode diatas diubah menjadi lebih clean akan seperti ini.

  print("Password : ");
  memcpy(secret_num, &DAT_04007e20, 0x32)
  buf = (char *)some_alloca(0x1e0);
  pass_salah = false;
  i = 0;
  pcVar2 = buf;
  while( true ) {
    input_char = getchar((int)pcVar2);
    buf[i] = (char)input_char;
    key = getkey();
    sec_num_i = secret_num[i];
    uVar4 = (uint)sec_num_i;
    bVar1 = buf[i];
    print("%c",(uint)bVar1);
    if ((buf[i] == '\r') || (i == 0x32)) break;
    if (((input_char & 0xff ^ key % 0x100) != uVar4) || (0x15 < i)) {
      pass_salah = true;
    }
    i = i + 1;
  }
  if (pass_salah) {
                    /* Nope */
    msg_str = (char *)0x4007e7f;
  }
  else {
                    /* Betul flagnya */
    msg_str = (char *)0x4007e62;
  }
  print(msg_str);

Sementara get_key memiliki fungsi seperti ini.

void getkey(void)
{
  FUN_00004a98((uint *)0x400bc38);
  return;
  /*
   * Note : Pada awalnya 0x400bc38 (atau 0xbc38 pada ghidra) akan berisi 0x1
   *        0000bc38  01            ??         01h
   *        0000bc39  00            ??         00h
   *        0000bc3a  00            ??         00h
   *        0000bc3b  00            ??         00h
   */
}

uint FUN_00004a98(uint *param_1)

{
  uint uVar1;
  
  uVar1 = *param_1 * 0x41c64e6d + 0x3039;
  *param_1 = uVar1;
  return uVar1 & 0x7fffffff;
}

Kesimpulan :

  1. secret_num berisi array of char yang berisi konstanta
  2. secret_num akan diiterasi
  3. setiap iterasi program akan menerima 1 byte input dan menxornya dengan nilai yang dihasilkan dari get_key
  4. hasil dari xor akan dibandingkan dengan nilai dari secret_num disetiap iterasi
  5. jika hasilnya tidak sama maka variable pass_salah akan diset true dan program menampilkan “Nope”

Untuk mendapatkan inputan yang tepat kita cuman perlu menxor nilai yang ada dari secret_num dengan return value dari get_key. Di bawah ini merupakan script solvernya untuk mendapatkan inputan yang tepat.

s = [0xd4, 0xd4, 0xe0, 0x68, 0x60, 0xed, 0x46, 0x4b, 0x11, 0x92, 0x4f, 0xc5, 0xf8, 0xc8, 0x7a, 0x3e, 0x66, 0xb8, 0xab, 0xf9, 0xef, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xd4, 0xe0, 0x68, 0x60, 0xed, 0x46, 0x4b, 0x11, 0x92, 0x4f, 0xc5, 0xf8, 0xc8, 0x7a, 0x3e, 0x66, 0xb8, 0xab, 0xf9, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00] # secret_num
uvar1 = 1
flag = ""
for i in range(0x32):
    uvar1 = uvar1 * 0x41c64e6d + 0x3039 # key
    flag += (chr(s[i] ^ (uvar1 & 0xff)))
print(repr(flag))

Jalankan

    % python solve.py
'r3tURnFroMc0r3bO0ooT\rs0\xa9.\x1b\x88\x85R\x8b\xa5\xa7M\xd6fRW\x9b\xa8c\xe0\xd94~\x134xQ\xb6\xb7'

Didapatkan string : r3tURnFroMc0r3bO0ooT

  % qemu-system-x86_64 -bios ./1ot.bin -nographic                                                                                                                                          !10023
qemu-system-x86_64: warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]
Password : r3tURnFroMc0r3bO0ooT

Betul, 100! itu flagnya 😉

Flag : wreck{r3tURnFroMc0r3bO0ooT}

7 thoughts on “Writeup : 1ot wreck-it CTF

  1. iya maksud saya decompile. Maksudnya menyesuaikan itu berarti terserah bagaimana kita mengartikan nama variable dan fungsinya.Contohnya: itu ada fungsi memcpy() itu mengartikannya bagaimana? Hasil decompilenya tidak ada tulisan memcpy(). Mohon pencerahannya saya masih pemula dan ingin belajar

    Like

  2. kalo boleh request, tolong dibuatkan artikel tentang cara mengubah hasil decompile menjadi “kode yang lebih clean”. Terima kasih

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s