Bagaimana PROCESS_DUP_HANDLE berubah menjadi PROCESS_ALL_ACCESS

Ada komentar menarik dalam artikel Keamanan Proses dan Hak Akses MSDN:


... jika proses A memiliki pegangan untuk memproses B dengan akses PROCESS_DUP_HANDLE , itu dapat menduplikasi pegangan semu untuk proses B. Ini membuat pegangan yang memiliki akses maksimum ke proses B.

Jika Anda secara bebas menerjemahkan ini ke dalam bahasa Rusia, dikatakan di sini bahwa memiliki deskriptor untuk proses dengan akses PROCESS_DUP_HANDLE kanan, kita bisa, menggunakan fungsi DuplicateHandle (...) , dapatkan deskriptor dengan masker akses maksimum yang diizinkan untuk proses ini.



Demonstrasi


Kode sumber yang mengeksploitasi fitur ini cukup sederhana:


#include <Windows.h> int wmain(int argc, PWSTR argv[]) { HANDLE ProcessAllAccessHandle; HANDLE ProcessDuplicateHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, _wtoi(argv[1])); if (ProcessDuplicateHandle) { if (DuplicateHandle(ProcessDuplicateHandle, GetCurrentProcess(), GetCurrentProcess(), &ProcessAllAccessHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { CloseHandle(ProcessAllAccessHandle); } CloseHandle(ProcessDuplicateHandle); } return 0; } 

Sebagai hasil kompilasi dan penautan, kami mendapatkan utilitas pengujian yang menggunakan pengidentifikasi proses target (PID) sebagai argumen. Kemudian utilitas membuka proses yang ditentukan dengan PROCESS_DUP_HANDLE tepat. Jadi, kami mensimulasikan kondisi yang diperlukan untuk ketersediaan deskriptor untuk proses dengan PROCESS_DUP_HANDLE yang tepat (== 0x40).


Sebagai demonstrasi, saya akan melacak utilitas yang dikumpulkan di WinDbg:


 0:000> lsa @$ip 0,3 > 13: if (ProcessDuplicateHandle) 14: { 15: if (DuplicateHandle(ProcessDuplicateHandle, 0:000> !handle @@C++(ProcessDuplicateHandle) 3 Handle 80 Type Process Attributes 0 GrantedAccess 0x40: None DupHandle HandleCount 9 PointerCount 260518 

Dan kemudian mengibaskan pergelangan tangan Dengan memanggil DuplicateHandle (...) kita mendapatkan deskriptor kedua untuk proses yang sama, tetapi dengan izin terluas:


 0:000> lsa @$ip 0,3 > 23: CloseHandle(ProcessAllAccessHandle); 24: } 25: CloseHandle(ProcessDuplicateHandle); 0:000> !handle @@C++(ProcessAllAccessHandle) 3 Handle 84 Type Process Attributes 0 GrantedAccess 0x1fffff: Delete,ReadControl,WriteDac,WriteOwner,Synch Terminate,CreateThread,,VMOp,VMRead,VMWrite,DupHandle,CreateProcess,SetQuota,SetInfo,QueryInfo,SetPort HandleCount 10 PointerCount 292877 

Poin kuncinya adalah nilai GrantedAccess, yang untuk deskriptor baru adalah 0x1fffff, yang sesuai dengan PROCESS_ALL_ACCESS . Sayangnya, WinDbg tidak menampilkan PID dari proses target. Tetapi untuk memastikan bahwa deskriptor diterima untuk proses yang diinginkan, Anda dapat melihat deskriptor oleh Process Explorer (setelah menentukan PID yang ditentukan dalam argumen baris perintah di debugger):


 0:000> dx argv[1] argv[1] : 0x1b7c2e2412c : "21652" [Type: wchar_t *] 


Di tangkapan layar, utilitas membuka deskriptor untuk menjalankan notepad.exe.


Mengapa ini terjadi?


Pertama, karena ketika menduplikasi deskriptor, jika masker akses untuk objek tidak diperluas (dan flag operasi DUPLICATE_SAME_ACCESS ditentukan khusus untuk kami), tidak ada verifikasi bahwa proses (di mana deskriptor yang digandakan akan dibuat) memiliki akses ke objek ini. Hanya diperiksa bahwa deskriptor proses yang diteruskan ke fungsi DuplicateHandle (...) memiliki masker akses yang diizinkan PROCESS_DUP_HANDLE . Dan kemudian menyalin deskriptor antara proses terjadi tanpa memeriksa hak akses (saya ulangi: jika deskriptor baru memiliki mask hak yang diperbolehkan tidak lebih luas dari deskriptor asli yang digandakan).


Dan kemudian harus dicatat bahwa panggilan ke GetCurrentProcess () mengembalikan konstanta, yang pseudo-descriptor (pseudo handle) yang sama disebutkan di awal publikasi ini. Ada dua pseudo-deskriptor yang didokumentasikan dengan nilai konstan yang secara fisik hilang dari tabel deskriptor proses. Tetapi deskriptor ini diproses oleh semua fungsi kernel (bersama dengan deskriptor yang biasa dari tabel deskriptor proses):


MakroNilaiDeskripsi
ZwCurrentProcess / NtCurrentProcess(MENANGANI) -1Penjelasan Proses
ZwCurrentThread / NtCurrentThread(MENANGANI) -2Penjelasan Utas

Ini adalah nilai NtCurrentProcess (== -1) yang mengembalikan panggilan GetCurrentProcess () .


Pseudo-deskriptor ini dalam kerangka proses tertentu berarti objek dari proses ini dengan hak PROCESS_ALL_ACCESS (sebenarnya, ada nuansa, tetapi artikel ini bukan tentang mereka). Ternyata tautan seperti itu untuk dirinya sendiri, tetapi melalui deskriptor:


Artinya, panggilan kita ke DuplicateHandle (ProcessDuplicateHandle, GetCurrentProcess (), ...) akan ditafsirkan sebagai berikut: dari proses open (target), duplikat pegangan dengan nilai -1. Dan untuk proses target (yang deskriptornya kita simpan di variabel ProcessDuplicateHandle), nilai -1 akan merujuk ke proses target yang sama dengan hak PROCESS_ALL_ACCESS . Karena itu, sebagai hasilnya, kami mendapatkan deskriptor untuk proses target dengan hak maksimum.


Alih-alih sebuah epilog


Saya ulangi pemikiran yang ditulis di awal: jika seseorang menerima deskriptor untuk proses dengan PROCESS_DUP_HANDLE dengan benar, maka di bawah model keamanan Windows ia akan bisa mendapatkan deskriptor lain untuk proses yang sama, tetapi dengan hak PROCESS_ALL_ACCESS (dan melakukan apa pun yang ia sukai dengan proses tersebut) )


Terima kasih kepada semua orang yang membaca publikasi sampai akhir. Saya mengundang semua orang untuk mengikuti survei untuk mencari tahu bagaimana publikasi seperti itu dapat menarik / bermanfaat bagi audiens.

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


All Articles