October 27, 2013

Write-up: NotSoSecure CTF

This is a writeup for the public CTF hosted by NotSoSecure for the celebration of SQLi Labs’s launch. It started 16:00 BST on Friday 25th October and ended 21:00 BST on Sunday 27th October. Three PizzaEaters’ members participated.

First flag

In the mail containing the instructions to start the CTF, the following URL was supplied:


This page requires an username and password, but there was no mention of it in the email.

We started with the classic attempt of injecting 1' or '1'='1 string for both username and password. Having no luck, we started looking around a bit closer.

We submitted the form empty, and noticed an HTTP 302 redirect with the following information:


We noticed the hex encoding and we decoded the string with this simple Python script:

s = '7365637265745f72656769737465722e68746d6c'
print ''.join([chr(int(s[n:n+2], 16)) for n in xrange(0, len(s), 2)])

The script basically takes those numbers 2 at a time and convert them to their respective ascii char. The output for it was ‘secret_register.html’.

Great! We now have a different page name to play with. Accessing it we were greeted with a registration form that we filled with some dummy values just to test.


After submitting our registration, we tried to login with our newly created user. At this point we found ourselves in a page with a crying figure lamenting that we were not Admin. :(


Looking at the requests that were made during the login phase, we noticed the following cookie being set:

Set-Cookie: session_id=Zm9vQGJhci5jb20%3D

This looks a lot like a base64 encoded value.

$ echo -n "Zm9vQGJhci5jb20=" | base64 -d

So, the page is setting a cookie containing the user’s email. And that is it, it seems.

At this point, nothing too interesting. Perhaps it was time to try the classic SQL Injection approach in the registration form, as doing it in the login form resulted on nothing.

We tried creating an user called !!' or name = 'admin just to see what would happen (after all, the name of the field in the form was ‘name’ so why not give it a shot?). We logged in with our new user and to our surprise the following cookie was set:

Set-Cookie: session_id=YWRtaW5Ac3FsaWxhYnMuY29t

Decoding that

$ echo -n "Zm9vQGJhci5jb20=" | base64 -d

Perfect! We just found the flaw. Now we need to figure out the password column and the table name to retrieve the password of the admin user. We assumed the column would be called password so we now only had to discover the table name. We looked up for tables with password-named columns with the following SQL:

!!' union select (select table_name from information_schema.columns where column_name = 'password'), 'a

Turns out it was called users. How predictable! Why didn’t we try users before typing all that SQL?

Now we only needed to know the user’s password. So we injected the following:

!!' union select password, name from users where name = 'admin

Which gave us sqlilabRocKs!! as the admin password.

Logging in with our newly found credentials we get:

Well done, Flag is 815290. 2nd flag is in file secret.txt

Second flag

The first flag revealed that the server had a secret.txt file, which we tried to read using load_file() in a SQLi only to find that we were unable to do so. How foolish of us! Of course it wouldn’t be that easy.

After we put some thought on it, sigsegv (a team member) suggested trying to read /etc/passwd. We did it by injecting this:

!!' union select load_file('/etc/passwd'), 'a

Guess what we found? An user which had his password field set:


We ssh’d into ctf.notsosecure.com using that username and password and it worked. Now we only had to read /secret.txt:

$ cat /secret.txt 
cat: /secret.txt: Permission denied

Oh snap! Why can’t we read it? ls -l revealed it could only be read by www-data:

$ ls -l
-r--------   1 www-data www-data   684 Oct 25 07:46 secret.txt

We couldn’t use sudo or su, we didn’t find any exploitable suid executable (we didn’t search thoroughly actually) and we could not put a script into /var/www to dump the contents of the file.

“Damn”, we thought, “how the hell are we going to read that file?”.

Looking for more options, we went to apache’s configuration directory and then into it’s mods-enabledfolder:

$ pwd
$ ls -l
lrwxrwxrwx 1 root root 30 Oct  6 17:17 userdir.conf -> ../mods-available/userdir.conf
lrwxrwxrwx 1 root root 30 Oct  6 17:17 userdir.load -> ../mods-available/userdir.load

“Mua-ha-ha!” Now everything seemed clear! Mod userdir lets us create a directory at the users home and access it via the webserver.

We then created the directory ~/public_html and put a little PHP script to dump the secret.txt file:

<?php echo file_get_contents('/secret.txt');

We opened the URL http://ctf.notsosecure.com/~temp123/ and there it was, the flag.

Well done, 2nd Flag is 128738213812990. email both the flags to ctf@notsosecure.com [...]

We had finally reached the goal of our quest. It was a pleasant challenge!

Thanks to SQLi Labs and NotSoSecure for all the fun.

See you next time!