Firmware Extraction using BusPirate and Emulation using QEMU

Posted by

Meddling with hardware and electronic devices seems like a very “hacker” thing to do. In that spirit, I felt it was important for me to understand how to extract firmware from a device’s memory chip and then emulate it for future vulnerability research.

Credits: Ryan Cornateanu and Robertino Benis for suggesting BusPirate (BP) and also helping me debug my firmware extraction setup. Novetta for publishing an easy to understand blog describing QEMU system mode emulation setup.

Hardware

Target Device

My target device has a Gigadevice GD25Q32CSIG flash memory chip. It supports Serial Peripheral Interface (SPI) communication which means that it contains separate pins for data and clock for synchronization.

BusPirate and SOP8 Cable

The Bus Pirate was designed for debugging, prototyping, and analysing “new or unknown chips”. Using a Bus Pirate, a developer can use a serial terminal to interface with a device, via such hardware protocols as SPII2C and 1-Wire.

https://en.wikipedia.org/wiki/Bus_Pirate

A SOP8 cable can be used to connect a 8-pin flash chip to the BP. This avoids the need to desolder the flash chip from the board.

Laptop and USB 2.0 Mini-B Cable

I’m using a laptop running Ubuntu 16.04 x86 and it is connected to BP via a USB 2.0 Mini-B cable.

Connections

Pinout Diagram

Flash Chip

The first step is to understand the meaning of relevant pins on the flash chip. It has the following pinout:

GD25Q32C Pinout

Chip Select (CS)

In a circuit board containing multiple integrated chips, it may be necessary to send data through the same input wires. In such cases, the CS pin can be used to select the chip that the data is intended for. For this particular memory chip, its CS pin must be pulled down to GND. If data is not intended for this chip, its CS pin must be pulled up to VCC. It must not be left floating (unconnected) after power on. This information is available in the datasheet.

Serial Out (SO), Serial In (SI) and Serial Clock (SCLK)

Data is fed into the chip through Serial In (SI) pin and sent out through Serial Out (SO) pin. This transmission is synchronized with the Serial Clock (SCLK). Data can be transmitted / received on the clock’s rising or falling edge.

VSS (GND) and VCC

The VSS pin supplies GND to the chip. This is usually 0V. The VCC pin must be supplied with HIGH voltage for the chip. Its maximum and minimum values must be gleaned from the chip’s datasheet and it must be ensured that VCC remains in that voltage range. If it crosses the maximum voltage rating, the chip may get fried. My chip has voltage range between -0.6V to 4.2V with an operating voltage of 3.3V. So, I’m setting my VCC at 3.3V.

Note: Do not give power to the target device through its power cable as this will increase the voltage provided to the flash chip.

Wiring

BusPirate Pinout
Flash ChipBusPirate
CS# Pin 1White cable (CS)
SO Pin 2Black cable (MISO)
VSS Pin 4Brown cable (GND)
SI Pin 5Grey cable (MOSI)
SCLK Pin 6Dark Pink cable (SCLK)
VCC Pin 8Red cable (+3.3V VCC)

Software

Flashrom

flashrom is a utility for identifying, reading, writing, verifying and erasing flash chips. It is designed to flash BIOS/EFI/coreboot/firmware/optionROM images on mainboards, network/graphics/storage controller cards, and various other programmer devices.

Flashrom website

I’ll be executing Flashrom on my Ubuntu machine.

Prerequisite Modules

The following commands install the required modules:

sudo apt-get update
sudo apt-get install -y libusb-1.0-0-dev pciutils libpci-dev git gcc make binwalk

Install Flashrom

cd /opt
sudo git clone https://github.com/flashrom/flashrom
cd flashrom
sudo make

Firmware Extraction

Probing the Flash Chip

The following Flashrom command is used to check if it supports the memory chip model:

sudo ./flashrom -p buspirate_spi:dev=/dev/ttyUSB0 -c "GD25Q32(B)"

The following command displays a list of all memory chip models supported by Flashrom:

./flashrom -L

If Flashrom is unable to detect or identify the chip, there could be five reasons:

  1. sudo might be required for Flashrom to detect the BP. Use sudo.
  2. The chip model argument may be incorrect. In my case, I’ve used -c "GD25Q32(B)" but if you don’t know which chip model to use, then skip that argument. Flashrom will automatically detect the chip for you.
  3. The connections are faulty. Verify the connections using a multimeter or oscilloscope.
  4. The memory chip isn’t getting enough power. In this case, use a multimeter to check voltage of VCC and VSS pins on the memory chip.
  5. Flashrom doesn’t support the memory chip model. In this case, use other methods to dump the firmware such as JTAG, if possible.

