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 3Kuliah 2: "Kontrol serangan hacker" 
Bagian 1 / 
Bagian 2 / 
Bagian 3Kuliah 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?