Quantcast

Exploiting network surveillance cameras like a Hollywood hacker 2: Cisco’s weaknesses

It’s turn for the business IP cameras by Cisco to undergo Craig Heffner’s examination security-wise, in particular the popular PVC2300 and WVC2300 models.

cisco-pvc-2300

Another camera to scrutinize

So I said, okay, D-Link is an easy target, as I mentioned – that’s why I picked them. Let’s move on to perhaps a more reputable vendor, like Cisco. The Cisco PVC2300 (see right-hand image) is kind of a mid-range business IP camera costing about $500. It, too, has a web server and it enforces authentication by using .htpasswd files which most people are probably already familiar with. So, basically, you put an .htpasswd file, or more specifically a symlink to a centralized .htpasswd file, in every directory that you want to be password protected (see leftmost image below). Looking through the firmware, every single directory in the web interface had a .htpasswd file. Except one.

Action to specify

Action to specify

No .htpasswd in oamp directory

No .htpasswd in oamp directory

Authentication principle

Authentication principle

 

The oamp directory did not have any .htpasswd file (see middle image above). What it had was a bunch of .xml files that were actually symlinks to this oamp.cgi binary. So I said, okay, let’s look at what this oamp.cgi thing does. As it turns out, it implements kind of its own little mini API that’s totally separate from the rest of everything else running in the web interface. So it expects you, when you make a request to it, to specify an action (see rightmost image above). And this action you can specify can be one of many different things including downloadConfigurationFile, updateFirmware and many others (see leftmost image below).

If SessionID is invalid

If SessionID is invalid

Valid SessionID requirement

Valid SessionID requirement

Allowed actions

Allowed actions

 

But they weren’t completely stupid – we’ll get to the stupid stuff later – because what they do is before executing an action they check to make sure that you’ve also provided a valid SessionID (see middle image above). If you have not provided a valid SessionID, the only action it lets you run is the login action (see rightmost image above). I said, okay, well, this in itself is interesting because they’re implementing authentication that’s totally separate from the authentication used everywhere else in the interface. So I started looking at how they actually handle this login action.

Oamp and L1_usr functions passed

Oamp and L1_usr functions passed

Password requirement

Password requirement

Specifying the username

Specifying the username

 

They expect you to specify a username (see leftmost image above) and a password (see middle image above), no surprises there. They then make two calls to this PRO_GetStr function. At this point, I have no idea what PRO_GetStr does, presumably it gets a string of some sort. But I do know that on the first call to this function they pass it two strings – Oamp and L1_usr (see rightmost image above). And on the second call, they pass Oamp and L1_pwd (see leftmost image below). The value returned for L1_usr is then compared against the username that you provided (see middle image below), and the value returned for L1_pwd is compared against the password you provided (see rightmost image below). So, presumably, this L1_usr and L1_pwd, whatever their values are, are the correct login for this oamp interface.

L1_pwd compared against password

L1_pwd compared against password

L1_usr compared against username

L1_usr compared against username

Oamp and L1_pwd functions passed

Oamp and L1_pwd functions passed

 

Another place for L1_usr and L1_pwd

Another place for L1_usr and L1_pwd

Now, the only other place I could find in the firmware that actually referenced L1_usr and L1_pwd was in the configuration file (see right-hand image). These values are hard-coded in the device’s running config under the oamp section of the configuration file. You can see that l1_usr is set to string L1_admin, and l1_pwd is set to the string L1_51. And this is a real problem because this whole oamp interface and these hard-coded accounts are completely undocumented, so no one knows they’re there except for people who bothered to look at the firmware, which of course an admin is never going to do that. And even if an admin knew that these were here, there’s no way for the admin to change this, there’s no interface for the admin to go in and change these values.

Not really secrets

Not really secrets

And the problem with having hard-coded secret passwords in your system and backdoors is that they don’t stay secrets for long. So we can use these backdoor accounts to exercise the login action and, sure enough, we get back a session ID (see leftmost image below). Now, as long as we send the session ID along with all of our other requests, we can invoke any of the other actions supported by oamp.cgi., including downloadConfigurationFile (see middle image below). And this gets us back – what appears to be base64 encoded data.

Not working

Not working

Invoking other actions

Invoking other actions

SessionID retrieved

SessionID retrieved

 

The problem is, if you try to base64 decode this – not working. You just get a bunch of junk (see rightmost image above). The reason for that becomes readily apparent when you look at the actual encode 64 function in the binary itself. They all are doing base64 encoding but they’re using a non-standard base64 key string. Luckily, it’s very easy in Python to substitute the standard base64 key string in Python’s base64 module with a custom key string like this (see rightmost image below).

Custom key string

Custom key string

Video from server room

Video from server room

Plaintext admin creds

Plaintext admin creds

 

And so, with a couple lines of Python we can easily decode the config, which gives us plaintext admin creds (see leftmost image above), which lets me see your server room (see middle image above). Now, the problem with viewing server rooms is it’s really exciting for, like, 10 seconds, and then you’re like: “Holy shit, this is boring!” So I went back and started looking at some more code, because that’s more exciting.

The loadFirmware action

The loadFirmware action

Now, the loadFirmware action is one of the other actions you can invoke, and it’s actually very interesting because instead of uploading a firmware file to the device you specify a URL, and then the device goes to that URL, downloads, presumably, the firmware, and then flashes it (see right-hand image). The problem is, that URL that you specify is shoved into a command line string that’s ultimately passed to libc system, and hopefully everyone here knows that taking unfiltered user-supplied content and passing it to system is bad (see leftmost image below). You don’t do it.

Same story

Same story

Command injection works wonders

Command injection works wonders

Bad choice

Bad choice

 

Shodan dork

Shodan dork

So we can easily do command injection through this parameter: just putting ;reboot; somewhere in your URL reboots the machine (see middle image above). And of course we can run whatever command we want at this point. This also affects the WVC2300 (see rightmost image above), it’s basically the same camera but with antennas. And there are hundreds of these cameras already out there online (see right-hand image) in hotels, obviously server rooms, and engineering companies who design things for the International Space Station (untold).
 

Read previous: Exploiting network surveillance cameras like a Hollywood hacker

Read next: Exploiting network surveillance cameras like a Hollywood hacker 3: Accessing the admin area on IQinVision

Like This Article? Let Others Know!
Related Articles:

Leave a comment:

Your email address will not be published. Required fields are marked *

Comment via Facebook: