FIRMADYNE is an automated and scalable system for performing emulation and
dynamic analysis of Linux-based embedded firmware. It includes the following
components:
modified kernels (MIPS: v2.6,
ARM: v4.1,
v3.10) for instrumentation of
firmware execution;
a userspace NVRAM library to emulate
a hardware NVRAM peripheral;
an extractor to extract a
filesystem and kernel from downloaded firmware;
a small console application to spawn
an additional shell for debugging;
and a scraper to download firmware from
42+ different vendors.
We have also written the following three basic automated analyses
using the FIRMADYNE system.
Accessible Webpages: This script iterates through each file
within the filesystem of a firmware image that appears to be served by a
webserver, and aggregates the results based on whether they appear to required
authentication.
SNMP Information: This script dumps the contents of the
public and private SNMP v2c communities to disk using no credentials.
Vulnerability Check: This script tests for the presence
of 60 known vulnerabilities using exploits from Metasploit. In addition, it
also checks for 14 previously-unknown vulnerabilities that we discovered.
For more information, including affected products and CVE's, refer to
analyses/README.md.
In our 2016 Network and Distributed System Security Symposium (NDSS)
paper, titled Towards Automated Dynamic Analysis for Linux-based Embedded Firmware, we evaluated the FIRMADYNE
system over a dataset of 23,035 firmware images, of which we were able to
extract 9,486. Using 60 exploits from the Metasploit Framework,
and 14 previously-unknown vulnerabilities that we discovered, we showed that
846 out of 1,971 (43%) firmware images were vulnerable to at least one exploit,
which we estimate to affect 89+ different products. For more details, refer to
our paper linked above.
Note: This project is a research tool, and is currently not production ready.
In particular, some components are quite immature and rough. We suggest
running the system within a virtual machine. No support is offered, but pull
requests are greatly appreciated, whether for documentation, tests, or code!
Setup
First, clone this repository recursively and install its dependencies.
Note that emulation of x86-based firmware is not currently supported, but installing
qemu-system-x86 resolves a packaging issue on certain Debian-based distributions.
Alternatively, use our modified version
of qemu-linaro for certain
firmware with an alphafs webserver that assumes a fixed memory mapping (not
recommended), or upstream qemu.
Usage
Set FIRMWARE_DIR in firmadyne.config to point to the root of this repository.
Use the extractor to recover only the filesystem, no kernel (-nk), no parallel operation (-np), populating the image table in the SQL server at 127.0.0.1 (-sql) with the Netgear brand (-b), and storing the tarball in images.
Identify the architecture of firmware 1 and store the result in the image table of the database.
./scripts/getArch.sh ./images/1.tar.gz
Load the contents of the filesystem for firmware 1 into the database, populating the object and object_to_image tables.
./scripts/tar2db.py -i 1 -f ./images/1.tar.gz
Create the QEMU disk image for firmware 1.
sudo ./scripts/makeImage.sh 1
Infer the network configuration for firmware 1. Kernel messages are logged to ./scratch/1/qemu.initial.serial.log.
./scripts/inferNetwork.sh 1
Emulate firmware 1 with the inferred network configuration. This will modify the configuration of the host system by creating a TAP device and adding a route.
./scratch/1/run.sh
The system should be available over the network, and is ready for analysis. Kernel messages are mirrored to ./scratch/1/qemu.final.serial.log. The filesystem for firmware 1 can be mounted to and unmounted from scratch/1/image with ./scripts/mount.sh 1 and ./scripts/umount.sh 1.
The default console should be automatically connected to the terminal. You may also login with root and password. Note that Ctrl-c is sent to the guest; use the QEMU monitor command Ctrl-a + x to terminate emulation.
FAQ
run.sh is not generated
This is a common error that is encountered when the network configuration is unable to be inferred. Follow the checklist below to figure out the cause.
inferNetwork.sh: Did this script find any network interfaces (e.g. Interfaces: [br0, 192.168.0.1])? If so, this is a bug; please report it. Otherwise, continue below.
qemu.initial.serial.log: Does this file end with Unable to mount root fs on unknown-block(8,1)? If so, the initial filesystem image was not generated correctly using kpartx. Try deleting the scratch directory corresponding to this firmware image, and restart at makeImage.sh. Otherwise, the initial emulation didn't produce any useful instrumentation. Try increasing the timeout in inferNetwork.sh from 60 to 120 and restarting at inferNetwork.sh.
qemu.initial.serial.log: Did the init process crash, and is this preceded by a failed NVRAM operation (e.g. nvram_get_buf: Unable to open key <foo>)? If so, see the FAQ entries below.
Log ends with "Kernel panic - not syncing: No working init found"
The firmware uses an initialization process with an unusual name. You'll need to manually inspect the filesystem to identify the correct one, then modify the script to specify its full path by appending a kernel boot parameter init=<path> to QEMU.
A process crashed, e.g. do_page_fault() #2: sending SIGSEGV for invalid read access from 00000000
It is likely that the process requested a NVRAM entry that FIRMADYNE does not have a default value for. This can be fixed by manually adding a source for NVRAM entries to NVRAM_DEFAULTS_PATH, an entry to NVRAM_DEFAULTS, or a file to OVERRIDE_POINT in libnvram. For more details, see the documentation for libnvram. Note that the first two options involve modifying config.h, which will require recompilation of libnvram.
How do I debug the emulated firmware?
With full-system QEMU emulation, compile a statically-linked gdbserver for the target architecture, copy it into the filesystem, attach it to the process of interest, and connect remotely using gdb-multiarch. You'll need a cross-compile toolchain; either use the crossbuild-essential-* packages supplied by Debian/Ubuntu, build it from scratch using e.g. buildroot, or look for GPL sources and/or pre-compiled binaries online. If you have IDA Pro, you can use IDA's pre-compiled debug servers (located in the dbgsrv subdirectory of the install), though they are not GDB-compatible.
With full-system QEMU emulation, pass the -s -S parameters to QEMU and connect to the stub using target remote localhost:1234 from gdb-multiarch. However, the debugger won't automatically know where kernel and userspace is in memory, so you may need to manually do add-symbol-file in gdb and break around try_to_run_init_process() in the kernel.
With user-mode QEMU emulation, chroot into the firmware image (optional), set LD_LIBRARY_PATH to contain the FIRMADYNE libnvram, and pass both the -L parameter with the correct path to the firmware /lib directory, and the binary of interest to QEMU. This is easiest to debug, because you can attach directly to the process using gdb-multiarch, and interact directly with the process, but the system state may not be accurate since the host kernel is being used. It is also somewhat insecure, because the emulated firmware can access the host filesystem and interact with the host kernel.
Compiling from Source
If you would like to compile the entire FIRMADYNE system from scratch
without using our pre-built binaries, please follow the steps below.
In order to build any of the binaries used by FIRMADYNE, you will need three
cross-compilation toolchains for the following architecture triples. Use only
musl libc as the C runtime library for the
toolchain; others have not been tested.
arm-linux-musleabi
mipseb-linux-musl
mipsel-linux-musl
To simplify the process of building cross-compilation toolchains with musl, we
recommend using the musl-cross project.
Follow the below steps to build these toolchains from source, or alternatively
click here to
download our pre-built toolchains.
Modify or set the following variables in config.sh
CFLAGS="-fPIC"
For little-endian MIPS, perform the following:
set TRIPLE=mipsel-linux-musl in config.sh
set LINUX_HEADERS_URL=https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.39.4.tar.xz in defs.sh
run ./clean.sh to clean out any previous builds
run ./build.sh to build and install the toolchain into /opt/cross
For big-endian MIPS, perform the following:
set TRIPLE=mipseb-linux-musl in config.sh
set LINUX_HEADERS_URL=https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.39.4.tar.xz in defs.sh
run ./clean.sh to clean out any previous builds
run ./build.sh to build and install the toolchain into /opt/cross
For little-endian ARM, perform the following:
set TRIPLE=arm-linux-musleabi, GCC_BOOTSTRAP_CONFFLAGS="--with-arch=armv6 --with-float=softfp", and GCC_CONFFLAGS="--with-arch=armv6 --with-float=softfp" in config.sh
set LINUX_HEADERS_URL=https://kernel.org/pub/linux/kernel/v4.x/linux-4.1.17.tar.xz in defs.sh
run ./clean.sh to clean out any previous builds
run ./build.sh to build and install the toolchain into /opt/cross
You should have the following directories, or wherever you installed the toolchains:
请发表评论