Firmware Dump

The following command dumps the firmware in the same directory as Flashrom:

sudo ./flashrom -p buspirate_spi:dev=/dev/ttyUSB0 -c "GD25Q32(B)" -r firmware.bin

Note: If you are running Flashrom from a VM (VirtualBox or VMware), the above command may hang leading to an unsuccessful read. I do not know how to resolve it.

Filesystem Extraction

Binwalk is a popular tool used for searching binary images for embedded files and executable code. The following command extracts embedded stuff from the firmware image:

binwalk -e firmware.bin

In the above output:

  • The first two columns display the offset of the corresponding component in the binary image in decimal and hexadecimal formats respectively.
  • U-Boot is an open source bootloader that supports MIPS-based processors among others. It is the job of the bootloader to load the OS into memory. In this case, U-Boot 1.1.3 is being used.
  • In this case, binwalk falsely identified LZMA compressed data. I extracted this section using the dd utility and tried to decompress it using unlzma but hit an error, "File format not recognized".
  • Squashfs is a compressed Linux-based filesystem especially used for embedded devices. It is the filesystem that we’re looking for and binwalk has already "unsquashed" it for us in the _firmware.bin.extracted/squashfs-root directory.

Emulation using QEMU

Quick Emulator (QEMU) is a free and open-source emulator. It can operate in two modes:

  • User mode emulation: used for emulating Linux binaries compiled for one CPU on another CPU.
  • System mode emulation: used for emulating an entire system – processor, disk, network interfaces, etc. This type of emulation creates a VM, essentially.

My priority is to use system emulation to determine the services that would be running on the target device at startup.

Firmware Binary Metadata

QEMU can emulate many architectures such as ARM, MIPS, PowerPC, etc. I’ll be using radare2 to determine the architecture and other information of the firmware binaries.

kan3k1@kaido:~/firmware/_firmware.bin.extracted/squashfs-root$ rabin2 -I ./bin/ls
arch     mips
baddr    0x400000
binsz    260852
bintype  elf
bits     32
canary   false
class    ELF32
crypto   false
endian   little
havecode true
intrp    /lib/ld-uClibc.so.0
laddr    0x0
lang     c
linenum  false
lsyms    false
machine  MIPS R3000
maxopsz  16
minopsz  1
nx       false
os       linux
pcalign  0
pic      false
relocs   false
relro    no
rpath    NONE
sanitiz  false
static   false
stripped true
subsys   linux
va       true 

From the above output, the following information can be gleaned about the firmware binaries:

  • MIPS architecture.
  • Little-endian format.
  • 32-bit.
  • Dynamically-linked

Considering the above information, QEMU’s qemu-system-mipsel is appropriate for emulation.

System Mode Emulation

Since the architecture of the firmware is MIPS Little-Endian, we can download the relevant Linux kernel and QEMU-specific disk image file using wget. We will also create a separate directory to store these files.

mkdir device_mips
cd device_mips

wget https://debian.people.org/~aurel32/qemu/mipsel/vmlinux-2.6.32-5-4kc-malta
wget https://debian.people.org/~aurel32/qemu/mipsel/debian_squeeze_mipsel_standard.qcow2

The following options of qemu-system-mipsel are relevant:

  • -M: MIPS board. I’m using Malta with 4Kc processor.
  • -kernel: The Linux kernel for Malta 4Kc processor. I’m using the pre-built kernel, vmlinux-2.6.32-5-4kc-malta.
  • -hda: Hard disk. I’m using the pre-built Debian Squeeze disk image, debian_squeeze_mipsel_standard.qcow2.
  • -nographic: No graphical interface.
  • -append: Kernel command line.
  • -redir: Port redirection.

The below command emulates Debian Squeeze on a MIPS Malta 4Kc processor with SSH on port 2222 and no graphical interface. It also mounts /dev/sda1 (hard disk) under root directory and disables kernel-mode ASLR.

qemu-system-mipsel -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mipsel_standard.qcow2 -nographic -append "root=/dev/sda1 nokaslr" -redir tcp:2222::22

