Reversing DVR firmware dumped from a flash chip

Benjamin Tamasi
6 min readAug 9, 2016

Following up on my previous article about reversing DVR firmware from a firmware update file, we will look at a similar scenario. We want to reverse engineer the firmware running on a DVR, but this time we don’t have a firmware update file available. So what we do is take the thing apart, desolder the EEPROM flash chip and dump its contents with an EEPROM reader. At this point we have something quite similar to the firmware update file, with some key differences. The binary we have is an exact copy of what is running on the system bit for bit. While the firmware update binary usually has only a few cramfs filesystem images, we will be dealing with not only cramfs but also with jffs2 filesystem data, as we dumped both the read-only and r/w partitions.

Lets take a look at an example firmware dump from a DVR.

# file dump.bindump.bin: data

As we can see the unix file utility doesn’t really tell us much about this file. Its up to us find out what it is. Lets fire up my favourite tool: binwalk.

# binwalk rom.binDECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
183872 0x2CE40 CRC32 polynomial table, little endian
524288 0x80000 JPEG image data, EXIF standard
524300 0x8000C TIFF image data, big-endian, offset of first image directory: 8
536803 0x830E3 Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://
540563 0x83F93 Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
786432 0xC0000 uImage header, header size: 64 bytes, header CRC: 0xB08C7B92, created: 2013-08-21 03:11:07, image size: 2285288 bytes, Data Address: 0x80008000, Entry Point: 0x80008000, data CRC: 0xF0840126, OS: Linux, CPU: ARM, image type: OS Kernel Image, compression type: none, image name: "Linux-3.0.8"
808600 0xC5698 gzip compressed data, maximum compression, from Unix, NULL date (1970-01-01 00:00:00)
3145728 0x300000 JFFS2 filesystem, little endian
7328280 0x6FD218 Zlib compressed data, compressed
7331880 0x6FE028 Zlib compressed data, compressed
7332196 0x6FE164 JFFS2 filesystem, little endian
7332320 0x6FE1E0 Zlib compressed data, compressed
7332888 0x6FE418 Zlib compressed data, compressed
7333408 0x6FE620 Zlib compressed data, compressed
7333944 0x6FE838 Zlib compressed data, compressed
7334480 0x6FEA50 Zlib compressed data, compressed
7335096 0x6FECB8 JFFS2 filesystem, little endian
7471184 0x720050 Zlib compressed data, compressed
7472252 0x72047C Zlib compressed data, compressed
... trimmed for this article ...
9183752 0x8C2208 Zlib compressed data, compressed
9183944 0x8C22C8 JFFS2 filesystem, little endian
13436378 0xCD05DA Executable script, shebang: "/bin/sh"
13439016 0xCD1028 JFFS2 filesystem, little endian
13440708 0xCD16C4 Zlib compressed data, compressed
13442564 0xCD1E04 Zlib compressed data, compressed
13443624 0xCD2228 JFFS2 filesystem, little endian
16390040 0xFA1798 Zlib compressed data, compressed
16391172 0xFA1C04 JFFS2 filesystem, little endian

What binwalk does is go through the entire binary file and look for magic numbers which tell us what kind of files are inside this blob 🙂 See a list of these magic numbers here. (Also known as the file signature)

So from the output of binwalk we can see the types of files it found and their offset. With this information we can dissect the file from the binary, and do something with it.

Our first problem is we see a lot of files and offsets, but we don’t really know which one to start with. Our first priority on this device was to find out the telnet password. In linux these passwords are usually stored in /etc/passwd so we can use strings to do a search.

# strings -a -t x rom.bin | grep passwd3a8f8c passwd
3a9028 passwd-
506112 passwd
818213 hmkpasswd
8185c4 passwd
82116f Lchpasswd

On the left we have listed the offsets, so we can get an idea of where to look in the file. The first line looks promising, so lets check back on our binwalk output, to see what is around that area. (Remember these are HEX values, so use the second column of the output from binwalk)

So in our case, this file must be somewhere around this part:

3145728       0x300000        JFFS2 filesystem, little endian
7328280 0x6FD218 Zlib compressed data, compressed

That JFFS2 filesystem looks perfect. Let’s take this file out of the binary so we can work with it. We need to start from the offset, and go till the end of the file. Since we don’t know where it ends, we don’t worry about this. Most tools with ignore trailing garbage at the end of files.

# dd if=rom.bin of=system.jffs2 skip=3145728 bs=113631488+0 records in
13631488+0 records out
13631488 bytes (14 MB, 13 MiB) copied, 21,9533 s, 621 kB/s

bs=1 is telling dd to use a block size of 1. We can make this larger to speed up the process, but then we must divide the skip number with whatever number we use for our block size.

Now lets look at our newly created file called system.jffs2

# file system.jffs2system.jffs2: Linux jffs2 filesystem data little endian

Looks good, lets mount it 🙂 For mounting it, here is a handy script.

# mkdir /tmp/jffs2
# sudo ./script.sh system.jffs2 /tmp/jffs2
26624+0 records in
26624+0 records out
13631488 bytes (14 MB, 13 MiB) copied, 0,100803 s, 135 MB/s
Successfully mounted system.jffs2 on /tmp/jffs2

Let’s take a look around our mounted jffs2 image.

# cd /tmp/jffs2/
# ls -la
total 35
drwxr-xr-x 23 root root 0 jan 1 1970 ./
drwxrwxrwt 14 root root 32768 aug 9 15:19 ../
drwxrwxr-x 2 516 516 0 okt 8 2013 bin/
drwxr-xr-x 2 516 516 0 jún 5 2013 boot/
drwxr-xr-x 2 516 516 0 jún 5 2013 dev/
drwxr-xr-x 5 516 516 0 júl 3 2014 etc/
drwxr-xr-x 2 516 516 0 jún 5 2013 hitoe/
drwxr-xr-x 2 516 516 0 jún 5 2013 home/
lrwxrwxrwx 1 516 516 9 okt 8 2013 init -> sbin/init*
drwxr-xr-x 2 516 516 0 okt 8 2013 lib/
lrwxrwxrwx 1 516 516 11 okt 8 2013 linuxrc -> bin/busybox*
drwxr-xr-x 2 516 516 0 jún 5 2013 lost+found/
-rwxr-xr-x 1 516 516 1341 jún 5 2013 mkimg.rootfs*
-rwxr-xr-x 1 516 516 431 jún 5 2013 mknod_console*
drwxr-xr-x 12 516 516 0 dec 2 2014 mnt/
drwxr-xr-x 2 root root 0 jan 1 1970 nfsdir/
drwxr-xr-x 2 516 516 0 jún 5 2013 nfsroot/
drwxr-xr-x 2 516 516 0 jún 5 2013 opt/
drwxr-xr-x 2 516 516 0 jún 5 2013 proc/
drwxr-xr-x 2 516 516 0 jún 5 2013 root/
drwxrwxr-x 2 516 516 0 okt 8 2013 sbin/
drwxr-xr-x 2 516 516 0 jún 5 2013 share/
drwxr-xr-x 2 516 516 0 jún 5 2013 sys/
drwxr-xr-x 2 516 516 0 júl 3 2014 tmp/
drwxrwxr-x 6 516 516 0 jún 5 2013 usr/
drwxr-xr-x 4 516 516 0 jan 1 1970 var/

Looks like a linux system with busybox. let’s take at the file: /etc/passwd

# cat etc/passwdroot:hldf85ln3UUCA:0:0::/root:/bin/sh

The passwd file contains users and their password hashes. By default passwords are encrypted with Crypt under embedded linux systems. We see a root user here, with its password hash. This still needs to be decrypted for us to see the password. If you haven’t already, install john with sudo apt install john. Now we can tell john to crack this password hash.

# john etc/passwdLoaded 1 password hash (descrypt, traditional crypt(3) [DES 128/128 SSE2-16])
Press 'q' or Ctrl-C to abort, almost any other key for status
Warning: MaxLen = 13 is too large for the current hash type, reduced to 8

John starts brute-forcing the password hash to reveal the password. This can take anywhere from a few seconds to a few months depending on the speed of the computer used, and the complexity of the password. Press any key to view the status of john. John has pretty good documentation, and you can see a step by step guide here, including a way to download a larger wordlist for dictionary based cracking. When john is done, use john --show etc/passwd to view the cracked password.

Using this method, you can take apart the firmware running on system, and analise its internal structure and workings. You will come across many types of filesystems and archives, jffs2, cramfs, cpio, gz, zip, etc. They each work differently, but they can all be extracted.

Happy reversing!

Originally published on my old blog on August 9, 2016.

--

--