Authentication Bypass vulnerability in camp, a Raspberry Pi camera server

Elias Hohl
4 min readJul 25, 2022

--

I recently had a look at camp , a Raspberry Pi camera server based on Python with more than 100 stars on Github. It uses the Tornado framework and supports username-password authentication. When we start the application via python3 server.py --require-login , a browser window will be opened showing a login screen.

Let’s see if we can bypass this. Before digging deeper, I need to show you a small modification I made to the original application. I removed the real camera part and the Websocket class, as I did not have a Raspberry Pi camera available at the time of testing.

Just execute

using above file to disable the unnecessary parts of the application.

Looking at the part of the source code where the Tornado handlers are defined

we can see that the whole root folder is served via StaticFileHandler . However, password.txt seems to be blocked first via ErrorHandler . According to the Tornado documentation, rules are parsed on a “first-match-wins” basis. So there should be no problem, ErrorHandler should trigger before the password file can get served. Let’s try with any other file from the root folder first, for example server.py :

We can acutally download the python script. This is nice, but as there is no secret information in there, it also does not really help. Trying to open http://localhost:8000/static/password.txt will lead to a 403 Forbidden error. Well, let’s test with some path traversal magic and encoding:

Surprisingly, all three of these techniques work and serve us the correct SHA-512 password hash!

Honestly, I did not expect this to work. Actually this is a security bug in the Tornado framework, as it is not applying access control on a “first-match-wins” basis as per the documentation. There should be no way to bypass such rules. One could argue that this only works when the secret file is within the served directory, but a block via ErrorHandler should still work. In other server applications like Apache, Nginx or Flask, such rules also work as expected. Especially in Apache and Nginx, it is quite common to have secret files within served directories and have them blocked with .htaccess rules or in the server configuration files. I contacted the Tornado maintainer regarding this, but he does not consider this a security bug, as the access rules are apparently “not intended to be secure”, although this is indicated nowhere in the documentation. He seems to be more interested in allowing users to serve files containing special characters like % in the filename, access files via percent-encoded characters or ./ magic, “because someone might be doing this intentionally”. Long story short, just avoid this exotic framework if you can.

As we now have the hash, we could crack it with a tool like hashcat or rainbow tables if the password is simple:

For longer passwords not contained in dictionaries, this will, however, not work. So let’s come to the second part of this authentication bypass.

There is a very suspicious line of code at the bottom of the server.py file:

PASSWORD is defined at the top of the file:

So PASSWORD is not the password, but the hash taken from the password.txt , the hash we retrieved above. So by knowing the hash, we also know the cookie secret. We can see how the authentication mechanism works by looking at the LoginHandler class:

The password submitted to the endpoint is hashed and the result is compared with the hash stored in password.txt . If the hashes match, an authentication cookie is generated with set_secure_cookie . As we know the cookie secret, we can, however, skip the first step and generate our own cookie. Digging a little into the Tornado source code, I figured out that the a valid cookie can be created using the following code snippet:

Then, we set this value (the string inside the single quotes) manually as the value of a cookie named camp in the browser and navigate to http://localhost:8000/ :

As it can be seen above, we are successfully logged in with this cookie!

The maintainer replied instantly after I reported this issue and fixed it in commit bf6af5c2e5cf713e4050c11c52dd4c55e89880b1.

The vulnerability has been assigned CVE-2022–37109.

The Github repository belonging to this post:

If you want to read about more vulnerabilities I discover, make sure you follow me on LinkedIn, Medium & Twitter:

If you run a company and are looking for an expert to make sure your web applications are secure, feel free to send an email to elias.hohl@ehtec.co to receive an offer.

--

--

Elias Hohl

CEO CryptoSearchTools | IPhO Gold medalist & Austrian national trainer | Cybersecurity expert