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}