I/O Ring (_IORING_OBJECT) RegBuffers arbitrary R/W primitive¶
Bir
_IORING_OBJECT'inRegBufferspointer'ını (veRegBuffersCount'unu) sahte birIOP_MC_BUFFER_ENTRYstructure array'i ile overwrite etmek, bir kernel write'ı tam bir arbitrary read/write primitive'ine dönüştürür, çünkü registered I/O-ring buffer'larına index ile güvenilir ve asla tekrar probe edilmezler.
Mechanism¶
Windows 11'in I/O ring'i (NtCreateIoRing / CreateIoRing API'si) bir process'in buffer'ları bir kez önceden register etmesine ve sonra daha sonraki submission-queue entry'lerinde onlara index ile referans vermesine izin verir. Kernel object'i _IORING_OBJECT, RegBuffers (kernel tarafı buffer-descriptor array'ine giden pointer) ve RegBuffersCount'u tutar.
Note
Exploit edilebilir invariant probe-once, trust-forever'dır. BuildIoRingRegisterBuffers buffer'ları register ettiğinde, kernel user array'ini doğrular ve onu RegBuffers'ın işaret ettiği bir kernel pool allocation'a kopyalar. Daha sonraki her read/write submission'ında, kernel caller'ın buffer index'ini alır, RegBuffers[index]'e yürür ve orada saklı Address/Length'i tekrar probe etmeden kullanır — RegBuffers'tan erişilebilen her şeyin register zamanında zaten incelendiğini varsayar. Dolayısıyla bir attacker RegBuffers'ı sahte bir array'e işaret edecek şekilde swap edebilirse (ve index'i geçerli kılacak kadar yüksek RegBuffersCount ayarlayabilirse), kernel attacker'ın sahte IOP_MC_BUFFER_ENTRY descriptor'larına koyduğu arbitrary Address değerlerini okur veya yazar. RegBuffers field'ına yapılan bir kontrollü write, sınırsız R/W'ye amplifiye edilir.
Her descriptor bir IOP_MC_BUFFER_ENTRY'dir (Win11 22H2+, build 22610+) ve field'ları arasında Address (target), Length, Type (0xC02), Size (0x80), AccessMode (1) ve ReferenceCount (1) vardır. Sahte entry'ye karşı bir read I/O kernel verisini dışarı yazar; bir write I/O attacker verisini kernel'in içine okur — yani işlem yönü "read/write"a göre terstir.
Walkthrough¶
- Ring'i oluştur ve adresini leak et.
CreateIoRing(...), ardından object'in kernel adresini geri elde etmek içinNtQuerySystemInformation(SystemHandleInformation)üzerinden_IORING_OBJECT'i enumerate et. - Usermode'da sahte bir descriptor array'i oluştur. Bir page'lik
IOP_MC_BUFFER_ENTRYentry'si allocate et; herAddress'i bir kernel target'a,Length'i transfer size'a,Type=0xC02,Size=0x80,AccessMode=1,ReferenceCount=1olarak ayarla. - Pointer'ı hijack et. Bug'ın arbitrary/limited write'ını kullanarak
IoRing->RegBuffers'ı usermode array adresi ile overwrite et veIoRing->RegBuffersCount'u yükselt ki seçilen index aralıkta olsun. - I/O sür.
BuildIoRingReadFile/BuildIoRingWriteFileile sahte buffer'a index ile referans veren submission entry'lerini sıraya al, sonraNtSubmitIoRing(SubmitIoRing). Bir write-file op'u file'danAddress'e okur -> arbitrary kernel write; bir read-file op'u kernelAddress'ini file'a yazar -> arbitrary kernel read. Named pipe'lar her zamanki file backend'idir, böylece hiçbir şey disk'e dokunmaz.
Fake IOP_MC_BUFFER_ENTRY (conceptual)
typedef struct _IOP_MC_BUFFER_ENTRY {
ULONG Size; // 0x80
ULONG Type; // 0xC02
PVOID Address; // <-- arbitrary kernel target
SIZE_T Length; // transfer size
ULONG AccessMode; // 1
ULONG ReferenceCount;// 1
} IOP_MC_BUFFER_ENTRY;
// After RegBuffers -> &fakeArray and RegBuffersCount = N:
BuildIoRingWriteFile(ring, fileRef, /*bufferIndex=*/0, len, 0, 0, flags);
SubmitIoRing(ring, 1, 0, NULL); // file bytes land at fakeArray[0].Address
Warning
Sahte array ve adresleri yalnızca RegBuffers'ın arkasındaki varlıklarıyla doğrulanır; RegBuffersCount çok küçük bırakılırsa index check başarısız olur ve op reddedilir. Teknik _IORING_OBJECT kernel adresini gerektirir, dolayısıyla onu write'tan önce bir handle-table leak ile (NtQuerySystemInformation) eşle.
Tipik post-exploitation kullanımı: mevcut ve SYSTEM process'lerin _KPROCESS/_EPROCESS'ini oku, sonra SYSTEM _EX_FAST_REF token'ını mevcut process'in Token'ı üzerine arbitrary-write et, böylece SYSTEM elde et.
Detection¶
Davranışsal imza, buffer'ları register eden, sonra effective target adresleri kernel space'e düşen read/write submission'ları yapan bir I/O ring'dir. HVCI/kCFG bu data-only primitive'i durdurmaz. EDR, hemen ardından backing file'ı anonim bir named pipe olan I/O-ring submission'ları gelen NtQuerySystemInformation(SystemHandleInformation)'ı işaretleyebilir.
Mitigation¶
Microsoft, sonraki 22H2/23H2 servicing'inde I/O ring path'ini sertleştirdi (örn. RegBuffers handling'ini taşıyarak/doğrulayarak ve registered-buffer güvenini kısıtlayarak). Single-entry variant'ı, tüm-array swap'inin engellendiği yerde hayatta kalır. Defense-in-depth: çok sayıda I/O ring'in unprivileged oluşturulmasını kısıtla ve leak+submit pattern'ini izle.