The above command execution will lead to a prompt where root:root credentials can be used to login. Since our aim is to emulate squashfs-root filesystem, we will copy over its contents into the above running VM. Three actions need to be taken:

  • Enable SSH on the VM.
  • tar.gz compress the squashfs-root directory contents and scp it into the VM.
  • SSH into the VM and tar.gz decompress the above tar.gz file.
service ssh start; service ssh status

tar zcf squashfs-root.tar.gz squashfs-root/
scp -P 2222 ./squashfs-root.tar.gz root@127.0.0.1:/root

ssh -p 2222 root@127.0.0.1
tar zxf squashfs-root.tar.gz
cd squashfs-root 

To correctly run binaries on the VM, we have to chroot at the squashfs-root directory. This allows the binaries to find other files relative to the squashfs-root filesystem instead of the VM’s Debian filesystem.

root@debian-mipsel:~/squashfs-root# chroot . ./bin/sh
/ # ls
web      mnt      proc     bin      dev      usr
sys      var      etc      lib      sbin     linuxrc

Startup Processes and Network Connections

Startup scripts are usually located in /etc/init.d/ directory. In this case, since we just copied the squashfs filesystem into the VM, the startup scripts haven’t been executed. This theory is proved by the output of ps and netstat commands. As it can be seen below, there are no processes or network connections.

/ # ps
  PID USER       VSZ STAT COMMAND

/ # netstat -anp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
netstat: /proc/net/tcp: No such file or directory
netstat: /proc/net/tcp6: No such file or directory
netstat: /proc/net/udp: No such file or directory
netstat: /proc/net/udp6: No such file or directory
netstat: /proc/net/raw: No such file or directory
netstat: /proc/net/raw6: No such file or directory
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
netstat: /proc/net/unix: No such file or directory 

The rcS script in /etc/init.d/ directory contains commands that run on startup.

/etc/init.d # ./rcS
./rcS: line 30: can't create /proc/sys/net/netfilter/nf_conntrack_icmp_timeout: nonexistent directory
./rcS: line 35: can't create /proc/sys/net/netfilter/nf_conntrack_expect_max: nonexistent directory
./rcS: line 37: can't create /proc/sys/net/netfilter/nf_conntrack_max: nonexistent directory
insmod: can't insert '/lib/modules/kmdir/kernel/drivers/net/rt_rdm/rt_rdm.ko': invalid module format
insmod: can't insert '/lib/modules/kmdir/kernel/drivers/net/raeth/raeth.ko': invalid module format
insmod: can't insert '/lib/modules/kmdir/kernel/net/netfilter/nf_conntrack_proto_gre.ko': invalid module format
insmod: can't insert '/lib/modules/kmdir/kernel/net/netfilter/nf_conntrack_pptp.ko': invalid module format
insmod: can't insert '/lib/modules/kmdir/kernel/net/shortcut-fe/shortcut-fe.ko': invalid module format
insmod: can't insert '/lib/modules/kmdir/kernel/net/shortcut-fe/shortcut-fe-cm.ko': invalid module format
./rcS: line 56: can't create /sys/sfe_ipv4/max_connections: nonexistent directory
insmod: can't insert '/lib/modules/ipt_STAT.ko': invalid module format
insmod: can't insert '/lib/modules/tp_domain.ko': invalid module format
insmod: can't insert '/lib/modules/pppol2tp.ko': invalid module format
insmod: can't insert '/lib/modules/l2tp_ppp.ko': invalid module format
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
mii_mgr: ioctl error
./rcS: line 74: can't open '/dev/null' 

The "invalid module format" errors are due to .ko modules being built using a different kernel version than the version that we used in the QEMU VM setup. ioctl is used for executing driver-specific commands that cannot be handle by Linux system calls. In this case, it failed. The Medium Access Control (MAC) layer controls the hardware responsible for interaction with wired, optical or wireless transmission medium. The Media Independent Interface (MII) is a standard interface used to connect Fast Ethernet MAC to a PHY (Physical) chip that is required to implement physical layer functions of the OSI model.

After executing the rcS script, the following processes and network connections were active:

