Apa itu buffer overflow?
Buffer overflow terjadi ketika sebuah program atau proses mencoba menulis data melebihi kapasitas blok memori tetap, atau buffer, yang telah dialokasikan untuk menyimpan data tersebut. Buffer punya batasan jumlah data yang bisa ditampung; kalau ada data berlebih, maka data itu bisa menimpa nilai data lain di alamat memori yang berdekatan dengan buffer tujuan. Overflow seperti ini sebenarnya bisa dicegah kalau program punya pengecekan batas (bounds checking) yang cukup untuk menandai atau membuang data berlebih saat dikirim ke buffer.
Apa itu serangan buffer overflow dan bagaimana cara kerjanya?
Memanfaatkan buffer overflow memungkinkan penyerang untuk mengendalikan atau merusak proses, atau memodifikasi variabel internalnya. Buffer overflow selalu masuk peringkat atas dalam daftar Common Weakness Enumeration (CWE) dan SANS Top 25 Most Dangerous Software Errors. Buffer overflow klasik diklasifikasikan sebagai CWE-120 dalam kamus kelemahan CWE. Walaupun sudah cukup dipahami, masalah ini masih sering terjadi bahkan di software dari vendor besar maupun kecil.
Buffer overflow bisa terjadi secara tidak sengaja atau disengaja oleh aktor jahat. Seorang penyerang bisa mengirim input yang sudah dirancang khusus — disebut sebagai arbitrary code — ke sebuah program. Program tersebut akan mencoba menyimpan input ke buffer yang kapasitasnya tidak cukup. Kalau data kelebihan itu ditulis ke memori yang berdekatan, maka data yang sudah ada sebelumnya akan tertimpa.
Data asli di dalam buffer biasanya mencakup return pointer dari fungsi yang sedang dieksekusi — yaitu alamat tujuan eksekusi berikutnya. Tapi penyerang bisa mengubah nilainya agar mengarah ke alamat yang mereka tentukan sendiri. Biasanya, alamat itu diarahkan ke lokasi di mana payload eksploit berada. Perubahan ini akan mengubah jalur eksekusi program dan mengalihkan kontrol ke kode berbahaya milik penyerang.
Misalnya, sebuah program sedang menunggu input nama dari pengguna. Alih-alih mengetik nama, hacker memasukkan perintah eksekusi yang panjangnya melebihi ukuran stack. Biasanya perintahnya pendek saja. Di lingkungan Linux misalnya, perintahnya bisa berupa EXEC(“sh”), yang menyuruh sistem untuk membuka jendela command prompt, alias root shell dalam istilah Linux.
Tapi, menuliskan perintah eksekusi ke buffer bukan berarti langsung bisa dijalankan. Penyerang juga harus menentukan alamat kembali (return address) yang mengarah ke perintah berbahaya tersebut. Program bisa mengalami crash sebagian karena terjadi stack overflow. Program lalu mencoba pulih dengan mengakses alamat kembali tadi, tapi karena sudah diubah oleh penyerang, alamat tersebut malah mengarah ke perintah yang sudah disisipkan. Artinya, penyerang harus tahu alamat pasti tempat perintah berbahaya disimpan.
Untuk menghindari kebutuhan akan alamat yang tepat, biasanya perintah berbahaya dipadati dengan NOP — atau no operation — yaitu instruksi komputer yang tidak melakukan apa-apa. Padding seperti ini ditempatkan di kedua sisi perintah untuk memperbesar kemungkinan alamat yang ditebak masuk ke dalam area tersebut. Kalau alamat yang ditentukan penyerang jatuh di mana saja dalam padding tersebut, maka perintah berbahaya akan tetap dijalankan.
Bahasa pemrograman seperti C dan C++ tidak punya mekanisme perlindungan untuk mencegah akses atau penimpaan data di bagian memori mana pun. Karena itu, mereka rentan terhadap serangan buffer overflow. Pelaku bisa langsung memanipulasi memori dengan kode pemrograman biasa.
Bahasa pemrograman modern seperti C#, Java, dan Perl mengurangi kemungkinan bug seperti ini muncul, karena mereka punya pengamanan terhadap kesalahan penanganan memori. Tapi tetap saja, buffer overflow bisa terjadi di lingkungan pemrograman mana pun yang memungkinkan manipulasi memori secara langsung — entah karena kelemahan di compiler, runtime library, atau fitur bahasa itu sendiri.
Jenis-jenis serangan buffer overflow
Teknik untuk mengeksploitasi kelemahan buffer overflow bisa bervariasi tergantung sistem operasi (OS) dan bahasa pemrograman yang digunakan. Tapi tujuan akhirnya selalu sama, yaitu memanipulasi memori komputer untuk mengubah atau mengendalikan jalannya program.
Buffer overflow dikategorikan berdasarkan lokasi buffer dalam memori proses. Mayoritas terdiri dari stack-based overflow atau overflow yang berbasis heap. Keduanya berada di memori utama (RAM) perangkat.
Beberapa jenis serangan buffer overflow antara lain:
Stack-based buffer overflow atau stack buffer overrun attack
Stack menyimpan data dengan struktur LIFO (last-in, first-out). Stack adalah area memori kontinu yang digunakan untuk menyimpan data terkait pemanggilan fungsi, termasuk parameter fungsi, variabel lokal fungsi, dan informasi manajemen seperti frame pointer dan instruction pointer. Biasanya stack kosong sampai program butuh input dari pengguna, seperti username atau password. Saat itu, program akan menulis alamat kembali ke stack, lalu input dari pengguna ditaruh di atasnya. Ketika stack diproses, input dari pengguna akan dikirim ke alamat kembali yang ditentukan program.
Namun, stack punya ukuran yang terbatas. Programmer yang membuat kode harus menentukan berapa banyak ruang yang disediakan untuk stack. Kalau input dari pengguna lebih panjang daripada ruang yang tersedia dan program tidak memverifikasi apakah input akan muat, maka terjadilah overflow.
Serangan overflow integer
Sebagian besar bahasa pemrograman mendefinisikan ukuran maksimum untuk tipe data integer. Ketika ukuran ini terlampaui, hasilnya bisa menyebabkan error, atau mungkin mengembalikan hasil yang tidak benar dalam batasan panjang integer. Serangan overflow integer dapat terjadi ketika integer digunakan dalam operasi aritmatika dan hasil dari perhitungan tersebut melebihi ukuran maksimum integer yang ada. Misalnya, 8 bit memori diperlukan untuk menyimpan angka 192. Jika proses menambahkan 64 ke angka ini, hasilnya 256 tidak bisa masuk ke dalam memori yang dialokasikan, karena membutuhkan 9 bit.
Serangan format string
Penyerang mengubah alur aplikasi dengan menyalahgunakan fungsi format string, seperti printf dan sprintf, untuk mengakses dan memanipulasi ruang memori lainnya.
Serangan overflow Unicode
Serangan ini mengeksploitasi kebutuhan memori yang lebih besar untuk menyimpan string dalam format Unicode dibandingkan dengan karakter dalam format American Standard Code for Information Interchange (ASCII). Serangan ini bisa digunakan pada program yang mengharapkan semua input berupa karakter ASCII.
Bagaimana cara mencegah serangan buffer overflow?
Ada beberapa cara untuk mencegah serangan buffer overflow, di antaranya adalah sebagai berikut:
- Gunakan proteksi runtime OS. Kebanyakan sistem operasi menggunakan proteksi runtime, seperti berikut, untuk mempersulit keberhasilan serangan buffer overflow:
- Address Space Layout Randomization (ASLR) secara acak menyusun posisi ruang alamat untuk area data penting dalam proses, termasuk basis file eksekusi dan posisi stack, heap, serta pustaka. Pendekatan ini membuat penyerang kesulitan untuk secara andal melompat ke fungsi tertentu di dalam memori.
- Data Execution Prevention menandai area memori sebagai eksekusi atau non-eksekusi. Ini mencegah penyerang menjalankan instruksi yang ditulis ke area data melalui buffer overflow.
- Structured Exception Handling Overwrite Protection dirancang untuk memblokir serangan yang menggunakan teknik penggantian Structured Exception Handler, yang melibatkan penggunaan stack-based buffer overflow.
- Jaga perangkat tetap terpatch. Vendor mengeluarkan patch perangkat lunak dan pembaruan untuk memperbaiki kerentanannya yang ditemukan. Namun, masih ada periode risiko antara penemuan kerentanannya dan patch yang dikeluarkan dan diterapkan.
- Ikuti prinsip privilege minimum (POLP). Pengguna dan aplikasi sebaiknya hanya diberikan izin yang diperlukan untuk melakukan pekerjaan atau tugas yang ditugaskan. Dengan mengikuti pendekatan POLP, potensi terjadinya serangan buffer overflow bisa diminimalkan. Pada contoh serangan stack overflow di atas, jendela command prompt yang dibuka berjalan dengan hak istimewa yang sama dengan aplikasi yang telah dikompromikan; semakin sedikit hak istimewa yang dimilikinya, semakin sedikit juga hak yang bisa digunakan penyerang. Sebisa mungkin, berikan hak istimewa sementara untuk pengguna dan aplikasi, lalu cabut setelah tugas selesai.
- Gunakan bahasa pemrograman yang aman memori. Alasan paling umum mengapa serangan buffer overflow berhasil adalah karena aplikasi gagal mengelola alokasi memori dan memverifikasi input dari klien atau proses lain. Aplikasi yang dikembangkan dalam C atau C++ sebaiknya menghindari fungsi pustaka standar yang tidak memeriksa batasan, seperti gets, scanf, dan strcpy. Sebaliknya, gunakan pustaka atau kelas yang dirancang untuk melakukan operasi string dan memori secara aman. Lebih baik lagi, gunakan bahasa pemrograman yang mengurangi kemungkinan terjadinya buffer overflow, seperti Java, Python, atau C#.
- Verifikasi data. Aplikasi web dan mobile yang dikembangkan in-house sebaiknya selalu memverifikasi input pengguna dan data dari sumber yang tidak dipercaya untuk memastikan bahwa data tersebut sesuai dengan batasan yang diharapkan dan untuk mencegah nilai input yang terlalu panjang. Setiap kebijakan keamanan aplikasi harus mengharuskan pengujian kerentanannya terhadap potensi serangan validasi input dan buffer overflow sebelum aplikasi diterapkan.