Skip to content

Win10 2004 EPROCESS offset changes for token payloads

Windows 10 2004 (20H1), _EPROCESS field offset'lerini yeniden düzenledi ve UniqueProcessId / ActiveProcessLinks / Token'ı hard-code eden her token-stealing payload'unu sessizce bozdu — bir güvenlik açığı değil, bir taşınabilirlik ve güvenilirlik dersi; ama hem saldırganların hem savunmacıların kernel-shellcode versiyonlama mantığını şekillendiren bir ders.

Mechanism

Note

Bir token-stealing payload, SYSTEM process'inin access token'ını mevcut process'e kopyalamak için kernel'in process list'ini gezer. Bunu _EPROCESS içindeki üç offset'i hard-code ederek yapar: UniqueProcessId (SYSTEM'i, PID 4'ü tanımak için), ActiveProcessLinks (tüm process'leri çift yönlü bağlı listeye dizen _LIST_ENTRY) ve Token (security token'a giden _EX_FAST_REF). Payload'un varsaydığı invariant, bu offset'lerin sabit olduğudur. Ama değiller: _EPROCESS, Microsoft'un build'ler arasında yeniden yerleşime soktuğu internal, dokümante edilmemiş bir structure'dır. Eski Windows 10 ile 2004/20H1 arasında, üç offset de ciddi şekilde kaydı. Yanlış build için derlenmiş bir payload, list'i gezerken yanlış field'ı dereference eder — "tek bir byte farkı bile kötü bir dereference'a yol açar" — ve escalate etmek yerine makineyi bug-check'e (BSOD) sokar.

Çıkarım yapısaldır: hard-code edilmiş offset'ler bir exploit/payload'u tam olarak tek bir kernel build'ine bağlar, dolayısıyla payload'lar ya build'i detect edip offset'leri seçmeli ya da runtime'da offset'leri dinamik olarak resolve etmelidir.

Walkthrough

Token-steal'in kavramsal akışı ve 20H1'in onu neden bozduğu (public keramas ve Connor McGarr write-up'larından — burada canlı offset'ler yeniden verilmiyor):

  1. current üzerine anchor at. Payload, mevcut _KTHREAD/_EPROCESS'i (klasik olarak _KPCR/gs base üzerinden) elde eder ve başlangıç _EPROCESS'ini alır.
  2. ActiveProcessLinks'i gez. _LIST_ENTRY'yi ileri doğru takip et, her node'da list-offset'i çıkararak _EPROCESS base'ini geri kazan.
  3. SYSTEM'i eşle. UniqueProcessId'yi 4 ile karşılaştır; SYSTEM, maksimum ayrıcalıklı bir token'ın kaynağıdır.
  4. Token'ı kopyala. SYSTEM'in Token fast-ref'ini oku ve mevcut process'in Token'ı üzerine yaz, sonra temizce return et. Tam primitive için eprocess-token-stealing, bunun bir code-exec/SMEP-bypass adımının üzerine nasıl bindiği için ise token-stealing-shellcode-smep-bypass'a bakın.
  5. 20H1 neyi değiştirdi. 2-4 adımlarındaki üç offset'in her biri taşındı. 1909'a ayarlanmış bir payload artık UniqueProcessId'yi yanlış slot'tan okur, asla PID 4 ile eşleşmez (ya da yanlışlıkla eşleşir) ve ActiveProcessLinks'i bir non-list field üzerinden gezer — anlık bir BSOD.

Warning

Yayımlanmış hiçbir offset üçlüsünü kalıcı/evrensel olarak görmeyin. Doğru mühendislik yanıtı (public kod'da da kullanılan), sabitleri içine gömmek yerine OS build'ini detect etmek (örn. NtBuildNumber / KUSER_SHARED_DATA) ve build-başına bir offset table'ını index'lemek, ya da offset'leri runtime'da sembollerden resolve etmektir.

Savunmacılar/araştırmacılar offset'leri nasıl yeniden hesaplar
  • Tam hedef build üzerinde WinDbg: dt nt!_EPROCESS ile UniqueProcessId, ActiveProcessLinks, Token'ı inceleyin.
  • Canlı bir kernel olmadan cross-version diffing için, Windows build başına parse edilmiş _EPROCESS layout'larını yayımlayan Vergilius Project.
  • Bu, bir savunmacının meşru bir Token'ın nerede değişmesi gerektiğini ve gerekmediğini bilen, build-aware bir detection yazmak için kullandığı aynı prosedürdür.

Detection

  • Davranışsal (build-agnostic): başarılı bir token steal'in etkisi, offset'ler kaysa bile invariant'tır — bir non-SYSTEM process aniden SYSTEM token'ı sunar (Token/_EX_FAST_REF'i artık SYSTEM'inkine eşittir). Bir process'in token'ını beklenen token'ıyla karşılaştıran EDR/kernel callback'leri (PsSetCreateProcessNotifyRoutine, object-callback veya periyodik integrity scan'ler), hangi build'in offset'leri kullanılmış olursa olsun sonucu yakalar.
  • Walk'ın telemetry'si: ActiveProcessLinks'i beklenmedik bir context'ten (non-image, RWX ya da yakın zamanda allocate edilmiş executable kernel memory) gezen kernel shellcode güçlü bir sinyaldir; CFG/HVCI/kCET violation event'leriyle eşleştirin.
  • Crash forensics: bir kernel write primitive'inin hemen ardından gelen bir bug-check dalgası (örn. attacker context'inde invalid pointer dereference) çoğu zaman, patch'lenmiş/ yükseltilmiş bir build'e karşı bayatlamış offset'ler kullanan bir payload'dur — crash-dump triyajında işe yarayan bir "failed exploit" göstergesi.

Mitigation

  • Patch'lenebilir bir CVE değil — bu bir offset drift'idir, dolayısıyla uygulanacak bir fix yoktur; bunun yerine ilgili kernel mitigation'ları, payload'un dayandığı tekniği sertleştirir:
  • HVCI / Hypervisor-Protected Code Integrity ve kCET/shadow stack'ler, hangi offset'leri kullanacağından bağımsız olarak, imzasız token-stealing shellcode'unu en baştan execute etmeyi zorlaştırır.
  • VBS / Credential Guard ve KASLR, process list'i ve SYSTEM _EPROCESS'ini güvenilir şekilde bulmanın maliyetini yükseltir.
  • Build'leri güncel tut: payload'lar build'e bağlı olduğundan, desteklenen, patch'li build'lerde kalmak; eski layout'lara ayarlanmış in-the-wild kit'lerin sessizce escalate etmek yerine yanlış ateşlemesi (ve gürültülü şekilde crash etmesi) anlamına gelir.

References