Kursus MIT "Keamanan Sistem Komputer". Kuliah 3: Buffer Overflows: Eksploitasi dan Perlindungan, Bagian 3

Institut Teknologi Massachusetts. Kursus Kuliah # 6.858. "Keamanan sistem komputer." Nikolai Zeldovich, James Mickens. Tahun 2014


Keamanan Sistem Komputer adalah kursus tentang pengembangan dan implementasi sistem komputer yang aman. Ceramah mencakup model ancaman, serangan yang membahayakan keamanan, dan teknik keamanan berdasarkan pada karya ilmiah baru-baru ini. Topik meliputi keamanan sistem operasi (OS), fitur, manajemen aliran informasi, keamanan bahasa, protokol jaringan, keamanan perangkat keras, dan keamanan aplikasi web.

Kuliah 1: “Pendahuluan: model ancaman” Bagian 1 / Bagian 2 / Bagian 3
Kuliah 2: "Kontrol serangan hacker" Bagian 1 / Bagian 2 / Bagian 3
Kuliah 3: “Buffer Overflows: Exploits and Protection” Bagian 1 / Bagian 2 / Bagian 3

Anda dapat menggunakan teknik menebak "kenari" untuk tujuan Anda sendiri untuk mengetahui keberadaan "lemah", dalam hal pemilihan, bit. Artinya, jika Anda menebak dengan benar, server akan melakukan reboot, dan ini akan berfungsi sebagai sinyal bagi Anda bahwa nilai yang ditetapkan cukup mudah ditebak. Dengan demikian, adalah mungkin untuk mengalahkan "kenari" acak, dengan asumsi bahwa setelah server reboot nilainya tidak akan berubah. Anda juga dapat menggunakan gadget untuk menerapkan urutan terkait beberapa serangan.

Selanjutnya, kita akan melihat cara yang lebih produktif di mana Anda dapat menggunakan semua metode ini untuk mengalahkan pencegahan eksekusi data, ruang alamat acak, dan "kenari".

Mari kita mengalihkan perhatian kita ke arsitektur 64-bit alih-alih arsitektur 32-bit. Yang pertama lebih cocok untuk pengacakan, sehingga mereka memberi Anda lebih banyak "peluang" untuk membela diri terhadap peretas. Dan sistem ini terlihat jauh lebih menarik dalam hal pembentukan serangan.

Arsitektur 64-bit jenis ini juga dipertimbangkan dari sudut pandang BROP , pemrograman “blind” yang berorientasi terbalik. Untuk kesederhanaan, kita mengasumsikan bahwa satu-satunya perbedaan antara mesin 64-bit dan 32-bit adalah bahwa pada mesin 64-bit, argumen diteruskan ke register, dan untuk mesin 32-bit, ke stack.



Ketika suatu fungsi mulai dieksekusi, diambil untuk "melihat" ke register tertentu untuk menemukan di mana argumen berada.

Sekarang mari kita masuk ke intisari dari kuliah hari ini - apa itu pemrograman berorientasi balik buta, atau BROP . Hal pertama yang akan kita lakukan adalah menemukan stop gadget. Ingat bahwa ketika kita mengatakan "gadget," kita pada dasarnya berarti mengembalikan alamat. Gadget diidentifikasi dengan alamat pengirim, alamat awal dari urutan instruksi yang ingin kami tuju. Jadi apa itu stop gadget?

Pada dasarnya, ini adalah alamat pengirim ke suatu tempat dalam kode, namun, jika Anda melompat di sana, maka cukup jeda programnya, tetapi jangan menyebabkan program mogok. Itulah mengapa ini disebut stop gadget.

Anda bisa melompat ke suatu tempat dalam kode, yang kemudian memulai panggilan sistem tidur, atau berhenti sebentar, atau sesuatu seperti itu. Ada kemungkinan bahwa program tersebut entah bagaimana akan “macet” dalam satu lingkaran tanpa akhir jika Anda melompat ke tempat ini. Tidak masalah mengapa berhenti terjadi, tetapi Anda dapat membayangkan beberapa skenario yang akan mengarah ke sana.

Apa gunanya gadget berhenti?

Segera setelah penyerang berhasil mengalahkan "kenari" menggunakan teknik interaktif menebak bit, ia dapat mulai menulis ulang alamat pengirim ini, alamat ret, dan mulai "meraba-raba" gadget berhenti. Perhatikan bahwa sebagian besar alamat acak yang dapat Anda tempatkan di tumpukan cenderung menyebabkan server mogok. Sekali lagi, pesan ini untuk Anda, penyerang, ini merupakan indikasi bahwa apa yang Anda temukan bukanlah gadget yang berhenti. Karena ketika server mogok, soket Anda ditutup, dan Anda, sebagai penyerang, mengerti bahwa Anda tidak mendapatkan gadget yang berhenti. Tetapi jika Anda menebak sesuatu dan soket setelah itu tetap terbuka untuk sementara waktu, Anda berpikir: "Ya, saya menemukan stop gadget ini!" Jadi ide dasar dari langkah pertama adalah menemukan gadget berhenti ini.



