4

I'm trying to extract an IP camera firmware. I am searching for a sleeping service that could be a major threat. According to nmap there are no running services such as telnet, SSH, FTP or similar; there are only few open ports such as HTTP, RSTP and unknown. The firmware is jvs3516cs-7601.bin from Jovision (my ipcam is a clone) and it comes from the German branch of the company. I've tried all the usual steps for the analysis but have not had much success. I've installed all the usual firmware analysis tools (lzmao, squash, cpio, cramfs) and tried to extract the firmware with, gzip, unzip, 7z and binwalk with no luck.

# binwalk -Bv jvs3516cs-7601.bin

Scan Time:     2018-06-13 17:52:52
Target File:   /root/jvs3516cs-7601.bin
MD5 Checksum:  8903156ca04081c393e16d6dff1580a0
Signatures:    344

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------

# binwalk -EJ jvs3516cs-7601.bin

DECIMAL       HEXADECIMAL     ENTROPY
--------------------------------------------------------------------------------
0             0x0             Falling entropy edge (0.316584)
270336        0x42000         Rising entropy edge (0.993798)
1904640       0x1D1000        Falling entropy edge (0.724715)
2031616       0x1F0000        Rising entropy edge (0.993043)
7753728       0x765000        Falling entropy edge (0.362255)

Entropy Exception: 'float' object cannot be interpreted as an integer




# xxd -a jvs3516cs-7601.bin | head
00000000: 5c1b 44f5 50ef dbfa 50ef dbfa 50ef dbfa  \.D.P...P...P...
00000010: 50ef dbfa 50ef dbfa 50ef dbfa 50ef dbfa  P...P...P...P...
00000020: 040c c49f e40c c49f 440b c49f 240b c49f  ........D...$...
00000030: 840b c49f 640a c49f c40a c49f 3c49 700d  ....d.......<Ip.
00000040: 441f 473f 441f 440e 441f 441f b91f 441f  D.G?D.D.D.D...D.
00000050: 401f 473f 2a2f 2c1f 441f 441f b91f 441f  @.G?*/,.D.D...D.
00000060: 4c1f 473f 441f 440d 441f 441f b91f 441f  L.G?D.D.D.D...D.
00000070: 481f 473f 273f 381f 441f 441f b91f 441f  H.G?'?8.D.D...D.
00000080: 541f 473f 441f 440e 441f 441f b91f 441f  T.G?D.D.D.D...D.
00000090: 501f 473f 2a2f 2c1f 441f 441f b91f 441f  P.G?*/,.D.D...D


# fdisk -l jvs3516cs-7601.bin
Disk jvs3516cs-7601.bin: 7,4 MiB, 7798784 bytes, 15232 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Is there anyone with better ideas then mine? Thanks in advance.

PS There's also a jvs3516cs-7601-ver.bin whit inside:

#####################3
#file format:
#module=name, the name want to update. such as boot,kernel,fs,config ...
#ver=3, version of the module
#offset=0, offset in the file
#size=0x100000, size in byte
#dev=/dev/mtdblock/0, dev used to update
#

module=uboot
ver=20
offset=0
size=0x40000
dev=/dev/mtdblock0

module=kernel
ver=3
offset=0x40000
size=0x1B0000
dev=/dev/mtdblock1

module=fs
ver=5608
offset=0x1F0000
size=0x580000
dev=/dev/mtdblock2

product=JVS-HI3516CS-7601
fileSize=0x770000

checksum=0x4b0163fa
7

Now I don't have the time to help you all the way, but I believe I can get you started. Let's think like some company trying to obfuscate something from the layperson, right? The first choice is usually bitwise xor.

We know the offsets of the bits and pieces already, thanks to jvs3516cs-7601-ver.bin.

Now looking at the jvs3516cs-7601.bin in a hex editor turns up a fascinating pattern: 44 1F (or D.). Typically we'd expect to see quite a few 00 occurrences on data with this entropy (start of file), but instead we see that D. pattern.

Don't ask me if there is some connection to the name of a developer or something like that, but we now have a working theory. The .bin file may have been xored word-wise with 0x1F44 (or 0x441F if it's Big Endian). Excerpt:

5C 1B 44 F5 50 EF DB FA 50 EF DB FA 50 EF DB FA
50 EF DB FA 50 EF DB FA 50 EF DB FA 50 EF DB FA
04 0C C4 9F E4 0C C4 9F 44 0B C4 9F 24 0B C4 9F
84 0B C4 9F 64 0A C4 9F C4 0A C4 9F 3C 49 70 0D
44 1F 47 3F 44 1F 44 0E 44 1F 44 1F B9 1F 44 1F
40 1F 47 3F 2A 2F 2C 1F 44 1F 44 1F B9 1F 44 1F
4C 1F 47 3F 44 1F 44 0D 44 1F 44 1F B9 1F 44 1F
48 1F 47 3F 27 3F 38 1F 44 1F 44 1F B9 1F 44 1F
54 1F 47 3F 44 1F 44 0E 44 1F 44 1F B9 1F 44 1F
50 1F 47 3F 2A 2F 2C 1F 44 1F 44 1F B9 1F 44 1F
64 1F 47 3F 44 1F 44 04 44 1F 44 1F B9 1F 44 1F
60 1F 47 3F A5 5F 38 1F 44 1F 44 1F B9 1F 44 1F
6C 1F 47 3F 54 1F 44 1F 44 1F 44 1F B9 1F 44 1F
AC 1F 47 3F 4B 1F 44 1F 44 1F 44 1F 44 1F 59 1F
6C 1F 47 3F 44 1F 44 1F 44 1F 44 1F 61 37 44 1F
6C 1F 47 3F 44 1F 44 1F 44 1F 44 1F 41 4F 44 1F
6C 1F 47 3F 45 1F 44 1F 44 1F 44 1F 41 4F 44 1F
40 1F 44 0F EE 15 44 1F 44 1F 44 1F 19 1F 44 1F

