A lot of the vulnerability research into IoT gear focuses purely on the offensive, with very little about how to fix issues and defend against attacks. We looked at an IP camera that allowed us to chain together small issues to gain root access. Fixing any one of the smaller issues would have made our job far harder. This gives us an ideal chance to write about how to fix the issues as well as discover them.

Our target is a Samsung branded indoor IP camera – the SNH-6410BN. In terms of quality and functionality, the camera isn’t bad, with reasonable picture quality and workable apps.

But, as is the norm with IP cameras, the network security was lacking.

Typically, a user would connect to the camera using the mobile app or “the cloud” using a website. But the camera still has SSH and a web server running on it. This is where our journey begins.

The web server only runs over HTTP, not HTTPS.

Issue 1 – there is no transport encryption used to access the device, so any credentials or data transmitted are open to interception or tampering.

Resolution 1 – use secure protocols where possible, using per-device random keys.

The web interface only uses just a “private key” (which is a password) for security – no username.

Issue 2 – there is only a single web service user account, which means that compromise leads to total control of the user functionality

Resolution 2 – provide finely grained access controls so that compromise of a normal account does not lead to total compromise.

A user needs to set the password the first time they connect. The web interface that is available for the user to do this is not mentioned in the documentation, so a user is unlikely to do so.

Issue 3 – if a user is not aware of the web interface, an attacker can connect, set their own password and takeover the device.

Resolution 3 – disable functionality that is not in use, or at least provide the facility for the user to enable rarely used functionality if they need it.

In mid-2014, Zenofex at the Exploiteers group found a way to reset the “private key” on another Samsung camera. Despite reporting this to Samsung, exactly the same vulnerability exists in this new camera.

The vulnerability lies in /classes/admin_set_privatekey.php. The section of the code that sets the new “private key” does not check to see if one is already set, allowing an attacker to change the “private key” to one of their choosing.

This is the offending PHP code to set a “private key” at first:

Contrast this to the function to change the “private key”, with the check highlighted in red:

A simple request to the IP camera will therefore reset the private key:

This allows us to access the web interface as a normal user would. This is a useful first step, but ultimately a root shell is our goal.

Issue 4 – an unauthenticated attacker can reset the password of the camera and take it over.

Resolution 4 – ensure important functionality is protected by authorisation controls and that there are no logical flaws.

Zenofex at Exploiteers also found a command injection vulnerability using the WEP key field on a form.

Oddly, this issue is not present on the new camera, despite the password reset issue still being present. Why has one issue been resolved, and another been left?

Instead we need to find a new way in.

At this stage, we could attack the camera in a black-box fashion. This is how we would approach most web applications.

We have the upper hand though – we can get the firmware and inspect it from the inside.

The firmware was available for download from the Samsung Support site as a tgz (a company rebrand seems to have caused it to vanish). We uncompress this and get a series of files:

Issue 5 – the firmware is not protected or encrypted, facilitating easy reverse engineering

Resolution 5 – use encryption to protect the firmware as a layer of defense against reverse engineering.

Starting with the ramdisk file – as by the extension, this is a gzipped file

This is an ext2 filesystem. This is easy to mount:

This appears to be the bulk of the root filesystem. Let’s have a look to see if the root user is enabled:

Issue 6 – there is only a single system user, root. Compromise of any service or process will lead to root access to the device

Resolution 6 – follow the principle of least privilege, requiring that the user account running the outward facing services have limited access to the device. Unless required, do not allow these accounts to login to the device with a password.

Descrypt has been used to hash the password. This is a poor choice of password hashing algorithm – it allows a maximum of 8 characters to be used. This makes a comprehensive brute-force attack entirely feasible.

Issue 7 – the choice of password hashing algorithm means that a weak password has been used, allowing the root password for all cameras running this firmware to be obtained

Resolution 7 – use md5 or sha512 hashes that allow for long, random passwords that are slow to brute-force.

Now that we have access to the filesystem, we want to find the part that handles user input from the web page. Poor user input validation is a common source of vulnerabilities in embedded systems. It’s an interesting system, using PHP driven web pages to send input to a series of cgi binaries.

