安全でない逆シリアル化

OWASP カテゴリ: MASVS-CODE: コード品質

概要

多くの場合、Java オブジェクト データを大量に保存または転送する場合は、最初にデータをシリアル化したほうが効率的です。その後、受信側のアプリ、アクティビティ、またはプロバイダによって、シリアル化解除プロセスが実行され、最終的にデータを処理します。通常、データはユーザーの介入なしにシリアル化され、シリアル化解除されます。ただし、シリアル化解除プロセスとその対象オブジェクト間の信頼関係は、シリアル化されたオブジェクトをインターセプトして変更するなど、悪意のある攻撃者によって悪用される可能性があります。これにより、悪意のある攻撃者がサービス拒否(DoS)、権限昇格、リモートコード実行(RCE)などの攻撃を実行できるようになります。

Serializable クラスはシリアル化を管理するための一般的なメソッドですが、Android にはシリアル化を処理するための独自のクラス Parcel があります。Parcel クラスを使用すると、オブジェクト データをバイト ストリーム データにシリアル化し、Parcelable インターフェースを使用して Parcel にパックできます。これにより、Parcel をより効率的に転送または保存できます。

ただし、Parcel クラスは高効率の IPC 転送メカニズムとして設計されているため、使用時には慎重に検討する必要があります。ローカル永続ストレージ内にシリアル化されたオブジェクトを保存するために使用すると、データの互換性の問題やデータの損失につながる可能性があります。データを読み取る必要がある場合は、Parcelable インターフェースを使用して Parcel を逆シリアル化し、オブジェクト データに戻すことができます。

Android でシリアル化解除を悪用する主なベクトルは 3 つあります。

  • カスタム クラス型から続くオブジェクトの逆シリアル化が安全であるという、デベロッパーの誤った前提を悪用します。実際には、任意のクラスから取得された任意のオブジェクトが悪意のあるコンテンツに置き換えられる可能性があり、最悪のシナリオでは、同じアプリまたは他のアプリのクラスローダを妨害する可能性があります。この干渉は、クラスの目的によっては、データの漏洩やアカウント乗っ取りなどにつながる可能性がある危険な値を挿入する形で発生します。
  • 設計上安全でないと考えられるシリアル化解除メソッドの悪用(CVE-2023-35669 など。ディープリンクのシリアル化解除ベクトルによる任意の JavaScript コード挿入を許可するローカル権限昇格の欠陥)。
  • アプリロジックの欠陥を悪用する(CVE-2023-20963 など。Android の WorkSource の parcel ロジック内の欠陥により、アプリが特権環境内でコードをダウンロードして実行できるローカル権限昇格の欠陥)。

影響

信頼できないデータや悪意のあるシリアル化されたデータをシリアル化解除するアプリケーションは、リモート コード実行攻撃やサービス拒否攻撃の対象となる可能性があります。

リスク: 信頼できない入力のシリアル化解除

攻撃者は、アプリケーション ロジック内での Parcel の検証がないことを利用して、任意のオブジェクトを挿入できます。このオブジェクトは逆シリアル化されると、アプリケーションが悪意のあるコードを強制的に実行し、サービス拒否(DoS)、権限昇格、リモートコード実行(RCE)につながる可能性があります。

この種の攻撃は軽微である可能性があります。たとえば、アプリに、検証後にシリアル化解除されるパラメータを 1 つだけ想定するインテントを含めることができます。攻撃者が想定されるエクストラ パラメータとともに、予期しない 2 つ目の悪意のあるエクストラ パラメータを送信すると、インテントがエクストラを Bundle として扱うため、挿入されたすべてのデータ オブジェクトが逆シリアル化されます。悪意のあるユーザーがこの動作を利用してオブジェクト データを挿入すると、逆シリアル化後に RCE、データの不正使用、データの損失につながる可能性があります。

リスクの軽減

ベスト プラクティスとして、シリアル化されたすべてのデータが信頼できないものであり、悪意のある可能性があると想定します。シリアル化されたデータの整合性を確保するには、データの検証チェックを実行して、アプリケーションで想定される正しいクラスと形式であることを確認します。

実用的な解決策としては、java.io.ObjectInputStream ライブラリに先読みパターンを実装することが挙げられます。シリアル化解除を担当するコードを変更することで、インテント内で明示的に指定されたクラスのセットのみをシリアル化解除できます。

Android 13(API レベル 33)では、Intent クラス内のいくつかのメソッドが更新されました。これらのメソッドは、古いメソッド(現在は非推奨)に代わる、より安全な方法と見なされています。getParcelableExtra(java.lang.String, java.lang.Class)getParcelableArrayListExtra(java.lang.String, java.lang.Class) などの新しい型安全なメソッドは、データ型チェックを実行して、アプリのクラッシュや、CVE-2021-0928 などの権限昇格攻撃の実行に悪用される可能性のある不一致の弱点を検出します。

次の例は、Parcel クラスの安全なバージョンを実装する方法を示しています。

クラス UserParcelableParcelable を実装し、ユーザーデータのインスタンスを作成すると、それが Parcel に書き込まれます。次に、readParcelable の次の型セーフなメソッドを使用して、シリアル化された小包を読み取ることができます。

Kotlin

val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)

Java

Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);

上記の Java の例では、メソッド内で UserParcelable.CREATOR が使用されています。この必須パラメータにより、想定される型が readParcelable メソッドに伝えられ、サポートが終了したバージョンの readParcelable メソッドよりもパフォーマンスが高くなります。

特定のリスク

このセクションでは、標準的でない軽減戦略が必要、または特定の SDK レベルで軽減されたリスクをまとめています。また、完全を期すためにその他のリスクも挙げています。

リスク: 不要なオブジェクトの逆シリアル化

クラス内に Serializable インターフェースを実装すると、指定されたクラスのすべてのサブタイプで自動的にインターフェースが実装されます。このシナリオでは、一部のオブジェクトが前述のインターフェースを継承する場合があります。つまり、シリアル化解除が意図されていない特定のオブジェクトが引き続き処理されます。これにより、意図せず攻撃対象領域が拡大する可能性があります。

リスクの軽減

クラスが Serializable インターフェースを継承している場合、OWASP ガイダンスに従い、クラス内のオブジェクトのセットが逆シリアル化されないように、readObject メソッドを次のように実装する必要があります。

Kotlin

@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
    throw IOException("Cannot be deserialized")
}

Java

private final void readObject(ObjectInputStream in) throws java.io.IOException {
    throw new java.io.IOException("Cannot be deserialized");
}

リソース