Summary

Nowadays, IoT - Internet of Things - is a term that is increasingly present in the world of technology. As the technology develops, the security risks also increase.
Firmware is software that’s embedded in a piece of hardware. You can think of firmware simply as “software for hardware.” However, it’s not an interchangeable term for software.
By getting access to a device’s firmware, we can disclose it’s filesystem. Tools like binwalk and dd can help us to do that extraction.
As a result, by having access to the filesystem, we can browser through the files and executables, search for vulnerabilities in the source code, reverse engineer the main components of the device’s software, etc.
There are many types of filesystems, like SQUASHFS, EXT and JFFS2, and they can be compressed using, for example, LZMA or CPIO.

Step 1 - Get the Firmware

First of all, you need to have access to a firmware. You can get one in the manufacter’s website, or by extracting it through a UART, by finding the “serial console” (VGRT) in the hardware board.
In this tutorial, we will be using the XIAOMI Mi Home Security 360° FHD 1080p security camera:

XIAOMI Mi Home Security 360° FHD 1080p

I found this post, which contains the firmware for the refered device.

I downloaded and unzipped the file.

$ unzip firmware.zip  
$ ls  
'guide to follow it.txt'   guiformat-x64.Exe   tf_recovery.img

Above, we can see that the compressed file came with 3 files: a text file, an executable and an image file.
What we want to look at is the image file, since the text file cannot contain a filesystem, and the executable is only a helper to recover the device firmware.

Step 2 - Gather information about the image file

First of all, let’s check what’s the file type, by running the file command:

$ file tf_recovery.img  
tf_recovery.img: u-boot legacy uImage, MVX2##I3g60b5603KL_LX318####[BR:\3757zXZ, Linux/ARM, OS Kernel Image (lzma), 1724412 bytes, Wed Jun  6 07:02:07 2018, Load Address: 0x20008000, Entry Point: 0x20008000, Header CRC: 0x5799CFC3, Data CRC: 0x2FF27A1D  

The returned response tells us it is a u-boot legacy uImage, containing a kernel image.
We can use a tool called binwalk, which is a “tool for searching binary images for embedded files and executable code”, as the manual page says.

$ binwalk tf_recovery.img

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
64            0x40            xz compressed data
2162688       0x210000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 6502290 bytes, 2019 inodes, blocksize: 131072 bytes, created: 2018-06-06 07:02:05  
9830400       0x960000        JFFS2 filesystem, little endian

As we can see above, binwalk revealed 3 files embedded in the image file. 2 of them are filesystems: a Squashfs and JFFS2.
Journalling Flash File System version 2 or JFFS2 is a log-structured file system for use with flash memory devices. JFFS2 has been included into the Linux kernel and is also available for a few bootloaders.
Squashfs is a compressed read-only file system for Linux, which compresses files, inodes and directories.

Step 3 - Extract the filesystems

Let’s extract the two filesystems. We can use binwalk or dd for that.

$ binwalk -Me tf_recovery.img
$ ls _tf_recovery.img.extracted
210000.squashfs  40  40.xz  960000.jffs2  jffs2-root  squashfs-root
$ ls jffs2-root/fs_1/ squashfs-root/
jffs2-root/fs_1/:
bin  data  etc  lib  sound

squashfs-root/:
bin  data  default.prop  dev  etc  lib  lib32  linuxrc  media  mnt  opt  proc  root  run  sbin  sys  tmp  ueventd.rc  usr  var

The extraction also decompressed the compressed filesystems (210000.squashfs and 960000.jffs2), which generated jffs2-root and squashfs-root folders.

If we wanted to extract, for example, only the jffs2 filesystem, we can use dd to carve out the jffs2 file, and a tool called jefferson to decompress the jffs2 file to a folder:

$ dd if=tf_recovery.img bs=1 skip=9830400 of=filesystem.jffs2
$ jefferson filesystem.jffs2 -d filesystem
$ ls filesystem/fs_1
bin  data  etc  lib  sound

With the dd command, we specified that we wanted a Block Size of 1 and we wanted to skip 9830400 bytes of content, since the JFFS2 file starts at that point, as seen in the previous binwalk command (binwalk tf_recovery.img). The output of the extraction should be called filesystem.jffs2

Step 4 - Explore

This is when the real fun begins, where you explore the system, reverse engineer the binaries that make the device work, etc.

Note that this is static analysis, not dynamic. You don’t have access to the running processes of the device. You’ll have to be creative and patient while exploring this type of filesystem.
If you want to explore a running filesystem from this (or another) device, you’ll have to use UART to get a shell, if you have access to an account (pray for root). There are a few tutorials on how to do that, from Flashback team and from 15/85 Security. Here are a few videos for that: Hacker’s Guide to UART Root Shells and Intro to Hardware Reversing: Finding a UART and getting a shell.

Now that we have access to the device filesystem, we can start exploring!

References

mitch002 “Mi Home Security Camera 360 Unbrick Method for MJSXJ05CM Model” [Jun. 9, 2020]
Pedro Ribeiro & Radek Domanski - Flashback Team “Hacker’s Guide to UART Root Shells” [Jan. 21, 2021]
Rbrei (Wikipedia) “SquashFS” [May 4, 2021]
Stefan Viehböck (sviehb) “Jefferson” [Oct. 15, 2020]
Tim Fisher “What Is Firmware?” [May 25, 2021]
Tony Gambacorta “Reversing Firmware - How does that work?” [Mar. 8, 2017]
Tony Sidaway (Wikipedia) “JFFS2” [May 15, 2021]