Membuat dll proxy untuk membajak operasi dll memeriksa

Ketika saya memeriksa keamanan perangkat lunak, salah satu poin untuk diperiksa adalah bekerja dengan pustaka dinamis. Serangan seperti DLL yang dibajak ("dll spoofing" atau "dll intersepsi") sangat jarang terjadi. Kemungkinan besar, ini disebabkan oleh fakta bahwa pengembang Windows menambahkan mekanisme keamanan untuk mencegah serangan, dan pengembang perangkat lunak lebih berhati-hati tentang keamanan. Tetapi yang lebih menarik adalah situasi ketika perangkat lunak target rentan.

Menjelaskan serangan secara singkat, DLL yang dibajak menciptakan situasi di mana beberapa file yang dapat dieksekusi mencoba memuat dll, tetapi penyerang mengintervensi dalam proses ini, dan bukannya perpustakaan yang diharapkan, bekerja dengan dll yang disiapkan secara khusus dengan muatan dari penyerang terjadi. Akibatnya, kode dari dll akan dieksekusi dengan hak aplikasi yang diluncurkan, oleh karena itu aplikasi dengan hak yang lebih tinggi biasanya dipilih sebagai target.

Agar pustaka memuat dengan benar, sejumlah kondisi harus dipenuhi: ukuran bit dari file yang dapat dieksekusi dan pustaka harus cocok, dan jika pustaka dimuat ketika aplikasi dimulai, dll harus mengekspor semua fungsi yang diharapkan aplikasi ini akan diimpor. Seringkali, satu impor tidak cukup - sangat diinginkan bahwa aplikasi melanjutkan kerjanya setelah memuat dll. Untuk ini, perlu bahwa fungsi perpustakaan yang disiapkan bekerja sama seperti aslinya. Cara termudah untuk melakukan ini adalah dengan hanya melewatkan panggilan fungsi dari satu perpustakaan ke yang lain. Ini adalah dll yang disebut proxy dll.



Di bawah potongan akan ada beberapa opsi untuk membuat perpustakaan seperti itu - baik dalam bentuk kode dan utilitas.

Ulasan teoretis kecil


Perpustakaan lebih sering dimuat menggunakan fungsi LoadLibrary, di mana nama perpustakaan dilewatkan. Jika alih-alih nama yang Anda lewati jalur lengkap, maka aplikasi akan mencoba memuat pustaka yang ditentukan. Misalnya, memanggil LoadLibrary ("C: \ Windows \ system32 \ version.dll") akan memuat dll yang ditentukan. Atau, jika perpustakaan tidak ada, itu tidak akan dimuat.

Sedikit kebosanan
Jika beberapa dll sudah dimuat ke dalam aplikasi, maka itu tidak akan dimuat lagi. Mengingat bahwa version.dll dimuat di awal hampir semua file-exe, pada kenyataannya, panggilan di atas tidak akan memuat apa pun. Tapi kami masih menganggap kasus umum, menganggap contoh sebagai panggilan ke beberapa perpustakaan abstrak.

Ini adalah masalah lain jika Anda menulis LoadLibrary ("version.dll"). Dalam situasi normal, hasilnya akan persis sama seperti pada kasus sebelumnya - C: \ Windows \ system32 \ version.dll akan dimuat, tetapi tidak sesederhana itu.

Pertama, perpustakaan akan dicari, yang akan berjalan dalam urutan berikut:

  1. Folder yang dapat dieksekusi
  2. Folder C: \ Windows \ System32
  3. Folder C: \ Windows \ System
  4. Folder C: \ Windows
  5. Folder ditetapkan sebagai saat ini untuk aplikasi
  6. Folder dari variabel lingkungan PATH

Beberapa lagi kebosanan
Saat memulai aplikasi 32-bit pada sistem 64-bit, semua panggilan ke C: \ Windows \ system32 akan diteruskan ke C: \ Windows \ SysWOW64. Ini hanya untuk keakuratan deskripsi, dari sudut pandang penyerang perbedaannya tidak terlalu penting.

