Cara Membaca Dan Menulis Ke Memori Program Mengunakan Skema Format String


Format string adalah cara praktis bagi pemrogram untuk menyiapkan sebuah string dari beberapa variabel. Mereka dirancang untuk menghemat waktu pemrogram dan membiarkan kode mereka terlihat jauh lebih bersih.Tanpa sepengetahuan beberapa pemrogram, format string juga dapat digunakan oleh penyerang untuk mengkompromikan keseluruhan program mereka. Dalam panduan ini, kita akan melihat bagaimana kita bisa menggunakan format string untuk mengeksploitasi program yang sedang berjalan.

Apa itu Format String?

Seperti yang disebutkan di atas, string format adalah metode yang rapi dimana pemrogram dapat menyusun string yang bisa mereka cetak atau simpan ke variabel. Dalam bahasa pemrograman C, format string terlihat seperti ini:

printf ("Kami memiliki% d anjing", 2);

Dan akan menampilkan sesuatu seperti ini:

Kami memiliki 2 anjing

Ramuan rahasia dalam format string adalah format specifier. Format specifier adalah % ddalam perintah yang baru kita tulis. Ketika program melihat specifier format, ia mengetahui untuk mengharapkan sebuah variabel untuk mengganti specifier tersebut. Dalam kasus ini, variabelnya adalah bilangan bulat 2. Inilah contoh lain:

char * person1 = "Bob";
char * person2 = "Alice";
buku int = 15;
printf ("% s dan% s memiliki% d buku", person1, person2, books);

Mari kita pergi baris demi baris dan berjalan melalui apa program itu.

Pada 2 baris pertama, kita mendefinisikan 2 senar, person1 dan person2 dan menetapkan nilai "Bob" dan"Alice" masing-masing. Pada baris ketiga, kita mendefinisikan sebuah variabel bilangan bulat yang diberi nama buku dan berikan nilainya 15. Akhirnya, pada baris terakhir kami mencetak string yang diformat. Dalam string, kita melihat 2 penspesifikasi format yang unik, % s dan % d. Seperti dugaan Anda, masing-masing mengharapkan jenis data yang berbeda. Yang pertama mengharapkan sebuah string, sementara yang terakhir mengharapkan sebuah bilangan bulat. Ada beberapa format specifiers lainnya juga. Ini termasuk % x yang mengharapkan nilai heksadesimal dan % c yang mengharapkan 1 karakter.

Sekarang kita tahu bagaimana menggunakan format string, saatnya belajar bagaimana menyalahgunakannya!

Mengambil Keuntungan dari Fungsi Rentan

Sementara format string tampaknya hanya menjadi teknik pemrograman yang berbeda untuk menggabungkan variabel dan string, ini sebenarnya tidak demikian. Contoh format string yang kita lihat di atas harus mengajukan 1 pertanyaan yang sangat penting: Apa yang terjadi bila Anda memiliki format specifier dalam sebuah string, namun apakah tidak ada variabel yang disertakan untuk menggantikan specifier format itu dalam string? Mari kembali ke mesin virtual Protostar untuk mencari tahu.

Sekali lagi, kita akan SSH masuk ke mesin virtual kita dengan username user dan password user. Setelah masuk, mungkin ada baiknya mengetikkan perintah berikut.

pesta

Ini akan membawa kita dari program shell kita ke program shell yang jauh lebih interaktif yang disebut Bash. Ini akan membuat pengalaman command line kita jauh lebih mulus.

Setelah itu diurus, kita akan langsung masuk dan melihat level format1. Mari beralih ke direktori yang sama dengan format1 yang dapat dieksekusi dengan mengetik:

cd / opt / protostar / bin

Sekarang, sebelum kita sembarangan melemparkan diri kita pada tantangan ini, mari kita lihat kode sumber yang terdapat pada Latihan Eksploitasi.

Kode sumber ini mungkin sedikit mengintimidasi orang-orang yang tidak terbiasa dengan pemrograman C, tapi saya janji itu tidak seburuk itu.

Dengan line-by-line, pertama-tama kita melihat sebuah bilangan bulat global yang diberi nama "target" dinyatakan tanpa nilai. Fakta bahwa variabel ini dinyatakan secara global, bukan di dalam fungsi sangat penting. Ini berubah dimana, dalam memori, variabel disimpan.

