Penetration testing

Fundamentals of IoT firmware reverse engineering

Pedro Tavares
March 15, 2022 by
Pedro Tavares

The Internet of Things (IoT) is a trendy topic these days, and the number of critical vulnerabilities found in these devices is huge. As these devices are often connected to internal networks, a successful exploration involves a tremendous risk to organizations, with criminals accessing internal networks as the best practices and cybersecurity guidelines are not in place. 

Firmware is the name of the software embedded in IoT devices. This piece of code provides low-level control over device-specific hardware components. Although encryption has been used in the last years to protect devices' firmware image files, the principles to understand and find weaknesses are the same used in the past.

First, we need to download the firmware files from the official repositories. When this is impossible, understand how the update process is done and impersonate legitimate access to the files. A real and common example is the usage of a customized user-agent by the devices when they need to update the firmware to the latest version. By intercepting the requests, we can get them easily.

The next steps do not have a magic execution order. Depending on the firmware, image protection mechanisms decrypting the file may be necessary or just unzipping and emulating it through user or full emulation. In the next section, we will understand how to do it in a short way.

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

Extracting firmware with binwalk

In this laboratory, we will use the model RT-AC5300 from Asus. To see the files embedded in the firmware file, we need to use the binwalk tool. Using the option "-e," all the files can be extracted from the .trx file. [CLICK IMAGES TO ENLARGE]

Source: GitBook - Segurança Informática

As presented in the screenshot, the firmware uses little-endian to represent the hardware bytes. According to the binwalk signatures, the device executes on a 3X ARM version or minor (we will confirm this later).

After running binwalk, we can navigate through the router file system and analyze the source-code files. This is a perfect scenario where a simple iteration with binwalk brought the entire www file system.

From this point, we can examine every file statically or simply use a source-code static analyzer to find entry points of potential vulnerabilities.

However, the challenge doesn't end here. Many times, running the file system partially (user-emulation) or in a full mode allows us to identify strange behaviors or even interact with the system in run-time without paying a lot of money by the devices. It's time to know two different types of emulation.

Usermode emulation

In the first place, it's important to get extended information about the file system we want to emulate. This step is crucial to create the exact environment with the same vars and components used on the real device.

By analyzing the "1C" file; extracted when the binwalk tool was executed; we can observe interesting information, namely:

  • Some important paths present (/dev and /root); and
  • Linux kernel version 2.6.36

The strings utility is a good friend in these scenarios to retrieve more information about the file system. Using the following command lines, we can get details about the "/root/initrd" file.

cat 1C | strings | grep "version"

Linux version 2.6.36.4brcmarm (yau@yau.chang) (gcc version 4.5.3 (Buildroot 2012.02) ) #1 SMP PREEMPT Fri Mar 4 20:12:49 CST 2016

2.6.36.4brcmarm SMP preempt mod_unload modversions ARMv7 

slabinfo - version: 2.1

squashfs: version 4.0 (2009/01/31) Phillip Lougher

 

cat 1C | strings | grep "/etc/" 

/etc/init

 

cat 1C | strings | grep "/root/"

/root/initrd

The initrd is the file used to boot the file system, and we can use it later to boot the system with the BuildRoot tool.

In sum, we got the following information from the file:

  • Kernel version: 2.6.36.4
  • gcc version 4.5.3
  • Buildroot 2012.02 (used to compile the rootFS)
  • ARMv7 processor - Dual Core 32-bit ARMv7 (Cortex-A9) @ 1.4GHz
  • FS ini present on /etc and /root folders

Another good way to check the devices' configuration is by accessing the vendor page or getting the device datasheet from the internet.

To emulate the HTTP server available on the firmware file system, we need to use the qemu-user-static approach. So, by moving the static binary "qemu-arm-static" inside the rootfs folder, we can execute a chroot and emulate the binary in user-land mode. For more details about this technique, see this article.

which qemu-arm-static

/usr/bin/qemu-arm-static

 

cp /usr/bin/qemu-arm-static squashfs-root/

cd squashfs-root/

 

sudo chroot . ./qemu-arm-static /bin/sh

At this point, the command "httpd" can be executed in order to run the www server. Nonetheless, some errors can be presented, for instance, related to the NVRAM. This is a typical problem we face during the emulation of the firmware. Details on how to emulate an NVRAM can be found in this section.

After setting up the NVRAM environment, we need to export the path into the "LD_PRELOAD" env var. Running the httpd command again, the file system will execute and interact with it.

sudo chroot . ./qemu-arm-static /bin/sh

export LD_PRELOAD="/firmadyne/libnvram.so"

httpd

As observed, the next step is the creation of a certificate into the /etc/cert.pem folder. From this point on, there are no secrets. We need to track all the errors we get to launch the router's landing page.

However, the qemu-static approach is useful when launching a single program, not the entire file system. Httpd will launch all the router software, in which case a full emulation would be more beneficial and probably less arduous to implement.

Full emulation

When the strings command was executed, we got an interesting string: Buildroot 2012.02. This represents the version used to compile the filesystem with the kernel version 2.6.36. In short, buildroot is a simple, efficient, and easy-to-use tool to generate embedded Linux systems through cross-compilation. We can select every component and all the characteristics of the new target system and compile a new kernel from scratch. But first, we need to know the details of the target system, and that's what we did in the previous section.

After getting the right version of Buildroot from this repository, the command "make menuconfig" must be prompted, and the most appropriate options chosen.

More details and extended steps can be seen in this article.

Finally, the kernel can be compiled by using the make command. Some errors will happen, so we need to fix them and learn many things. In the end, an old kernel from scratch will be created.

A structure something like this will be present on our output/images folder:

-rw-r--r-- 1 kali kali 62914560 Jul 27 18:30 rootfs.ext2

-rw-r--r-- 1 kali kali 1437696 Jul 27 18:30 rootfs.squashfs

-rw-r--r-- 1 kali kali 4085760 Jul 27 18:30 rootfs.tar

-rwxr-xr-x 1 kali kali 446 Jul 27 18:30 start-qemu.sh

-rwxr-xr-x 1 kali kali 8880 Jul 27 18:27 versatile-pb.dtb

-rw-r--r-- 1 kali kali 2871872 Jul 27 18:27 zImage

The full emulation can be achieved by using the following command:

qemu-system-arm -M versatilepb -kernel zImage -dtb versatile-pb.dtb -drive file=rootfs.ext2,if=scsi,format=raw -append "rootwait root=/dev/sda console=ttyAMA0,115200"

From this point, we can interact with the landing pages of the router and debug it with burp-suite to find web vulnerabilities that are often called or introduced by components or software available inside the file system extracted from the initial firmware image.

What should you learn next?

What should you learn next?

From SOC Analyst to Secure Coder to Security Manager — our team of experts has 12 free training plans to help you hit your goals. Get your free copy now.

Final thoughts on reverse engineering

The emulation of firmware is not a trivial task as there is no magic formula to do it. Each firmware file is unique, and the process can be hard depending on the complexity and protection of the files. For instance, executing firmware under an old kernel version can be a tremendous task, mainly to fix all the errors that we can get during the process. On the other hand, the usage of encryption adds an extra layer of complexity. The trick consists of analyzing old firmware versions of the target device to understand how the encryption process can work or get the unpacked firmware from an actual device.

In sum, emulating firmware brings many benefits, including the test of the software without buying the real device. Let's analyze firmware seriously.

 

Sources:

Pedro Tavares
Pedro Tavares

Pedro Tavares is a professional in the field of information security working as an Ethical Hacker, Malware Analyst and a Security Evangelist. He is also Editor-in-Chief of the security computer blog seguranca-informatica.pt.

In recent years, he has invested in the field of information security, exploring and analyzing a wide range of topics, such as malware, reverse engineering, pentesting (Kali Linux), hacking/red teaming, mobile, cryptography, IoT, and security in computer networks. He is also a Freelance Writer.