Ketika Anda menjalankan file exe, OS memuat semua perpustakaan dari bagian impor file. Secara umum, kita dapat mengasumsikan bahwa OS memaksa file untuk memanggil LoadLibrary, melewati semua nama pustaka yang ditulis di bagian impor. Karena dalam 99,9% kasus ada nama dan bukan jalur, ketika aplikasi dimulai, semua pustaka yang dimuat akan dicari dalam sistem.

Dari daftar lokasi pencarian dll, dua poin sangat penting bagi kami - 1 dan 6. Jika kami menempatkan versi.dll di folder yang sama dari tempat file diluncurkan, maka alih-alih sistem, yang dimuat akan dimuat. Situasi ini hampir tidak pernah ditemui, karena jika ada kesempatan untuk meletakkan perpustakaan, maka, kemungkinan besar, adalah mungkin untuk mengganti file yang dapat dieksekusi itu sendiri. Tapi tetap saja, situasi seperti itu mungkin terjadi. Misalnya, jika file yang dapat dieksekusi terletak di folder yang dapat ditulisi dan merupakan layanan dengan mulai otomatis, maka itu tidak dapat diubah saat layanan itu sendiri sedang berjalan. Atau file yang diluncurkan diperiksa secara eksternal dengan checksum sebelum memulai, kemudian mengganti file masih bukan pilihan. Tetapi untuk menempatkan perpustakaan di sebelahnya akan sangat nyata.

Anda mungkin tidak dapat membuat file di samping file yang dapat dieksekusi, tetapi Anda dapat membuat folder. Dalam situasi ini, mekanisme pengalihan WinSxS (alias "DotLocal") dapat bekerja.

Secara singkat tentang DotLocal
Manifes file mungkin berisi ketergantungan pada perpustakaan versi tertentu. Dalam hal ini, pada awal file yang dapat dieksekusi (misalnya, biarkan itu menjadi application.exe), OS akan memeriksa keberadaan folder bernama application.exe.local di folder yang sama dengan file itu sendiri. Folder ini harus memiliki subfolder dengan nama kompleks seperti amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.9600.19291_none_6248a9f3ecb5e89b di dalamnya yang sudah ada perpustakaan comctl32.dll. Nama pustaka dan informasi untuk nama folder harus ditunjukkan dalam manifes, berikut adalah contoh dari proses pertama yang muncul. Jika tidak ada folder atau file, maka pustaka akan diambil dari C: \ Windows \ WinSxS. Dalam contoh ini, C: \ Windows \ WinSxS \ amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.9600.19291_none_6248a9f3ecb5e89b \ comctl32.dll.

Tapi ini lebih merupakan pengecualian daripada aturan. Tetapi situasi ketika pencarian dll mencapai nomor 6 dalam daftar cukup nyata. Jika aplikasi mencoba memuat dll yang tidak ada di sistem atau di sebelah file, maka semua pencarian akan naik hingga 6 poin, yang berpotensi menjadi folder yang dapat ditulis.

Sebagai contoh, instalasi Python khas paling sering terjadi di folder C: \ Python (atau tutup). Pemasang python sendiri menyarankan untuk menambahkan foldernya ke variabel sistem PATH. Sebagai hasilnya, kami memiliki batu loncatan yang baik untuk memulai serangan - folder ini dapat ditulis oleh semua pengguna dan segala upaya untuk memuat pustaka yang tidak ada akan menuju pencarian jalur dari PATH.

Sekarang setelah teorinya selesai, pertimbangkan penciptaan payload - proxy library itu sendiri.

Opsi pertama. Pustaka proxy yang jujur