Alih-alih disimpan di stack, variabel target akan disimpan di data yang tidak diinisiasi atau bagian BSS dari program. Ini berarti kita tidak akan bisa begitu saja menumpuk tumpukan dengan jumlah karakter yang tidak senonoh untuk mengubah nilai variabel target seperti yang telah kita lakukan dengan kerentanan stack overflow di artikel sebelumnya.

Melanjutkan untuk melihat program ini, kita melihat sebuah fungsi yang dideklarasikan dengan nama "vuln." Aku bertanya-tanya apakah ini adalah di mana kita akan menemukan format string kerentanan.

Hal pertama yang terjadi pada fungsi vuln adalah panggilan ke fungsi printf. Panggilan ini akan mencetak isi variabel bernama string. Pertama kita lihat referensi ke variabel string pada baris 8 ketika dideklarasikan sebagai parameter untuk fungsi vuln. Ini berarti bahwa ketika fungsi vuln dipanggil, sebuah string dilewatkan sebagai argumen dan diberi nama variabel "string" untuk digunakan dalam fungsi.

Selanjutnya, kita melihat pernyataan "jika". Intinya, pernyataan tersebut mengatakan "jika target variabel memiliki nilai selain nol, cetak string berikut." Dari pernyataan ini jika kita dapat menyimpulkan bahwa tujuan kita adalah memodifikasi variabel target.

Akhirnya, kita bisa melihat ke bawah pada 17 fungsi utama program ini. Di dalam fungsi utama adalah panggilan ke fungsi vuln yang baru kita lihat, dengan nilai "argv [1]" dilewatkan sebagai argumen. Variabel "argv [1]" mengacu pada argumen baris perintah pertama yang diberikan pada program saat dijalankan. Di sinilah kita akan menempatkan eksploitasi kita begitu selesai.

Untuk saat ini, mari kita coba untuk menjawab pertanyaan yang kita ajukan diatas: Apa jadinya bila Anda memiliki format specifier tanpa variabel untuk menggantinya dengan?

Kebenaran Ganjil

Kita dapat melihat dari kode sumber di atas bahwa string apa pun yang kita lewati sebagai argumen baris perintah pada program akan dicetak pada baris 10 dengan panggilan ke fungsi printf. Mengetahui hal itu, mari kita berhenti membicarakannya dan melihat apa yang sebenarnya terjadi jika kita melewatkan format specifier sebagai argumen itu.

Nah, itu ... aneh. Ketika kita melewati format specifier % d, daripada mencetak "% d" atau melempar kesalahan seperti yang kita duga, kita mendapatkan bilangan bulat acak. Dari mana bilangan bulat itu berasal? Kita bisa menyalakan GDB, debugger GNU, dan mencoba menggali melalui program untuk menemukannya, tapi melihat memori dalam bentuk bilangan bulat agak berantakan. Mungkin ada cara kita bisa mendapatkan nomor ini dalam bentuk heksadesimal.

Seperti yang telah kami sebutkan sebelumnya, ada format lain yang memperkirakan nilai heksadesimal. Mari kita lihat apa yang terjadi jika kita mengganti % d dengan % x sebagai argumen kita:

Lihatlah, kita mendapatkan nilai (disorot dengan warna merah diatas) yang terlihat sangat mirip dengan nilai heksadesimal. Mari kita lihat apakah kita dapat menemukan nilai ini di suatu tempat di memori dengan debugger GDB.

Untuk memulai GDB dan menempelkannya ke programformat1 mari ketik yang berikut ini.

format gdb1

Begitu GDB sudah dimulai, kita perlu mengatur breakpoint. Melihat kembali kode sumbernya, baris 14 sepertinya pilihan yang bagus. Untuk mengatur breakpoint, kita mengetik:

istirahat 14

Sekarang kita siap untuk menjalankan program ini. Di GDB, Anda dapat menjalankan sebuah program dengan argumen baris perintah dengan menggunakan perintah rundengan argumen baris perintah setelahnya. Dalam kasus ini, kita akan mengetikkan:

jalankan% x

Ini akan menjalankan program dengan % x sebagai argumennya. Begitu kita menjalankan program kita harus memukul breakpoint, seperti yang terlihat pada gambar di bawah ini.

Saat kita mencapai titik impas, eksekusi program dihentikan.Dari sini, kita bisa memeriksa potongan memori individu dengan perintah x . Mari kita mulai dengan melihat tumpukannya. Untuk melakukan ini, kita akan mengetikkan:

x / 32x $ esp

