UPDATE: On reddit there were some comments about newer axis IP cameras. It turns out that they put a lot of effort in securing the newer models and keeping them up to date. This means that the attacks described below won’t work on them. What a pleasant surprise.


With every passing year DDoS attacks seem to become more common and more powerful than in the year before. The attacks on krebsonsecurity.com and DynDNS were probably the events that drew the most media attention to the topic in 2016 and led to a sudden awareness of how fragile modern internet infrastructure actually is.

But how on earth was it possible for a single group of attackers to build a botnet so massive that it could easily take down almost any web site and even bring well known DDoS protection services to their limits? The answer is IoT. For a long time internet of things devices like IP cams were pretty low level targets for hacking as they either had standard credentials or were awfully coded and easily exploitable. I decided to take a look at one of those cams to see if it was as bad as most of us think.

Finding an IP cam

After a friend of mine made me aware that there are several AXIS IP cams exposed to the internet without authentication on some parts of their web interface I really became curious how bad their security practices actually are. Since I’m not too eager to hack random surveillance cams on the internet I decided to search for a cheap one online. My friend told me some names of the exposed cameras and I was pretty surprised.

Whenever I heard that IP cams were exposed to the internet I actually thought that these are some cheap chinese plastic cameras that people bought to check what their cats are doing while they are at work or to investigate who pushes over the trash cans every weekend, just to find out it’s their drunk husband coming home from the pub in the middle of the night. But many of the cams are actually quite expensive. Massive security cameras you’d otherwise only see in a bank, at least judging from their case, that can easily go up to 600 € or more.

Since I didn’t want to spend that much just to satisfy my curiosity I bought one of the cheaper models on ebay. In the end I decided to get an AXIS 206 IP camera. Sure enough the mail man put the camera on a place where he never put a package before just when it heavily started to snow. When I found it I couldn’t use it since there was actually a lot of moisture in the package. I really have to get a camera to monitor those mail men… Anyway, since I didn’t want to risk damaging the camera I decided to put it in rice over night and to investigate it at a later time.

Setting It Up

When I was communicating with the seller to ask him whether or not the camera was behind a firewall and a password was set, he just told me that his user name was root and the password was pass, without going into detail about any firewall involved. He also told me he would factory reset it before he ships it to me. When I first started the camera I tried to find where it was located in the network. I did this with arp -a. I then went on to surf the web interface and was greeted with a login prompt. I typed in root and pass and landed on a page where I could see the video stream

There’s not much I can do here. From the manual I downloaded I knew that there should be some settings available like a screenshot function or controls for resolution and compression. However since this camera was first introduced in 2004 or something and had it’s last firmware update in a time where Ed Hardy trucker caps were still seen as acceptable and everybody had a myspace account (2006 for those of us who repress the memory of that time) they only seem work when directX is enabled. This shouldn’t be the only time the manual would let me down.

What first caught my attention was the “Setup” button in the upper right corner. This is the admin control panel where you can edit some hardware settings and have access to some pretty dangerous functions. I often read that it’s possible to upload your own web files and scripts to the camera. However, I didn’t have much luck doing this (the manual lied again!11) and didn’t really find a good explanation on the topic. I decided to play with the options a little bit and eventually reset the camera to factory settings to have an untouched system again.

Bruteforcing my way in

Much to my surprise I was prompted to type in a new user and password on first start, which I set to root:root. What this means is that there is no pre-configured default password. Using root and pass was actually the decision of the seller and he failed to correctly set it back to factory settings. Since the seller is probably not the only person in the world who uses these “creative” user/pass combinations, I quickly wrote a shell script that was able to bruteforce weak login credentials. I couldn’t use something fancy with multi-threading for a reason I’ll explain later.

The various problems of this script aside, e.g. no actual check of the input or the fact that it would print the first user/pass combination as success if the internet connection failed or any other status code was returned, it worked pretty good for a 3 minute coding job. I could now easily bruteforce the login page for popular credentials I obtained from a list called “Sucuri_Top_Wordpress_Passwords.txt” with slight modifications. Hi @brutelogic!

So much “fuuuuuuuu”

So let’s say I found a camera publicly available on the internet, bruteforced my way in and now had access to the admin page, how could I get a shell? Due to the fact that the camera was supposed to be able to handle scripts written by the user there were some functionalities that allowed us to read and write files.