/etc/init.d # ps
  PID USER       VSZ STAT COMMAND
    1 admin     2500 S    init [2]
    2 admin        0 SW   [kthreadd]
    3 admin        0 SW   [ksoftirqd/0]
    4 admin        0 SW   [watchdog/0]
    5 admin        0 SW   [events/0]
    6 admin        0 SW   [cpuset]
    7 admin        0 SW   [khelper]
    8 admin        0 SW   [netns]
    9 admin        0 SW   [async/mgr]
   10 admin        0 SW   [pm]
   11 admin        0 SW   [sync_supers]
   12 admin        0 SW   [bdi-default]
   13 admin        0 SW   [kintegrityd/0]
   14 admin        0 SW   [kblockd/0]
   15 admin        0 SW   [ata/0]
   16 admin        0 SW   [ata_aux]
   17 admin        0 SW   [kseriod]
   18 admin        0 SW   [rpciod/0]
   19 admin        0 SW   [khungtaskd]
   20 admin        0 SW   [kswapd0]
   21 admin        0 SWN  [ksmd]
   22 admin        0 SW   [aio/0]
   23 admin        0 SW   [nfsiod]
   24 admin        0 SW   [crypto/0]
   29 admin        0 SW   [scsi_eh_0]
   30 admin        0 SW   [scsi_eh_1]
   34 admin        0 SW   [kjournald]
   82 admin     3036 S <  udevd --daemon
  157 admin     2972 S <  udevd --daemon
  159 admin     2972 S <  udevd --daemon
  173 admin        0 SW   [ksuspend_usbd]
  174 admin        0 SW   [khubd]
  177 admin        0 SW   [kpsmoused]
  426 1         2280 S    /sbin/portmap
  444 102       2556 S    /sbin/rpc.statd
  513 admin     2844 S    dhclient -v -pf /var/run/dhclient.eth0.pid -lf /var/
  613 admin    28556 S    /usr/sbin/rsyslogd -c4
  645 1         2976 S    /usr/sbin/atd
  664 admin     6448 S    /usr/sbin/cron
  820 admin     7468 S    /usr/sbin/sshd
  931 101       8884 S    /usr/sbin/exim4 -bd -q30m
  949 admin     2096 S    /sbin/getty 38400 tty1
  950 admin     2096 S    /sbin/getty 38400 tty2
  951 admin     2096 S    /sbin/getty 38400 tty3
  952 admin     2096 S    /sbin/getty 38400 tty4
  953 admin     2096 S    /sbin/getty 38400 tty5
  954 admin     2096 S    /sbin/getty 38400 tty6
  955 admin     2096 S    /sbin/getty -L ttyS0 9600 vt100
  956 admin    10788 S    {sshd} sshd: root@pts/0
  958 admin     8120 S    -bash
  978 admin     1020 S    ./bin/sh
 1046 admin        0 SW   [flush-8:0]
 1047 admin     1008 R    ps
/etc/init.d # 
/etc/init.d # 
/etc/init.d # 
/etc/init.d # 
/etc/init.d # 
/etc/init.d # netstat -anp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      426/portmap
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      820/sshd
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      931/exim4
tcp        0      0 0.0.0.0:46588           0.0.0.0:*               LISTEN      444/rpc.statd
tcp        0      0 10.0.2.15:22            10.0.2.2:48776          ESTABLISHED 956/0
tcp        0      0 :::22                   :::*                    LISTEN      820/sshd
tcp        0      0 ::1:25                  :::*                    LISTEN      931/exim4
udp        0      0 0.0.0.0:620             0.0.0.0:*                           444/rpc.statd
udp        0      0 0.0.0.0:111             0.0.0.0:*                           426/portmap
udp        0      0 0.0.0.0:58615           0.0.0.0:*                           444/rpc.statd
udp        0      0 0.0.0.0:68              0.0.0.0:*                           513/dhclient
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
unix  3      [ ]         DGRAM                      2393 613/rsyslogd        /dev/log
unix  2      [ ]         DGRAM                       682 82/udevd            @/org/kernel/udev/udevd
unix  2      [ ]         DGRAM                      2755 956/0               
unix  3      [ ]         DGRAM                       687 82/udevd            
unix  3      [ ]         DGRAM                       686 82/udevd 

The above information is useful to understand the processes and network connections that would be active on the embedded system on startup.

Thanks for reading!

In this article, I described my procedure to extract firmware from an embedded system using BusPirate and then emulate it using QEMU. This can be a starting point for embedded system firmware vulnerability research.

Thank you for reading! If you have any questions, leave them in the comments section below and I’ll get back to you as soon as I can!

References:

Images:

Leave a Reply

Your email address will not be published.