X pertama adalah kependekan dari "pemeriksaan". Perintah ini memungkinkan kita untuk memeriksa memori, jadi namanya pas. The / 32menetapkan bahwa kita ingin memeriksa 32 segmen empat byte berikutnya. Akhir x pada akhirnya memberitahu GDB bahwa kita ingin melihat bagian memori ini dalam format heksadesimal. Istilah terakhir $ esp memberitahu perintah untuk mulai melihat memori pada awal kerangka tumpukan saat ini. Mari kita lihat output apa yang kita dapatkan dari perintah ini.

Sekarang kita bisa melihat 1 ton data dari tumpukan, tapi 1 bagian harus menempel pada kita: Nilai heksadesimal yang sama yang dicetak sebelumnya adalah duduk di tumpukan!

Kami akhirnya memiliki jawaban atas pertanyaan kami. Bila format specifier tidak memiliki variabel yang sesuai untuk menggantikannya, program hanya akan mengambil nilai di memori di lokasi di mana ia diharapkan memiliki variabel yang sesuai.Ketika kita memiliki sebuah program yang secara tidak semestinya memungkinkan pengguna untuk mencetak sebuah string yang berisi format specifier, penyerang memperoleh kemampuan untuk membaca data langsung dari memori.

Pergi dari Membaca Data ke Menulis Data

Sementara membaca data kita seharusnya tidak bisa menarik, menulis data yang seharusnya tidak bisa kita tulis jauh lebih menyenangkan. Dengan kesenangan ini datang komplikasi, namun begitu berpegang pada keyboard Anda dan bersiap-siap.

Ada 1 lagi format specifier yang belum kita bicarakan. Specifier ini adalah % n. Sementara setiap format specifier lainnya difokuskan untuk membaca tipe data tertentu,% n difokuskan pada penulisan data. Secara khusus,% n akan menulis panjang format string sampai titik tersebut ke alamat variabel. Yang penting untuk dicatat di sini adalah bahwa format specifier% n mengharapkan alamat variabel, bukan variabel itu sendiri.

Tunggu sebentar! Jika program yang kita cari secara otomatis akan mengambil alamat untuk dibaca dari penspesifikasi format lainnya, apakah secara otomatis akan mengambil alamat untuk ditulis ke untuk specifier% n? Tentu itu akan terjadi.

Ke mana Kami Ingin Menjadi

Agar kita dapat menimpa variabel target, kita perlu menuliskan alamatnya ke memori dan kemudian mengatur% n untuk menulis ke alamat itu. Untuk melakukan itu, pertama kita perlu tahu di mana masukan asli kita ada di stack.

Langkah 1
Menemukan Tempat Kita Mulai

Untuk menemukan di mana variabel string berada, mari kita restart program di GDB.Kali ini, kita akan mengetikkan perintah berikut.

jalankan AAAA.% x.% x

Sekali lagi, kita akan memukul breakpoint, dan kita bisa mulai menggali. Untuk menemukan lokasi alamat string, kita akan mengetikkan berikut ini.

p string

Dalam perintah ini, p adalah singkatan dari print. Perintah ini akan mencetak variabel apa pun yang kita berikan padanya, beserta lokasi variabel itu. Output kita akan terlihat seperti ini:

Dari output ini, kita dapat mengumpulkan bahwa variabel string terletak di 0xbffff987.Jika kita memeriksa memori di lokasi itu, sebenarnya, kita akan menemukan representasi heksadesimal dari keempatnya. Seperti yang kita ketik di awal masukan kita.

Inilah tipenya: Alamat memori ini (0xbffff987) lebih tinggi dari alamat memori data yang kita baca menggunakan format specifier. Ini berarti bahwa jika kita menyediakan string dengan penspesifikasi format yang cukup, kita akan terus memanjat alamat memori sampai akhirnya kita kembali ke awal string kita. Jika kita melakukan matematika, kita bisa mengetahui berapa format string yang harus kita lakukan:

Dengan mengurangkan alamat data pada tumpukan dari alamat awal dari variabel string, kita dapat melihat bahwa keduanya berjarak 547 byte. Dengan membulatkannya hingga 548 dan membagi dengan empat, kita dapat melihat bahwa kita memerlukan kira-kira 137 penspesifikasi format untuk kembali ke string asli kita. Ini terdengar seperti pekerjaan untuk skrip Python.

Langkah 2
Menulis Skeleton untuk Eksploitasi Kami

Mari keluar dari GDB dan ketik perintah berikut untuk kembali ke direktori home kami.

CD

Pendek dan manis. Begitu kita di rumah, ayo gunakan editor teks Nano untuk membuka dokumen teks baru.

