Thursday, April 3, 2014

Reversing the Dropcam Part 2: Rooting your Dropcam

by Kris Brosch

In the last Dropcam post, I wrote about reversing the USB setup procedure that the Dropcam uses to initially connect to your WiFi network. After exploring the USB tunneling protocol, the next step was to take it apart, or at least take off the back of the enclosure:


The main controller is an Ambarella A5s system-on-chip, which contains an ARM processor, video processing hardware, USB device controller, and other peripherals. We had actually already guessed that the Dropcam used an Amborella chip based on the iManufacturer USB descriptor field value “Linux 2.6.38.8 with ambarella_udc”. Ambarella chips are also used in GoPro cameras. Quite a bit of work has been done reversing GoPro cameras by other researchers. One researcher, who goes by evilwombat, has done some particularly interesting work, including writing a firmware parser and tools that can be used to load custom code onto GoPro cameras over the USB port, which can be found at his Github page (https://github.com/evilwombat?tab=repositories).

Here's a picture of the Dropcam with some of the components identified:


The most useful thing to identify was the UART (serial) port (zoomed in the picture above, and labeled with TX, RX, 3.3v, and GND). Upon examining the board, that 4-pin footprint looked suspiciously like a serial port. One pad was connected to ground, and another was connected to a thick trace meaning it was likely a power connection, and it is in fact at 3.3v when the camera is powered on. The other two pads were connected to resistors via small traces and they were both at 3.3v with the camera powered on. This is consistent with an embedded system serial port – UARTs usually are at a “high” voltage level when they are idle; the TX line would be high because no data was being transmitted and the RX line would be pulled high when no input is connected. I tried connecting the RX line of a serial adapter to each of the two pads and found that when I configured the adapter for a baudrate of 115200 and powered the Dropcam, I could see Linux boot messages being transmitted:
[    0.000000] Linux version 2.6.38.8 (dropcambuild@ubuntu-dropcam-build) (gcc version 4.5.2 (Sourcery G++ Lite 2011.03-41) ) #15 PREEMPT Mon Oct 1 16:59:51 PDT 2012
[    0.000000] CPU: ARMv6-compatible processor [4117b365] revision 5 (ARMv6TEJ), cr=00c5387f
[    0.000000] CPU: VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
[    0.000000] Machine: Coconut
[    0.000000] Memory policy: ECC disabled, Data cache writeback
[    0.000000] Ambarella: AHB   = 0x60000000[0xf0000000],0x01000000 0
[    0.000000] Ambarella: APB   = 0x70000000[0xf1000000],0x01000000 0
[    0.000000] Ambarella: PPM   = 0xc0000000[0xe0000000],0x00200000 9
[    0.000000] Ambarella: BSB   = 0xc8c00000[0xe8c00000],0x00400000 9
[    0.000000] Ambarella: DSP   = 0xc9000000[0xe9000000],0x07000000 9
[    0.000000] Ambarella: HAL   = 0xc00a0000[0xfee00000],0x00009f34 9
[    0.000000] bootmem_init: high_memory = 0xc8a00000
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 35052
[    0.000000] Kernel command line: console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw rootfstype=ubifs init=/linuxrc
[    0.000000] PID hash table entries: 1024 (order: 0, 4096 bytes)
[    0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
[    0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
[    0.000000] Memory: 138MB = 138MB total
[    0.000000] Memory: 136244k/136244k available, 5068k reserved, 0K highmem
(etc.)

Once I had identified the TX and RX pads and successfully received some data, I soldered some wires to those pads and brought them out of the case so that I could access the serial port with the Dropcam assembled:
After the boot messages, there is eventually a login prompt:
########  ########   #######  ########   ######     ###    ##     ##
##     ## ##     ## ##     ## ##     ## ##    ##   ## ##   ###   ###
##     ## ##     ## ##     ## ##     ## ##        ##   ##  #### ####
##     ## ########  ##     ## ########  ##       ##     ## ## ### ##
##     ## ##   ##   ##     ## ##        ##       ######### ##     ##
##     ## ##    ##  ##     ## ##        ##    ## ##     ## ##     ##
########  ##     ##  #######  ##         ######  ##     ## ##     ##

Ambarella login:

Getting root via the Ambarella bootloader 

Unfortunately, I didn't know the root password. I tried logging in as root with a few guessed passwords, but none of them worked. I was considering different ways to dump or modify the Flash chip contents when I happened upon a solution on a forum. After reading some posts on one of the GoPro forums I tried a trick mentioned there to get a bootloader prompt – transmitting a newline immediately upon powering the device on is the key. If you hold down the “Enter” key, repeatedly sending newlines to the serial connection while powering the Dropcam, you can access to the Ambarella bootloader:
             ___  ___  _________                _
            / _ \ |  \/  || ___ \              | |
           / /_\ \| .  . || |_/ /  ___    ___  | |_
           |  _  || |\/| || ___ \ / _ \  / _ \ | __|
           | | | || |  | || |_/ /| (_) || (_) || |_
           \_| |_/\_|  |_/\____/  \___/  \___/  \__|
----------------------------------------------------------
Amboot(R) Ambarella(R) Copyright (C) 2004-2007
BST (137166), HAL (137166)
Arm freq: 480000000
iDSP freq: 120000000
Core freq: 120000000
Dram freq: 336000000
AHB freq: 120000000
APB freq: 60000000
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot>
amboot> help
The following commands are supported:
help    bios    boot    diag
dump    erase   nand_erase      exec
hal     hotboot netboot ping
r8      r16     r32     reboot
reset   setenv  setmem  show
usbdl   w8      w16     w32
xmdl    bapi
Use 'help' to get help on a specific command
amboot>
Once you have access to the bootloader, rooting the Dropcam is just like getting root on any Linux computer where you have access to the bootloader. You can copy the kernel command line shown in the boot messages:
console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw rootfstype=ubifs init=/linuxrc
And change the init parameter to start a shell:
amboot> boot console=ttyS0 ubi.mtd=lnx root=ubi0:rootfs rw rootfstype=ubifs init=/bin/sh
After doing this and watching the kernel boot again, I was left with a root shell:
[    3.130000] UBIFS: reserved for root:  0 bytes (0 KiB)
[    3.130000] VFS: Mounted root (ubifs filesystem) on device 0:13.
[    3.140000] Freeing init memory: 136K
/bin/sh: can't access tty; job control turned off
/ #
My goal was to edit the /etc/shadow file so that I could log in to the camera after a normal boot. Interestingly, the /etc/shadow file on the Dropcam is a link to /mnt/dropcam/shadow:
/ # ls -l /etc/shadow
lrwxrwxrwx    1 root     root           21 Oct  1  2012 /etc/shadow -> ../mnt/dropcam/shadow
You therefore need to mount the /mnt/dropcam filesystem to access the shadow file:
/ # cat /etc/fstab
# /etc/fstab: static file system information.
#
# <file system> <mount pt>     <type>   <options>         <dump> <pass>
#/dev/root       /              ext2    rw,noauto         0      1
#proc           /proc          proc     defaults          0      0
devpts          /dev/pts       devpts   defaults,gid=5,mode=620   0      0
#tmpfs           /tmp           tmpfs    defaults          0      0
sysfs           /sys           sysfs    defaults          0      0
debugfs         /debug         debugfs    defaults        0      0

# extra mounts
/dev/mtdblock9 /mnt/dropcam jffs2 defaults 0 0
/ # mount -tjffs2 /dev/mtdblock9 /mnt/dropcam
And then remove the password hashes from it:
/ # cat /mnt/dropcam/shadow
root::10933:0:99999:7:::
bin:*:10933:0:99999:7:::
daemon:*:10933:0:99999:7:::
adm:*:10933:0:99999:7:::
lp:*:10933:0:99999:7:::
sync:*:10933:0:99999:7:::
shutdown:*:10933:0:99999:7:::
halt:*:10933:0:99999:7:::
uucp:*:10933:0:99999:7:::
operator:*:10933:0:99999:7:::
nobody:*:10933:0:99999:7:::
default::10933:0:99999:7:::
The /mnt/dropcam filesystem is interesting because it appears to be where camera-specific configuration files are stored:
/ # ls /mnt/dropcam/
bpcmap.bin           keycert.pem          softmac
fv.txt               provisioned          wpa_supplicant.conf
hwver                shadow
This implies that different cameras might have different root passwords. The wpa_supplicant.conf file contains the configuration for the wireless network. Most interesting of these files, however, is the keycert.pem file, which contains a public/private RSA key pair and a client certificate. The certificate is issued by a “Dropcam Certificate Authority”, and the common name is set to the unique ID of my Dropcam (the same identifier used to set up the camera and view its stream, as discussed in my previous post):
$ openssl x509 -in keycert.pem -noout -text | grep CN
        Issuer: C=US, CN=Dropcam Certificate Authority, O=Dropcam
        Subject: C=US, CN=d024378182da4f37b0e981946989f40a, O=Dropcam
This means that each Dropcam has a unique client certificate that it uses to authenticate to the Dropcam cloud servers.

Exploring the running device

Now that I had modified the shadow file, I was able to log in as root without a password and inspect the system after it had booted normally:
# ps
PID   USER     TIME   COMMAND
    1 root       0:02 init
    2 root       0:00 [kthreadd]
    3 root       0:00 [ksoftirqd/0]
    4 root       0:00 [kworker/0:0]
    5 root       0:00 [kworker/u:0]
    6 root       0:00 [khelper]
    7 root       0:00 [kworker/u:1]
  402 root       0:00 [sync_supers]
  404 root       0:00 [bdi-default]
  406 root       0:00 [kblockd]
  513 root       0:00 [kswapd0]
  514 root       0:00 [fsnotify_mark]
  515 root       0:00 [aio]
  517 root       0:00 [crypto]
  558 root       0:00 [mtdblock0]
  563 root       0:00 [mtdblock1]
  568 root       0:00 [mtdblock2]
  573 root       0:00 [mtdblock3]
  578 root       0:00 [mtdblock4]
  583 root       0:00 [mtdblock5]
  588 root       0:00 [mtdblock6]
  593 root       0:00 [mtdblock7]
  598 root       0:00 [mtdblock8]
  603 root       0:00 [mtdblock9]
  611 root       0:00 [ubi_bgt0d]
  628 root       0:00 [ubifs_bgt0_0]
  629 root       0:00 [kworker/0:1]
  643 root       0:00 [flush-ubifs_0_0]
  646 root       0:00 [jffs2_gcd_mtd9]
  699 root       0:00 [kworker/u:2]
  735 root       0:00 /bin/ash /usr/bin/bootstrap.sh
  736 root       0:00 -sh
  738 root       0:00 syslogd -C128 -S
  740 root       0:00 klogd
  743 root       0:00 /usr/bin/connect
  744 root       0:00 logger -t connect
  745 root       0:00 /usr/bin/connect
  746 root       0:00 /usr/bin/connect
  760 root       0:00 [file-storage]
  772 root       0:00 [kworker/0:2]
  784 root       0:00 [cfg80211]
  804 root       0:00 [AR6K Async]
  811 root       0:00 [ksdioirqd/mmc1]
  822 root       0:00 wpa_supplicant -iwlan0 -c/mnt/dropcam/wpa_supplicant.conf
  829 root       0:00 [dsplogd]
  830 root       0:00 [vsyncd]
  860 root       0:00 [sh]
  861 root       0:00 [sh]
  868 root       0:00 ps
The /usr/bin/connect binary performs most of the operations of the Dropcam. It handles the TLS connections made out to Dropcam cloud servers (which, based on strings in the connect binary, carry a protocol named droptalk). It also handles loading and unloading the kernel driver, as well as the userspace portions of the USB mass storage network tunnel (which again, based on strings in the binary, is named FSNL). The connect binary is UPX packed, but it can be unpacked easily:
$ strings connect | tail -1
UPX!
$ upx -d connect
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2013
UPX 3.09        Markus Oberhumer, Laszlo Molnar & John Reiser   Feb 18th 2013

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
    789596 <-    420052   53.20%   linux/armel   connect

Unpacked 1 file.
One thing that we noticed when looking at this binary was that it contains references to the Lua scripting language. We weren't sure why until we saw that it was writing to a file named /tmp/connect.bin and then running this command via a call to system():
rm -rf /tmp/connect && mkdir /tmp/connect && tar zx -f /tmp/connect.bin -C /tmp/connect && rm /tmp/connect.bin
The connect binary itself contains an embedded tarball that gets extracted to /tmp/connect when it runs. The tarball contains an assortment of files including a number of compiled Lua scripts:
$ dd if=./connect of=connect.tar.gz bs=1 skip=473404 count=168451
168451+0 records in
168451+0 records out
168451 bytes (168 kB) copied, 0.364475 s, 462 kB/s
$ tar -tzvf connect.tar.gz
-rw-r--r-- dropcambuild/dropcambuild 10376 2013-04-22 20:25 dispatch.bin
-rw-r--r-- dropcambuild/dropcambuild  1243 2013-04-22 20:25 hello.bin
-rw-r--r-- dropcambuild/dropcambuild   545 2013-04-22 20:25 hwver.bin
-rw-r--r-- dropcambuild/dropcambuild  4279 2013-04-22 20:25 ir.bin
-rw-r--r-- dropcambuild/dropcambuild   879 2013-04-22 20:25 list.bin
-rw-r--r-- dropcambuild/dropcambuild   650 2013-04-22 20:25 main.bin
-rw-r--r-- dropcambuild/dropcambuild  2363 2013-04-22 20:25 monitor.bin
-rw-r--r-- dropcambuild/dropcambuild   708 2013-04-22 20:25 motion.bin
-rw-r--r-- dropcambuild/dropcambuild  2010 2013-04-22 20:25 net.bin
-rw-r--r-- dropcambuild/dropcambuild  2607 2013-04-22 20:25 oldiags.bin
-rw-r--r-- dropcambuild/dropcambuild  3280 2013-04-22 20:25 persistence.bin
-rw-r--r-- dropcambuild/dropcambuild   329 2013-04-22 20:25 platform.bin
-rw-r--r-- dropcambuild/dropcambuild  3365 2013-04-22 20:25 platform_a5s.bin
-rw-r--r-- dropcambuild/dropcambuild   551 2013-04-22 20:25 platform_local.bin
-rw-r--r-- dropcambuild/dropcambuild   822 2013-04-22 20:25 ravg.bin
-rw-r--r-- dropcambuild/dropcambuild   191 2013-04-22 20:25 rtp.bin
-rw-r--r-- dropcambuild/dropcambuild   643 2013-04-22 20:25 settings.bin
-rw-r--r-- dropcambuild/dropcambuild  9931 2013-04-22 20:25 states.bin
-rw-r--r-- dropcambuild/dropcambuild   912 2013-04-22 20:25 status.bin
-rw-r--r-- dropcambuild/dropcambuild  3822 2013-04-22 20:25 streams.bin
-rw-r--r-- dropcambuild/dropcambuild  3047 2013-04-22 20:25 update.bin
-rw-r--r-- dropcambuild/dropcambuild   601 2013-04-22 20:25 usb.bin
-rw-r--r-- dropcambuild/dropcambuild  2602 2013-04-22 20:25 util.bin
-rw-r--r-- dropcambuild/dropcambuild  1468 2013-04-22 20:25 watchdog.bin
-rw-r--r-- dropcambuild/dropcambuild 54727 2013-04-22 20:25 droptalk_pb.bin
-rw-r--r-- dropcambuild/dropcambuild  1504 2013-04-22 20:25 containers.bin
-rw-r--r-- dropcambuild/dropcambuild  5879 2013-04-22 20:25 decoder.bin
-rw-r--r-- dropcambuild/dropcambuild  1038 2013-04-22 20:25 descriptor.bin
-rw-r--r-- dropcambuild/dropcambuild  9360 2013-04-22 20:25 encoder.bin
-rw-r--r-- dropcambuild/dropcambuild   615 2013-04-22 20:25 listener.bin
-rw-r--r-- dropcambuild/dropcambuild 20750 2013-04-22 20:25 protobuf.bin
-rw-r--r-- dropcambuild/dropcambuild  1505 2013-04-22 20:25 text_format.bin
-rw-r--r-- dropcambuild/dropcambuild  1525 2013-04-22 20:25 type_checkers.bin
-rw-r--r-- dropcambuild/dropcambuild  3620 2013-04-22 20:25 wire_format.bin
-rw-r--r-- dropcambuild/dropcambuild  1686 2013-04-22 17:24 a5s_boot.sh
-rw-r--r-- dropcambuild/dropcambuild    78 2013-04-18 16:45 wpa_supplicant_a5s.conf
-rwxr-xr-x dropcambuild/dropcambuild  1286 2013-04-18 16:45 udhcpc.script
-rwxr-xr-x dropcambuild/dropcambuild  1310 2013-04-18 16:45 udhcpc_provision.script
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_01_3D_hwrev_1.bin
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_01_3D_hwrev_2.bin
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_02_3D_hwrev_1.bin
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_02_3D_hwrev_2.bin
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_03_3D_hwrev_1.bin
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_03_3D_hwrev_2.bin
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_04_3D_hwrev_1.bin
-rw-r--r-- dropcambuild/dropcambuild 17536 2013-04-18 16:45 ov9715_04_3D_hwrev_2.bin
-rw-r--r-- dropcambuild/dropcambuild 27453 2013-04-18 16:45 ambarella_udc-pre-v16.ko
-rwxr-xr-x dropcambuild/dropcambuild 86828 2013-04-18 16:45 wmiconfig
We'll have another blog post coming up detailing how to reverse engineer those compiled Lua scripts, with a tool to make the RE work easier.

Transport encryption and exploring traffic interception


I wanted to perform a man-in-the-middle attack so that I could decode the TLS traffic from the camera. Every Dropcam has its own client certificate issued by the Dropcam CA; with a copy of my camera's client certificate I could start a TLS connection to the Dropcam servers, but I couldn't yet convince the connect binary to connect to me. In order to perform the man-in-the-middle attack, the simplest option was to patch the server certificate checking code out of the connect binary. This involved flipping one bit in the unpacked binary, re-packing it, and uploading it to the camera.

The droptalk transport layer has decent protection against eavesdropping attacks. It uses an OpenSSL TLSv1 connection with ephemeral elliptic curve Diffie-Hellman (ECDHE) key exchange, and either 128 or 256 bit AES encryption. This is the list of the two cipher suites that the connect binary will accept for the droptalk connection:
ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA
The ECDHE key exchange method made performing a man-in-the-middle on the droptalk connection harder because most tools that I tried to use didn't support that key exchange algorithm. However, I was able to hack some code together that given the camera's private key and certificate would let me intercept and examine the draptalk traffic between my patched connect binary and the Dropcam servers. Here's a picture of my test setup:
My Linux machine was configured to act as a wireless access point, using iptables to redirect the dropcam traffic to my listening droptalk interception process. In addition, I had a serial terminal connected to the Dropcam's serial port so I could upload the patched connect binary and examine its behavior. The droptalk protocol is simple – there is a 3-byte header consisting of a one-byte message type followed by a big-endian two-byte length field. Luckily, there is a function in the connect binary for converting droptalk message type identifiers (the first byte in the header) to human-readalble strings for inclusion in debug output:
The function provides us with this list of droptalk message types:
00      RESERVED
01      REDIRECT
02      START_STREAM
03      STOP_STREAM
04      WIFI_SCAN
05      WIFI_CONNECT
06      RESTART
07      UPDATE
08      DEBUG
09      SET_STATUS_LIGHT
0a      SET_ILLUMINATOR_LIGHT
0b      SET_AUDIO_GAIN
0c      STOP_BACKFILL
0d      AUDIO_PAYLOAD
0e      SET_IR_LED
0f      SET_DPTZ
10      FORCE_IDR
11      EXEC
12      SET_WATERMARK
13      AUDIO_SOUND
14      SET_IMAGE_PROPERTIES
40      PING
80      HELLO
81      STREAM_BEGIN
82      STREAM_END
83      RTP
84      WIFI_SCAN_LIST
85      WIFI_CONNECT_STATUS
86      EVENT
87      BACKFILL_COMPLETE
8a      UPDATE_RESULT
8c      OFFLINE_DIAGNOSTIC_REPORT
8e      STATUS_REPORT
Message types 0x01 through 0x14 appear to be messages that are sent to the camera, while types 0x80 through 0x8e appear to be intended to originate from the camera. Most of the droptalk messages contain protobufs which are decoded in the Lua code. For example, here's the first HELLO packet that my Dropcam sends when it connects to nexus.dropcam.com, decoded with the protoc tool:
7: "Dropcam Connect - Version: 162, Build: 57 (jenkins-connect-release-node=linux-57, a29560dbb0724e7dbafb19b9ac1268b6fb62f1d6, origin/hotfix/basil)"
9: 2
8: "Build: 181 (jenkins-ambarella-181, 8732f64a79aecdd16bf6562775015c303d71c839), Linux: Linux Ambarella 2.6.38.8 #15 PREEMPT Mon Oct 1 16:59:51 PDT 2012 armv6l GNU/Linux"
1: 1
5: 3
6: 15
4: "0.0.0.0"
2: 4
3: 162
The nexus.dropcam.com server always replies with a REDIRECT to an “oculus” server:
1: "oculus121.dropcam.com"
The client then connects to the “oculus” server and sends another HELLO packet. Most of the configuration options in the Dropcam web interface correspond to droptalk messages that are sent to the camera. When the server is ready to receive data, it sends a START_STREAM message with some video parameters in it. Video data is sent from the camera in RTP droptalk messages containing RTP formatted video data.

Conclusions

In this post I've written about the process of reverse engineering the Dropcam from the point of opening the case to having a basic understanding of its network protocols. With the information we've gathered, you could start to piece together a protobuf definition file (.proto file) for the various message types and write your own droptalk client or server. Alternatively, you could use the root shell on the Dropcam to modify or add to its functionality. Comment below, email us, or tweet @IncludeSecurity if you try these things, we'd love to hear what modifications you make to your Dropcam.

Stay tuned for the final blog post in this series regarding Lua scripts!

5 comments :

  1. This is by far the most interesting thing I've read all week. Keep up the great work!

    ReplyDelete
  2. Hi Kris,
    Well executed analysis in both parts,brilliant!

    ReplyDelete
  3. Hi Kris,
    impressive and very usefull analysis. Did u go ahead on this ? I'm interested in loading a different firmware on the device...

    ReplyDelete
  4. I am considering purchasing this device but given its lack of ability to store to local NAS and the requirement to purchase an expensive cloud storage option from the manufacturer, I will hold off until a better firmware is made available by the community.

    ReplyDelete
  5. Hi Kris, I have a doubt after read this amazing post, the Video Data, is encrypted too?, or just the API connection? they say in his page, that video is encrypted into the camera... I would like to check if that be true.

    ReplyDelete