Skip to content

Attacking the Windows NVIDIA Driver (Project Zero)

Oliver Chang (Project Zero), NVIDIA'nın Windows kernel-mode display driver'ında (nvlddmkm.sys), DxgkDdiEscape interface'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:

  1. DxgkDdiEscapeD3DKMTEscape() ile çağrılabilen, vendor'a özgü bir ioctl tüneli (dxgkrnl.sys DDI'ının parçası). NVIDIA kabaca 400 farklı escape command code'u implemente eder; her biri nvlddmkm.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.
  2. Named device objeleri\\.\NvAdminDevice, \\.\UVMLiteController, \\.\UVMLiteProcess* ve \\.\NvStreamKmsDeviceIoControl ç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 DxgkDdiEscape handler'larını bounds-check et; user'dan verilen tüm pointer'larda ProbeForWrite/ProbeForRead kullan; /GS (stack cookie'leri) ve /GUARD:CF ile 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).

References