Enterprise IoT Pentesting: Firmware Extraction and Analysis

Matt Brown
Matt Brown

Cover Image for Enterprise IoT Pentesting: Firmware Extraction and Analysis

In the first segment in our Enterprise IoT Pentesting series, we obtained an unrestricted root shell over UART on our Uniview SC-3243 device.

Now we want to leverage that shell to extract the device firmware for analysis.

Enumerating Device Partitions

First, we will run the mount command to see what partitions are clearly mounted:

root@root:~$ mount
rootfs on / type rootfs (rw)
devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
proc on /proc type proc (rw,nosuid,nodev,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,relatime)
tmpfs on /tmp type tmpfs (rw,relatime,size=90672k)
tmpfs on /var type tmpfs (rw,relatime)
devtmpfs on /dev type devtmpfs (rw,relatime,size=52076k,nr_inodes=13019,mode=755)
tmpfs on /root type tmpfs (rw,relatime,size=90672k)
ubi0:program on /program type ubifs (rw,relatime,assert=read-only,ubi=0,vol=0)
ubi1:config on /config type ubifs (rw,sync,relatime,assert=read-only,ubi=1,vol=0)
ubi2:cfgbak on /cfgbak type ubifs (rw,sync,relatime,assert=read-only,ubi=2,vol=0)
ubi3:calibration on /calibration type ubifs (ro,sync,relatime,assert=read-only,ubi=3,vol=0)
tmpfs on /etc type tmpfs (rw,relatime)
pstore on /sys/fs/pstore type pstore (rw,relatime)

The program, config, cfgbak and calibration ubifs partitions can be seen mounted.

Next, by reading the /proc/mtd file, we can see all of the device partitions on flash storage:

root@root:~$ cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 08000000 00020000 "spi4.0"
mtd1: 00100000 00020000 "spl"
mtd2: 00080000 00020000 "ddrinit"
mtd3: 00200000 00020000 "uboot"
mtd4: 00080000 00020000 "env"
mtd5: 00600000 00020000 "calibration"
mtd6: 00100000 00020000 "cliinfo"
mtd7: 00800000 00020000 "config"
mtd8: 00100000 00020000 "runtime"
mtd9: 00600000 00020000 "cfgbak"
mtd10: 00700000 00020000 "kernel"
mtd11: 00080000 00020000 "update"
mtd12: 05e00000 00020000 "program"
mtd13: 00080000 00020000 "other"

The block device for each of these partitions is accessible via /dev/mtdblockX:

root@root:~$ ls -l /dev/mtdblock*
brw-rw----    1 root     root       31,   0 Dec 31  1969 /dev/mtdblock0
brw-rw----    1 root     root       31,   1 Dec 31  1969 /dev/mtdblock1
brw-rw----    1 root     root       31,  10 Dec 31  1969 /dev/mtdblock10
brw-rw----    1 root     root       31,  11 Dec 31  1969 /dev/mtdblock11
brw-rw----    1 root     root       31,  12 Dec 31  1969 /dev/mtdblock12
brw-rw----    1 root     root       31,  13 Dec 31  1969 /dev/mtdblock13
brw-rw----    1 root     root       31,   2 Dec 31  1969 /dev/mtdblock2
brw-rw----    1 root     root       31,   3 Dec 31  1969 /dev/mtdblock3
brw-rw----    1 root     root       31,   4 Dec 31  1969 /dev/mtdblock4
brw-rw----    1 root     root       31,   5 Dec 31  1969 /dev/mtdblock5
brw-rw----    1 root     root       31,   6 Dec 31  1969 /dev/mtdblock6
brw-rw----    1 root     root       31,   7 Dec 31  1969 /dev/mtdblock7
brw-rw----    1 root     root       31,   8 Dec 31  1969 /dev/mtdblock8
brw-rw----    1 root     root       31,   9 Dec 31  1969 /dev/mtdblock9

SD Card Firmware Exfil

Now that we know where the firmware is we need to get it off the device. Luckily, this camera has an sdcard slot!

img1
Uniview SC-3243 SD card slot

After inserting a fresh SD card in the device, it automatically mounts it to /mnt/sdcard:

root@root:~$ df -h | grep sdcard
df: /dev/pts: No such file or directory
/dev/mmcblk0p1           29.1G     64.0K     29.1G   0% /mnt/sdcard

We are going to copy the firmware in two different ways.

First, we will recursively copy all files and directories to the SD card:

cp -rP /program/ /mnt/sdcard/
cp -rP /tmp /mnt/sdcard/
cp -rP /var /mnt/sdcard/
cp -rP /config/ /mnt/sdcard/
cp -rP /cfgbak/ /mnt/sdcard/
cp -rP /calibration/ /mnt/sdcard/
cp -rP /etc/ /mnt/sdcard/
cp -rP /lib /mnt/sdcard/
cp -rP /bin /mnt/sdcard/
cp -rP /sbin /mnt/sdcard/
cp -rP /usr /mnt/sdcard/

Second, we will use dd to make raw copies of all 14 flash partitions:

dd if=/dev/mtdblock0 of=/mnt/sdcard/mtdblock0.bin bs=4M
dd if=/dev/mtdblock1 of=/mnt/sdcard/mtdblock1.bin bs=4M
dd if=/dev/mtdblock2 of=/mnt/sdcard/mtdblock2.bin bs=4M
dd if=/dev/mtdblock3 of=/mnt/sdcard/mtdblock3.bin bs=4M
dd if=/dev/mtdblock4 of=/mnt/sdcard/mtdblock4.bin bs=4M
dd if=/dev/mtdblock5 of=/mnt/sdcard/mtdblock5.bin bs=4M
dd if=/dev/mtdblock6 of=/mnt/sdcard/mtdblock6.bin bs=4M
dd if=/dev/mtdblock7 of=/mnt/sdcard/mtdblock7.bin bs=4M
dd if=/dev/mtdblock8 of=/mnt/sdcard/mtdblock8.bin bs=4M
dd if=/dev/mtdblock9 of=/mnt/sdcard/mtdblock9.bin bs=4M
dd if=/dev/mtdblock10 of=/mnt/sdcard/mtdblock10.bin bs=4M
dd if=/dev/mtdblock11 of=/mnt/sdcard/mtdblock11.bin bs=4M
dd if=/dev/mtdblock12 of=/mnt/sdcard/mtdblock12.bin bs=4M
dd if=/dev/mtdblock13 of=/mnt/sdcard/mtdblock13.bin bs=4M

We then use a USB SD card adapter to transfer the files to our Linux computer for analysis:

img2
USB SD card adapter

Firmware Analysis

Now that we have all the firmware files transfered to our Linux computer, we can start to look for sensitive credentials, keys, config files, and other notable artifacts.

First we find that the device's passwd file is located in the config partition:

[nmatt@arch-dtop fw]$ cat config/KeyInfo/passwd 
root:$6$Bd3NQn7hZqAn8UM2$CoBkMSnmoo5pWQxj7GobbCukzAEFWlBKYR0mZ/rr/Rkuqfza/GKxxMkApIOamAsEdPe6vAmpA2YMcjcx8VuU0/:0:0:Linux User,,,:/root:/usr/bin/uvsh

Next we recursively search for any signs of private keys:

[nmatt@arch-dtop fw]$ grep -r "PRIVATE KEY"
grep: mtdblock12.bin: binary file matches
config/SignedCert.key:-----BEGIN RSA PRIVATE KEY-----
config/SignedCert.key:-----END RSA PRIVATE KEY-----
grep: mtdblock0.bin: binary file matches
grep: mtdblock7.bin: binary file matches
grep: program/bin/wpa_supplicant: binary file matches
program/bin/ssl_cert_nobrand.pem:-----BEGIN RSA PRIVATE KEY-----
program/bin/ssl_cert_nobrand.pem:-----END RSA PRIVATE KEY-----
program/bin/ssl_cert.pem:-----BEGIN RSA PRIVATE KEY-----
program/bin/ssl_cert.pem:-----END RSA PRIVATE KEY-----
grep: program/bin/update_move: binary file matches
grep: program/lib/libwolfssl.so.35.5.1: binary file matches

This turned out one interesting lead: config/SignedCert.key. However, we will need to do more reverse engineering to understand what this keypair is used for.

An interesting bit of analysis we can perform is to determine if the config partition containing the key and password hash above is encrypted at rest. Recall from above that our reading of /proc/mtd showed that the config partition corresponds to mtd7 or the block device /dev/mtdblock7.

We can use the following binwalk command to generate an entropy graph that can help confirm or deny that the config partition is encrypted:

binwalk -E mtdblock7.bin

This produces the following entropy graph.

img3
Entropy graph of mtdblock7

That is definitely not encrypted. Entropy graphs for encrypted (or compressed) data look like a straight line high up on the Y axis.

Let's see if we can find our password hash in the strings of the raw config partition dump.

[nmatt@arch-dtop fw]$ strings mtdblock7.bin | grep root:\\$
root:$6$iGl0.gIgbzWIeEq3$2vADLE2GSQbRpoFGsqFymtyvK5ixFr0S9cnS1ISCS9J1PPuqvudZSc6L36Ocqeq3oFsdRP8eHfHc8pnh1.J4p1:0:0:Linux User,,,:/root:/usr/bin/uvsh
root:$6$Bd3NQn7hZqAn8UM2$CoBkMSnmoo5pWQxj7GobbCukzAEFWlBKYR0mZ/rr/Rkuqfza/GKxxMkApIOamAsEdPe6vAmpA2YMcjcx8VuU0/:0:0:Linux User,,,:/root:/usr/bin/uvsh

We were able to find our password hash and another! (which I believe is the hash of the default password "123456")

Have a Connected Device to Secure?

Check out Brown Fine Security's IoT Penetration Testing services!