AFD.sys AfdNotifyRemoveIoCompletion write-where (CVE-2023-21768)¶
afd!AfdNotifyRemoveIoCompletion'da eksik birProbeForWrite, user-mode bir caller'ın driver'ın yazacağı bir kernel pointer'ı geçmesine izin verir; bu da I/O Ring primitive'i üzerinden tam read/write'a yükseltilen constrained bir arbitrary write verir.
Mechanism¶
Root cause: user-supplied bir pointer'a, user-mode olduğu doğrulanmadan yapılan kernel write
AFD.sys (WinSock için Ancillary Function Driver), AfdNotifyRemoveIoCompletion
tarafından servis edilen bir notification IOCTL'i expose eder. Handler,
0x18 offset'indeki field'ı bir pointer olan user-controlled bir input
structure alır; driver, bir I/O completion dequeue edildiğinde bu pointer'a
yazar — kaldırılan entry sayısını (efektif olarak sabit 1 değerini) o
adrese store eder.
Bir kernel driver'ın, user mode'dan gelen herhangi bir output pointer üzerinde
enforce etmesi gereken invariant şudur: dereference etmeden önce ProbeForWrite
ile (ya da PreviousMode kontrol ederek) doğrula; böylece bir caller, kernel'a
bir kernel adresine yazdıramaz. Vulnerable build, write'ı koşulsuz olarak
gerçekleştirip PreviousMode != 0 (user-mode caller) path'inde ProbeForWrite'ı
atlıyordu. Bu, bir untrusted-pointer dereference'tır (CWE-822): low-privileged
bir process bir kernel adresi sağlar ve kernel uysalca oraya yazar — fixed'a
yakın bir değerle yapılan bir write-what-where.
Walkthrough¶
Gabriel Landau'nun public root-cause analizi, patch'in standart
PreviousMode/ProbeForWrite gate'ini eklediğini gösteriyor. Kavramsal olarak,
patch'li mantık şöyle:
/* patched: validate the user pointer before writing to it */
if (PreviousMode == 0) { /* kernel-mode caller: trusted */
*out_ptr = entries_removed; /* field_0x18 */
} else { /* user-mode caller: must probe */
ProbeForWrite(out_ptr, sizeof(*out_ptr), 1);
*out_ptr = entries_removed;
}
Pre-patch binary'de else/ProbeForWrite branch'i yoktur, dolayısıyla write,
out_ptr nereyi gösterirse göstersin user-mode caller'lar için tetiklenir.
Write'ı sürmek ve I/O Ring ile yükseltmek
Write yalnızca bir completion gerçekten dequeue edildiğinde tetiklenir, dolayısıyla exploit şunları yapar:
- AFD socket'ine bir
IoCompletion(completion port) object'i associate eder veNtSetIoCompletionile bir record queue'lar; böylece internalKeRemoveQueueEx/remove'un başarılı olmasını ve vulnerable store'u tetiklemesini sağlar. 0x18field'ını seçilen bir kernel adresine point eder, böylece oraya1değeri yazdırılır. Bu constrained bir primitive'dir (küçük, fixed bir değer yazar), bu yüzden bir kernel_IORING_OBJECT'ine yöneltilir:- Önce object'in
RegBuffersCount'unu1yapar. - Sonra
RegBufferspointer'ını, forge edilmişnt!_IOP_MC_BUFFER_ENTRYdescriptor'larını tutan bir user-mode adrese set eder. - Attacker'ın istediği yere point eden bir registered buffer table ile,
dokümante edilmiş I/O Ring API'leri (
BuildIoRingReadFile/BuildIoRingWriteFile) arbitrary kernel read ve write gerçekleştirir.
Ardından arbitrary R/W, SYSTEM process (PID 4) token'ını locate etmek ve onu
attacker'ın process'ine swap etmek için kullanılır; bu da NT AUTHORITY\SYSTEM'e
yükseltir — Linux'taki commit_creds tarzı token replacement'a analog.
PreviousMode meselenin kalbi
Bug, field'ın writable olması değil; driver'ın bir kernel caller'ı
(PreviousMode == 0) bir user caller'dan hiç ayırt etmemesidir. Fix, eklenen
tek bir validation'dan ibaret; handler'ın geri kalan her şeyi değişmemiştir.
Etkilenen: Windows 11 21H2/22H2 ve Windows Server 2022; Ocak 2023 cumulative
update'inde düzeltildi.
Detection¶
- EDR / kernel telemetry: low-integrity ya da standard-user bir process'in
\Device\Afd'ye bir handle açıp, kernel-space bir adrese (>= 0x8000000000000000, x64'te) resolve olan bir output pointer ile notify IOCTL'i issue etmesi temel anomalidir. - I/O Ring abuse: AFD notify aktivitesini
NtCreateIoRing/NtSubmitIoRingkullanımıyla ve şüpheli user adreslerine point eden registered buffer'larla korele et — public PoC'ler (örneğin yaygın olarak paylaşılan GitHub release'leri) tam olarak bu sırayı izler, dolayısıyla AFD-IOCTL → IoRing zinciri üzerindeki behavioral signature'lar etkilidir. - Token swap: post-exploitation'da, primary token'ı artık SYSTEM (PID 4) token'ıyla eşleşen beklenmedik bir process, EDR'lerin yakaladığı privilege escalation göstergesidir.
- Crash signature: başarısız (misfired) denemeler,
afd.sysiçinde geçersiz bir kernel adresine yazarken birKMODE_EXCEPTION_NOT_HANDLED/ bugcheck üretir.
Mitigation¶
- Vendor patch:
AfdNotifyRemoveIoCompletion'aProbeForWritekontrolünü ekleyen Microsoft Ocak 2023 security update'ini kur (CVE-2023-21768 için MSRC advisory). Bu, otoriter (authoritative) fix'tir. - Hardening: HVCI / Kernel-mode Hardware-enforced Stack Protection'ı enable et ve KASLR'yi intact tut; böylece arbitrary-R/W → token-theft adımının maliyetini yükseltirsin; local logon'u kısıtla ve standart LPE-reduction policy uygula.
- Exploit-chain mitigation: destekleniyorsa, I/O Ring registered-buffer primitive'inin abuse'unu monitor/block etmek, bu constrained write'ı tam kernel R/W'ye dönüştüren post-bug capability'yi sınırlar.