Sunday, April 27, 2014

CONFidence Dragon Sector CTF Teaser 2014 (Web400)

Dragon Sector CTF Teaser ini merupakan CTF Teaser untuk individual. Saya hanya akan membahas soal Web400 karena untuk soal yang lain yang berhasil saya selesaikan, sudah banyak yang membahas.

Soal CTFnya adalah:

Fiery Technologies has created a new login system, where all secret messages are encoded. Please, check if you can bypass the authorization and read dragon's private stuff: http://23.253.207.102/

dragon-web1

Sesuai isi webnya, kita bisa login sebagai guest password guest. Setelah login, kita diberikan source code website ini, tentunya dengan sebagian informasi tidak muncul.

dragon-web2

Bagian penting yang memeriksa login adalah sebagai berikut:

$auth = unserialize($auth); 

if(!is_array($auth))
return false;

$auth['hmac_t'] = sha1(sha1($auth['username'].$auth['hmac_t'].$auth['password']).$secret_salt);

if($auth['hmac_t'] !== $auth['hmac'])
return false;


$message = '';

foreach($database as $row)
{
if($row['username'] == $auth['username'])
if($row['password'] == $auth['password'])
{
$message = $row['secret_message'];
return true;
}
}

 

Ada beberapa pemeriksaan yang dilakukan:


  1. Setelah unseralize(), “auth” harus berupa array
  2. Nilai $auth['hmac_t']  harus sama dengan $auth['hmac']
  3. Username harus sama dengan database ($row[“username”])
  4. Password harus sama dengan database ($row[“password”])

Pemeriksaan pertama artinya kita tidak bisa menggunakan attack berbasis unserialize yang menghasilkan PHP class (dan di soal ini memang tidak ada kelas PHP sama sekali). Untuk bagian kedua, sepertinya sulit, karena kita tidak tahu secret_salt maupun password. Tapi PHP memiliki fitur “reference” dan di dalam array, kita bisa memiliki “self reference”.

Self reference ini sifatnya bisa “forward reference”:


<?php
$a = array();
$a["hello"] = &$a["world"];
$a["world"] = "Hi there";

echo $a["hello"];

?>

Hasilnya adalah “Hi There”. Jadi kita bisa mengassign bahwa $a[“hello”] adalah reference ke $a[“world”], padahal $a[“world”] belum didefinisikan sampai baris berikutnya.

Nah untuk membypass pemeriksaan HMAC, kita bisa melakukan ini:


<?php
$auth = array();
$auth["username"] = "dragon";
$auth["hmac_t"] = "b";
$auth["hmac"] = &$auth["hmac_t"];
echo urlencode(serialize($auth));
?>

Setelah $auth[“hmac_t”] dihitung nilainya, $auth[“hmac”] akan selalu sama nilainya dengan $auth[“hmac_t”], dan ketika dibandingkan memakai strict comparison (===) hasilnya akan sama. Jadi pemeriksaan hmac akan lolos.


Berikutnya adalah pemeriksaan username: dari file database.php, bisa dilihat bahwa ada dua username: “guest” dan “dragon”. Jadi kita bisa memakai username “dragon” (karena itu yang kita inginkan).


Untuk pemeriksaan password, digunakan operator “==” dan bukan “===”, jadi kita bisa menggunakan trik PHP ini:


echo “anystring”==(boolean)1;


Karena sisi kanan adalah boolean, makan perbandingan boolean akan dilakukan, dan jika string tidak kosong, maka PHP akan melakukan cast string menjadi boolean true.


Dengan menambahkan ini, kita akan membypass semua requirement di atas:


$auth["password"] = (boolean)1;


dragon-web3


Di dalam index.php, kita melihat kode ini:


printmsg($message,$auth['password'])


Kita berhasil menipu proses login, sehingga bisa masuk ke index.php, tapi $message didekrip dengan menggunakan key (boolean)1, bukan password yang benar, jadi teks yang muncul kacau. Langkah berikutnya adalah: kita harus bisa mendekrip dengan password yang benar, tapi apa passwordnya?


