ICS2C 2018 Final - Crypto & Reversing

Weird Cipher 250

Challenge

Diberikan file cipher.txt yang isinya sebagai berikut.

cipher: U2FsdGVkX1/EI9UEUzWcCoy94xDAPMdKW5I+mHNRohtpTOwRhLpRM8hLj333fv+fpfCa
password: h4ckthepl4net
hint: bruteforce cipher

Sepertinya cipher masih dalam bentuk base64.

$ echo 'U2FsdGVkX1/EI9UEUzWcCoy94xDAPMdKW5I+mHNRohtpTOwRhLpRM8hLj333fv+fpfCa' | base64 -d
Salted__�#� S5�
��� �<�J[�>�sQ� iL� ��Q3�K�}�~�����

Solution

Hasilnya mengandung string Salted__, berarti kita bisa gunakan openssl untuk dekrip ciphernya. Hint soal mengatakan bruteforce cipher, maka kita coba semua kemungkinan enkripsi yang digunakan.

Implementation

#! /usr/bin/env python

import os

pos = ["aes-128-cbc", "aes-128-ecb", "aes-192-cbc", "aes-192-ecb", "aes-256-cbc", "aes-256-ecb", "base64", "bf", "bf-cbc", "bf-cfb", "bf-ecb", "bf-ofb", "camellia-128-cbc", "camellia-128-ecb", "camellia-192-cbc", "camellia-192-ecb", "camellia-256-cbc", "camellia-256-ecb", "cast", "cast-cbc", "cast5-cbc", "cast5-cfb", "cast5-ecb", "cast5-ofb", "des", "des-cbc", "des-cfb", "des-ecb", "des-ede", "des-ede-cbc", "des-ede-cfb", "des-ede-ofb", "des-ede3", "des-ede3-cbc", "des-ede3-cfb", "des-ede3-ofb", "des-ofb", "des3", "desx", "rc2", "rc2-40-cbc", "rc2-64-cbc", "rc2-cbc", "rc2-cfb", "rc2-ecb", "rc2-ofb", "rc4", "rc4-40", "seed", "seed-cbc", "seed-cfb", "seed-ecb", "seed-ofb"]

os.system("mkdir z-out")

for i in pos:
    cmd = "echo 'U2FsdGVkX1/EI9UEUzWcCoy94xDAPMdKW5I+mHNRohtpTOwRhLpRM8hLj333fv+fpfCa' | openssl %s -d -a -k h4ckthepl4net > z-out/z-%s.txt" % (i,i)
    print cmd
    os.system(cmd)

Dan jalankan.