The following little Python script takes the knowledge from jvs3516cs-7601-ver.bin and places it into the parts variable (a list of triplets/tuples). It then opens the input file (no sophisticated error or path handling here, so run from same folder as the jvs3516cs-7601.bin is located in) and reads the parts one by one and then dumps them into an output file as per tuple contents:

# Tested with Python 2.7!
import io
import struct

parts = [("uboot.img", 0, 0x40000), ("kernel.img", 0x40000, 0x1B0000), ("fs.img", 0x1F0000, 0x580000)]
inpname = "jvs3516cs-7601.bin"

with io.open(inpname, "rb") as infile:
    for nm, offset, size in parts:
        infile.seek(offset)
        x = infile.read(size)
        assert len(x) / 2, "%s has odd length." % (nm)
        newx = ""
        for woffs in range(0, len(x), 2):
            word = struct.unpack("H", x[woffs:woffs+2])
            val = word[0] ^ 0x1F44 # the magic xor value we assume based on "D."
            newx += struct.pack("H", val)
        assert len(newx) == len(x), "Input and output size differ."
        assert len(newx) == size, "Output size (%d) for %s differs from expected value %d." % (len(newx), nm, size)
        with io.open(nm, "wb") as outf:
            outf.write(newx)

The outcome are three files that after a cursory look, seem like the real deal. I.e. the deobfuscated uImage, kernel image and file system image.

Once we have those files, using file on the shell gives something like this:

$ file *.img
fs.img:     Squashfs filesystem, little endian, version 4.0, 5722880 bytes, 614 inodes, blocksize: 262144 bytes, created: Fri Oct 20 10:32:21 2017
kernel.img: u-boot legacy uImage, Linux-3.0.8, Linux/ARM, OS Kernel Image (Not compressed), 1644712 bytes, Thu Nov  5 07:18:50 2015, Load Address: 0x80008000, Entry Point: 0x80008000, Header CRC: 0xAAC0A340, Data CRC: 0x1FDABC77
uboot.img:  data

I am unsure why it doesn't recognize the U-Boot image. But it might just as well be a proprietary loader replacing what was formerly a U-Boot image.

If you create a folder rootfs and have the (Debian) package squashfuse installed, you can run sudo mount -t squashfs fs.img rootfs to mount the SquashFS image. The outcome will look like this:

$ ls -l rootfs/
total 2
drwxrwxrwx 2 root root 1386 2017-09-30 08:11 bin
drwxrwxrwx 2 1001 1001    3 2006-04-19 06:05 boot
drwxrwxrwx 2 1001 1001    3 2017-06-30 05:57 dev
drwxrwxrwx 7 root root  259 2017-10-20 12:32 etc
drwxrwxrwx 5 root root  149 2017-09-30 08:11 home
lrwxrwxrwx 1 1001 1001    9 2013-09-23 12:46 init -> sbin/init
drwxrwxrwx 2 1001 1001  650 2014-05-04 08:36 lib
lrwxrwxrwx 1 1001 1001   11 2013-09-23 12:46 linuxrc -> bin/busybox
drwxrwxrwx 2 1001 1001    3 2006-04-19 06:04 lost+found
-rwxrwxrwx 1 1001 1001 1341 2011-04-21 05:40 mkimg.rootfs
-rwxrwxrwx 1 1001 1001  431 2011-04-21 05:40 mknod_console
drwxrwxrwx 2 1001 1001    3 2006-04-19 06:05 mnt
drwxrwxrwx 2 1001 1001    3 2008-05-21 11:17 nfsroot
drwxrwxrwx 2 1001 1001    3 2006-04-19 06:04 opt
drwxrwxrwx 2 1001 1001    3 2006-04-19 06:04 proc
drwxrwxrwx 7 root root  143 2017-09-30 08:11 progs
drwxrwxrwx 2 1001 1001   31 2013-08-22 03:35 root
drwxrwxrwx 2 1001 1001  878 2017-10-20 12:32 sbin
drwxrwxrwx 2 1001 1001    3 2006-04-19 06:05 share
drwxrwxrwx 2 1001 1001    3 2006-04-19 06:04 sys
drwxrwxrwx 2 1001 1001    3 2006-04-19 10:46 tmp
drwxrwxrwx 6 1001 1001   62 2013-08-22 03:35 usr
drwxrwxrwx 3 1001 1001   26 2013-08-22 03:35 var
lrwxrwxrwx 1 root root   10 2017-10-20 12:32 wifi -> /home/wifi

So it seems that the SquashFS is readable and sound. At least that part has been verified.

Now, as I mentioned at the beginning, I don't have the time to verify my findings so far, but my findings will give you a clue of the mindset the developers of the company where in and what else they may have in store. It looks like the real deal, but I may have overlooked some details. Either way this should get you started.

| improve this answer | |
  • Ok I've got all three .img files and I'm going to analyze them. Thanks a lot: this script is the Open Sesame I was looking for. – Cristian Alberti Jun 14 '18 at 20:11
  • The kernel.img was binwalked (so I've found that's a uImage fs) and was converted with dd if=kernel.img bs=64 skip=1 of=kernel.custom . Another way could be the extract-uimage.sh script. – Cristian Alberti Jun 14 '18 at 21:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.