Jika kita lihat hasil dekripsi dari beberapa dua blok pertama ketika kita login sebagai guest, terlihat dua string ini sebagai dua blok pertama: “        <h1>You'” (dalam hex: 20202020202020203c68313e596f7527)  dan “re logged in as “ (dalam hex: 7265206c6f6767656420696e20617320). Perhatikan: aneh sekali ada spasi di depan “<h1>You”. Seolah-olah ini sengaja dibuat sebagai known plaintext yang akan tetap sama ketika kita login sebagai user mana saja. Berbekal dua blok itu, berikutnya akan kita coba known plaintext attack.


Sebelum melakukan known plaintext attack, kita harus mendapatkan dulu string aslinya sebelum dienkrip dengan (boolean)1.


Fungsi printmsg adalah seperti berikut ini. Ini merupakan modulo-encryption


<?php
function printmsg($message,$pwd)
{
$mod = gmp_init('fffffffdffffffffffffffffffffffff',16);
$mul = gmp_init('b562a81099dff41937c5ae51ba7427a4',16);
$key = str_repeat(sprintf('%04x',crc32($pwd)&0xffff),8);
$key = gmp_init($key,16);
$pwd = gmp_init($pwd,16);
for($i=0;$i<strlen($message);$i+=32)
{
$msg = substr($message,$i,32);
$msg = gmp_init($msg,16);
$msg = gmp_add($msg,$pwd);
$msg = gmp_mul($msg,$mul);
$msg = gmp_add($msg,$key);
$msg = gmp_mod($msg,$mod);
$msg = gmp_strval($msg,16);
$msg = str_pad($msg,32,'0',STR_PAD_LEFT);
$msg = pack('H*',$msg);
echo $msg;
}
}
?>

Operasi yang dilakukan pada kode di atas adalah: untuk tiap satu blok 16 byte (32 byte string heksadesimal), lakukan ini:



decrypted = ((msg + pwd) * mul + key) % mod


Kita tahu bahwa string yang kita dapat (decrypted) adalah hasil dari dekripsi dengan key 1. Jadi kita perlu kembalikan lagi pesan yang aslinya



decrypted = ((msg + 1) * mul + key) % mod


Nilai key kita ketahui dari:


<?php
$key = str_repeat(sprintf('%04x',crc32($pwd)&0xffff),8);
?>

Yang mengulangi hasil crc32($pwd) dalam bentuk heksadesimal sebanyak 8 kali. Dalam kasus ini:


echo str_repeat(sprintf('%04x',crc32(“1”)&0xffff),8);


Hasilnya adalah:


efb7efb7efb7efb7efb7efb7efb7efb7


Dengan menggunakan hex editor, saya mendapatkan data yang diterima dari server



83b36a91153e36d645b3cc5b24e349c4d5f86add64857e1b6d6c048aebd547bdc805abd8648 …


Kita hanya perlu dua blok pertama saja (karena hanya punya dua plaintext):



83b36a91153e36d645b3cc5b24e349c4 dan d5f86add64857e1b6d6c048aebd547bd


Kita bisa menggunakan Wolfram di Raspberry Pi, untuk menyelesaikan persamaan tersebut. Tuliskan persamaannya apa adanya, tambahkan Solve, dan tambahkan bahwa persamaan ini dilakukan dalam Modulo tertentu (prefix 16^^ adalah untuk menyatakan literal heksadesimal):



Solve[{((msg + 1)*16^^b562a81099dff41937c5ae51ba7427a4+
       16^^efb7efb7efb7efb7efb7efb7efb7efb7) ==
       16^^83b36a91153e36d645b3cc5b24e349c4},
       {msg}, Modulus –> 16^^fffffffdffffffffffffffffffffffff]


Dan hasilnya:


{{msg -> 223771863340694085175727173816760330489}}


Atau dalam hexadesimal: a858e4aad8cfbb2da797ac107c72e8f9. Kita lakukan hal yang sama untuk blok kedua:



Solve[{((msg + 1)*16^^b562a81099dff41937c5ae51ba7427a4+
       16^^efb7efb7efb7efb7efb7efb7efb7efb7) ==
       16^^d5f86add64857e1b6d6c048aebd547bd},
       {msg}, Modulus –> 16^^fffffffdffffffffffffffffffffffff]


Dan hasilnya:



{{msg -> 242594421588216051499208124666235775630}}


Dalam heksadesimal: b681fc704017fe8f5ce577d699c6ea8e


Sekarang kita punya 2 data asli sebelum dienkrip, dan 2 known plaintext:


a858e4aad8cfbb2da797ac107c72e8f9 –> known plaintext = 20202020202020203c68313e596f7527
b681fc704017fe8f5ce577d699c6ea8e -> known plaintext = 7265206c6f6767656420696e20617320

Berdasarkan rumus dekripsi di atas:


decrypted = ((msg + pwd) * mul + key) % mod


Kita punya 2 persamaan, dengan 2 unknown (pwd dan key). Kita selesaikan ini dalam wolfram:



Solve[
{    ((16^^a858e4aad8cfbb2da797ac107c72e8f9 + pwd)*
      16^^b562a81099dff41937c5ae51ba7427a4+key)
         == 16^^20202020202020203c68313e596f7527,
    ((16^^b681fc704017fe8f5ce577d699c6ea8e + pwd)*
      16^^b562a81099dff41937c5ae51ba7427a4+key)
         == 16^^7265206c6f6767656420696e20617320}
   ,{pwd, key},
   Modulus –> 16^^fffffffdffffffffffffffffffffffff]


Hasilnya:


{{pwd -> 288594978817253855719850634468445308235 + 340253855540352171535659707399537251486 C[1], key -> C[1]}}


Perhatikan bahwa nilai key adalah C[1] dan nilai pwd dinyatakan dalam A+B*C[1], ini artinya ada banyak solusi, kita bisa menggunakan key berapa saja, dan kita bisa mendapatkan nilai pwd yang sesuai persamaan tersebut. Meski wolfram menyatakan bahwa nilai C bisa apa saja, dari kode dekripsi kita mengetahui bahwa nilai C ini merupakan hasil repetisi dari crc332(x)&0xffff, karena operasi AND dengan 0xffff, maksimum hanya ada 65536 key yang mungkin. Jadi algoritmanya adalah: untuk tiap i dari 0 sampai 65535 buat nilai C (nilai key), lalu hitung nilai pwd berdasarkan formula hasil wolfram, lalu cek apakah crc32(pwd)&0xffff sama dengan i yang kita gunakan untuk menghasilkan C.


Kita bisa membrute force ini dengan cepat di PHP (perhatikan kode yang dicomment, saya mencoba dulu untuk account guest, untuk memastikan bahwa algoritma ini berhasil):


<?php 

//dragon: {{key -> 288594978817253855719850634468445308235 + 340253855540352171535659707399537251486 C[1], n -> C[1]}}
//guest: {{key -> 280430390139727558925837228457913706258 + 340253855540352171535659707399537251486 C[1], n -> C[1]}}

$mod = gmp_init('fffffffdffffffffffffffffffffffff',16);

#for dragin
$k = gmp_init("288594978817253855719850634468445308235", 10);
$m = gmp_init("340253855540352171535659707399537251486", 10);
#test for guest
#$k = gmp_init("280430390139727558925837228457913706258", 10);
#$m = gmp_init("340253855540352171535659707399537251486", 10);

for ($i= 0; $i<65536; $i++) {

$h = str_repeat(sprintf('%04x',$i&0xffff),8);
$z = gmp_init($h, 16);
$n = gmp_mul($z, $m);
$p = gmp_add($n, $k);
$p = gmp_mod($p,$mod);

$key = gmp_strval($p, 16);
$key = str_pad($key,32,'0',STR_PAD_LEFT);

if ((crc32($key)&0xffff)==$i) {
print "found key: ". $key." ".$h."\n";
}

}

?>

Hasilnya:


found key: 7964d5b99f9ee50d04da5046d15ea053 486e486e486e486e486e486e486e486e
found key: 1b306891ad8f289d4ba3a294014f5329 722b722b722b722b722b722b722b722b
found key: 7864d3604826ee20dc500b9c1e2f143d 85928592859285928592859285928592