My attention was quickly drawn to one binary called debugcgi. Running strings on this shows that it has potential – there are clearly system commands, and they are using string formatting:

Unfortunately, it isn’t called from any of the PHP scripts, so we can’t determine how it works that way. We are going to need to disassemble it. IDA Pro is one of the best ways of doing this. It handles ARM code well, and generates graphs that make identify and tracing functionality very easy.

Issue 8 – debug functionality that cannot be used conventionally has been left in a release build, exposing the system to attack

Resolution 8 – remove debug functionality from release devices. Do this in the build scripts so that it does not happen by mistake.

We already suspect that the binary is running system commands from strings. A good start is looking for calls to system and exec, and then tracing back. In this case, this quickly leads us to a large function that is visually interesting, as well as containing a number of the command strings such as ls, netstat and ifconfig.

Using a combination of playing with the debugcgi binary on the camera, using IDA, and looking at how other CGI binaries are called, it’s fairly easy to work out how to start calling commands. The parameter msubmenu can accept a number of options:

data

setting

shell

The one we are interested in is shell:

When using shell, you can call a whitelisted series of commands, including ls, netstat and so on. There is nothing inherently dangerous though.

Notice that ls needs an argument – the directory we are viewing. This argument is substituted into the string using string formatting and snprintf:

Practically every shell allows you to run multiple commands on a single line though, for example:

This will run the ls command, whoami and uname. If we pass that as a command_arg, we get the following:

Issue 9 – user input is treated as trusted and passed through to a system call, allowing command injection as root

Resolution 9 – treat all user input with distrust and strictly sanitise and whitelist it. Unless absolutely required, do not use systems calls.

Brilliant – we have command injection, as root. But not all is well.

Trying any command with a space in fails. The device isn’t expecting spaces in the path – even when escaped to %20 – so can’t handle them. We need to work around this.

Thankfully again, a feature of BASH comes to our rescue – brace expansion. Anything in curly braces will be expanded e.g.

becomes:

So let’s give that a go:

Nice, now we have a proper case of command injection.

Good to have, but let’s get an interactive root shell. There is already ssh running on port 1022.

Issue 10 – the device is running ssh, a service which is unlikely to be used

Resolution 10 – disable services that are unlikely to be used. Don’t rely on non-standard port numbers for protection.

Changing the password to one of our own would get us in.

We’d normally use the command passwd to change a password. Most desktop distributions have a passwd command that can run without user input. Busybox – the binary that provides the bulk of shell functionality on the camera – has to be different though. It needs input from the user. This isn’t something we can do with command injection.

We’re root though – we can edit /etc/shadow and replace the hash directly. Piping an entirely new shadow file in would work, but a number of characters – namely < and > that are required for piping – can’t be passed via the command injection. How do we work around this?

The device has sed. Sed can do an in-place search and replace on the file, looking for the old hash (Y9IdQjgdLn0p6) and replacing it with a new one (Um8sjRjZKSEI2, generated using openssl passwd 12345678):

We don’t even need to know the original hash by extracting the firmware – we can just recover by reading out /etc/shadow.

Now we can ssh in using our password, gaining a full root shell.

Issue 11 – the combination of all the previous issues means we have gained a root shell to the device

Resolution 11 – secure development practices, code reviews, penetration tests, and reverse engineering would have found at least some of these issues.

We have gone from local unauthenticated network access to a root shell via a reliable network attack. It’s not the end of the world – it’s not a backdoor remotely accessible from the Internet, for example.

Finally, we can code this up into a Python script (available in our GitHub repo):

Conclusion

Samsung Techwin’s response was fairly good in the end – they clearly understood the problems and addressed some of them in a later firmware update. The web interface and SSH have been disabled.

It took them a while to respond in full, but at least they did respond. That’s rather different to a lot of IoT vendors, so kudos to Samsung for doing so.

That said, they hadn’t addressed everything. However, they only had to remove the web interface to close the RCE vulnerability entirely.