$ python weird-cipher.py
...
$ cd z-out/
$ strings * | grep "CTF"
CTF{m4st
CTF{m4st3r1nG_0pen_3s_3s_3l_12345}

Flag

CTF{m4st3r1nG_0pen_3s_3s_3l_12345}

Serial 250

Challenge

Diberikan file executable 64-bit yang meminta kita memasukkan token yang valid.

$ file reverse-1
reverse-1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=38da236519b4e31062c347f16ada06145c4495f3, not stripped
$ ./reverse-1
 Enter token for message
 > deomkicer
 Invalid token!

Solution

Gunakan IDA untuk melihat disassembly file, lalu lihat fungsi main dari program tersebut.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rsi@63
  _BYTE *v4; // rax@66
  _BYTE *v5; // rbx@66
  char v6; // r12@66
  unsigned __int64 v7; // rax@66
  unsigned __int64 v8; // rax@67
  __int64 v9; // rbx@68
  __int64 v10; // rax@68
  __int64 v11; // rax@68
  int result; // eax@68
  __int64 v13; // rbx@68
  char v14; // [sp+1Fh] [bp-231h]@65
  char v15; // [sp+20h] [bp-230h]@65
  unsigned __int64 i; // [sp+28h] [bp-228h]@65
  char s; // [sp+30h] [bp-220h]@1
  char v18; // [sp+31h] [bp-21Fh]@43
  char v19; // [sp+32h] [bp-21Eh]@13
  char v20; // [sp+33h] [bp-21Dh]@15
  char v21; // [sp+34h] [bp-21Ch]@19
  char v22; // [sp+35h] [bp-21Bh]@5
  char v23; // [sp+36h] [bp-21Ah]@33
  char v24; // [sp+37h] [bp-219h]@21
  char v25; // [sp+38h] [bp-218h]@27
  char v26; // [sp+39h] [bp-217h]@35
  char v27; // [sp+3Ah] [bp-216h]@23
  char v28; // [sp+3Bh] [bp-215h]@5
  char v29; // [sp+3Ch] [bp-214h]@61
  char v30; // [sp+3Dh] [bp-213h]@17
  char v31; // [sp+3Eh] [bp-212h]@11
  char v32; // [sp+3Fh] [bp-211h]@27
  char v33; // [sp+40h] [bp-210h]@37
  char v34; // [sp+41h] [bp-20Fh]@7
  char v35; // [sp+42h] [bp-20Eh]@59
  char v36; // [sp+43h] [bp-20Dh]@9
  char v37; // [sp+44h] [bp-20Ch]@53
  char v38; // [sp+45h] [bp-20Bh]@47
  char v39; // [sp+46h] [bp-20Ah]@39
  char v40; // [sp+47h] [bp-209h]@7
  char v41; // [sp+48h] [bp-208h]@49
  char v42; // [sp+49h] [bp-207h]@63
  char v43; // [sp+4Ah] [bp-206h]@41
  char v44; // [sp+4Bh] [bp-205h]@21
  char v45; // [sp+4Ch] [bp-204h]@49
  char v46; // [sp+4Dh] [bp-203h]@3
  __int64 v47; // [sp+238h] [bp-18h]@1

  v47 = *MK_FP(__FS__, 40LL);
  printf(" Enter token for message\n > ", argv, argv);
  std::operator>><char,std::char_traits<char>>(&edata, &s);
  if ( strlen(&s) <= 0x1C )
    reject();
  if ( v46 )
    reject();
  if ( v22 != v28 )
    reject();
  if ( v34 != v40 )
    reject();
  if ( v36 != 49 )
    reject();
  if ( v31 != 79 )
    reject();
  if ( v19 != 69 )
    reject();
  if ( v20 != 67 )
    reject();
  if ( v30 != 114 )
    reject();
  if ( (unsigned __int8)checkA(v22, v21, 13) ^ 1 )
    reject();
  if ( (unsigned __int8)checkB(v24, v44, -5) ^ 1 )
    reject();
  if ( (unsigned __int8)checkC(s, v27) ^ 1 )
    reject();
  if ( v28 != v34 )
    reject();
  if ( v25 != v32 )
    reject();
  if ( v27 != 78 )
    reject();
  if ( v44 != 54 )
    reject();
  if ( v23 != 56 )
    reject();
  if ( v26 != 119 )
    reject();
  if ( v33 != 112 )
    reject();
  if ( (unsigned __int8)checkA(v36, v39, 7) ^ 1 )
    reject();
  if ( (unsigned __int8)checkB(v43, v31, 41) ^ 1 )
    reject();
  if ( (unsigned __int8)checkC(v18, v25) ^ 1 )
    reject();
  if ( v22 != v34 )
    reject();
  if ( v23 != v38 )
    reject();
  if ( v41 != v45 )
    reject();
  if ( v32 != 86 )
    reject();
  if ( v37 != 104 )
    reject();
  if ( v34 != 45 )
    reject();
  if ( v45 != 72 )
    reject();
  if ( (unsigned __int8)checkA(v23, v35, 9) ^ 1 )
    reject();
  if ( (unsigned __int8)checkB(v26, v29, 49) ^ 1 )
    reject();
  v3 = (unsigned int)v19;
  if ( (unsigned __int8)checkC(v42, v19) ^ 1 )
    reject();
  std::allocator<char>::allocator(&v14, v3);
  std::string::string(&v15, &unk_4014A5, &v14);
  std::allocator<char>::~allocator(&v14);
  for ( i = 0LL; ; ++i )
  {
    v8 = std::string::length((std::string *)&v15);
    if ( v8 <= i )
      break;
    LODWORD(v4) = std::string::operator[](&v15, i);
    v5 = v4;
    v6 = *v4;
    v7 = strlen(&s);
    *v5 = v6 ^ ~*(&s + i % v7);
  }
  v9 = std::string::c_str((std::string *)&v15);
  LODWORD(v10) = std::operator<<<std::char_traits<char>>(&std::cout, " >>> ");
  LODWORD(v11) = std::operator<<<std::char_traits<char>>(v10, v9);
  std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
  std::string::~string((std::string *)&v15);
  result = 0;
  v13 = *MK_FP(__FS__, 40LL) ^ v47;
  return result;
}