Mari kita mulai dengan yang relatif sederhana - kami akan membuat perpustakaan proxy yang jujur. Kejujuran dalam hal ini menyiratkan bahwa semua fungsi dalam dll akan terdaftar secara eksplisit, dan untuk setiap fungsi panggilan fungsi dengan nama yang sama dari perpustakaan asli akan ditulis. Bekerja dengan perpustakaan seperti itu akan sepenuhnya transparan untuk kode yang dipanggil: jika ia memanggil beberapa fungsi, maka ia akan menerima jawaban yang benar, hasil, dan segala sesuatu yang harus terjadi berdampingan.

Berikut ini tautan ke contoh jadi ( github ) dari pustaka versi.dll.

Sorotan Kode:

  • Semua prototipe fungsi dari tabel ekspor perpustakaan asli dijelaskan dengan jujur.
  • Pustaka asli dimuat dan semua panggilan ke fungsi kami dilemparkan ke dalamnya.

Mudahnya , aplikasi terus bekerja dengan benar, tanpa mengalami "efek khusus". Sangat tidak nyaman bahwa saya harus menulis banyak kode seragam untuk masing-masing fungsi, apalagi, dengan hati-hati memeriksa kebetulan prototipe.

Opsi kedua. Sederhanakan penulisan kode


Ketika berhadapan dengan perpustakaan seperti version.dll, di mana tabel impor kecil, hanya ada 17 fungsi, dan prototipe sederhana, maka perpustakaan proxy yang jujur ​​adalah pilihan yang baik.



Tetapi jika proksi untuk perpustakaan, misalnya, bcrypt, maka semuanya lebih rumit. Ini tabel impornya:



57 fitur! Dan berikut adalah beberapa contoh prototipe:




Anggap saja tidak ada yang mustahil, tetapi membuat proxy yang jujur ​​untuk perpustakaan semacam itu tidak terlalu menyenangkan.

Anda dapat menyederhanakan kode jika Anda sedikit curang dengan fungsi. Kami mendeklarasikan semua fungsi di perpustakaan sebagai __declspec (telanjang), dan dalam kode body - assembler yang hanya membuat jmp pada fungsi dari perpustakaan asli. Ini memungkinkan kami untuk tidak menggunakan prototipe panjang, tetapi untuk menempatkan pengumuman sederhana di mana saja tanpa parameter tampilan:

batal foo ()

Ketika aplikasi memanggil fungsi kita, pustaka proxy tidak akan melakukan manipulasi dengan register dan stack, memungkinkan fungsi asli untuk melakukan semua pekerjaan sebagaimana mestinya.

Contoh ( github ) pustaka versi.dll dengan pendekatan ini.

Sorotan:

  • Pustaka asli dimuat, dan semua panggilan ke fungsi kami dilemparkan ke dalamnya. Badan fungsi dan memuat dibungkus dalam makro.

Pengoperasian aplikasi yang mudah dan benar dan fakta bahwa bahkan sejumlah besar fungsi mudah dijelaskan, berkat makro. Sangat tidak nyaman bahwa menyapu agak tak terduga di x64. Visual Studio (di suatu tempat sejak 2012, jika saya ingat benar) melarang menggunakan sisipan telanjang dan asm dalam kode 64-bit. Saat menulis proksi dari awal, setiap fungsi perlu memverifikasi bahwa itu dijelaskan dalam file-def, bahwa dokumen asli dimuat, dan isi dari fungsi tersebut dijelaskan.

Opsi ketiga. Kami membuang tubuh secara umum


Menggunakan telanjang menyarankan satu opsi lagi. Anda dapat membuat tabel impor, yang untuk semua fungsi akan merujuk ke satu baris kode nyata:

void nop () {}

Perpustakaan seperti itu akan dimuat oleh aplikasi, tetapi tidak akan berfungsi. Saat memanggil salah satu fungsi, tumpukan kemungkinan besar akan robek atau kotoran lain akan terjadi. Tapi ini tidak selalu buruk - jika, misalnya, tujuan dari injeksi dll adalah hanya untuk menjalankan kode dengan hak yang diperlukan, maka itu cukup untuk mengeksekusi payload dari perpustakaan proxy DllMain dan diam-diam menghentikan aplikasi dengan segera. Dalam hal ini, itu tidak akan datang ke panggilan nyata ke fungsi, dan tidak akan ada kesalahan-crash.

