Buffer Overflow merupakan
salah satu penyebab yang paling banyak menimbulkan masalah pada keamanan
komputer baik yang bersifat lokal maupun jaringan. Menurut laporan CERT/CC,
buffer overflow merupkan penyebab dari
50% semua bug keamanan yang dilaporkan dan dijadikan advisori oleh CERT/CC.
Riset yang dilakukan oleh Crispin Cowan dan teamnya menganggap buffer overflow
sebagai Vulnerbility of Decade.
Dengan
tersedianya program-program eksploitasi di Internet, baik dalam bentuk source
code maupun binary seringkali menjadi menjadi alat untuk membuka celah keamanan
sehingga membuat orang tertarik untuk melakukan eksperimen yang sporadis tanpa
memperdulikan akibat yang dapat ditimbulkannya. Banyak orang yang mengklaim dirinya
sebagai seorang cracker jika sudah berhasil mengekspoitasi komputer orang lain,
tanpa memahami cara kerja eksploitasi tersebut sesungguhnya.
Terlepas dari
masalah tersebut diatas, tulisan ini akan membahas apakah sebenarnya buffer
overflow itu dan bagaimana mekanisme untuk mengeksploitasinya/penyerangan
menggunakan buffer over flow, cara memperbaiki sistem yang ter-buffer
overflow dan bagaimana cara menghindari/mencegah agar sistem kita aman dari
serangan melalui eksploitasi buffer overflow. Untuk memahami hal
tersebut diatas diperlukan juga pemahaman mengenai cara kerja sistem prosesor
di level bawah, seperti pemrograman asembly, manajemen memori text, stack, data
dan sebagainya. Pada tulisan ini sebagai referensi dipilih prosesor Intel x86
yang mengorganisasi sistem memori menggunakan 4 byte dan menggunakan Linux
sebagai sistem operasinya.
Manajemen Memori pada Proses
Sebuah proses dipandang dari sudut manajemen memori dibedakan menjadi tiga bagian:
1. Text; memuat instruksi kode program.
Bagian ini biasanya hanya bisa dibaca, dan setiap usaha untuk menuliskan data
ke bagian ini akan menyebabkan kesalahan segmentation
violation.
2. Data; memuat data, baik yang
telah diinisialisasi maupun yang belum. Selain dapat dibaca, biasanya bagian
ini juga memperbolehkan suatu proses untuk melakukan penulisan padanya.
3. Stack; yang dapat dialokasikan
secara dinamis, biasanya dimanfaatkan untuk menyimpan variabel lokal maupun
untuk melewatkan parameter fungsi.
Pengaksesan data
ke bagian ini menggunakan metoda yang disebut LIFO (Last In First Out).
Jenis data yang harus juga diketahui adalah buffer yang pada bahasa C
diimplementasikan sebagai array. Array dapat dibedakan ke dalam dua jenis
berdasarkan metoda pengalokasiannya, yaitu array statis dan array dinamis.
Array statis dialokasikan di bagian data saat program dimuatkan ke memori,
sedangkan array dinamis dialokasikan di dalam stack saat run time.
Stack
Stack dapat
dibayangkan sebagai sebuah blok dari memori yang dapat memuat data secara dinamis.Beberapa hal yang harus diketahui pada prosesor Intel sehubungan
dengan stack adalah :
1. Penggunaan metoda Big Edian
dalam mengorganisasikan sistem memori. Di sini MSB (Most Significant Bit)
terletak pada alamat memori yang lebih kecil dibandingkan LSB (Least Significant
Bit).
2. Penambahan besar stack
dilakukan kearah alamat memori yang lebih kecil. Di sini posisi bawah dari
stack mempunyai alamat yang tetap. Posisi atas stack yang alamat memorinya
lebih kecil dari posisi bawah selalu berubah.
3. Register stack pointer (SP)
selalu menunjuk ke posisi atas dari stack.
4. Untuk memindahkan data ke stack
digunakan instruksi PUSH yang secara otomatis akan menurunkan nilai SP sebesar
4 byte. Sedangkan untuk mengambil data dari stack digunakan instruksi POP yang
secara otomatis juga akan menaikkan nilai SP sebesar 4 byte.
Blok memori dari
stack ini biasanya di bagi lagi menjadi apa yang disebut dengan stack frame.
Setiap stack frame berisi data yang berhubungan dengan pemanggilan
sebuah fungsi. Biasanya posisi awal dari frame ini akan ditunjuk oleh frame
pointer (FP). Dengan bantuan FP ini, maka pengaksesan ke variabel lokal maupun
parameter fungsi dapat dilakukan menggunakan sistem pengalamatan relatif. Pada
prosesor Intel, register EBP berfungsi sebagai frame pointer.
Sebelum
memanggil fungsi fungsi() dua buah argumen sebagai parameter ditambahkan ke
stack. Instruksi call di atas secara implisit juga akan menyimpan instruction
pointer (IP) ke stack. IP ini selanjutnya akan di sebut dengan return address
(RET).
Kode assembly
yang di bangkitkan pada fungsi fungsi();
1.
pushl %ebp
2.
movl %esp, %ebp
3.
subl $24,%esp
Baris pertama
akan menyimpan frame pointer EBP ke stack untuk kemudian di baris kedua
menggunakan SP sebagai frame pointer yang aktual. Pada baris ketiga 24 byte
memori untuk variabel lokal buffer[ ] dialokasikan di stack, walau hanya 8 byte
yang digunakan.
1.
movl 8(%ebp), %eax
2.
movl %eax, -8(%ebp)
3.
movl 12(%ebp), %eax
4.
movl %eax, -4(%ebp)
Baris diatas
adalah pengesetan variabel buffer [ ] dengan parameter dari fungsi. Seperti
telihat pada baris pertama, pengaksesan ke parameter satu di stack dilakukan
dengan pengalamatan relatif terhadap frame pointer EBP. Setelah nilai ini
disimpan di register EAX maka dengan cara yang sama nilai ini di simpan ke
stack yang jaraknya –8 byte terhadap frame pointer EBP (baris kedua). Stack ini
identik dengan variable buffer[0]. Baris ketiga dan keempat melakukan hal yang
sama untuk parameter dua. Dari uraian diatas, maka blok memori stack dapat
dilukiskan seperti gambar 2.
Seperti yang
sudah dikemukakan diatas, prosesor yang digunakan adalah Intel x86 yang
menggunakan bus 32-bit atau 4 byte dalam pengaksesan ke memori, sehingga
pengalamatan ke memori selalu merupakan kelipatan 4 byte, sedangkan besar dari
tipe data int dan pointer adalah 4 byte juga.
Jika variable lokal merupakan sebuah array, maka array dengan indeks terkecil mempunyai alamat memori yang paling kecil juga. Dengan kata lain elemen variable array di simpan dengan indeks membesar ke arah alamat memori yang juga membesar. Pada stack, ini adalah arah ke posisi bawah dari blok memori stack. Sifat stack inilah yang memungkinkan terjadinya buffer overflow.
Buffer overflow memiliki arti suatu keadaan di mana data yang diisikan ke suatu
buffer mempunyai ukuran yang lebih besar dibandingkan ukuran buffer itu
sendiri. Untuk lebih memahami buffer overflow, dapat dianalogikan dengan
kehidupan sehari-hari, yaitu saat kita mengisi bak mandi melebihi daya
tampungnya, maka air yang kita isikan akan meluap (overflow).
Berikut ini
contoh sebuah program dalam bahasa C yang mengandung buffer overflow.
# Coba2.c
#include<stdio.h>
void fungsi(char* txt)
{
char
buffer[4];
strcpy(buffer,
txt);
}
int main()
{
char
buffer[17];
int i;
for (I=0;
i<16;I++)
buffer[i]=0x19;
fungsi(buffer);
return 0;
}
Setelah
sukses dikompilasi maka ketika program diatas dieksekusi akan ada pesan segmentation
violation. Mengapa demikian ? Karena di fungsi fungsi(), variable array
buffer didefinisikan hanya berukuran 4 byte, sedangkan data yang disalinkan
kepadanya berukuran sebesar 17 byte. Sebagai catatan, fungsi stcpy() akan
menyalinkan data yang direferensi oleh pointer txt ke buffer sampai karakter
null ditemukan di txt.
Berikut ini
adalah sintaks fungsi strcpy :
Char *strcpy(char *dest, const
char *src);
Code Pointer Integrity Checking
Tujuan dari metode ini agak berbeda
dengan bounds checking. Alih-alih berusaha mencegah korupsi code pointer, ia
berusaha mendeteksi bahwa sebuah code pointer telah tekorupsi sebelum ia
di-deferensikan. Jadi meskipun penyerang sukses dalam mengkorupsi code pointer,
code pointer yang terkorupsi tidak akan digunakan karena korupsi terdeteksi
setiap saat sebelum digunakan.
Sumber: dari berbagai sumber
No comments:
Post a Comment