I/O Ring single-entry overwrite R/W primitive (Win11 22H2)¶
Tüm
RegBuffersarray'ini swap etmek yerine, içindeki tek bir pointer'ı overwrite et ki I/O ring'in bir slot'u usermode'da attacker kontrollü birIOP_MC_BUFFER_ENTRY'ye işaret etsin; bu, dar bir arbitrary-write'ı (veya increment'i) tam kernel read/write'a yükseltir.
Mechanism¶
Win11 22H2+'da RegBuffers, IOP_MC_BUFFER_ENTRY descriptor'larına giden bir pointer array'idir (pool tag IrRB). Kernel bir submission'ın buffer index'ini RegBuffers[index]'e çözer, sonra transfer için kullanılan Address/Length'e sahip descriptor'ı almak için o pointer'ı dereference eder.
Note
Tüm-array hijack'i RegBuffers pointer field'ının kendisini kontrol etmeni gerektirir ki bu çoğunlukla kısıtlı bir bug'ın erişiminin dışındadır (örn. yalnızca bir komşu allocation'a erişen bir heap overflow, ya da yalnızca bir pointer'ı flip edebilen bir write-where). Single-entry variant'ı bunu gevşetir: yalnızca pointer array'inin bir elemanını overwrite etmen yeterlidir ki RegBuffers[k] usermode'da oluşturduğun sahte bir IOP_MC_BUFFER_ENTRY'ye işaret etsin. Aynı probe-once-trust-forever invariant'ı geçerlidir — o pointer'ın arkasındaki descriptor olduğu gibi kullanılır, dolayısıyla Address'i kernel R/W target'ı olur. Bu minimal corruption'dır: tek bir pointer overwrite'ı (bir pointer'ın low byte'larına uygulanan bir arbitrary-increment primitive'inden bile elde edilebilir) IoRingReadWritePrimitive'i verir.
Yalnızca bir entry hijack edildiği için, ring'in geri kalanı geçerli kalır ve object kullanılabilir kalır, bu da tekrarlı read/write'lar için sistemi stable tutar.
Walkthrough¶
- Bir ring oluştur ve N buffer register et
BuildIoRingRegisterBuffersile, böyleceRegBuffersN pointer'lık bir kernel array'i olur veRegBuffersCount = N. - Usermode'a sahte bir descriptor yerleştir. Bilinen bir usermode adresinde bir
IOP_MC_BUFFER_ENTRY(Size=0x80,Type=0xC02,Address=<kernel target>,Length,AccessMode=1,ReferenceCount=1) oluştur. - Bir pointer'ı overwrite et. Bug'ı kullanarak usermode descriptor adresini tek bir slot
RegBuffers[k]'ye yaz. Yalnızca bir arbitrary-increment ile, slot istediğin yere işaret edene kadar tekrar tekrar increment et (ya da increment içine düşsün diye önceden sahte bir array spray'le). - Index k ile submit et.
BuildIoRingWriteFile(ring, pipeRef, k, len, ...)+SubmitIoRing-> file byte'larıfake.Address'e kopyalanır -> arbitrary kernel write.BuildIoRingReadFile(ring, pipeRef, k, len, ...)+SubmitIoRing-> kernelfake.Addressbyte'ları pipe'a yazılır -> arbitrary kernel read.- Privilege escalation (data-only). SYSTEM process token'ını oku, sonra onu mevcut process'in
_EPROCESS.Token'ı üzerine arbitrary-write et.
Single-entry overwrite shape
Warning
Sahte descriptor her submission süresince mapped ve geçerli kalmalıdır. RegBuffersCount'u değiştirme ki index k aralıkta kalsın. Bug bir write yerine bir increment ise, önceden contiguous bir usermode sahte array hazırla ki herhangi bir increment edilmiş pointer değeri yine geçerli bir descriptor'a düşsün.
Bu tam olarak CVE-2025-21333'ün kullandığı primitive'dir: bir RegBuffers array'inin yanına reclaim edilen bir paged-pool overflow'u, exploit'in tek bir entry pointer'ını bir usermode IOP_MC_BUFFER_ENTRY ile overwrite etmesine, ardından I/O ring üzerinden kernel R/W sürmesine izin verir.
Detection¶
Array hijack'i ile aynı data-only imza ama minimal corruption ile: bir RegBuffers slot'u artık usermode'a işaret ediyor. Overflow eden allocation üzerinde Driver Verifier / Special Pool, artı çözümlenen buffer adresi non-paged kernel memory olan I/O-ring submission'larını izlemek pratik gözlemlenebilirlerdir. kCFG/HVCI onu engellemez.
Mitigation¶
Patch seviyesi önemlidir: Microsoft 22H2/23H2 servicing'inde registered-buffer handling'ini sıkılaştırdı. Overflow/write erişimini tek bir pointer slot'una ulaşılamayacak şekilde azaltmak onu etkisiz kılar; aksi halde küçük ayak izi, tam bir RegBuffers swap'i imkansız olduğunda bunu tercih edilen primitive yapar.