エクスプロイト&脆弱性
「Log4Shell」に続いて確認されたLog4jの脆弱性「CVE-2021-45105」を徹底解説
Apache Log4jで確認されたサービス拒否(DoS)攻撃に関連する脆弱性(CVE-2021-45105)について説明します。この脆弱性は「Log4Shell」(CVE-2021-44228)の直接的な亜種ではないものの、同種の攻撃経路であり、攻撃者が制御するログ情報のLookup機能を悪用する点で両者は類似しています。
本稿では、Apache Log4jで確認されたサービス拒否(DoS)攻撃に関連する脆弱性(CVE-2021-45105)について説明しています。この脆弱性は「Log4Shell」(CVE-2021-44228)の直接的な亜種ではないものの、同種の攻撃経路であり、攻撃者が制御するログ情報のLookup機能を悪用する点で両者は類似しています。ただしこの脆弱性の場合、JNDI以外のLookupが悪用される可能性があります。
Apache Log4jのAPIは、Lookupにおける変数の置換をサポートしています。この際、細工された変数を用いることで、制御不能な再帰的な置換により、アプリケーションをクラッシュさせることが可能になります。つまりこれにより、Lookupのコマンドを制御できる攻撃者(例:Thread Context Map経由など)は不正なLookup変数を細工し、DoS攻撃を引き起こすことができます。この現象は、バージョン2.16.0 を含むLog4jの各バージョンのテストで確認されています。
■ どのような脆弱性か
StrSubstitutorクラスでネストした変数を置換する場合、substitutor()クラスを再帰的に呼び出します。しかし、ネストされた変数自体が置換対象の変数として参照される場合、同じ文字列が再帰的に呼び出されることになり、無限再帰が発生し、サーバへサービス拒否攻撃を実行する状態となってしまいます。例えば、パターンレイアウトに${ctx.apiversion}のContext Lookupがあり、その置換値が${${ctx.apiversion}}であれば、その変数は再帰的に自身を置き換える状態となってしまいます。
■ コードの流れ
置換対象の変数を指定して StrSubstitutor.substitute() メソッドが呼び出されます。
図1:このコードはGitHubのこの箇所から取得可能です
StrSubstitutor.substitute()メソッドは、元の変数のLookup (ctx.apiversion)で呼び出されます。
図2:このコードはGitHubのこの箇所から取得可能です
さらにStrSubstitutor.substitute()の呼び出しからStrSubstitutor.checkCyclicSubstitution() メソッドの呼び出しが実行されます。
図3:このコードはGitHubのこの箇所から取得可能です
そしてこのStrSubstitutor.checkCyclicSubstitution()メソッドがpriorVariablesリストを保持し、現在の変数とリストを比較することによって、変数の循環的な置換を検出しようと試みます。
図4:このコードはGitHubのこの箇所から取得可能です
ここで変数が以下の自身の値
に解決され、
が再帰的に呼び出されることになります。
図5:このコードはGitHubのこの箇所から取得可能です
そして再び、解析される値から変数が検出されます。しかし、StrSubstitutor.substitute()メソッドの再帰的呼び出しではpriorVariablesリストが含まれていません。こうした理由からStrSubstitutor.checkCyclicSubstitution()メソッドは循環的な置換を検出できないため、結果的に無限再帰が発生することになります。
図6:このコードはGitHubのこの箇所から取得可能です
なお、循環的な置換がStrSubstitutor.checkCyclicSubstitution()メソッドで捕らえられた場合でも、こうして発生した例外的な置換はAppenderControl.TryCallAppender()によってのみ捕えられ、結果としてログへの書き込みに失敗するという点にも注意が必要です。
図7:このコードはGitHubのこの箇所から取得可能です
■ 修正パッチの解析
バージョン2.17.0では、StrSubstitutorを継承する以下の2つのクラスが作成されました。ConfigurationStrSubstitutorのクラスは、設定パラメータの文字列置換のみを解析します。
そしてRuntimeStrSubstitutorのクラスは、ユーザから提供された入力内容を含む文字列も解析します。ただし再帰的な評価は許可されず、この部分はStrSubstitutor.substitute()メソッドによって強制されています。
図8:このコードはGitHubのこの箇所から取得可能です
さらにまた、StrSubstitutor.substitution()の再帰的な呼び出しは、すべてpriorVariablesのリストを保持しています。こうして、StrSubstitutor.isCyclicSubstitution()メソッドによる循環的な置換を取得できます。
図9:このコードはGitHubのこの箇所から取得可能です
■ 脆弱性利用の実証
本脆弱性の利用を実証するため「log4j-vulnerable-app.jar」という典型的な脆弱なアプリケーションを作成し、log4j のバージョン 2.16.0 でコンパイルしてみました。このアプリケーションは、以下を含むカスタムパターンレイアウトで構成されています。
また、HTTPサーバが実装され、受信したX-Api-VersionヘッダをThread Context Mapの変数apiversionの値として設定しています。その上で、生成されたメッセージがログに記録されるように準備しました。
この脆弱なアプリケーションは、対象のサーバ上で次のように実行されます。
- java -jar log4j-vulnerable-app.jar
poc.pyスクリプトは、以下のように実行されます。
- python poc.py client <host>
ここでは、この脆弱なアプリケーションを実行しているホストが指定されます。
スクリプトが実行されると、X-Api-Version ヘッダに 以下の値が設定されたHTTPリクエストが脆弱なアプリケーションに送信されます。
このメッセージをログに記録しようとすると、この脆弱なアプリケーションは、以下のスタックトレースを残してクラッシュします。
図10:このコードはGitHubのこの箇所から取得可能です
■ 結論
今回説明した脆弱性への修正パッチは、2021年12月18日にApacheからリリースされています。Apacheは、緩和策も記載していますが、この脆弱性への完全な解決策としては、最新バージョンへのアップグレードを推奨しています。なお、今回紹介した脆弱性の利用に関連する手法は、現在大きく注目されており、この脆弱性の修正パッチの有無にかかわらず、さらなる脆弱性が発見される可能性もあり、今後も注意が必要です。
参考記事:
by Trend Micro Research Team
翻訳:与那城 務(Core Technology Marketing, Trend Micro™ Research )