Attacking the Windows NVIDIA Driver (Project Zero)¶
Oliver Chang (Project Zero), NVIDIA'nın Windows kernel-mode display driver'ında (
nvlddmkm.sys),DxgkDdiEscapeinterface'ini ve user mode'dan erişilebilen birkaç açık device objesini fuzz'layarak 16 güvenlik açığı buldu.
Mechanism¶
Note
Windows graphics driver'ları, user-mode caller'lara iki geniş attack surface açar:
DxgkDdiEscape—D3DKMTEscape()ile çağrılabilen, vendor'a özgü bir ioctl tüneli (dxgkrnl.sysDDI'ının parçası). NVIDIA kabaca 400 farklı escape command code'u implemente eder; her birinvlddmkm.sys'teki kernel-mode kod tarafından parse edilir. Dispatch table büyüktür, büyük ölçüde dokümante edilmemiştir ve tarihsel olarak OS'in kendisi tarafından test edilmemiştir.- Named device objeleri —
\\.\NvAdminDevice,\\.\UVMLiteController,\\.\UVMLiteProcess*ve\\.\NvStreamKms—DeviceIoControlçağrılarını doğrudan kabul ederler ve yine kernel koduna yönlendirirler.
Tekrar tekrar kırılan invariant: bu handler'lar caller'dan verilen buffer'lara ve pointer'lara bounds-check ya da pointer validation olmadan yazar veya kopyalar; bu da write-what-where koşulları, stack overflow'lar ve kernel memory disclosure'a yol açar.
Walkthrough¶
Attack surface'e ulaşmak
// Via D3DKMT (user mode, no special privileges)
D3DKMT_ESCAPE escape = {0};
escape.hAdapter = adapter_handle;
escape.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
escape.pPrivateDriverData = attacker_buf;
escape.PrivateDriverDataSize = attacker_buf_size;
D3DKMTEscape(&escape); // routes to nvlddmkm DxgkDdiEscape
// Via raw device (NvStreamKms example)
HANDLE h = CreateFileW(L"\\\\.\\NvStreamKms", GENERIC_READ|GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
DeviceIoControl(h, NVSTREAM_IOCTL_CODE, in_buf, in_len, out_buf, out_len, &ret, NULL);
Bug class 1 – NvStreamKms'te stack buffer overflow (process-creation callback)
nvlddmkm.sys, gelen image path'ini yanlış bir length parametresiyle wcscpy_s kullanarak kopyalayan bir process-creation callback'i register eder. Özel olarak craft edilmiş bir UNC path, sabit stack buffer'ını aşar. Driver stack cookie'leri (/GS) olmadan derlendiği için, taşan path'e eklenen bir ROP chain doğrudan saved return address'i kontrol eder.
[stack frame]
[local wchar_t buf[MAX_PATH]] <- wcscpy_s writes here
[return address] <- overwritten with ROP gadget
Bug class 2 – UVMLiteController'da write-what-where
Belirli UVMLiteController IOCTL code'ları için output handler'lar, ProbeForWrite çağırmadan ya da output-buffer length'ini kontrol etmeden Irp->UserBuffer'a sabit bir değer (örn. 0xffff, 0x1f, 0) yazar. Caller, lpOutBuffer argümanı üzerinden Irp->UserBuffer'ı kontrol eder ve bu sınırlı keyfi kernel write'lara olanak tanır.
Bug class 3 – Kernel memory disclosure
Birkaç DxgkDdiEscape handler'ı, uninitialized stack ya da heap bölgelerini caller'dan verilen output buffer'a kopyalar ve KASLR'ı kıran kernel pointer'larını leak eder.
Fuzzing yaklaşımı
Chang, ~400 escape code'unun her birini hedefleyen özel bir fuzzer inşa etti. Input generation, pPrivateDriverData blob'unu mutate etti; crash'ler kernel debugger ya da live-kernel monitoring ile yakalandı. Yaklaşım, görece kısa bir kampanyada driver yüzeyinde 16 bug verdi ve vendor DDI handler'larının düz black-box fuzzing'inin son derece etkili olduğunu gösterdi.
Zaman çizelgesi
| Tarih | Olay |
|---|---|
| Temmuz 2016 | NVIDIA'ya ilk disclosure |
| Ekim 2016 | 16 bug'ın 14'ü patch'lendi |
| Aralık 2016 | Ek patch yayınlandı |
| Şubat 2017 | Kalan iki bug çözüldü; blog yayınlandı |
Detection¶
- Graphics dışı process'lerden ya da low-integrity session'lardaki process'lerden gelen
D3DKMTEscapeçağrılarını izle. - Beklenmedik caller'lardan
\\.\NvAdminDevice/\\.\UVMLiteController/\\.\NvStreamKmsüzerinde kernel ETW tracing. - Driver Verifier (
/driver nvlddmkm.sys), test sırasında birçok OOB erişimi yakalar.
Mitigation¶
- Vendor: tüm
DxgkDdiEscapehandler'larını bounds-check et; user'dan verilen tüm pointer'lardaProbeForWrite/ProbeForReadkullan;/GS(stack cookie'leri) ve/GUARD:CFile derle. - OS düzeyi: write erişiminin normal user-mode graphics stack'leri tarafından gerekmediği yerlerde named device erişimini
NT AUTHORITY\SYSTEM'a ya da belirli privileged SID'lere sınırla. - Sandbox: low-integrity renderer/GPU-process sandbox'larının named device objelerini doğrudan açamayacağından emin ol (Windows AppContainer policy).