Exploiting an IP Camera Control Protocol: Redux
Last May, I wrote about a remote password disclosure vulnerability I found in a proprietary protocol used to control ~150 different low-end IP cameras. The exploit I wrote was tested on the Rosewill RXS-3211, a rebranded version of the Edimax IC3005. The vulnerability remained unpatched in the RXS-3211 until July of last year, when a supposed fix was provided . Unfortunately, I've been busy working on other projects, so I just recently got around to testing it. Spoiler: the results weren't good. The following post documents how easy it is to still exploit this particular vulnerability, alternative ways to exploit the protocol, and how to create your own firmware images to run whatever you want on devices that you now control.
The Patch Is 0.1% Effective
After flashing the latest firmware image to one of my cameras and installing the new management application, I did exactly what I did the first time: fired up Wireshark again and looked through the traffic. It was clear from the dumps that they were at least obfuscating the traffic now, but the sad fact remained that when I entered my password into the client application, no traffic was sent to the server before I was granted access. Clearly, authentication in the protocol is still occurring client-side. Not good.
With that knowledge, I thought it'd be fun to first explore what all one can do without even having the admin password. Thankfully, this was much easier than would be expected, given my fateful acquisition of Edimax's implementation of the protocol. While working on creating custom firmware images, I downloaded a number of GPL source packages released by Edimax. In the IC3010 package, I realized that Edimax had included more source code than normal, including one folder labeled "enet_EDIMAX". After a quick look, I realized I now had the source to the protocol I had been reversing. Win.
Rather than describing what one can do while unauthenticated, it would probably be faster to describe what one *can't* do. Reboots, factory resets, reading any and all device settings, performing WLAN surveys, toggling LEDs...it is even possible to perform remote, unauthenticated firmware flashing on some models. Basically the only thing that isn't possible to do is grabbing remote frames from the camera. You can read through the code for yourself here: enet_agentd.h enet_agentd.c. After some quick Python scripting, I confirmed that all of the supported functions on the RXS-3211 were still vulnerable to exploitation, even if the admin password was no longer in cleartext. If anyone reading has one of the cameras that supports wireless or firmware flashing (IC-1000, maybe others), I'd love to see if the other enet functionality works.
Obviously, the patch wasn't very effective. However, for the sake of curiosity and thoroughness, I wanted to see if it was still possible to recover the admin password. To do so meant figuring out how the traffic was being encoded. and if it could be defeated . The header format I described in my previous post was still intact, but the body was obviously scrambled somehow. While this could have required a serious reverse engineering effort, it turned out to be fairly simple.
In such situations, there's only a few options: encryption, compression, or both. After changing the password on the device a few times and observing how the traffic changed, it became obvious that either very weak encryption was being used or the data was compressed, as there was an easily discernible pattern between the input text and the output. Comparing the passwords "1111111111" and "1234567890", it became clear that compression was the winner: the length of packets with the former password were a few bytes shorter than the latter. Compression algorithms often work by shrinking 'runs' of data in some way, and hence, will compress the same character in succession much more efficiently than different ones. To find out which algorithm, I then went back and ran strings on the management executable, which gave me my answer: zlib compression. Yes...their solution to remote password disclosure was to compress the password before sending it. Brilliant. After this, all it took was a single line of Python to make things work perfectly again: zlib.decompress(data[12:-4],-15).
To demonstrate these vulnerabilities, I threw together a simple Python script: enet_pwn.py. With this, an attacker can disclose the admin password and others stored on all devices using the enet protocol (including the "patched" RXS-3211), grab many of the common settings shared between devices, and perform reboots and factory resets on the cameras. Obligatory disclaimer: I am not responsible for any illegal use of this tool.
For all the vulnerabilities I've pointed out in their software, I still really like the Edimax cameras for their low cost and high "hackability". Creating firmware images for the devices can allow you do some cool things other cameras can't, and for ~30 dollars for the low end ones, it's a pretty good deal. In fact, the first time I bought one, I had actually considered turning it into a poor man's pentesting drop box (which it does quite well). However, because of how easy it is to create firmware images for the cameras, attackers can also install anything they like once getting the admin password. This could allow them to gain further unauthorized access to a network.
While creating custom firmware for these cameras is a little more complicated than simply using the firmware mod kit, it isn't by much. I've created a few basic scripts that handle everything, which basically just automate the process described here. All someone needs to do is use the extract_edimax.sh script to extract the image, modify the root filesystem to their liking, and then recompile with the build_edimax.sh script. Edimax provides a toolchain for compiling your own applications, which can also be found in my repository in the tools directory. For me, getting netcat on there was enough for everything I wanted. I should note though that any flashing you do could damage your device, so be careful. It is usually possible to recover through a serial terminal on the device, but it's usually best to avoid that annoyance.
For end users, the easiest thing to do is simply to block incoming UDP packets on port 13364. It's possible to make your own firmware image that isn't vulnerable, but this is left as an exercise for the reader (or possibly a later post).
For the developers, here is, once again, some possible pseudocode for the server:
if discovery request: allow else if any other valid request encrypted with admin password hash: allow else: deny deny deny
Never send cleartext passwords. Don't even send hashes unless you have to. And definitely don't send them to clients. It's not that complicated. If you can't do that much, you shouldn't be rolling your own protocols.