Secara matematis, semua nilai tersebut bisa digunakan untuk mendekrip message, tapi saat ini kita hanya punya dua blok awal message (sisanya adalah encrypted data yang didekrip dengan key (boolean)1), dan saya malas untuk mencari encrypted text asli dari semua blok messagenya. Karena hanya ada 3 kemungkinan, saya melakukan brute force saja. Meskipun semua key bisa digunakan untuk mendekrip pesan, tapi password yang disimpan di server hanya salah satu dari key tersebut, jadi kita perlu mencoba-coba.


Kita bisa langsung mencoba 3 solusi tersebut dengan curl. Solusi pertama dan kedua gagal, dan solusi ketiga berhasil (7864d3604826ee20dc500b9c1e2f143d), untuk mendapatkan flagnya, kita set $auth[“password”]=”7864d3604826ee20dc500b9c1e2f143d”:


image


Dan kita dapatkan flagnya:


<div class="jumbotron">
<div class="container">
<h1>You're logged in as dragon! </h1>
<p>Congratulations! You've found the flag:</p>
<p><a class="btn btn-primary btn-lg" role="button" href="javascript:void(prompt('The Flag','DrgnS{2971f86fc7a161d514e7dbdad5dbfa26}'))">&laquo; DrgnS{2971f86fc7a161d514e7dbdad5dbfa26} &raquo;</a></p>
</div>
</div>

Monday, April 14, 2014

PlaidCTF2014 Write Up: rsa(forensic450)

Sebenarnya kami tidak berhasil menyelesaikan challenge RSA ini tepat waktu, jadi kami tidak mendapatkan poin. Tapi karena kami sudah berhasil menyelesaikan challengenya, jadi kami tuliskan pembahasannya.

Inti dari solusinya adalah berdasarkan paper: "Reconstructing RSA Private Keys from Random Key Bits"

http://cseweb.ucsd.edu/~hovav/papers/hs09.html

Ada source code yang bisa didownload di situ, tapi sourcenya perlu dimodifikasi. Berikut ini adalah modifikasi yang saya lakukan:

Program aslinya akan menghasilkan RSA key (atau membaca dari  file), merusak key tersebut, lalu merekonstruksi kembali keynya. Bagian membaca dari file diganti agar membaca bit yang tersedia dan bit yang tidak tersedia dianggap corrupt.

Saya menambahkan opsi -x <corrupted_file>. File input harus diperbaiki dulu supaya nilai modulus benar. Ini bisa didapatkan dari public key:

openssl pkey -pubin -in public.pub -text -noout
Contoh file input berdasarkan soal:

Jalankan program:

./rsa -x corrupted.pem

Hasilnya: solution0.asn

Ubah ke format der:

openssl asn1parse -genconf solution0.asn -out newkey.der

Check bahwa keynya benar, dan tuliskan ke PEM:

openssl rsa -in newkey.der -inform der -text -check > key.pem

Decrypt pesan:

openssl rsautl -decrypt -in encrypted -out plaintext.txt -inkey key.pem

Hasilnya adalah:

crypt0>>>f0rensics3~

Sunday, April 13, 2014

PlaidCTF2014 Write Up: curlcore (Forensic250)

Dalam challenge ini, Kita diberi sebuah file coredump dari curl yang sedang mengakses sebuah situs https, dan sebuah file capture pcap, serta beberapa library pelengkap curl. Tadinya ingin meload file ini di gdb untuk melihat session master key, tapi ternyata meload file ini sulit dilakukan. Setelah mencari-cari di Internet, didapat link ini yang menyatakan bahwa kita bisa langsung mengekstrak dari key dari memori dengan mudah jika kita tahu session idnya:
http://www.cloudshield.com/blog/advanced-malware/how-to-decrypt-openssl-sessions-using-wireshark-and-ssl-session-identifiers/
Jadi solusinya cukup mudah: cari session id menggunakan wireshark di paket handshake, dan didapat:
19ab5edc02f097d5074890e44b483a49b083b043682993f046a55f265f11b5f4
Kemudian kita cari di curlcure nilai hex tersebut, dan kita mundur sedikit untuk mendapatkan master keynya:
191e5042e6b31371aa65258e13b2dc714d984df8d68fad678ff0a2fc49476d65c3a161f718572c3f5db8566a0de89e58
Keduanya kita masukkan ke file sslkey.txt (semuanya satu baris):
RSA Session-ID:19ab5edc02f097d5074890e44b483a49b083b043682993f046a55f265f11b5f4 Master-Key:191e5042e6b31371aa65258e13b2dc714d984df8d68fad678ff0a2fc49476d65c3a161f718572c3f5db8566a0de89e58
Lalu kita pakai file ini di wireshark:
settingssl
Kita sekarang bisa melihat requestnya:
ssl2
Dan jika kita ikuti SSL Streamnya:
ssl
Kita mendapatkan flagnya.

PlaidCTF2014 Write Up: g++ (Reversing200)

Tantangan reversing kali ini agak unik karena bukan mereverse binary, tapi source code sebuah file C++. Source codenya menggunakan template dan macro secara ekstensif. Saya tidak akan membahas sampai detail, hanya beberapa point penting saja. Nama file yang diberikan adalah solveme.cpp dengan Makefile yang akan menghasilkan file key.h. Dibutuhkan waktu beberapa detik untuk mengkompilasi file ini, karena compiler perlu menjalankan template untuk menghasilkan program statik (jadi kita tidak bisa menghack binary-nya). Untuk bisa memahami program ini, saya mengekspansi dulu makronya dengan g++ –E
g++ –E solveme.cpp > expanded.cpp
Di titik itu, kode masih cukup sulit dipahami, tapi ada bagian yang cukup penting, yang mengakses keynya cuma template “r” ini:
template <int a, int b>
struct r { 
  static const int rr = d<g<((a)<<2)>::r,g<((a)<<2)+1>::r,g<((a)<<2)+(1<<1)>::r,g<((a)<<2)+(1<<1)|1>::r,key<b>::r,key<b+1 +1 +1 +1>::r,key<b+1 +1 +1 +1 +1 +1 +1 +1>::r,key<b+1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1>::r>::r;
};
Template r ini cuma dipakai di satu tempat untuk mengurangi sebuah nilai di kiri:
template <int n>
struct gg {  static const int r = (m<((u<n,n|2>::r)),(((1<<(1<<3))|1)),(((1<<(1<<3))|1))>::r) - r<(n>>2),((n)&3)>::rr;
};
Kita sederhanakan sedikit dengan menghitung konstantanya
template <int n>
struct gg { 
  static const int r = (m<((u<n,n|2>::r)),257,257>::r) - r<(n>>2),((n)&3)>::rr;
};
Di titik ini, field r dihitung dengan mengurangi bagian sisi kiri dengan sisi kanan (di bagian program berikutnya bisa dipahami ini harus 0). Template sisi kiri ini konstan terhadap key, jadi bisa diprint satu per satu. Hasilnya adalah konstanta ini:
{15, 25, 172, 31, 100, 17, 225, 137, 162, 71, 187, 191, 11, 105, 176, 94};
Berbagai penyederhanaan lain dilakukan. Yang paling banyak berpengaruh adalah template n yang ternyata hanya menghitung a-b, a+b, dan operasi identitas, semuanya secara rekursif. Setelah ini ditulis ulang, semuanya jadi lebih jelas. Di akhir, saya mendapatkan persamaan linear dengan modulo seperti ini:
( (k_12*202 -   68*k_4  ) -    ( k_8*87 -  13*k_0)  ) mod 257 = 15
( (k_13*202 -   68*k_5  ) -    ( k_9*87 -  13*k_1)  ) mod 257 = 25
( (k_14*202 -   68*k_6  ) -    ( k_10*87 -  13*k_2)  ) mod 257 = 172
( (k_15*202 -   68*k_7  ) -    ( k_11*87 -  13*k_3)  ) mod 257 = 31
( (k_12*122 -   244*k_4  ) -    ( k_8*71 -  29*k_0)  ) mod 257 = 100
( (k_13*122 -   244*k_5  ) -    ( k_9*71 -  29*k_1)  ) mod 257 = 17
( (k_14*122 -   244*k_6  ) -    ( k_10*71 -  29*k_2)  ) mod 257 = 225
( (k_15*122 -   244*k_7  ) -    ( k_11*71 -  29*k_3)  ) mod 257 = 137
( (k_12*42 -   228*k_4  ) -    ( k_8*247 -  173*k_0)  ) mod 257 = 162
( (k_13*42 -   228*k_5  ) -    ( k_9*247 -  173*k_1)  ) mod 257 = 71
( (k_14*42 -   228*k_6  ) -    ( k_10*247 -  173*k_2)  ) mod 257 = 187
( (k_15*42 -   228*k_7  ) -    ( k_11*247 -  173*k_3)  ) mod 257 = 191
( (k_12*90 -   148*k_4  ) -    ( k_8*39 -  125*k_0)  ) mod 257 = 11
( (k_13*90 -   148*k_5  ) -    ( k_9*39 -  125*k_1)  ) mod 257 = 105
( (k_14*90 -   148*k_6  ) -    ( k_10*39 -  125*k_2)  ) mod 257 = 176
( (k_15*90 -   148*k_7  ) -    ( k_11*39 -  125*k_3)  ) mod 257 = 94
Sampai di titik ini saya sempat bingung bagaimana menyelesaikannya, dicoba memakai wolfram online, inputnya terlalu panjang. Ternyata ini bisa diselesaikan dengan Mathematica, dan untungnya ada versi gratisnya untuk Raspberry Pi. Dengan  menggunakan commandline wolfram, persamaan tersebut bisa diubah menjadi:
LinearSolve[{{13, 0, 0, 0, -68, 0, 0, 0, -87, 0, 0, 0, 202, 0, 0, 0},
{0, 13, 0, 0, 0, -68, 0, 0, 0, -87, 0, 0, 0, 202, 0, 0},
{0, 0, 13, 0, 0, 0, -68, 0, 0, 0, -87, 0, 0, 0, 202, 0},
{0, 0, 0, 13, 0, 0, 0, -68, 0, 0, 0, -87, 0, 0, 0, 202},
{29, 0, 0, 0, -244, 0, 0, 0, -71, 0, 0, 0, 122, 0, 0, 0},
{0, 29, 0, 0, 0, -244, 0, 0, 0, -71, 0, 0, 0, 122, 0, 0},
{0, 0, 29, 0, 0, 0, -244, 0, 0, 0, -71, 0, 0, 0, 122, 0},
{0, 0, 0, 29, 0, 0, 0, -244, 0, 0, 0, -71, 0, 0, 0, 122},
{173, 0, 0, 0, -228, 0, 0, 0, -247, 0, 0, 0, 42, 0, 0, 0},
{0, 173, 0, 0, 0, -228, 0, 0, 0, -247, 0, 0, 0, 42, 0, 0},
{0, 0, 173, 0, 0, 0, -228, 0, 0, 0, -247, 0, 0, 0, 42, 0},
{0, 0, 0, 173, 0, 0, 0, -228, 0, 0, 0, -247, 0, 0, 0, 42},
{125, 0, 0, 0, -148, 0, 0, 0, -39, 0, 0, 0, 90, 0, 0, 0},
{0, 125, 0, 0, 0, -148, 0, 0, 0, -39, 0, 0, 0, 90, 0, 0},
{0, 0, 125, 0, 0, 0, -148, 0, 0, 0, -39, 0, 0, 0, 90, 0},
{0, 0, 0, 125, 0, 0, 0, -148, 0, 0, 0, -39, 0, 0, 0, 90}
}, {15, 25, 172, 31, 100, 17, 225, 137, 162, 71, 187, 191, 11, 105, 176, 94}
, Modulus -> 257
]
Hasilnya:
{67, 43, 43, 95, 148, 209, 143, 156, 162, 149, 136, 150, 95, 67, 45, 45}
Kita coba jadikan string di python:
>>> s = [67, 43, 43, 95, 148, 209, 143, 156, 162, 149, 136, 150, 95, 67, 45, 45]
>>> [chr(x) for x in s]
['C', '+', '+', '_', '\x94', '\xd1', '\x8f', '\x9c', '\xa2', '\x95', '\x88', '\x96', '_', 'C', '-', '-']
Hasilnya depannya dan belakangnya benar, tapi tengahnya salah. Karena operasi mod 257, sebagian solusinya perlu kita kurangkan dari 257.
>>> [chr(257-x) for x in s]
['\xbe', '\xd6', '\xd6', '\xa2', 'm', '0', 'r', 'e', '_', 'l', 'y', 'k', '\xa2', '\xbe', '\xd4', '\xd4']
Ok sekarang tengahnya benar, kita bisa gabungkan:
>>> [chr(257-x) if x>95 else chr(x) for x in s]
['C', '+', '+', '_', 'm', '0', 'r', 'e', '_', 'l', 'y', 'k', '_', 'C', '-', '-']
Atau dalam bentuk string:
>>> "".join([chr(257-x) if x>95 else chr(x) for x in s])
'C++_m0re_lyk_C—'
Itulah flagnya

PlaidCTF2014 Write Up: doge_stege (Forensics100)

Ini adalah soal kategori forensik tapi sebenarnya bisa juga kategori steganography.

Berikut adalah deskripsi soalnya:

You were startled to learn the The Plague has been behind many of the most popular internet memes. We believe he hides information in these funny pictures with steganography in order to broadcast his messages through time without detection. Find the hidden message, stop the signal.
Hidden message adalah sebuah gambar berikut.


Kita diminta untuk mencari flag yang tersembunyi dari gambar.

Soal ini sangat mudah diselesaikan dengan stegsolve, dengan beberapa kali klik sudah terlihat pada random color map terlihat teks tersembunyi.



Karena warnanya diacak, kita bisa melakukan beberapa kali klik untuk melihat beberapa variasi warna sampai terlihat teks tersembunyi, pctf{keep_doge_alive_2014} yang menjadi flag.



PlaidCTF2014 Write Up: MtPox (Web150)

Ini adalah soal kategori web application tapi juga menggunakan teknik kriptanalisis.

Berikut adalah deskripsi soalnya:

The Plague has traveled back in time to create a cryptocurrency before Satoshi does in an attempt to quickly gain the resources required for his empire. As you step out of your time machine, you learn his exchange has stopped trades, due to some sort of bug. However, if you could break into the database and show a different story of where the coins went, we might be able to stop The Plague.

