デシリアライズ攻撃(シリアライズされたデータを不正に操作して悪意のあるコードを実行する攻撃)は、組織に深刻な損害をもたらす可能性があります。開発者とセキュリティチームは、攻撃がいつ、どこで、どのように発生したかを明らかにする重要な脆弱性を積極的に特定する必要があります。
この脅威の最近の例は、CVE-2023-34040で、Apache Kafka用のSpringのデシリアライズ攻撃ベクトルで、RCE(リモートコード実行)につながる可能性があります。実際、デシリアライズの脆弱性の蔓延は、OWASP Top 10 リストで強調されており、最も重大なウェブアプリケーションセキュリティリスクのトップ10に「信頼できないデータのデシリアライズ」が含まれています。
SBOMSoftware 部品表)エンジンを搭載した OPSWAT MetaDefender Core™のようなツールは、デシリアライズ攻撃の検出と防止に不可欠です。これらにより、開発者はコードを効率的にスキャン・分析し、脆弱性を見落とさないようにすることができます。
このブログでは、CVE-2023-34040の詳細とその悪用について、また同様の脅威からオープンソースのコンポーネントを保護する方法について、フェローが説明します。
CVE-2023-34040について
CVE-2023-34040は、Spring for Apache Kafkaにおけるデシリアライズ攻撃のベクトルを明らかにするもので、通常とは異なる設定が適用された場合に悪用される可能性がある。この脆弱性は、攻撃者がデシリアライズ例外レコードヘッダの1つに悪意のあるシリアライズオブジェクトを構築することを可能にし、RCEを引き起こす可能性があります。この脆弱性は、Spring for Apache Kafkaのバージョン2.8.1から2.9.10および3.0.0から3.0.9に影響します。
具体的には、アプリケーション/コンシューマは、以下のような特定の設定や条件の下で脆弱性を持つ:
- ErrorHandlingDeserializer クラスが、レコードのキーまたは値に対して設定されていません。
- コンシューマの checkDeserExWhenKeyNull および/または checkDeserExWhenValueNull プロパティが true に設定される。
- 信頼できないソースは、Kafkaトピックへのパブリッシュを許可される。
アパッチ・カフカ
ApacheSoftware Foundationによって開発されたApache Kafkaは、データベース、センサー、mobile デバイスなど様々なソースからのリアルタイムデータストリームをキャプチャ、処理、応答、ルーティングするために設計された分散イベントストリーミングプラットフォームである。
例えば、商品チェックアウトの完了や支払いなど、顧客のアクティビティに反応するサービスに通知を流すことができる。
Apache Kafkaでは、イベント(レコードまたはメッセージとも呼ばれる)は、データが読み込まれたり書き込まれたりするたびに、アプリケーションで発生したことを表すデータユニットとして機能する。各イベントには、キー、値、タイムスタンプ、およびオプションのメタデータヘッダが含まれます。
キーバイナリ (NULLでも可) | バイナリ値 (NULLでも可) | ||||
圧縮タイプ [なし、gzip、snappy、lz4、zstd]。 | |||||
ヘッダー(オプション)
| |||||
パーティション+オフセット | |||||
タイムスタンプ(システムまたはユーザー設定) |
イベントは永続的に保存され、トピックに編成される。Kafkaトピックにイベントを送信(書き込み)するクライアントアプリケーションはプロデューサーと呼ばれ、イベントをサブスクライブ(読み取り、処理)するものはコンシューマーと呼ばれる。
Apache KafkaのためのSpring
Apache KafkaとSpringエコシステムを接続するには、開発者はJavaアプリケーションでの統合を簡素化するSpring for Apache Kafkaを使用できる。
Spring for Apache Kafkaは、Kafkaでイベントを送受信するプロセスを簡素化する堅牢なツールとAPIを提供し、開発者が大規模で複雑なコーディングをすることなく、これらのタスクを達成できるようにします。
シリアライズとデシリアライズ
シリアライゼーションとは、オブジェクトの状態を文字列やバイトストリームに変換する仕組みである。一方、デシリアライズはその逆のプロセスで、シリアライズされたデータを元のオブジェクトやデータ構造に変換します。シリアライゼーションによって複雑なデータを変換し、ファイルに保存したり、ネットワーク経由で送信したり、データベースに保存したりできるようになります。シリアライゼーションとデシリアライゼーションは、分散システムでのデータ交換に不可欠であり、ソフトウェア・アプリケーションのさまざまなコンポーネント間の通信を促進する。Javaでは、writeObject()がシリアライズに使用され、readObject()がデシリアライズに使用される。
デシリアライズはバイト・ストリームや文字列をオブジェクトに変換するため、入力データの不適切な取り扱いや適切な検証の欠如は、重大なセキュリティ脆弱性をもたらし、RCE攻撃につながる可能性があります。
脆弱性分析
CVEの説明によると、OPSWAT フェローは、セキュリティ脆弱性を誘発するために、checkDeserExWhenKeyNullと checkDeserExWhenValueNullをtrueに設定した。空のキー/値を持つレコードを送信し、コンシューマーがプロデューサーからKafkaレコードを受信する際にデバッグを行うことで詳細な分析を行ったところ、フェローはレコード処理中に以下のようなワークフローを発見した:
ステップ1:レコード(メッセージ)の受信
レコードを受信すると、コンシューマーはinvokeIfHaveRecords()メソッドを呼び出し、次にinvokeListener()メソッドを呼び出して、レコードの実際の処理のために登録されたレコードリスナー(@KafkaListenerアノテーションが付けられたクラス)をトリガーします。
そしてinvokeListener()はinvokeOnMessage()メソッドを呼び出す。
ステップ2:記録のチェック
invokeOnMessage()メソッド内では、レコード値と構成プロパティに対していくつかの条件が評価され、その後に実行される次のステップが決定される。
レコードに NULL キーまたは値があり、checkDeserExWhenKeyNullまたはcheckDeserExWhenValueNullプロパティが明示的にtrue に設定されている場合、checkDeser()メソッドが呼び出されてレコードが検査されます。
ステップ3:ヘッダーから例外をチェックする
checkDesr()では、コンシューマは、getExceptionFromHeader()を継続的に呼び出して、レコードのメタデータから例外が存在する場合はそれを取得し、その結果をexceptionという変数に格納します。
ステップ4:ヘッダーからの例外の抽出
getExceptionFromHeader()メソッドは、Kafka レコードのヘッダーから例外を抽出して返すように設計されています。まずレコードのヘッダを取得し、次にバイト配列に格納されているヘッダの値を取得します。
その後、ヘッダー値のバイト配列をbyteArrayToDeserializationException()メソッドに転送し、さらに処理する。
ステップ5:データのデシリアライズ
byteArrayToDeserializationException()では、resolveClass()関数をオーバーライドして、 許可されたクラスのみにデシリアライズを制限します。この方法によって、明示的に許可されていないクラスのデシリアライズを防ぐことができます。ヘッダーのバイト配列値は、resolveClass() で設定された条件を満たす場合にのみ、byteArrayToDeserializationException ()内でデシリアライズできます。
しかし、DeserializationExceptionクラスは標準のExceptionクラスを拡張し、4 つのパラメータを持つコンストラクタを含んでいます。最後のパラメータcause は、IOExceptionやClassNotFoundException など、DeserializationException のトリガーとなった元の例外を表します。
Throwableクラスは、Javaで例外やエラーとしてスローされるすべてのオブジェクトのスーパークラスとして機能します。Javaプログラミング言語では、Throwable、Exception、Errorなどの例外処理クラスは、安全にデシリアライズできます。例外がデシリアライズされるとき、Javaは、通常のクラスに適用されるものよりも要求の少ないチェックで、クラスのThrowable親クラスをロードし、インスタンス化することを許可します。
ワークフローと包括的な分析に基づき、シリアライズされたデータが親クラスThrowableを継承する悪意のあるクラスに対応する場合、条件チェックをバイパスする可能性があります。これにより、悪意のあるオブジェクトのデシリアライズが可能になり、有害なコードが実行され、RCE攻撃につながる可能性があります。
開発
分析で示されているように、この脆弱性を悪用するには、Kafkaヘッダーレコードを介してコンシューマーに送信される悪意のあるデータを生成する必要があります。最初に、攻撃者はThrowableクラスを拡張した悪意のあるクラスを作成し、次にリモートコード実行を達成するためにガジェットチェーンを利用しなければなりません。ガジェットはアプリケーション内で悪用可能なコードスニペットであり、それらを連鎖させることで、攻撃者は有害なアクションをトリガーする「シンクガジェット」に到達することができます。
以下は、Spring for Apache Kafkaのこの脆弱性を悪用できる悪意のあるクラスです:
次に、悪意のあるクラスのインスタンスが作成され、DserializationExceptionクラスのコンストラクタのcauseパラメータの引数として渡されます。DeserializationExceptionインスタンスはその後バイトストリームにシリアライズされ、悪意のあるKafkaレコードのヘッダーの値として使用されます。
攻撃者が被害者を欺いて悪意のあるプロデューサを使わせることに成功すれば、コンシューマに送られるKafkaレコードをコントロールすることができ、システムを侵害する機会を作り出すことができる。
脆弱なコンシューマーが、悪意のあるプロデューサーから、NULLのキーと値を含むKafkaレコードを、レコードヘッダ内の悪意のあるシリアライズされたインスタンスとともに受け取ると、コンシューマーは、デシリアライズ処理を含むレコードを処理します。これは最終的にリモート・コードの実行につながり、攻撃者はシステムを侵害することができます。
MetaDefender CoreSBOMによるCVE-2023-34040の緩和
CVE-2023-34040に関連するリスクを効果的に軽減するために、企業はオープンソースコンポーネントの可視化と制御を提供する包括的なソリューションを必要としています。
MetaDefender Core基盤技術であるSBOMは、強力な答えを提供します。使用中のすべてのソフトウェアコンポーネント、ライブラリ、依存関係の包括的なインベントリとして機能することで、SBOMは、組織がプロアクティブかつ効率的な方法でオープンソースコンポーネントを追跡、保護、更新することを可能にします。
SBOMを使えば、セキュリティチームは次のことができる:
- 脆弱なコンポーネントを迅速に特定デシリアライズ攻撃の影響を受けるオープンソースのコンポーネントを即座に特定します。これにより、脆弱なライブラリにパッチを適用するか、置き換えるかの迅速な対応が可能になります。
- 積極的なパッチ適用とアップデートの徹底:SBOM を通じてオープンソースのコンポーネントを継続的に監視し、デシリアライゼーションの脆弱性を未然に防ぎます。SBOM は、古くなったコンポーネントや安全でないコンポーネントを検出できるため、タイムリーな更新が可能になり、攻撃にさらされる機会を減らすことができます。
- コンプライアンスとレポーティングの維持SBOM は、規制の枠組みがソフトウェアサプライチェーンの透明性をますます義務付けているため、組織がコンプライアンス要件を満たすのに役立ちます。
おわりに
デシリアライズの脆弱性は、広範なアプリケーションを悪用するために使用される可能性のある、重大なセキュリティの脅威です。これらの脆弱性は、アプリケーションが悪意のあるデータをデシリアライズするときに発生し、攻撃者が任意のコードを実行したり、機密情報にアクセスしたりすることを可能にする。Spring for Apache Kafkaの脆弱性CVE-2023-34040は、デシリアライズ攻撃の危険性を痛感させるものだ。
デシリアライゼーション攻撃を防ぐには、OPSWAT MetaDefender Core そのSBOMテクノロジーのような高度なツールの導入が不可欠です。組織はソフトウェアのサプライチェーンを深く可視化し、脆弱性のタイムリーなパッチを確実に適用し、進化し続ける脅威から身を守ることができます。オープンソースコンポーネントを積極的に保護することは、単なるベストプラクティスではありません。