Saya sedang melakukan kursus di perguruan tinggi, di mana salah satu lab adalah untuk melakukan eksploitasi buffer overflow pada kode yang mereka berikan kepada kami. Ini berkisar dari eksploitasi sederhana seperti mengubah alamat kembali untuk fungsi pada tumpukan untuk kembali ke fungsi yang berbeda, sampai kode yang mengubah register program / keadaan memori tetapi kemudian kembali ke fungsi yang Anda panggil, yang berarti bahwa fungsi yang Anda panggil benar-benar lupa akan exploit.
Saya melakukan riset tentang ini, dan jenis eksploitasi ini digunakan cukup banyak di mana-mana bahkan sekarang, dalam hal-hal seperti menjalankan homebrew di Wii , dan jailbreak yang tidak ditambatkan untuk iOS 4.3.1
Pertanyaan saya adalah mengapa masalah ini begitu sulit untuk diperbaiki? Sudah jelas ini adalah salah satu exploit besar yang digunakan untuk meretas ratusan hal, tetapi sepertinya akan sangat mudah untuk memperbaikinya dengan hanya memotong input apa saja melewati panjang yang diizinkan, dan hanya membersihkan semua input yang Anda ambil.
EDIT: Perspektif lain yang saya ingin jawaban untuk dipertimbangkan - mengapa pencipta C tidak memperbaiki masalah ini dengan mengimplementasikan kembali perpustakaan?
sumber
Ini tidak benar-benar tidak akurat untuk mengatakan bahwa C sebenarnya "rawan kesalahan" oleh desain . Selain dari beberapa kesalahan yang menyedihkan seperti
gets
, bahasa C tidak bisa benar-benar dengan cara lain tanpa kehilangan fitur utama yang menarik orang ke C di tempat pertama.C dirancang sebagai bahasa sistem untuk bertindak sebagai semacam "rakitan portabel." Fitur utama dari bahasa C adalah bahwa tidak seperti bahasa tingkat yang lebih tinggi, kode C sering memetakan sangat dekat dengan kode mesin yang sebenarnya. Dengan kata lain,
++i
biasanya hanya sebuahinc
instruksi, dan Anda dapat sering mendapatkan ide umum tentang apa yang akan dilakukan prosesor pada saat run-time dengan melihat kode C.Tetapi menambahkan memeriksa batas implisit menambahkan banyak overhead tambahan - overhead yang programmer tidak meminta dan mungkin tidak mau. Overhead ini melampaui penyimpanan ekstra yang diperlukan untuk menyimpan panjang setiap array, atau instruksi tambahan untuk memeriksa batas array pada setiap akses array. Bagaimana dengan aritmatika pointer? Atau bagaimana jika Anda memiliki fungsi yang mengambil pointer? Lingkungan runtime tidak memiliki cara untuk mengetahui apakah pointer itu berada dalam batas blok memori yang dialokasikan secara sah. Untuk melacak hal ini, Anda memerlukan beberapa arsitektur runtime yang serius yang dapat memeriksa setiap penunjuk terhadap tabel blok memori yang saat ini dialokasikan, di mana saat ini kita sudah masuk ke wilayah runtime yang dikelola gaya Java / C # -style.
sumber
Saya pikir masalah sebenarnya bukan bahwa jenis bug ini sulit untuk diperbaiki, tetapi mereka sangat mudah untuk dibuat: Jika Anda menggunakan
strcpy
,sprintf
dan teman-teman dengan (tampaknya) cara paling sederhana yang dapat bekerja, maka Anda mungkin telah membuka pintu untuk buffer overflow. Dan tidak ada yang akan menyadarinya sampai seseorang mengeksploitasinya (kecuali jika Anda memiliki ulasan kode yang sangat baik). Sekarang tambahkan fakta bahwa ada banyak programmer biasa-biasa saja dan bahwa mereka hampir selalu berada di bawah tekanan waktu - dan Anda memiliki resep untuk kode yang begitu penuh dengan buffer overflow sehingga akan sulit untuk memperbaikinya semua hanya karena ada begitu banyak dari mereka dan mereka bersembunyi dengan sangat baik.sumber
sizeof(ptr)
umumnya 4 atau 8. Itu batasan C lain: tidak ada cara untuk menentukan panjang array, hanya dengan memberikan pointer padanya.#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]) / (sizeof(a) != sizeof(void *))
yang akan memicu pembagian waktu kompilasi-oleh-nol. Yang cerdas lain yang pertama kali saya lihat di Chromium adalah#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]) / !(sizeof(a) % sizeof((a)[0]))
yang memperdagangkan segelintir positif palsu untuk beberapa negatif palsu - sayangnya itu tidak berguna untuk char []. Anda dapat menggunakan berbagai ekstensi kompiler untuk membuatnya lebih andal, misalnya blogs.msdn.com/b/ce_base/archive/2007/05/08/… .Sulit untuk memperbaiki buffer overflow karena C tidak menyediakan alat yang berguna untuk mengatasi masalah. Ini adalah kesalahan bahasa mendasar yang tidak dilindungi oleh buffer asli dan secara virtual, jika tidak sepenuhnya, tidak mungkin untuk menggantinya dengan produk unggulan, seperti yang dilakukan C ++
std::vector
danstd::array
, dan sulit bahkan dalam mode debug untuk menemukan buffer overflows.sumber
std::vector
diimplementasikan secara efisien. Danvector::operator[]
membuat pilihan yang sama untuk kecepatan dibandingkan keselamatan. Keamanan dalamvector
berasal dari membuatnya lebih mudah untuk bergerak di sekitar ukuran, yang merupakan pendekatan yang sama dengan perpustakaan C modernrealloc
(C99 juga memungkinkan Anda untuk mengukur susunan array menggunakan ukuran runtime yang ditentukan tetapi konstan melalui variabel otomatis apa pun, hampir selalu lebih disukai daripadachar buf[1024]
). Kedua, masalahnya tidak ada hubungannya dengan memperluas buffer, itu berkaitan dengan apakah buffer membawa ukuran dengan mereka dan memeriksa ukuran itu ketika Anda mengaksesnya.vector::operator[]
apakah batas melakukan pengecekan dalam mode debug - sesuatu yang tidak bisa dilakukan oleh array asli - dan kedua, tidak ada cara di C untuk menukar tipe array asli dengan yang dapat melakukan pemeriksaan batas, karena tidak ada templat dan tidak ada operator kelebihan beban. Di C ++, jika Anda ingin pindah dariT[]
kestd::array
, Anda bisa langsung saja menukar typedef. Di C, tidak ada cara untuk mencapai itu, dan tidak ada cara untuk menulis kelas dengan fungsionalitas yang setara, apalagi antarmuka.std::vector<T>
danstd::array<T, N>
lakukan di C ++. Tidak akan ada cara untuk merancang dan menentukan perpustakaan apa pun, bahkan perpustakaan standar, yang dapat melakukan ini.std::vector
juga tidak pernah bisa berukuran statis. Sedangkan untuk generik, Anda dapat menjadikannya generik sebanyak yang diinginkan oleh C - sejumlah kecil operasi fundamental pada void * (tambah, hapus, ubah ukuran) dan semua yang ditulis secara khusus. Jika Anda akan mengeluh bahwa C tidak memiliki generik gaya C ++, itu jauh di luar lingkup penanganan buffer yang aman.Masalahnya bukan dengan C bahasa .
IMO, satu-satunya kendala utama untuk diatasi adalah bahwa C hanya diajarkan secara buruk . Praktik buruk dan informasi yang salah selama puluhan tahun telah dilembagakan dalam buku pedoman dan catatan kuliah, meracuni pikiran setiap generasi baru programmer sejak awal. Siswa diberi deskripsi singkat tentang fungsi I / O "mudah" seperti
gets
1 atauscanf
kemudian dibiarkan sendiri. Mereka tidak diberi tahu di mana atau bagaimana alat-alat itu bisa gagal, atau bagaimana mencegah kegagalan itu. Mereka tidak diberitahu tentang penggunaanfgets
danstrtol/strtod
karena itu dianggap alat "canggih". Lalu mereka melepaskan dunia profesional untuk melampiaskan malapetaka mereka. Tidak banyak programmer yang lebih berpengalaman yang tahu lebih baik, karena mereka menerima pendidikan yang sama dengan otak yang rusak. Ini menjengkelkan. Saya melihat begitu banyak pertanyaan di sini dan di Stack Overflow dan di situs lain di mana sudah jelas bahwa orang yang mengajukan pertanyaan sedang diajarkan oleh seseorang yang tidak tahu apa yang mereka bicarakan , dan tentu saja Anda tidak bisa hanya mengatakan "Profesor Anda salah," karena dia seorang Profesor dan Anda hanya seorang pria di Internet.Dan kemudian Anda memiliki kerumunan yang meremehkan jawaban yang dimulai dengan, "menurut standar bahasa ..." karena mereka bekerja di dunia nyata dan menurut mereka standar itu tidak berlaku untuk dunia nyata . Saya bisa berurusan dengan seseorang yang berpendidikan buruk, tetapi siapa pun yang bersikeras tidak tahu apa-apa hanya akan merusak industri ini.
Tidak akan ada masalah buffer overflow jika bahasa diajarkan dengan benar dengan penekanan pada penulisan kode aman. Ini bukan "sulit", itu bukan "maju", itu hanya berhati-hati.
Ya, ini sudah menjadi kata-kata kasar.
1 Yang, untungnya, akhirnya dicabut dari spesifikasi bahasa, meskipun akan mengintai dalam kode warisan senilai 40 tahun selamanya.
sumber
sprintf
, tetapi itu tidak berarti bahasanya tidak cacat. C itu cacat dan yang cacat - seperti bahasa apapun - dan itu penting bahwa kita mengakui orang-orang cacat sehingga kami dapat terus memperbaikinya.Masalahnya adalah salah satu kepicikan manajerial daripada ketidakmampuan programmer. Ingat, aplikasi 90.000 baris hanya membutuhkan satu operasi tidak aman untuk sepenuhnya tidak aman. Hampir di luar kemungkinan bahwa aplikasi apa pun yang ditulis di atas penanganan string yang tidak aman secara fundamental akan 100% sempurna - yang berarti aplikasi tersebut tidak aman.
Masalahnya adalah bahwa biaya tidak aman tidak dibebankan ke penerima yang tepat (perusahaan yang menjual aplikasi hampir tidak pernah harus mengembalikan harga pembelian), atau tidak terlihat jelas pada saat keputusan dibuat ("Kita harus mengirim pada bulan Maret tidak peduli apa! "). Saya cukup yakin bahwa jika Anda memfaktorkan biaya jangka panjang dan biaya untuk pengguna Anda daripada keuntungan perusahaan Anda, menulis dalam C atau bahasa terkait akan jauh lebih mahal, mungkin sangat mahal sehingga jelas merupakan pilihan yang salah dalam banyak bidang di mana kebijaksanaan konvensional saat ini mengatakan bahwa itu adalah suatu keharusan. Tapi itu tidak akan berubah kecuali kewajiban perangkat lunak yang lebih ketat diperkenalkan - yang tidak diinginkan oleh siapa pun di industri ini.
sumber
Salah satu kekuatan besar menggunakan C adalah memungkinkan Anda memanipulasi memori dengan cara apa pun yang Anda inginkan.
Salah satu kelemahan besar menggunakan C adalah memungkinkan Anda memanipulasi memori dengan cara apa pun yang Anda inginkan.
Ada versi aman dari semua fungsi yang tidak aman. Namun, programmer dan kompiler tidak secara ketat menegakkan penggunaannya.
sumber
Mungkin karena C ++ sudah melakukan ini, dan kompatibel dengan kode C. Jadi jika Anda ingin tipe string aman dalam kode C Anda, Anda hanya menggunakan std :: string dan menulis kode C Anda menggunakan kompiler C ++.
Subsistem memori yang mendasarinya dapat membantu untuk mencegah buffer overflows dengan memperkenalkan blok penjaga dan memeriksa validitasnya - sehingga semua alokasi memiliki 4 byte 'fefefefe' ditambahkan, ketika blok ini ditulis, sistem dapat melempar wobbler. Ini tidak dijamin untuk mencegah penulisan memori, tetapi akan menunjukkan bahwa ada sesuatu yang salah dan perlu diperbaiki.
Saya pikir masalahnya adalah rutinitas strcpy dll yang lama masih ada. Jika mereka dihapus demi strncpy dll maka itu akan membantu.
sumber
Sangat mudah untuk memahami mengapa masalah kelebihan tidak diperbaiki. C cacat di beberapa daerah. Pada saat itu kekurangan itu dipandang dapat ditoleransi atau bahkan sebagai fitur. Sekarang, beberapa dekade kemudian, kekurangan itu tidak dapat diperbaiki.
Beberapa bagian dari komunitas pemrograman tidak ingin lubang-lubang itu dipasang. Lihat saja semua perang api yang dimulai dari string, array, pointer, pengumpulan sampah ...
sumber
memcpy()
tersedia dan memilikinya hanya cara standar menyalin segmen array secara efisien.