Buffer Overflow merupakan teknik exploitasi yang sudah dikenal sejak dulu, namun saya baru mempelajarinya hari ini, sedikit ketinggalan memang, namun tidak ada kata terlambat untuk pengetahuan. Buffer adalah suatu area didalam memori yang telah dialokasikan sebelumnya untuk menyimpan value yang akan digunakan nantinya. Sementara itu, buffer overflow terjadi karena data yang dimasukkan kedalam buffer melebihi besarnya buffer yang duah dialokasikan pada saat deklarasi variabel, sehingga dapat memanipulasi return address yang dapat mengubah alur program yang telah dibuat.
Stack.
Bicara soal buffer, dan apapun yang berhubungan dengan memori, maka pembahasan tidak jauh dari stack, sebuah tumpukan didalam memori yang bekerja dengan mekanisme Last In First Out (LIFO), yang artinya object yang terakhir masuk kedalam stack, yang pertama dikeluarkan dari stack.
Gambar 1: ilustrasi stack (sumber) |
Perlu diingat, stack tumbuh dari higher address menuju ke lower address. Puncak stack itu sendiri dapat ditemui pada register ESP pada CPU.
Buffer dalam Stack.
Dari beberapa sumber yang saya temukan, seperti artikel yang membahas buffer overflow, maupun video dari securitytube. Buffer overflow ini terjadi karena besar alokasi memori untuk buffer tidak cukup / lebih kecil dari data yang akan dimasukkan kedalam buffer, sehingga mampu memanipulasi return address yang ada pada stack.
Gambar 2: Stack Frame |
Perhatikan gambar 2, gambar ini merupakan gambar stack yang berisi argument, return address, old EBP, dan buffer. Seperti yang sudah disebut sejak awal, buffer overflow terjadi karena nilai buffer yang melebihi size buffer itu sendiri, sehingga kelebihanya dapat memanipulasi nilai return address yang ada pada stack. Jika EIP menuju ke return address yang sudah dimanipulasi, maka program bisa dialikan untuk keperluan yang tidak semestinya.
Gambar 3: Kondisi Buffer Overflow |
Gambar ini menunjukkan stack yang sudah diisi oleh \xaa, anggap saja besar buffer berisi 4 byte / char (buffer[4]), sementara input yang dimasukkan sebanyak 12 byte, sehingga berhasil mencapai ke return address, kondisi ini akan menyebabkan segmentation fault karena alamat \xaa\xaa\xaa\xaa tidak ditemukan dalam memori. Namun lain halnya jika \xaa\xaa\xaa\xaa yang ada pada return address diganti dengan suatu alamat didalam memori yang kemungkinan berupa shellcode?, maka program tersebuat akan mengeksekusi shellcode yang sudah dibuat, tentu saja ini merupakan masalah yang sangat serius jika terjadi.
Mengenal Lebih Dekat Buffer Overflow.
Untuk mengetahui apa yang sebenarnya terjadi pada memori, disini saya membuat contoh sederhana buffer overflow.
#include <stdio .h> SayHello(){ printf("Hello..\n"); } Input(){ char buffer[10]; gets(buffer); puts(buffer); } main(){ Input(); return 0; }
Fungsi gets() pada bahasa C memiliki kelemahan buffer overflow, karena fungsi ini tidak melakukan checking terhadap besar input yang dimasukkan. Untuk mempermudah analisis, saya meng-compile kode tersebut ke dalam format 32bit.
gcc -m32 -ggdb -mpreferred-stack-boundary=2 -o demo32 demo.c
Jalankan demo32 menggunakan gdb.
Untuk keperluan debugging, beberapa breakpoint saya pasang sebelum dan sesudah proses input. Berikut kondisi register dan stack pada saat breakpoint 1.
Pada breakpoint 1 perhatikan nilai ebp, sementara di stack belum terlihat informasi yang dicari.
Di breakpoint 2, terdapat informasi menarik didalam stack, perhatikan tanda warna merah, old-EBP (0xffffd468) sudah muncul sesuai dengan gambar stack diawal, yang berarti sesudahnya merupakan return address (0x08048465).
Pada breakpoint 3, input yang saya masukkan 'aaaaaaaaaa' jika diperhatikan, stack berisi nilai 0x61 ('a') sebanyak 10 kali, meskipun besar buffer 10 byte, memori yang dipakai sebesar 12 byte, jika besar buffer 5 byte, maka memori yang dialokasikan 8 byte untuk buffer. Selanjutnya jika 0x08048465 dibedah, maka akan merujuk ke step berikutnya.
Return Address.
Setelah sebelumnya mengenal stack dan proses input kedalam stack, bagian yang menarik adalah proses manipulasi return address. Untuk mencapai return address, perlu dilakukan perhitungan, jika pada masalah ini, buffer sebesar 10 byte, ditambah old-EBP sebesar 4 byte, maka 4 byte selanjutnya adalah return address. Dalam kasus ini, saya menyiapkan fungsi yang tidak dipanggil di main program, SayHello().
1 #include <stdio .h> 2 3 SayHello(){ 4 printf("Hello..\n"); 5 } 6 7 Input(){ 8 char buffer[10]; 9 gets(buffer); 10 puts(buffer); 11 } 12 13 main(){ 14 Input(); 15 return 0; 16 }
Untuk mengalihkan return address ke fungsi SayHello(), maka perlu diketahui alamat dari fungsi SayHello() didalam memori.
Diketahui fungsi SayHello() berada di alamat 0x0804842b. Sesuai perhitungan, input yang dimasukkan berjumlah: 10 byte buffer + 4 byte old-EBP + 4 byte RET address yang dituju.
aaaaaaaaaa(10)+bbbb(4)+0x0804842b
Dibawah ini merupakan program ketika berjalan normal,
root@x250:~/labs# printf "aaaaaaaaaa" | ./demo32 aaaaaaaaaa
Sementara, dibawah ini ketika input melebihi buffer, program mengalami segmentation fault.
root@x250:~/labs# printf "aaaaaaaaaabbbb" | ./demo32 aaaaaaaaaabbbb Segmentation fault root@x250:~/labs#
Kemudian untuk mengalihkan return address, input seperti contoh. Address dari fungsi SayHello() perlu dibalik karena memori menyimpan data dalam bentuk little endian.
Terjadi segmentation fault, namun berhasil menemukan fungsi SayHello().
Sekian catatan pribadi saya mengenai buffer overflow, jika ada yang salah atau kurang silahkan disampaikan.
Referensi :
http://insecure.org/stf/smashstack.html
https://www.exploit-db.com/docs/28475.pdf
http://www.securitytube.net/groups?operation=view&groupId=4
Referensi :
http://insecure.org/stf/smashstack.html
https://www.exploit-db.com/docs/28475.pdf
http://www.securitytube.net/groups?operation=view&groupId=4
This comment has been removed by the author.
ReplyDelete