Langkah kedua adalah Anda ingin menemukan gadget yang menghapus entri tumpukan menggunakan perintah pop . Oleh karena itu, Anda harus menggunakan urutan instruksi yang dirancang dengan cermat ini untuk mencari tahu kapan Anda mengambil salah satu dari tumpukan gadget ini. Urutan ini akan terdiri dari alamat probe dari alamat probe , alamat stop dari alamat stop, dan alamat kegagalan sistem dari alamat crash .

Dengan demikian, alamat probe adalah apa yang akan kita masukkan ke stack. Ini akan menjadi alamat gadget potensial untuk menghapus stack, stop address - ini adalah apa yang kami pertimbangkan pada langkah pertama, ini adalah alamat gadget stop. Kemudian alamat crash hanya akan menjadi alamat kode yang tidak dapat dieksekusi. Di sini Anda cukup meletakkan alamat nol (0 x 0), dan jika Anda menerapkan fungsi ret ke alamat ini dan mencoba mengeksekusi kode di sana, ini akan menyebabkan crash program.



Jadi kita bisa menggunakan jenis alamat ini untuk mencari tahu di mana gadget ini membersihkan tumpukan tumpukan bermunculan .

Saya akan memberikan contoh sederhana. Misalkan kita memiliki dua contoh probe berbeda, trap trap dan trap trap . Misalkan, dengan bantuan probe, kita akan "menyelidiki" beberapa alamat, misalkan dimulai dengan 4 dan berakhir dengan delapan: 0x4 ... 8, dan di belakangnya adalah alamat berikutnya dari bentuk 0x4 ... C. Secara hipotesis, kita dapat berasumsi bahwa salah satu dari dua alamat ini adalah alamat gadget popping stack .

Perangkap perangkap akan mendapatkan nol alamat 0x0 dan 0x0, dan biarkan gadget berhenti berhenti memiliki alamat acak seperti 0xS ... 0xS ..., itu tidak masalah. Gadget berhenti ini menunjuk ke kode sleep (10), menyebabkan program berhenti.

Mari kita mulai dengan operasi penyelidikan , yang menghapus beberapa register dan kembali dalam urutan berikut: pop rax; ret . Apa yang akan terjadi Ketika sistem melompat ke alamat ini, penunjuk tumpukan akan pindah ke bagian tengah gadget kami. Apa yang akan dilakukan gadget di sini? Benar, lakukan operasi pop rax .



Dan kemudian ikuti ret , yang akan memindahkan fungsi ke baris teratas gadget, yaitu untuk berhenti , dan fungsi akan berhenti tanpa merusak keseluruhan program. Jadi, menggunakan gadget ini, penyerang dapat mengatakan bahwa alamat probe milik salah satu fungsi ini, yang membersihkan tumpukan, karena koneksi klien server terbuka.

Sekarang mari kita asumsikan bahwa alamat probe kedua menunjuk ke sesuatu seperti xor rax, rax, ret untuk beberapa register.



Jadi apa yang terjadi jika kita mencoba beralih ke gadget ini? Perhatikan bahwa itu tidak menghapus apa pun di stack, itu hanya mengubah isi register. Jadi, kita akan kembali ke alamat 0x0 yang terletak di atas. Dan ini akan menyebabkan sistem crash. Sambungan klien ke server akan terputus, dan peretas akan mengerti bahwa ini bukan gadget untuk menghapus tumpukan stack .

Dengan cara ini Anda dapat menggunakan serangkaian jebakan yang lebih aneh dan menghentikan gadget, misalnya Anda dapat menghapus dua item pada tumpukan. Untuk melakukan ini, Anda hanya perlu menempatkan instruksi jebakan lain di sini, dan jika gadget ini tidak menghapus dua elemen, Anda akan menemukan diri Anda di salah satu perangkap ini, dan kode akan berhenti dijalankan. Materi kuliah menjelaskan tentang sesuatu yang disebut gadget BROP, yang berguna jika Anda tidak ingin kembali ke pemrograman. Tapi hari ini saya juga akan memberi tahu Anda bagaimana Anda dapat menggunakan gadget pop sederhana ini untuk meluncurkan serangan serupa. Setelah Anda memahami hal ini, berurusan dengan gadget BROP akan jauh lebih mudah.