Doesn’t this look promising? Here’s the editor:

Perfect! All I now had to do is changing one of the countless cgi scripts to give us a reverse shell. So I opened a random cgi script, put code for a reverse shell using netcat in there and clicked on “Save File”. Now we’ve finally got a she… crap.

What’s not a big surprise but didn’t even come to my mind for one second was that I’m in a read-only file system (CramFS). So naturally my try to edit the cgi script failed. Now how can I get a shell if I can’t directly write to the web root? Since the edit script allowed me to list directory contents I opened the web root /usr/html again and saw that there was a symlink to /etc/httpd/html on /local. It looks like this was the place where the user was expected to upload his own web files.

Finally I can go on, put a shell script in there, make it executable and get myself a nice reverse shell! Since I didn’t know if netcat was available on the system I used the id command first. I opened the web page and saw this:

Srsly. That’s how disappointment looks like in one picture. I knew that the web server supported cgi and some form of server side includes. The latter was not really working for me for some reason, but cgi scripts had to work. The whole camera basically consists of shell scripts and plastic. So obviously the web server didn’t know that I wanted to execute scripts there. However, luckily I can edit it’s configuration file.

It turns out that the camera runs boa. Boa is web server software that was actively developed until 2005 and contained some pretty serious security vulnerabilities on it’s own, even though I couldn’t use them to get access. It has a pretty dumb standard setting though that I can use later on for some fun. Anyway I browsed through the configuration files on the camera and found this line in boas config:

That should allow me to run my cgi script instead of just printing its content to my browser. I restarted the camera in the admin panel and visited the page again, just to be greeted by one of the most beautiful lines you can read when hacking a box:

But somehow this didn’t feel right. When I bought the camera I hoped to find a cmd injection vulnerability and pwn the camera with style but all I did so far was using an editor provided by the manufacturer.

From an earlier nmap scan I knew that there were 3 open ports. 80, 21 and 49152. I played with upnp on 49152 a little but there was nothing of interest. So I tried to connect to ftp with my bruteforced credentials and was given access. Since the web editor sucks big time I decided to download the /etc/ and /usr/ folders to see if I can find some vulnerabilities in the source code. The easiest way to download folders on ftp servers recursively is by using wget

wget -r ftp://user:pass@host/folder

I now had the content of the web root on my local machine and could inspect it further. It didn’t take long until I found a command injection vulnerability in one of the shell scripts, called nattraversal.cgi.

As seen above $IFS was first backed up and then set to “&”. The developer did this in order to tell “for” to split the $QUERY_STRING variable by “&”.

$QUERY_STRING contains everything inside the URL bar right after “?”, so if the url was nattraversal.cgi?parameter1=X&parameter2=Y the script would first process parameter1 and then later process parameter2.

Processing in this case means that the script splits the parameter-value combination by “=”. The parameter name will become a variable name in the script with the same value as in the URL. So if the query string contains ?parameter1=X it will assign the value X to a variable called parameter1.

But from the code I knew that the parameter name in the URL had to be either “action”, “port” or “router”, otherwise an error would be thrown. I decided to use action. In the next line there was a check whether or not the value is empty, but that’s not interesting. What I was looking for was in line 46. It’s “eval” processing user controlled input. I want to note that there were several options here to execute system commands. I decided to use backticks and place my payload inside them. To confirm the vulnerability I wanted to use the sleep command. The finished payload was


I had to use IFS as spaces were encoded, which made the request fail. After 15 seconds the page loaded with the following content:

The time delay told me that the command was executed successfully. The 502 error was thrown due to the fact that there was some error message output later in the script. Cgi 1.1 requires that there are two line feed chars present, which wasn’t the case since this cgi file is supposed to be called by another file instead of queried directly.

Anyway, I’m now able to execute system commands. The next step is getting a proper shell. I tried using netcat, but every time I wanted to connect back to my machine the connection immediately closed. After some time I gave up on the netcat reverse shell. Since these devices very often run some modified minimal linux distros it was safe to assume that it was using busybox with some networking tools.

Busybox is a small utility that unites many linux tools in one small binary. It’s often used on systems where storage space is tight and not much functionality is needed. So after connecting to ftp again I was searching for a way to get a shell without netcat and found something interesting in /usr/sbin/. It contained a file called telnetd which linked to busybox. My final payload was