nano exploit.py

Begitu kita berada di Nano, mari kita ketik kerangka yang mengeksploitasi:

Melalui kode ini, baris pertama memberitahu shell Bash bahwa ketika mencoba menjalankan file ini, ia harus menggunakan kompiler Python. 2 baris berikutnya mengimpor modul yang kita perlukan untuk memanfaatkannya. Modul "os" akan memungkinkan kita melakukan panggilan sistem untuk menjalankan program format1. Modul struct akan berguna ketika harus menulis alamat memori di lain waktu.

Baris 4 menciptakan paus mutlak dari sebuah variabel string yang diberi nama muatan. Di dalam variabel itu, kita akan menyimpan 4 Seperti juga 137 format specifiers. Ini sangat penting untuk mencatat periode yang ditempatkan di dalam string.Bergantung pada berapa lama atau singkat muatannya, format string akan mengambil data dari memori dalam potongan yang sedikit berbeda.Kita perlu memastikan bahwa keempat dari kita Sebagai tinggal di bagian memori yang sama yang akan dibaca oleh satu format specifier. Saat berlatih sendiri, Anda hanya perlu bermain-main dengan senar Qsampai Anda menemukan kombinasi yang sesuai.

Setelah selesai menulis skrip kerangka, kita bisa menyimpannya dan menjalankannya. Menjalankan kerangka eksploitasi kami menghasilkan output sebagai berikut:

Karena kami menyediakan 137 penspesifikasi format, kami mendapat 137 potongan memori empat byte. Ini termasuk memori yang kita lihat di GDB tadi.

Melihat hasilnya, kita bisa melihat empat As kita (disorot dengan warna merah).Tampaknya kami terlalu banyak menilai format penspesifikasi yang kami butuhkan. Hal ini kemungkinan besar karena struktur memori program sedikit berbeda saat dijalankan di GDB dan bukan dengan sendirinya. Mengedit eksploitasi kami jadi kami hanya menyediakan 132 penspesifikasi format dan bukan 137 yang harus menempatkan kami sesuai dengan keinginan kami:

Sempurna. Dari sini kita bisa melihat cahaya di ujung terowongan. Kemuliaan eksploitasi hampir sampai kepada kita, tapi ada satu langkah lagi.

Langkah 3
Menemukan dan Menimpa Variabel Target

Kita perlu kembali ke GDB sekali lagi untuk mendapatkan informasi penting. Kita akan mengganti empat As pada awal string payload kita dengan alamat target variabel. Dengan cara itu, kita bisa mengganti pengubah% x terakhir untuk pengubah% n yang akan membaca alamat target dan menimpa dengan panjang string. Untuk mendapatkan alamat target, kita harus mengetikkan berikut ini ke dalam GDB.

p & target

The & di depan nama variabel memberitahukan GDB bahwa kita menginginkan alamat variabel, bukan nilai dari variabel itu sendiri.Menjalankan perintah tersebut menghasilkan hasil sebagai berikut:

Sekarang, yang harus kita lakukan adalah menampar anak nakal itu ke dalam program kita dan kita harus baik untuk pergi. Eksploitasi terakhir kita akan terlihat seperti ini sekarang:

Ada 2 perubahan yang dilakukan: Pertama, kami menambahkan variabel baru yang disebut "alamat". Ini akan menampung alamat dari variabel target. Kami menggunakan fungsi struct.pack untuk menyimpan alamat dalam format yang akan diinterpretasikan program1 dengan benar.

Perubahan kedua terjadi saat kita menciptakan variabel payload. Alih-alih memulai string dengan empat As, kita mulai dengan variabel alamat sekarang. Kami memastikan untuk menyertakan periode sesudahnya untuk memastikan alamatnya sesuai dengan lokasi pembacaan format.Kami juga mencetak satu specifier% x format dan malah mencetak specifier format% n pada tempatnya. Hal ini dilakukan agar format% n specifier akan membaca alamat yang kita tulis di awal string dan menimpa data pada alamat tersebut. Dalam hal ini, alamat itu akan (semoga!) Menjadi alamat dari variabel target.

Langkah 4
Kesuksesan kita

Begitu kita membuat perubahan yang diperlukan pada program ini, mari kita lihat apa yang terjadi saat kita menjalankannya:

Program ini mengkonfirmasikan bahwa kita menekan variabel target dengan sempurna dan menumpuk data dengan kita sendiri. Kemenangan manis.



















 

Comments

Popular Posts