Contoh pada github , lagi untuk version.dll.

Sorotan Kode:

  • Semua fungsi dari file def merujuk ke satu fungsi nop.

Mudahnya perpustakaan proxy semacam itu ditulis hanya untuk beberapa menit. Sangat tidak nyaman bahwa aplikasi yang disebut berhenti bekerja.

Opsi keempat. Ambil utilitas yang sudah jadi


Menulis dll itu bagus, tetapi tidak selalu nyaman dan tidak terlalu cepat, jadi Anda harus mempertimbangkan opsi otomatis.

Anda dapat mengikuti jalur virus lama - ambil perpustakaan yang prokinya ingin kami buat, buat bagian kode yang dapat dieksekusi di dalamnya, tulis muatannya di sana dan ubah titik masuk ke bagian ini. Bukan cara termudah, karena Anda dapat secara tidak sengaja memecahkan sesuatu, Anda harus menulis assembler, ingat perangkat file PE. Ini bukan cara kami.

Untuk mengoperasikan pembajakan dll, kami akan menambahkan pembajakan dll.



Ini relatif mudah dilakukan. Kami menyalin pustaka yang proxynya ingin kami buat dan menambahkan beberapa dll dengan fungsi sewenang-wenang ke tabel impor dari salinan ini. Sekarang unduhan akan berlangsung sepanjang rantai - di awal file yang dapat dieksekusi, dll proxy akan dimuat, yang akan memuat pustaka yang ditentukan itu sendiri.

“Hei, kamu mengganti memuat satu perpustakaan dengan yang lain. Apa gunanya Semua sama akan diperlukan untuk kode dll! " Semuanya benar, tetapi ada nalar. Sekarang akan ada lebih sedikit persyaratan untuk perpustakaan dengan muatan. Anda dapat menentukan nama apa pun, yang utama adalah hanya mengekspor satu fungsi, yang dapat memiliki prototipe apa pun. Masukkan nama utama perpustakaan dan fungsinya di tabel impor.

Perpustakaan dengan muatan bisa menjadi salah satu untuk semua kesempatan.

Anda dapat memodifikasi tabel impor dengan banyak editor PE, misalnya CFF explorer atau pe-bear. Bagi saya sendiri, saya menulis sebuah utilitas kecil dalam C # yang mengoreksi tabel tanpa gerakan yang tidak perlu. Sumber di github , binar di bagian Release .

Kesimpulan


Dalam artikel tersebut, saya mencoba mengungkap metode dasar untuk membuat dll proxy, yang saya gunakan sendiri. Tinggal memberi tahu bagaimana mempertahankan.

Tidak banyak rekomendasi universal:

  • Jangan menyimpan file yang dapat dieksekusi, terutama yang dijalankan dengan izin tinggi, dalam folder yang dapat ditulis oleh pengguna.
  • Lebih baik untuk pertama-tama menemukan dan memverifikasi keberadaan perpustakaan sebelum melakukan LoadLibrary.
  • Lihatlah metode perlindungan yang ada yang tersedia di OS. Sebagai contoh, di Windows 10, Anda dapat mengatur flag PreferSystem32 sehingga pencarian dll tidak dimulai dengan folder file yang dapat dieksekusi, tetapi dengan system32.

Terima kasih atas perhatian Anda, saya akan senang mendengar pertanyaan, saran, saran dan komentar.

UPD: Atas saran para komentator, saya mengingatkan Anda bahwa Anda harus memilih perpustakaan dengan cermat dan hati-hati. Jika perpustakaan termasuk dalam daftar Diketahui atau namanya mirip dengan MinWin (ApiSetSchema, api-ms-win-core-console-l1-1-0.dll - itu saja), maka kemungkinan besar tidak akan mungkin untuk mencegatnya karena fitur pemrosesan dll di OS.

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


All Articles