Tetapi apakah Anda semua mengerti bagaimana kita dapat menggunakan fungsi penyelidikan untuk gadget ini? Misalkan Anda menemukan lokasi potongan kode yang memungkinkan Anda untuk menghapus tumpukan dengan fungsi pop , hapus satu elemen dari itu, tetapi Anda tidak benar-benar tahu di mana mendaftar fungsi pop ini akan berfungsi. Anda baru tahu bahwa dia sudah siap untuk dieksekusi. Tetapi Anda perlu tahu di mana daftar gadget pop ini akan berfungsi, karena dalam arsitektur 64-bit, register mengontrol di mana argumen fungsi yang ingin Anda panggil berada.

Dengan demikian, tujuan kami adalah untuk dapat membuat gadget yang memungkinkan kami menghapus nilai yang kami masukkan ke register tumpukan tertentu, dan pada akhirnya kami memulai pemanggilan sistem panggilan sistem , yang memungkinkan kami melakukan sesuatu yang buruk.
Jadi sekarang kita perlu menentukan register mana yang digunakan oleh gadget pop .



Untuk melakukan ini, kita dapat memanfaatkan panggilan sistem jeda. Jika panggilan sistem dijeda, maka eksekusi program ditangguhkan, dan tidak menerima argumen apa pun. Dan ini berarti bahwa sistem mengabaikan semua yang ada di register.

Bahkan, untuk menemukan instruksi jeda yang sedang kami jalankan, Anda dapat menautkan semua gadget pop ini sehingga kami dapat meletakkan semuanya di tumpukan, dan di antara masing-masingnya kami memasukkan nomor syscall untuk dijeda. Kemudian kita akan melihat apakah kita dapat "menunda" program dengan cara ini. Biarkan saya memberi Anda sebuah contoh nyata.

Di sini, kami meletakkan gadget di bilah alamat pengirim yang menghapus register RDI dan menerapkan fungsi ret padanya. Di atasnya kita akan meletakkan nomor syscall untuk dijeda.

Misalkan kita memiliki satu lagi gadget yang terletak di atas yang mengimplementasikan fungsi pop di register lain, katakan RSI , lalu ret . Dan kemudian kita meletakkan nomor syscall lagi untuk berhenti. Dan kami melakukan ini untuk semua gadget yang kami temukan, dan kemudian kami berakhir di bagian atas tumpukan dengan alamat diduga untuk syscall .



Ingat kembali bagaimana Anda menggunakan panggilan sistem ini. Anda harus memasukkan nomor syscall di register RAX , kemudian memanggil fungsi syscall libc , yang akan melakukan panggilan sistem yang diminta.

Jadi apa yang terjadi ketika kita menjalankan kode ini? Kami akan datang ke sini, ke baris alamat ret , kami akan melompat ke alamat gadget ini, perlu dicatat bahwa penyerang tahu bahwa gadget ini, terletak di sebelah kanan, menghapus sesuatu dari tumpukan, tetapi belum tahu di mana register itu berada.

Jadi, jika kita lompat ke alamat retret , apa yang terjadi? Dia akan menggunakan fungsi pop untuk menjeda syscall , di beberapa register yang tidak diketahui oleh penyerang, dan kemudian kita akan terus naik rantai operasi ini ke atas tumpukan.

Dengan demikian, kami berharap bahwa salah satu gadget ini akan melakukan fungsi pop untuk nomor syscall dalam register RAX yang sesuai. Jadi pada saat kami tiba di sini, di bagian atas tumpukan, dalam perjalanan "mengacaukan" semua register yang memiliki nomor syscall untuk dijeda, kami berharap bahwa kami masih memiliki satu register, yang seharusnya benar. Karena jika salah satu gadget kita melakukan ini, dan kemudian, ketika melakukan ret , setelah beberapa saat kita akan kembali ke sini, di bagian atas tumpukan, maka kita akan mendapat jeda. Sekali lagi, jeda bertindak sebagai sinyal kepada penyerang. Karena jika anggapan alamat untuk syscall ini salah, maka program akan gagal.

Jadi apa yang memungkinkan kita melakukan fase serangan ini? Kami masih belum tahu di register mana gadget pop berada, tetapi kami tahu bahwa salah satunya akan membebaskan RAX yang ingin kami kontrol. Dan kita mungkin tahu alamat syscall , karena kami berhasil menjeda sistem.

