Skip to content

Notes on how to audit a maximized flashed firmware image #107

@tlaurion

Description

@tlaurion

This is raw notes. This will get edited multiple times prior of having a base to create a wiki page.

The quick way, no-brainer, is to reflash the same downloaded/compiled firmware and keeping settings, as already documented at:
https://osresearch.net/Updating#reflashing-the-same-firmware

Unless flashrom itself was compromised (meaning that the payload is tampered), reflashing the same firmware image using the menu options to flash new firmware while keeping settings will reflash IFD+ME+GBE+coreboot+payload+current CBFS files. Since coreboot measures its bootblock + ramstage+romstage+payload, and then Heads measures added CBFS content, TOTP/HOTP measurements should be the same, and TOTP/HOTP unsealing of secrets should match.

But that doesn't answer the introspection need of some users to actually validate that the firmware components are actually in the expected state. And those notes, even better, if translated into code, should answer those needs.


Taking an internal backup of the firmware to be inspected externally

From Heads Recovery console, one can easily obtain actual BOARD_NAME and FW_VER that includes the bits we want to differenciate properly the actual board and flashed Heads commit ID in case we want to go back.

From Heads recovery shell, with a USB thumb drive ready to receive backup, we mount usb drive in read+write and we take a backup with proper naming scheme:

mount-usb rw
flashrom -p internal -r /media/$CONFIG_BOARD-$(echo $FW_VER|awk -F ' ' '{print $2}'_backup.rom
umount /media

Extracting content from that firmware image for validation

Unfortunately, coreboot-utils are precompiled for a limited number of distributions today. I'll take for granted here that debian12 is installed under QubesOS (see unman's repo https://qubes.3isec.org/Templates_4.1/)
Then a quick sudo apt install coreboot-utils binwalk will make the tools available with/without sudo.

Each Heads board's build comes with a hashes.txt file. It is either dowloadable from CircleCI artifacts, or can be seen, and copy pasted from a build's output given on screen.
Following download instructions will lead you to the hashes step of any CircleCI build: https://osresearch.net/Downloading

Now.
After having mounted our USB thumb drive under a debian-12 based qube with tools installed, from command line, having passed the USB thumb drive to our newly booted qube:

sudo mount /dev/sda1 /media
mkdir -p /tmp/inspection
cp /media/x230-hotp-maximized-Heads-v0.2.0-1296-g139ecb8_backup.rom /tmp/inspection
cd /tmp/inspection

And then we work on our copied to memory image.

We can extract partitions not managed from coreboot actual versions measurements:

user@heads-backup-extraction:/tmp/inspection$ ifdtool -x x230-hotp-maximized-Heads-v0.2.0-1296-g139ecb8_backup.rom
File x230-hotp-maximized-Heads-v0.2.0-1296-g139ecb8_backup.rom is 12582912 bytes
  Flash Region 0 (Flash Descriptor): 00000000 - 00000fff 
  Flash Region 1 (BIOS): 0001b000 - 00bfffff 
  Flash Region 2 (Intel ME): 00003000 - 0001afff 
  Flash Region 3 (GbE): 00001000 - 00002fff 
  Flash Region 4 (Platform Data): 00fff000 - 00000fff (unused)
user@heads-backup-extraction:/tmp/inspection$ ls -al
total 24592
drwxr-xr-x  3 user user     4096 Nov 25 10:56 .
drwxrwxrwt 12 root root     4096 Nov 25 11:22 ..
-rw-r--r--  1 user user     4096 Nov 25 12:05 flashregion_0_flashdescriptor.bin
-rw-r--r--  1 user user 12472320 Nov 25 12:05 flashregion_1_bios.bin
-rw-r--r--  1 user user    98304 Nov 25 12:05 flashregion_2_intel_me.bin
-rw-r--r--  1 user user     8192 Nov 25 12:05 flashregion_3_gbe.bin
-rw-r--r--  1 user user 12582912 Nov 25 10:14 x230-hotp-maximized-Heads-v0.2.0-1296-g139ecb8_backup.rom

Here one could compare ME against what is downloaded and extracted from Heads scripts, GBE and IFD configs against what is stored under Heads tree.

But most questions are related to the firmware integrity itself, Heads itself:

user@heads-backup-extraction:/tmp/inspection$  cbfstool x230-hotp-maximized-Heads-v0.2.0-1296-g139ecb8_backup.rom print
FMAP REGION: COREBOOT
Name                           Offset     Type           Size   Comp
cbfs master header             0x0        cbfs header        32 none
fallback/romstage              0x80       (unknown)       85100 none
cpu_microcode_blob.bin         0x14d80    microcode       26624 none
fallback/ramstage              0x1b600    (unknown)       97676 none
config                         0x33400    raw               834 none
revision                       0x33780    raw               691 none
fallback/dsdt.aml              0x33a80    raw             14615 none
vbt.bin                        0x37400    raw              1433 LZMA (4281 decompressed)
cmos_layout.bin                0x37a00    cmos_layout      1884 none
fallback/postcar               0x381c0    (unknown)       25816 none
fallback/payload               0x3e700    simple elf    7320519 none
heads/initrd/.gnupg/pubring.kbx 0x739b00   raw             11549 none
heads/initrd/.gnupg/trustdb.gpg 0x73c880   raw              1320 none
heads/initrd/etc/config.user   0x73ce00   raw                22 none
(empty)                        0x73ce80   null          4337432 none
bootblock                      0xb5fdc0   bootblock       65536 none

Here again, a lot of stuff there.

Get build hashes for current rom

x230-hotp-maximized-Heads-v0.2.0-1296-g139ecb8_backup.rom is commit 139ecb8
We go over linuxboot/heads@139ecb8
We click grewn mark, follow the rabbit to the build for x230-hot-maximized at https://app.circleci.com/jobs/github/osresearch/heads/5448, click the "Output hashes" if this is an old build without artifacts, and copy the output of the hashes there to a local hashes.txt file we make sure to have available for our situation. We can directly save the output from CircleCI's Download Icon, which in our case leads to https://circleci.com/api/v1.1/project/github/osresearch/heads/5448/output/104/0?file=true&allocation-id=63751e76e6fb893f12bf3473-0-build%2F97AF006

From now on I consider we have that file saved into hashes.txt to be used locally.

Extract content from coreboot's payload

user@heads-backup-extraction:/tmp/inspection$ mkdir -p payload_content
user@heads-backup-extraction:/tmp/inspection$ cbfstool x230-hotp-maximized-Heads-v0.2.0-1296-g139ecb8_backup.rom extract -n fallback/payload -f payload_content/payload -m x86

We now have coreboot's payload.

user@heads-backup-extraction:/tmp/inspection/payload_content$ ls -al
total 7180
drwxr-xr-x 2 user user    4096 Nov 25 12:22 .
drwxr-xr-x 3 user user    4096 Nov 25 10:56 ..
-rw-r--r-- 1 user user   11525 Nov 25 10:58 hashes.txt
-rw-r--r-- 1 user user 7328543 Nov 25 12:14 payload

We extract the content with binwalk

user@heads-backup-extraction:/tmp/inspection/payload_content$ binwalk --extract payload 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ELF, 32-bit LSB executable, Intel 80386, version 1 (SYSV)
13013         0x32D5          xz compressed data
2072933       0x1FA165        MySQL MISAM compressed data file Version 8
2910340       0x2C6884        bix header, header size: 64 bytes, header CRC: 0xE802F8, created: 2106-02-07 01:21:09, image size: 186 bytes, Data Address: 0x1000000, Entry Point: 0x83BB9000, data CRC: 0x50F, compression type: none, image name: ""
2923392       0x2C9B80        xz compressed data
3035935       0x2E531F        xz compressed data

user@heads-backup-extraction:/tmp/inspection/payload_content$ ls -al
total 7184
drwxr-xr-x 3 user user    4096 Nov 25 12:22 .
drwxr-xr-x 3 user user    4096 Nov 25 10:56 ..
-rw-r--r-- 1 user user   11525 Nov 25 10:58 hashes.txt
-rw-r--r-- 1 user user 7328543 Nov 25 12:14 payload
drwxr-xr-x 2 user user    4096 Nov 25 12:22 _payload.extracted

user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ ls | while read file; do sha256sum $file; done | while read line; do hash=$(echo $line | awk -F " " '{print $1}'); file=$(echo $line | awk -F " " '{print $2}'); grep $hash ../hashes.txt && echo "we have match for $hash in $file"; done
b3387d6b04c6246198638a46a2fec5bb7623a99a97189a26c5e019524aef8992  build/x86/x230-hotp-maximized/initrd.cpio.xz
we have match for b3387d6b04c6246198638a46a2fec5bb7623a99a97189a26c5e019524aef8992 in 2E531F.xz

So we have a matching initrd.cpio.xz into 2E531F.xz
We could dig more into that, and will (after all, everything hashes.txt is detailing why we have a match for the payload).

hashes.txt can be used under recovery shell to verify hashes of the initrd binaries

The paths undr hashes.txt are relative. If we have hashes.txt under USB thumb drive, from Heads recovery shell:
cd /
mount-usb
sha256sum -c /media/hashes.txt

Will give OK reports for all files that were found, including libraries and binaries and scripts user by heads, as seen under

Check bzImage

Unfortunately, this requires CircleCI bzImage since mathing can only be made against decompressed binary.

After our binwalk --extract command:

user@heads-backup-extraction:/tmp/inspection/payload_content$ cd _payload.extracted/
user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ ls
2C9B80.xz  2E531F  2E531F.xz  32D5  32D5.xz
user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ wget -O extract-vmlinux https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-vmlinux
user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ chmod +x ./extract-vmlinux 
user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ ls -al
total 47316
drwxr-xr-x 2 user user     4096 Nov 25 12:34 .
drwxr-xr-x 3 user user     4096 Nov 25 12:22 ..
-rw-r--r-- 1 user user  4405151 Nov 25 12:22 2C9B80.xz
-rw-r--r-- 1 user user 12882944 Nov 25 12:22 2E531F
-rw-r--r-- 1 user user  4292608 Nov 25 12:22 2E531F.xz
-rw-r--r-- 1 user user 19528104 Nov 25 12:22 32D5
-rw-r--r-- 1 user user  7315530 Nov 25 12:22 32D5.xz
-rwxr-xr-x 1 user user     1695 Nov 25 12:34 extract-vmlinux

user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ ./extract-vmlinux 32D5 > vmlinux_local

Download bzImage from CircleCI artifact

user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ ./extract-vmlinux bzImage > vmlinux_remote
user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$ diff vmlinux_local vmlinux_remote
user@heads-backup-extraction:/tmp/inspection/payload_content/_payload.extracted$

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions