Dengan awal tahun 2019, adalah baik untuk mengingat masa lalu dan memikirkan masa depan. Mari kita melihat ke belakang selama 30 tahun dan merenungkan artikel ilmiah pertama tentang fuzzing:
“Sebuah Studi Empiris tentang Keandalan Utilitas UNIX” dan karya 1995 berikutnya
“Revisi Fuzzing” oleh penulis yang sama
Barton Miller .
Pada artikel ini, kami akan mencoba menemukan bug dalam versi modern Ubuntu Linux menggunakan
alat yang sama seperti pada karya fuzzing asli. Anda harus membaca dokumen asli tidak hanya untuk konteks, tetapi juga untuk pemahaman. Mereka ternyata sangat kenabian sehubungan dengan kerentanan dan eksploitasi selama beberapa dekade mendatang. Pembaca yang penuh perhatian dapat melihat tanggal publikasi artikel asli: 1990. Yang lebih penuh perhatian akan memperhatikan hak cipta di komentar sumber: 1989.
Ulasan singkat
Bagi mereka yang belum membaca dokumen (walaupun ini harus benar-benar dilakukan), bagian ini berisi ringkasan singkat dan beberapa kutipan yang dipilih.
Program fuzzing menghasilkan aliran karakter acak, dengan kemampuan untuk hanya menghasilkan karakter yang dapat dicetak atau tidak dicetak. Ia menggunakan nilai awal tertentu (benih), memastikan hasil yang dapat direproduksi, yang seringkali tidak dimiliki oleh fuzzer modern. Seperangkat skrip dijalankan pada program yang diuji dan memeriksa keberadaan dump dasar. Hang terdeteksi secara manual. Adaptor memberikan input acak untuk program interaktif (artikel 1990), layanan jaringan (1995), dan aplikasi X grafis (1995).
Artikel 1990 menguji empat arsitektur prosesor (i386, CVAX, Sparc, 68020) dan lima sistem operasi (4.3 BSD, SunOS, AIX, Xenix, Dynix). Dalam artikel 1995, pilihan platform yang serupa. Dalam artikel pertama, 25-33% dari utilitas gagal, tergantung pada platform. Dalam artikel berikutnya, angka-angka ini berkisar dari 9% hingga 33%, dengan GNU (di SunOS) dan Linux memiliki tingkat kegagalan terendah.
Artikel 1990 menyimpulkan bahwa 1) programmer tidak memeriksa batas-batas array atau kode kesalahan, 2) makro membuatnya sulit untuk membaca dan men-debug kode, dan 3) bahasa C sangat tidak aman. Sangat tidak aman
gets
fungsi dan sistem tipe C. Terutama disebutkan Selama pengujian, penulis menemukan kerentanan Format String bertahun-tahun sebelum eksploitasi massal mereka. Artikel diakhiri dengan survei terhadap pengguna tentang seberapa sering mereka memperbaiki bug atau melaporkannya. Ternyata melaporkan bug itu sulit dan ada sedikit minat untuk memperbaikinya.
Artikel 1995 menyebutkan perangkat lunak sumber terbuka dan membahas mengapa ia memiliki lebih sedikit kesalahan. Kutipan:
Ketika kami menyelidiki penyebab kegagalan, muncul fenomena yang mengganggu: banyak bug (sekitar 40%) yang dilaporkan pada tahun 1990 masih ada dalam bentuk persisnya pada tahun 1995. ...
Metode yang digunakan di sini sederhana dan sebagian besar otomatis. Sulit untuk memahami mengapa pengembang tidak menggunakan sumber yang mudah dan gratis ini untuk meningkatkan keandalan.
Hanya dalam 15-20 tahun teknik fuzzing akan menjadi praktik standar untuk vendor besar.
Bagi saya juga sepertinya pernyataan 1990 ini meramalkan peristiwa masa depan:
Seringkali, gaya singkat pemrograman C dibawa ke ekstrim, bentuk menang atas fungsi yang benar. Kemungkinan overflow dalam buffer input adalah lubang keamanan potensial, seperti yang ditunjukkan oleh worm Internet baru - baru ini .
Metodologi uji
Untungnya, 30 tahun kemudian, Dr. Barton masih menyediakan
kode sumber, skrip, dan data lengkap untuk mereproduksi temuannya : contoh terpuji yang harus diikuti oleh peneliti lain. Script berfungsi tanpa masalah, dan alat fuzzing hanya membutuhkan perubahan kecil untuk dikompilasi dan dijalankan.
Untuk pengujian ini, kami menggunakan
skrip dan input dari repositori dasar fuzz-1995 , karena ada daftar
aplikasi teruji yang terbaru. Menurut
README , di sini adalah input acak yang sama seperti dalam penelitian asli. Hasil di bawah ini untuk Linux modern diperoleh
persis pada kode fuzzing yang sama dan input data seperti pada artikel aslinya. Hanya daftar utilitas untuk pengujian yang berubah.
Utilitas berubah lebih dari 30 tahun
Jelas, ada beberapa perubahan dalam paket perangkat lunak Linux selama 30 tahun terakhir, meskipun beberapa utilitas terbukti telah melanjutkan silsilah mereka selama beberapa dekade. Jika memungkinkan, kami mengambil versi modern dari program yang sama dari artikel 1995. Beberapa program tidak lagi tersedia, kami menggantinya. Pembenaran untuk semua penggantian:
cfe
⇨ cc1
: Setara dengan preproses C dari artikel 1995.dbx
⇨ gdb
: Setara dengan debugger 1995.ditroff
⇨ groff
: ditroff
tidak lagi tersedia.dtbl
⇨ gtbl
: Setara dengan GNU Troff dari utilitas dtbl
lama.clisp
⇨ clisp
: Implementasi standar clisp
.more
⇨ more
less
: Lebih sedikit lebih banyak!prolog
⇨ swipl
: Ada dua opsi untuk prolog: SWI Prolog dan GNU Prolog. Prolog SWI lebih disukai karena merupakan implementasi yang lebih tua dan lebih lengkap.awk
⇨ gawk
: awk
versi GNU.cc
⇨ gcc
: Kompiler C standar.compress
⇨ gzip
: GZip adalah turunan konseptual dari utilitas Unix compress
lama.lint
⇨ splint
: Lint ditulis ulang di bawah GPL./bin/mail
⇨ /usr/bin/mail
: Utilitas yang Setara dengan cara yang berbeda.f77
⇨ fort77
: Ada dua variasi kompiler Fortan77: GNU Fortran dan Fort77. Yang pertama direkomendasikan untuk Fortran 90, dan yang kedua untuk dukungan Fortran77. Program f2c
didukung secara aktif, daftar perubahannya telah dipertahankan sejak 1989.
Hasil
Teknik fuzzing 1989 masih menemukan kesalahan pada 2018. Tetapi ada beberapa kemajuan.
Untuk mengukur kemajuan, Anda perlu dasar. Untungnya, kerangka kerja semacam itu ada untuk utilitas Linux. Meskipun Linux tidak ada pada saat artikel asli pada tahun 1990, tes kedua pada tahun 1995 meluncurkan kode fuzzing yang sama pada utilitas dari distribusi Slackware 2.1.0 1995. Hasil yang sesuai diberikan dalam
tabel 3 artikel 1995 (hal. 7-9) . Dibandingkan dengan pesaing komersial, GNU / Linux terlihat sangat bagus:
Persentase kerusakan utilitas dalam UNIX versi Linux gratis adalah yang tertinggi kedua: 9%.
Jadi, mari kita bandingkan utilitas Linux 1995 dan 2018 dengan alat fuzzing 1989:
| Ubuntu 18.10 (2018) | Ubuntu 18.04 (2018) | Ubuntu 16.04 (2016) | Ubuntu 14.04 (2014) | Slackware 2.1.0 (1995) |
---|
Kecelakaan | 1 (f77) | 1 (f77) | 2 (f77, ul) | 2 (swipl, f77) | 4 (ul, flex, indent, gdb) |
Membeku | 1 (mantra) | 1 (mantra) | 1 (mantra) | 2 (mantra, unit) | 1 (tagag) |
Total Diuji | 81 | 81 | 81 | 81 | 55 |
Kegagalan / pembekuan,% | 2% | 2% | 4% | 5% | 9% |
Yang mengejutkan, jumlah Linux lumpuh dan macet masih lebih besar dari nol, bahkan pada Ubuntu versi terbaru. Jadi,
f77
memanggil program
f2c
dengan kesalahan segmentasi, dan program
spell
tergantung pada dua versi dari input tes.
Bug apa?
Saya dapat secara manual mengetahui akar penyebab beberapa bug. Beberapa hasil, seperti kesalahan glibc, tidak terduga, sementara yang lain, seperti sprintf dengan buffer ukuran tetap, dapat diprediksi.
Kegagalan ul
Bug di
ul sebenarnya adalah bug di glibc. Secara khusus, itu dilaporkan di
sini dan di
sini (orang lain menemukannya di
ul
) pada tahun 2016. Menurut pelacak bug, kesalahan masih belum diperbaiki. Karena bug tidak dapat direproduksi di Ubuntu 18.04 dan yang lebih baru, ia diperbaiki pada tingkat distribusi. Dilihat oleh komentar pada pelacak bug, masalah utama bisa sangat serius.
Kecelakaan f77
Program
f77
datang dalam paket fort77, yang itu sendiri adalah skrip shell di sekitar
f2c
, sumber penerjemah dari Fortran77 ke C. Debugging
f2c
menunjukkan bahwa kegagalan terjadi ketika fungsi
errstr
mencetak pesan kesalahan yang terlalu panjang.
Kode sumber f2c menunjukkan bahwa fungsi sprintf digunakan untuk menulis string panjang variabel ke buffer ukuran tetap:
errstr(const char *s, const char *t) #endif { char buff[100]; sprintf(buff, s, t); err(buff); }
Tampaknya kode ini telah dipertahankan sejak pembuatan
f2c
. Program ini memiliki
sejarah perubahan setidaknya sejak tahun 1989. Pada tahun 1995, ketika melakukan fuzzing kembali, kompiler Fortran77 tidak diuji, jika tidak masalahnya akan ditemukan sebelumnya.
Bekukan mantra
Sebuah contoh bagus dari kebuntuan klasik.
spell
delegasi
ispell
melalui pipa.
spell
membaca baris teks demi baris dan menghasilkan catatan pemblokiran ukuran baris dalam
ispell
. Namun,
ispell
membaca maksimum
BUFSIZ/2
byte pada suatu waktu (4096 byte pada sistem saya) dan mengeluarkan catatan pemblokiran untuk memastikan bahwa klien telah menerima data validasi yang telah diproses sejauh ini. Dua input tes yang berbeda memaksa
spell
untuk menulis string lebih dari 4096 karakter untuk
ispell
, yang mengakibatkan kebuntuan:
spell
menunggu
ispell
membaca seluruh string, sementara
ispell
menunggu
spell
mengonfirmasi bahwa ia telah membaca koreksi ejaan asli.
Gantung unit
Sekilas, sepertinya ada kondisi loop tak terbatas.
libreadline
tampaknya berada di
libreadline
dan bukan di
units
, meskipun versi
units
lebih baru tidak mengalami kesalahan ini. Log perubahan menunjukkan bahwa pemfilteran input telah ditambahkan yang secara tidak sengaja dapat memperbaiki masalah ini. Namun, penyelidikan menyeluruh atas alasan-alasannya berada di luar cakupan blog ini. Mungkin cara untuk menggantung
libreadline
masih ada.
Swipl crash
Demi kelengkapan, saya ingin menyebutkan crash
swipl
, meskipun saya tidak mempelajarinya dengan saksama, karena bug telah lama diperbaiki dan tampaknya memiliki kualitas yang cukup tinggi. Kegagalan sebenarnya adalah pernyataan (mis. Yang seharusnya tidak pernah terjadi) yang dipanggil saat mengonversi karakter:
[Thread 1] pl-fli.c:2495: codeToAtom: Assertion failed: chrcode >= 0
C-stack trace labeled "crash":
[0] __assert_fail+0x41
[1] PL_put_term+0x18e
[2] PL_unify_text+0x1c4
…
Kecelakaan selalu buruk, tetapi setidaknya di sini program dapat melaporkan kesalahan, macet lebih awal dan keras.
Kesimpulan
Selama 30 tahun terakhir, fuzzing tetap menjadi cara yang sederhana dan dapat diandalkan untuk menemukan bug. Meskipun
penelitian aktif sedang berlangsung di
daerah ini , bahkan fuzzer 30 tahun yang lalu berhasil menemukan kesalahan dalam utilitas Linux modern.
Penulis artikel asli meramalkan masalah keamanan yang akan menyebabkan C selama beberapa dekade mendatang. Dia berpendapat dengan meyakinkan bahwa kode yang tidak aman terlalu mudah untuk ditulis dalam C dan harus dihindari jika memungkinkan. Secara khusus, artikel menunjukkan bahwa bug muncul bahkan dengan pentahapan paling sederhana, dan pengujian tersebut harus dimasukkan dalam praktik pengembangan perangkat lunak standar. Sayangnya, saran ini belum diikuti selama beberapa dekade.
Semoga Anda menikmati retrospektif 30 tahun ini. Tunggu artikel Fuzzing berikutnya di tahun 2000, di mana kami akan memeriksa seberapa kuat aplikasi Windows 10 dibandingkan
dengan Windows NT / 2000 yang setara ketika diuji dengan fuzzer . Saya pikir jawabannya sudah bisa ditebak.