Oke skenarionya sepertinya disetting supaya mirip dengan bitcoin dan MtGox. Berikut adalah tampilan web aplikasi PlaidCoin exchange.



Setelah melihat-lihat dan mencoba web tersebut vulnerable terhadap Local File Inclusion pada file "index.php". Kita bisa melihat source code admin.php dengan membuka url "index.php?page=admin.php". Berikut adalah source code dari admin.php.

Perhatikan bahwa ketika belum terotentikasi kita diberi 2 cookie: 

  • auth berisi serialize(false), yaitu "b:0;"
  • hsh berisi sha256(SECRET + ";0:b")
Apa itu SECRET ? Kita tidak tahu isi dari SECRET, tapi kita diberitahu bahwa SECRET adalah 8 byte dan sangat kecil peluang untuk dibrute force.

Perhatikan lagi source code di atas baris 5-8, kita ingin membuat $auth bernilai true sekaligus ingin agar cookie hsh yang kita kirimkan diterima sebagai cookie yang valid.

Oke kalau begitu kita harus mengirim cookie auth yang bila di-unserialize menjadi true dan sekaligus hasil dari sha256(SECRET + strrev(cookie auth)) sama dengan cookie hsh yang kita kirimkan. 

Kita hanya punya hsh berisi sha256(SECRET + ";0:b"), bagaimana caranya kita bisa membuat sha256(SECRET + ";1:b") padahal kita tidak tahu SECRET ?

Jawabannya adalah dengan hash length extension attack, silakan baca di ilmuhacking tentang lebih detil bagaimana attack ini bekerja.

Jadi kondisinya sekarang:

  • Dari hsh cookie diketahui bahwa sha256(SECRET + ";0:b") = ef16c2bffbcf0b75672...
  • Jika kita ingin unserialize(str) bernilai true, maka str tersebut harus diawali dengan "b:1;" (sisanya boleh berisi string apapun, tidak akan mengubah hasil unserializenya).
Dengan hash extension attack, bila diketahui sha256(SECRET + ";0:b") = X, kita bisa mencari sha256(SECRET + ";0:b" + padding + append) tanpa mengetahui SECRET.

Perhatikan bahwa kita bisa menyelesaikan soal ini bila append berisi ";1:b" :
  1. cookie auth yang kita kirim adalah append + pending + "b:0;" (dibalik karena ada strrev di baris ke-7) yaitu cookie auth = "b:1;" + append + "b:0;"
  2. unserialize("b:1;"+padding+"b:0;") adalah true karena diawali dengan "b:1;"
  3. sha256(SECRET + strrev(cookie auth)) adalah sha256(SECRET+";0:b"+padding+";1:b")
Tugas kita sekarang tinggal mencari tahu berapa nilai sha256(SECRET+";0:b"+padding+";1:b") tanpa tahu SECRET.

Dengan tools hash_extender kita bisa dengan mudah mendapatkan nilai sha256(SECRET+";0:b"+padding+";1:b") tanpa perlu tahu SECRET. Output dari tools tersebut tinggal kita balik kemudian diubah menjadi bentul url encoded sehingga hasilnya menjadi 2 cookie berikut:

Dengan dua cookie tersebut kita akan menembus otentikasi dan masuk ke halaman admin.


Selanjutnya dari source code jelas terlihat ada SQL injection pada parameter query. Selanjutnya saya serahkan saja pada sqlmap untuk melakukan eksploitasi sql injection.



PlaidCTF2014 Write Up: Twenty (Crypto20)

Ini adalah soal kategori kriptografi, seperti biasa kita diberi ciphertext dan diminta untuk men-dekrip menjadi plaintext.

Berikut adalah deskripsi soalnya:

It's so far in the past, computers haven't even been imagined, let alone used. But somehow The Plague has already been here, building an evil army of hackers. Can you find his secret message?

Ciphertext yang diberikan adalah:

fvoxoxfvwdepagxmwxfpukleofxhwevefuygzepfvexwfvufgeyfryedojhwffoyhxcwgmlxeylawfxfurwfvoxecfezfvwbecpfpeejuygoyfefvwxfpwwfxojumwuxfuffvwawuxflecaazubwjwoyfvwyepfvwuxfhwfjlopwckaohvfjlzopwoaahevupgwpfvuywjoywjdwyfufjupouvbuaajwuaoupkecygjwoyfvwuxxdofvyeacmwbvuzoyhlecpwzcbroyhdofvfvwgcgwdveheffvwrwlxfelecpxuzwuygfvexwfvufbuyfgempoyhxcofxbplfelecpcybawxujfexwffawgoxkcfwxfvechvflecgfubrawfvoxdofvuaoffawjepwfubfmcffvwyuhuoyzcghwkubrwpxogeyfryediubroxvwgufwupwswplfojwofvoyrezaorxuyhmcfxvofjuyfvwlpwubepkepufoeyuygojukwpxeyozobufoeyezzpwwgejzepuaaleczoaagebrwfxaorwfvufxubeybwkfzepwohyfeluaadvoawaudlwpxjcggldufwpuygfpexxfuaaecfezmcxoywxxoxiuoazepjwuyglecpwxcoyhjwbosoaalwnvomoffvoxoyfvwbecpfpeejheeygeofogupwlecbeyhpufcaufoeyxfvwzauhoxxoybwywdbplkfejohvfvuswyxumubrgeepxocxweagbplkfe

Kalau dilihat sepintas ada banyak pola dan perulangan huruf, ini adalah ciri dari substitution atau shift cipher.

Saya menggunakan tools substitution cipher solver yang menggunakan algoritma hill climbing untuk mencari kunci. Hill climbing adalah salah satu algoritma optimasi dan dekripsi adalah salah satu problem optimasi. Skor optimasi diukur berdasarkan kemiripan hasil dekripnya dengan teks bahasa inggris yang dilihat dari statistik 4-gram. Bila hasil dekripsinya banyak mengandung 4 huruf (THEY, HAVE,TION dsb) yang banyak ditemukan dalam bahasa inggris, maka skornya akan tinggi, semakin tinggi skor berarti semakin mendekati ke kunci yang benar. Bila skor masih rendah, kita akan mencari kunci lain dengan mengubah sedikit kunci sebelumnya agar skornya semakin tinggi.

Dalam waktu singkat hill climbing berhasil menemukan kunci substitusinya.