Windows Object Manager and handle-table attack surface¶
Ob* Object Manager ve per-process handle table: kernel object'lerin
OBJECT_HEADER, reference count ve type descriptor üzerinden nasıl isimlendirilip erişildiği; handle-entry manipülasyonu, object type-confusion ve refcount imbalance'ın neden tekrar eden bir corruption hedefi olduğu ve defender'ın bunu nasıl tespit ettiği.
Mechanism¶
Handle-to-object indirection ve refcount invariant'ı neden kırılgandır
Windows'ta bir device, file, event, process ya da thread gibi her named
kernel entity bir executive object'tir ve her object'in önünde bir
OBJECT_HEADER bulunur. Bu header, object'in TypeIndex'ini (bir
OBJECT_TYPE descriptor'ına çözülür), güvenlik descriptor'ını ve iki ayrı
lifetime sayacını taşır: bir handle count ve daha geniş bir pointer
(reference) count. Pointer count, açık handle'ların yanı sıra
ObReferenceObject* ile kernel içinde alınmış her counted reference'ın
toplamıdır. Kritik invariant şudur: object ancak pointer count sıfıra
indiğinde free edilir; her increment tam olarak bir decrement ile
dengelenmelidir.
User-mode object'e asla doğrudan pointer ile değil, bir opaque handle
ile dokunur. Handle, per-process _HANDLE_TABLE'a bir index'tir; tablo tek
bir düz array değil, üç seviyeli bir hierarchy'dir ve tablonun adresi
_EPROCESS'in ObjectTable alanında tutulur. Her _HANDLE_TABLE_ENTRY
iki şey encode eder: object header'a giden bir encoded pointer ve o handle
için verilmiş granted access mask. Bir syscall handle verdiğinde kernel
entry'yi çözer, pointer'ı reconstruct eder ve granted access'i istenen
operasyona karşı doğrular.
Attack surface tam da bu iki noktadan doğar:
- Handle-table entry manipulation. Bir arbitrary kernel write primitive'i handle entry'yi hedef alırsa, ya encoded object pointer'ı başka bir object'e yönlendirebilir ya da granted access mask'i yükseltip read-only bir handle'ı full-access yapabilir — kernel bir daha ObpValidate etmeden ona güvenir.
- Type confusion. Handle-to-object çözümü object'in gerçek
TypeIndex'ine güvenir. Bir bug object'i A type'ı beklerken B type'ı gibi yorumlarsa (ya da free edilmiş bir slot'a başka type reallocate olursa), type-specific field'lar yanlış interpret edilir — klasik bir type-confusion. - Reference-count imbalance. Nadiren çalışan bir error path'te kaçırılan bir increment/decrement sayacı dengesizleştirir. Over-reference bir resource leak'tir; under-reference ise object'i hâlâ başka client'lar ona pointer tutarken erkenden free ettirir — doğrudan bir use-after-free. Windows 8.1 handle entry'lere per-handle inverted reference count (x64'te 0x7FFF'den geriye sayan) ekledi; bu, biased pointer count değerleri üretir ama temeldeki "her ref bir deref ister" invariant'ını değiştirmez.
Walkthrough¶
Aşağıdaki adımlar public object-manager internals yazılarından türetilmiş
kavramsal bir gözlemdir; struct offset'leri build'e özgüdür ve her hedefte
dt nt!_OBJECT_HEADER / dt nt!_HANDLE_TABLE_ENTRY ile doğrulanmalıdır.
1. Object header ve iki sayacı gör. WinDbg'de bir object'ten header'a geç ve lifetime sayaçlarını incele:
PointerCount'un HandleCount'tan çok büyük görünmesi bir bug değil — Windows
8.1'in inverted-refcount bias'ıdır (her handle pointer count'a 0x7FFF katkı yapar).
2. Handle'ı entry'ye çöz. Per-process tabloyu dolaş ve bir handle'ın hangi object'e ve hangi granted access ile bağlandığını gör:
Buradaki iki güven noktası — entry'nin encoded object pointer'ı ve
GrantedAccess — kernel-write ile tamper edilirse istismarın kalbini oluşturur.
3. Imbalance'ı gözlemle. Under-reference'ı laboratuvar ortamında görmek için
Microsoft'un tag-based tracing'i kullanılır; !obtrace per-tag reference vs
dereference dengesini raporlar:
Bir tag'de dereference'ların reference'ları aşması, object'in erken free edilebileceği anlamına gelir — free sonrası reallocate edilen slot üzerinde bir UAF/type-confusion penceresi açar. Bu, sömürünün mantıksal önkoşuludur; buradaki altitude'da weaponize edilmiş bir tetikleyici verilmez.
Neden under-reference bir UAF'a dönüşür (kavramsal)
Detection¶
- Reference-tracing /
!obtrace. Gflags ile object reference tracing'i aç;ObReferenceObjectByHandleWithTag/ObDereferenceObjectWithTagçağrılarına eşleştirilmiş tag'ler, bir tag'de reference vs dereference mismatch'i olarak under/over-reference'ı doğrudan gösterir. "Permanent" trace option'ı object free edildikten sonra bile trace'i saklar — under-reference avında değerli. - Cross-view handle consistency. Bir process'in
_HANDLE_TABLE'ını dolaş ve handle'ların işaret ettiği object type'larını bağımsız kaynakla karşılaştır; bir entry'ninTypeIndex'i ile hedef object header'ın type'ı uyuşmuyorsa bu bir type-confusion / entry-tamper sinyalidir. - GrantedAccess anomalileri. Beklenenden geniş granted access mask taşıyan handle'lar (örn. bir read handle'ının aniden full-access görünmesi) entry manipülasyonuna işaret eder; EDR/telemetry handle duplication ve access upgrade'lerini loglayabilir.
- Pool / object-header integrity. Memory-forensics (Volatility object scan'leri) pool tag'lerden object'leri oyup çıkarır ve list'lerden düşmüş ya da tutarsız refcount taşıyan object'leri yakalar.
- Crash telemetry. Object-manager bölgesinde tekrarlayan
BAD_POOL/KERNEL_MODE_HEAP_CORRUPTIONbugcheck'leri, refcount imbalance kaynaklı UAF'ın klasik gürültüsüdür.
Mitigation¶
- Type-safe reference API'leri. Driver'lar
ObReferenceObjectByHandle'ı beklenenOBJECT_TYPEargümanıyla çağırmalı ki type-confusion baştan reddedilsin; her reference'ı tam olarak bir dereference ile eşle (tag'li varyantlar denetimi kolaylaştırır). - Handle-table'ı bütünlüğüyle koru. Handle-entry ya da granted-access tampering bir mevcut arbitrary kernel write primitive gerektirir; VBS/HVCI, Kernel CFG, PatchGuard/HyperGuard ve Driver Signature Enforcement o primitive'e ulaşma çıtasını yükseltir.
- Least privilege on handles. Object'leri minimum gerekli access ile aç ve
OBJ_PROTECT_CLOSEgibi attribute'larla kritik handle'ları koru; gereksiz duplicate handle'lardan kaçın. - Fuzzing + statik analiz. Refcount imbalance'lar nadir error path'lerde yaşar; driver'ları Driver Verifier (özellikle reference-count doğrulama) ve syscall fuzzing ile sür, böylece kaçırılan deref'ler shipping'ten önce ortaya çıksın.