Setelah kami melakukan ini, kami dapat memeriksa gadget ini satu per satu dan mencari tahu mana yang membuat sistem berhenti sementara. Dengan kata lain, kami memotong segalanya antara baris alamat ret dan bagian atas tumpukan untuk langsung menuju syscall . Kami akan memeriksa apakah jeda atau kegagalan sistem telah terjadi. Jika terjadi kegagalan, kami mengidentifikasi gadget yang menyebabkannya, misalnya, ada di baris bawah di sebelah kanan, ini pop rdi . Singkirkan dia dan coba yang berikutnya. Letakkan di sini di baris di atas ret alamat alamat asli untuk syscall . Bisakah kita menghentikan sementara program? Ya, itu berarti kami mengetahui bahwa gadget populer ini harus membebaskan RAX . Apakah itu jelas?

Hadirin: Jadi, cara untuk menebak alamat untuk pemanggilan sistem hanyalah transfer perangkat secara buta?

Profesor: ya, benar, dan ada cara untuk mengoptimalkan proses ini dalam materi kuliah ketika Anda menggunakan ekstensi file PLT dan hal-hal serupa. Dengan serangan sederhana yang saya jelaskan, Anda benar-benar hanya menempelkan alamat di sini, dan memastikan apakah itu menyebabkan jeda atau tidak. Sebagai hasil dari pengujian, kami menemukan lokasi syscall . Kami akan mencari tahu di mana instruksi yang menjalankan pop RAX berada . Kami juga membutuhkan gadget yang menjalankan pop di beberapa register lain juga. Anda dapat melakukan tes serupa untuk mereka. Oleh karena itu, alih-alih menjeda nomor syscall , gunakan push untuk beberapa perintah lain yang, misalnya, menggunakan argumen RAX dan RDI .

Dengan demikian, Anda dapat menggunakan fakta bahwa untuk set register tertentu yang ingin Anda kendalikan, ada beberapa jenis syscall yang akan memberi Anda sinyal penyerang untuk mengetahui apakah Anda berhasil memecahkannya atau tidak. Jadi, pada akhir fase ini, Anda akan memiliki alamat syscall dan alamat tumpukan gadget, yang memungkinkan Anda untuk masuk ke register sewenang-wenang.

Dan sekarang mari kita lanjutkan ke langkah ke 4, yang akan disebut menulis - merekam. Langkah keempat adalah merekam panggilan sistem. Untuk memanggil write , kita harus memiliki gadget berikut:

pop rdi, ret;
pop rsi, ret;
pop rdx, ret;
pop rax, ret;
syscall



Bagaimana register ini digunakan oleh system call? Yang pertama adalah soket, atau, lebih umum, deskriptor file yang akan Anda transfer untuk ditulis. Yang kedua adalah buffer, yang ketiga adalah panjang buffer ini, yang keempat adalah nomor panggilan sistem dan yang kelima adalah syscall itu sendiri.

Jadi, jika kita menemukan semua gadget ini, maka kita dapat mengontrol nilai-nilai yang tertanam dalam argumen, yang, pada gilirannya, ditempatkan di register ini, karena kita hanya "mendorong" mereka ke stack.

Apa yang seharusnya menjadi soket? Di sini kita harus menebak sedikit. Anda dapat memanfaatkan fakta bahwa Linux membatasi jumlah koneksi terbuka simultan untuk file yang mencapai nilai 2024. Dan juga bahwa itu harus menjadi minimum dari semua yang tersedia.

Saya ingin tahu apa yang akan kita masukkan ke buffer pointer? Itu benar, kami bermaksud menggunakan fragmen teks dari program di sini, kami akan meletakkannya di pointer di suatu tempat dalam kode program. Ini memungkinkan kita untuk membaca file biner dari memori menggunakan panggilan klien yang benar dari soket. Kemudian penyerang dapat mengambil file biner ini, menganalisisnya secara offline offline, menggunakan GDB atau alat lain untuk mencari tahu di mana semua ini berada. Penyerang tahu bahwa sekarang, setiap kali server "crash", set hal-hal acak yang sama akan disimpan di dalamnya. Jadi sekarang penyerang dapat mengetahui alamat dan penyeimbang untuk isi tumpukan, ia dapat menyerang gadget ini secara langsung. Ini dapat langsung menyerang kerentanan lain, mencari tahu cara membuka shell dan sejenisnya. Dengan kata lain, di tempat Anda memberikan biner peretas, Anda dikalahkan.

Beginilah cara kerja serangan BROP . Seperti yang saya katakan, dalam materi kuliah ada banyak cara untuk mengoptimalkan proses ini, tetapi pertama-tama Anda harus memahami materi utama, jika tidak optimasi kehilangan artinya. Karena itu, Anda dapat berbicara dengan saya tentang pengoptimalan secara individu atau setelah kelas.