Terdapat banyak pengecekan input di fungsi main, salah satunya input harus 0x1C atau 28 karakter. Beberapa pengecekan menggunakan fungsi checkA, checkB, dan checkC.

__int64 __fastcall checkA(char a1, char a2, int a3)
{
  return a3 + 2 * a1 == a2;
}

__int64 __fastcall checkB(char a1, char a2, int a3)
{
  return a3 + a1 == 2 * a2;
}

__int64 __fastcall checkC(char a1, char a2)
{
  return a1 - 32 == a2;
}

Input kita harus melewati semua pengecekan tersebut tanpa memanggil fungsi reject. Gunakan z3 untuk mencari serial yang cocok untuk dapatkan flagnya. Berikut kodenya.

Implementation

from z3 import *
import string

s = Solver()
flag = string.printable

num = [BitVec(i, 32) for i in range(28)]

# masukin constraint biar hasilnya cuma berada di rentang flag
for i in range(28):
    for j in range(0x00, 0xff):
        if(chr(j) not in flag):
            s.add(num[i] != j)

# nambahin karakter pasti ke constraint
s.add(num[4] == num[10])
s.add(num[16] == num[22])
s.add(num[18] == 49)
s.add(num[13] == 79)
s.add(num[1] == 69)
s.add(num[2] == 67)
s.add(num[12] == 114)

s.add(13 + 2 * num[4] == num[3])
s.add(-5 + num[6] == 2 * num[26])
# s.add(num[0] - 32 == num[9])

s.add(num[10] == num[16])
s.add(num[7] == num[14])
s.add(num[9] == 78)
s.add(num[26] == 54)
s.add(num[5] == 56)
s.add(num[8] == 119)
s.add(num[15] == 112)

s.add(7 + 2 * num[18] == num[21])
s.add(41 + num[25] == 2 * num[13])
s.add(num[0] - 32 == num[7])

s.add(num[4] == num[16])
s.add(num[5] == num[20])
s.add(num[23] == num[27])
s.add(num[14] == 86)
s.add(num[19] == 104)
s.add(num[16] == 45)
s.add(num[27] == 72)

s.add(9 + 2 * num[5] == num[17])
s.add(49 + num[8] == 2 * num[11])
s.add(num[24] - 32 == num[1])

www = s.check()
model = s.model()
manga = [0 for i in range(28)]
print model
for i in range(28):
    index = eval(str(model[i])[2:])
    manga[index] = eval(str(model[model[i]]))

# print "".join([chr(eval(str(model[model[i]]))) for i in range(28)])
print "Solusi:", "".join([chr(manga[i]) for i in range(28)])

Dan jalankan.

$ python serial.py
[k!11 = 84,
 k!24 = 101,
 k!17 = 121,
 k!27 = 72,
 k!16 = 45,
 k!19 = 104,
 k!14 = 86,
 k!23 = 72,
 k!20 = 56,
 k!4 = 45,
 k!0 = 118,
 k!25 = 117,
 k!21 = 105,
 k!15 = 112,
 k!8 = 119,
 k!5 = 56,
 k!26 = 54,
 k!9 = 78,
 k!7 = 86,
 k!10 = 45,
 k!6 = 113,
 k!3 = 103,
 k!12 = 114,
 k!2 = 67,
 k!1 = 69,
 k!13 = 79,
 k!18 = 49,
 k!22 = 45]
Solusi: vECg-8qVwN-TrOVp-y1h8i-Heu6H

Lalu masukkan solusi tersebut sebagai token pada program reverse-1.

$ ./reverse-1
 Enter token for message
 > vECg-8qVwN-TrOVp-y1h8i-Heu6H
 Invalid token!

Sepertinya solusi yang diberikan masih kurang satu karakter di bagian awal. Kita coba bruteforce semua kemungkinan karakter dan jalankan di program reverse-1.

import os, string

serial = "vECg-8qVwN-TrOVp-y1h8i-Heu6H"
pos = string.ascii_letters + string.digits

for i in pos:
    token = i + serial
    cmd = "echo '%s' | ./reverse-1 | grep '>>>'" % (token)
    a = os.system(cmd)

Dan jalankan.

$ python reverse-char.py
 >  >>> N0tAllTh0s3Wh0W4nd3rAr3LO5t

Flag

CTF{N0tAllTh0s3Wh0W4nd3rAr3LO5t}