Tuesday, May 21, 2013

Buffer Overflow


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.


Pengertian Dasar
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