Untuk saat ini, cukup mengatakan bahwa ini adalah dasar-dasar bagaimana Anda dapat meluncurkan serangan BROP . Anda harus menemukan stop gadget, menemukan gadget yang menjalankan fungsi entri tumpukan pop , mencari tahu di mana register mereka berada, di mana syscall berada, dan kemudian memulai menulis , berdasarkan informasi yang diperoleh.



Jadi, cepat membahas topik, bagaimana Anda bertahan melawan BROP ? Jadi hal yang paling jelas yang Anda miliki adalah pengacakan ulang. Karena fakta bahwa server yang "jatuh" tidak muncul kembali, jangan membuat versi acak dari diri mereka sendiri, bertindak sebagai sinyal yang memberikan peluang bagi penyerang untuk menguji berbagai hipotesis tentang bagaimana program bekerja.

Salah satu cara mudah untuk melindungi diri sendiri adalah memastikan Anda melakukan exec bukan fork saat menghidupkan kembali proses Anda. Karena ketika Anda menjalankan proses, Anda membuat ruang yang benar-benar baru secara acak, setidaknya itulah yang terjadi di Linux. Di Linux, ketika Anda mengkompilasi menggunakan PIE , Eksekusi Independen Posisi , executable independen lokasi, ketika Anda menggunakan exec, Anda hanya mendapatkan ruang alamat acak baru.

Cara kedua adalah dengan menggunakan Windows, karena OS ini pada dasarnya tidak memiliki fungsi yang setara dengan fork . Ini berarti bahwa ketika Anda menghidupkan kembali server di Windows, itu akan selalu memiliki ruang alamat acak baru.

Seseorang di sini bertanya apa yang akan terjadi jika setelah kerusakan server ia tidak memutuskan? Jadi, jika selama server crash kita entah bagaimana "menangkap" kesalahan ini dan menjaga koneksi terbuka untuk sementara waktu, kita dapat membingungkan penyerang sehingga dia tidak menerima sinyal tentang kegagalan dan berpikir bahwa dia telah menemukan alamat yang benar.

Dalam hal ini, serangan BROP Anda akan berubah menjadi serangan DOS . Karena Anda baru saja mendapatkan semua proses zombie potensial yang ada di sekitar. Mereka tidak berguna, tetapi Anda tidak dapat membiarkan mereka melangkah lebih jauh, jika tidak, Anda harus menghapus informasi ini.

Hal lain yang mungkin Anda pikirkan adalah melakukan pemeriksaan perbatasan yang kita bicarakan sebelumnya. Materi perkuliahan mengatakan bahwa metode ini tidak produktif, karena harganya 2 kali lebih mahal, tetapi Anda masih bisa menggunakannya.

Beginilah cara kerja BROP . Sedangkan untuk pekerjaan rumah, pertanyaan yang lebih rumit ditanyakan di sana: bagaimana jika Anda menggunakan hash saat ini? Artinya, lamanya waktu selama Anda me-restart program. Apakah ini cukup untuk mencegah serangan seperti ini? Perhatikan bahwa hashing tidak secara ajaib memberi Anda bit entropi jika data yang Anda masukkan ke hash mudah diprediksi. Jika hash Anda mengandung miliaran bit, itu tidak masalah. Tetapi jika Anda hanya memiliki beberapa makna di sana, penyerang hanya akan menebaknya. Tentu saja, menggunakan hash waktu acak lebih baik daripada tidak menggunakan apa pun untuk melindungi terhadap peretas, tetapi ini tidak akan memberi Anda keamanan yang harus Anda andalkan.


Versi lengkap dari kursus ini tersedia di sini .

Terima kasih telah tinggal bersama kami. Apakah Anda suka artikel kami? Ingin melihat materi yang lebih menarik? Dukung kami dengan melakukan pemesanan atau merekomendasikannya kepada teman-teman Anda, diskon 30% untuk pengguna Habr pada analog unik dari server entry-level yang kami temukan untuk Anda: Seluruh kebenaran tentang VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps dari $ 20 atau bagaimana membagi server? (opsi tersedia dengan RAID1 dan RAID10, hingga 24 core dan hingga 40GB DDR4).

Dell R730xd 2 kali lebih murah? Hanya kami yang memiliki 2 x Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100 TV dari $ 249 di Belanda dan Amerika Serikat! Baca tentang Cara Membangun Infrastruktur Bldg. kelas menggunakan server Dell R730xd E5-2650 v4 seharga 9.000 euro untuk satu sen?

Source: https://habr.com/ru/post/id418099/


All Articles