Introduction
不正な直列化は、歴史的に理解が非常に難しい脆弱性とされ、いわばブラックボックスのような存在でした。しかし、OWASPトップ10の他の問題も同様の課題を抱えております。直列化は、オブジェクトをバイトストリームに変換し、保存や他システムへの転送を行う技術です。直列化されたオブジェクトが逆直列化される際、危険な問題が発生する可能性がございます。この現象は、PHPでは「Marshalling」「Unmarshalling」、Pythonでは「pickling」「unpickling」と呼ばれることもあります。OWASPトップ10の観点から、この問題を見ていきましょう。
まず直列化について見ていく必要がございます。直列化は、データを送信や保存する際にアプリ内で行われる処理です。アプリ同士で内部構造が全く異なる場合にも便利な手法です。
オブジェクトは保存や転送のために直列化されることがありますが、いずれかのタイミングで逆直列化される必要がございます。その逆直列化処理の中に脆弱性が潜む可能性があります。最大の問題は、逆直列化がアプリでアクセス可能などんなオブジェクトも変換してしまう点にあり、攻撃者がオブジェクトの属性を変更したり、新たなオブジェクトを挿入するリスクがあることです。直列化の処理は、アプリの設計や直列化対象のオブジェクトによって、様々な脆弱性を引き起こす可能性がございます。以下にいくつか例を挙げますが、これ以外にも悪用方法は存在します。
パラメータ経由
例として、オブジェクトに「isSuperUser:false」という属性があるとします。攻撃者はオブジェクトを逆直列化し、属性を変更した上で再直列化するかもしれません。するとサーバは新たなオブジェクトを受け取り、ユーザーをSuperUserとして扱います。これは不正な逆直列化が発生する一例に過ぎません。
また、攻撃者はサーバがプロパティとして利用していると推測されるパラメータを追加する可能性もございます。例えば、サーバが直列化オブジェクト内の isSuperUser 属性を使用している場合、逆直列化時にそのパラメータが受け入れられることがあります。
アプリの論理経由
例として、ユーザープロフィール設定の更新機能に着目します。この機能には、ユーザーのソーシャルメディアリンクを更新する呼び出しが含まれております。通常、ユーザーはURIのユーザー名部分(例: https://twitter.com/theXSSrat の /theXSSrat 部分)のみ編集可能ですが、直列化オブジェクトにはフルURLが含まれている場合があります。
攻撃者がURLを自身の攻撃サーバ向けに変更すると、リクエストが送信され、ビジネスロジックの悪用によりブラインドSSRFが発生したことが示唆されます。本来、フルURLはオブジェクト内に存在すべきではなく、これが悪用される結果となりました。
マジックイベント経由
まず、マジックイベントとは何かを説明します。これは、プログラミング言語で自動的に呼び出されるメソッドのことです。例としてPHPには __sleep() というマジックメソッドがございます。これらのメソッドはデータを扱う際、攻撃に対して脆弱となることがあり、例えば逆直列化されたオブジェクトを通じて攻撃が行われる可能性がございます。
オブジェクトの挿入経由
前述の例は主にデータの挿入や変更に依存しておりましたが、場合によっては任意のオブジェクトが挿入されるケースもございます。オブジェクト指向プログラミングでは、オブジェクトが利用可能なメソッドは親クラスから委譲されるため、通常ユーザーオブジェクトは未公開の管理者機能(例: /makeAdmin.php)にはアクセスできません。しかし、攻撃者は makeAdmin クラスに属する直列化済みオブジェクトを、予期しないエンドポイントに送信することで、たとえばプロフィール画像変更機能を通じ、自らを管理者に昇格させる可能性があります。逆直列化処理は渡されたオブジェクトを無条件に変換してしまうため、予期せぬパラメータエラーが発生する場合もございますが、一旦被害が生じるとユーザーが自ら管理者権限を得てしまう恐れがございます。
これまでにいくつかの例を見ましたが、不正な逆直列化は大きく三種類に分類できます。
これは保存型逆直列化攻撃とも呼ばれ、攻撃者が仕掛けた攻撃ベクトルが保存され、後に逆直列化される仕組みです。攻撃が失敗すればエラー情報が、成功すれば期待する結果が返されるため、詳細な情報が得られることが多いですが、攻撃ベクトルが後で発動し、攻撃者が直接操作できないケースもございます。こうした場合、ブラインド逆直列化攻撃に分類されます。
ブラインド逆直列化攻撃は、攻撃者が送信した攻撃ベクトルがアプリに保存されるものの、例えばバックエンドシステムから発動するため、攻撃者自身が直接トリガーできない場合に発生します。この種の脆弱性は検出が難しく、攻撃者はテスト対象のオブジェクトを把握するために、アプリの内部構造を深く理解している必要がございます。
遅延実行逆直列化と呼ばれるのは、攻撃ベクトルがJavaのガベージコレクションやPHPのfile.close()など、未使用オブジェクトを整理するための二次システムによって実行されるためでございます。
ここまでPHPについて述べましたが、不正な逆直列化はその他多くのプログラミング言語にも見受けられます。それぞれの言語は直列化の過程に独自の工夫が施されており、Wikipediaのページでも示されているように多様な直列化フォーマットが存在します。直列化フォーマットが非常に多いことから、未だ発見されていない脆弱性も多数存在するのは自然な流れです。
https://en.wikipedia.org/wiki/Comparison_of_data-serialization-formats
最初の例は、Pythonが直列化をどのように扱うかです。Pythonでは直列化に pickle と marshal の二つの方法があり、Rubyでは逆直列化に対して marshal という用語が用いられます。
まず、pickleを使用する際は必ずインポートする必要がございます。
import pickle
これにより、Pythonでpickleモジュールを利用できるようになり、その後モジュールを活用できます。
次に、変数にデータを追加し、ファイルとともにpickle関数に渡してオブジェクトを直列化します。
data.append(“a”)
pickle.dump(data, file)
逆の操作も同様に簡単です。
data = pickle.load(file)
Rubyでは、marshalモジュールをインポートし、同じ手順を踏むことで同様の機能が実現されます。
data = Marshal.dump(“a”)
ご覧の通り、オブジェクトを直列化する際にデータをファイルに書き込む必要はなく、同様に逆直列化も容易に行えます。
deseriliased_data = Marshal.load(data)
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
Javaでは、使用可能なメソッドが3種類用意されており、まずはこれらを実装する必要がございます。
class s implements java.io.Serializable
次に、ObjectOutputStream上でwriteObjectメソッドを呼び出す必要がございます。
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
最後に、オブジェクトを直列化します。
// Method for serialization of object
out.writeObject(object);
ほぼ同様の手順で、逆直列化の場合はreadObjectメソッドを使用する点に注意してください。
// Reading the object from a file
FileInputStream file = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(file);
// Method for deserialization of object
object1 = (Demo)in.readObject();
ここまでプログラミング言語について述べましたが、直列化されたデータであるJSONのようなフォーマットでも、この問題は発生する可能性がございます。詳しい例は以下でご覧いただけます。
https://medium.com/r3d-buck3t/insecure-deserialization-with-json-net-c70139af011a
この攻撃者は、不正な逆直列化を悪用し、サーバ上でコードを実行することに成功しました。
最初の例 は、“Concrete5” というアプリで発見されたCVEです。このアプリはログファイルの存在チェックを行っておりました。しかし、当該ログファイルには直列化されたオブジェクトが含まれており、任意のオブジェクトを注入できるため、PHPコードを任意に実行される危険性がございました。
詳細は こちらをご覧ください。
二つ目の例 は非常に単純です。ユーザーオブジェクトが直列化され、アカウント種別が含まれております。初期状態では「user」に設定されていますが、逆直列化された後にadminへ変更され、再直列化されてサーバへ送信される可能性があります。
a:4:{i:0;i:132;i:1;s:7:”user-1”;i:2;s4:”user”;i3;s32:”b6a8b3bea87fe0e05022f8f3c88bc960”;}
太字部分のパラメータをadminに変更し直列化することで、攻撃者はサーバに送信して権限を昇格させることが可能です。
a:4:{i:0;i:132;i:1;s:7:”user-1”;i:2;s4:”admin”;i3;s32:”b6a8b3bea87fe0e05022f8f3c88bc960”;}
この脆弱性の検出方法は、使用しているプログラミング言語に依存いたします。各言語は直列化オブジェクトを示す独自の手法を持っているため、それぞれの認識方法を学ぶ必要がございます。アプリで利用されるすべてのデータを確認し、オブジェクトを特定した際には、実際に逆直列化して悪用可能か検証することが求められます。
最初の対策は最も単純ですが、実装は容易ではございません。基本として直列化の使用は避け、JSONやXMLなどの簡易なデータフォーマットへの移行が推奨されます。しかし、既に多く直列化を使用している場合、その完全な廃止は難しいこともございます。
直列化をどうしても使用する必要がある場合、ユーザー管理データを厳密に検証し、安全な内容であることを確認する必要がございます。例えば、ユーザーが自らのアカウント種別をadminに変更することは許されるべきではありません。また、型やパラメータに対する厳格な制約を回避する手法が実証されている点にも留意が必要です。
さらに、直列化処理をサンドボックスや低権限環境で実行することで、攻撃成功時の影響を軽減する対策も有効です。
OWASPトップ10の「監視とログの不十分さ」と同様、逆直列化時に発生した例外を記録し、後で検証することも重要です。
また、APIセキュリティなどの対策が、入出力されるすべてのデータに対して正しいパラメータとオブジェクトであるか検証することも有効です。
この種の脆弱性に対抗するための有力なツールがいくつか存在します。中には、Burp Suite拡張であるJava Deserialization Scannerのように、言語に特化したものもございます。また、Burp Suite Pro自体も直列化オブジェクトを検出する機能を有しており、場合によっては十分な対策となります。
また、ysoserialは、不正な逆直列化の検証用Proof of Conceptの生成に有用なツールとして利用されることがあります。
FindBugsプラグインも脆弱性を発見するのに役立つアプリです。
さらに、対象のメモリを消費させDoS攻撃を引き起こすために設計されたJinfinityもございます。
Serianalyzerは、Java向けの静的コード解析ツールです。
最後に、デフォルトのポートをスキャンするよう設計されたSerializekillerスクリプトの利用も可能です。設定を変更すれば、すべてのポートに対応させることもできます。
この問題は多くの複雑さを伴いますが、予見やテストが不可能なものではありません。テスターが直列化オブジェクトの認識と悪用方法を習得することは、サイバーセキュリティコミュニティ全体に大いに寄与することでしょう。
最新情報を購読