I decided to give it a shot and tried to connect.

Aww yiss, shell!

Having fun

Since the hard work was done I decided to poke around the file system. I eventually found some funny stuff.


The /etc/passwd file contained three entries. Namely “root”, “nobody” and “logout”. Interestingly root and logout both had password hashes (Yes, at the time the camera was first introduced people saved the passwords directly in passwd instead of shadow). Since it had a password hash I wanted to see if I can actually log in as logout.

So I reconnected, but this time I used logout as username. I didn’t know the password and just guessed it’s maybe also logout. As soon as I pressed enter I had a shell again

uid=498(logout) gid=506(logout)

Oh boy. So if you ever encounter an old axis camera with exposed telnet but you can’t bruteforce the root account, try using logout:logout and enjoy your shell. Now there’s not much you can do with this user. However, due to the fact that there is a sheer amount of vulnerable out of date software and that the kernel is over 10 years old, privilege escalation shouldn’t be that big of a problem.

Bittersweet Revenge

Has your business website ever been taken down by a DDoS attack? Did you fantasize about revenge every sleepless night after the incident? I have good news for you. Finally you can get your bittersweet revenge by DoSing an IoT cam. *record scratch* Wait what?

Well, not exactly the whole cam but the web interface, which would at least make it impossible to exploit the command injection vulnerability. Above I explained that a single threaded shell script is enough to brute force the camera. The truth is, that you would likely DoS the cam if you used multi threading. The reason for this is a configuration option in boa.

You read that right. Boa accepts 30 simultaneous connections before it throws an error and blocks the web interface for every user that connects after that. This means I can DoS the camera with a shell one-liner for a short amount of time:

i=0; while [ “$i” -lt “35” ]; do (nc 80 &); i=$(($i+1)); done

This is how the interface will look like after that:

Congratulations, you DoSed an IP cam and finally got your symbolic revenge. I hope you feel better now.

Give it some style

The whole user interface burns your eyes to say the least. But since we can upload our own web files we can make it suck a little bit less. Now I’m not that good at bootstrap, but even these 90’s .gif riddled personal home pages look more interesting than the current interface. So here is my attempt for a better UI:

Well I don’t say it looks way better than before but it’s bearable now. Also it’s responsive, so you can use your phone to surf the page for whatever reason.

Random Lulz

So since these cams are regularly visited not only by mirai bots, but also by people who like to monitor strangers’ security cameras (for research purposes I suppose), you can be sure that as soon as you connect them to the internet you aren’t the only one who sees its video stream. So why not give these strangers a show? How about:

A robber being defeated by a shop owner and his glorious folding chair?

This absolutely legit video of a ghost caught by a security camera to scare the sh*t out of them?

Or an extremely creepy image filling the whole page after 20 seconds watching the stream?

You see, the trolling possibilities are basically limitless.


At first you could be a little bit surprised from a security point of view. The page exposed to the viewer was actually pretty secure and you could argue that without the password there’s not much that you could do.

However, this whole cam runs on software that’s a decade old, with web server software that, I’m not joking here, allowed you to overwrite the admin password just by using an overly long username for HTTP authentication. The whole interface doesn’t use a single CSRF token and there’s more XSS than I could count. And I’m 100 % sure, that there would be numerous SQL injections if they used a database instead of flat files.

But the actual problem we see is that this particular camera is still widely deployed and reachable from the internet, more than a decade after its release and last security update. The IoT problem is only getting worse without better guidelines and actual consequences for the manufacturers.

Now I don’t want to bash axis too much. I have absolutely no idea how well or poorly their new camera models are secured. (See update) All I can say is that this particular cam was absolutely insecure once you gained access.

You shouldn’t have a web interface where you could change system files. Even though the user is given the opportunity to create a strong password, he will most likely use root:pass anyway like the ebay seller, except you force him to use a stronger one.

So if you think about buying one of those, don’t connect it to the internet. Not even once. Even in a local network they are far from being secure and could be pwned through CSRF or DNS rebinding and I’m sure you don’t want an attacker controlled device inside your home network. If you really have to, I suggest that you follow the tutorial of Rob Graham, to find out how to turn your Raspberry Pi into a router for your IoT device test network.

Also make sure to follow me on twitter.