SuiteCRM:PHARデシリアライゼーションの脆弱性によるコード実行
Sam Sanoop
2021年5月7日
0 分で読めますSuiteCRMは、サーバー向けの無料のオープンソース顧客関係管理アプリケーションです。このアドバイザリでは、SuiteCRMに存在するPHARデシリアライゼーションの脆弱性について詳しく解説します。この脆弱性を悪用すると、認証済みの管理者が基盤となるオペレーティングシステム上でコマンドを実行できる可能性があります。この問題はリリース7.11.19で修正されています。
PHPでは、PHAR(PHP Archive)ファイルを使用して、PHPアプリケーションやPHPライブラリを1つのアーカイブファイルにまとめることができます。PHPのPHAR形式は、複数のPHPコードを保存して実行できる単一ファイル形式です。PHARファイルには、アーカイブ内のファイルに関するメタデータが含まれています。このメタデータはPHARファイル内にシリアライズ形式で保存されます。
phar://ラッパーを介してPHARファイルに対するファイル操作を行うと、PHARファイルのメタデータがデシリアライズされます。そのため、攻撃者はPHARファイルをアップロードすることで、unserialize()関数を使わずにPHPオブジェクトインジェクションを実行できる可能性があります。この脆弱性は、Sam Thomas氏がBlackHat USA 2018で発表した「It's a PHP Unserialization Vulnerability Jim, but Not as We Know It」という研究によって広く知られるようになりました。この問題は複数のコンテンツ管理システムに影響しました。この脆弱性を悪用するには、次の2つの条件を満たす必要があります。
file_existsなど、制御可能でunserialize()関数を呼び出すPHPのファイルシステム関数があること対象システムに.phar、.zip、または.jpeg拡張子のPHARファイルをアップロードでき、そのファイルのパスが判明していること
PHARデシリアライゼーションの技術的詳細
根本原因
この脆弱性は、CVE-2020-8801への対応としてSuiteCRMのメンテナーが実装したセキュリティ対策を回避します。CVE-2020-8801のアドバイザリに記載されているように、SuiteCRMの管理者パネル内にあるバックアップ、インポート、UpgradeWizardなど複数の領域で、PHARデシリアライゼーションを引き起こす可能性があります。この問題は、リリース7.11.13でコミット571cbfa209da4c8280a5359f301115de25b4c6e3により修正されました。

ユーザーパラメーターにphar:// URIが含まれているかどうかを確認するために、strpos関数が使用されています。しかし、strpos関数を使用しているため、このチェックでは大文字と小文字が区別されます。そのため、大文字を使ってチェックを回避し、PHAR://として呼び出すことでPHARデシリアライゼーションを引き起こせます。
利用可能なガジェットを探す
PHAR URIのチェックに対するSuiteCRMのパッチを回避することは可能ですが、SuiteCRMのメンテナーは、攻撃者がデシリアライゼーションを利用して実行できる、あらゆるPOPガジェットが悪用されないよう対策を講じていました。危険と見なされたクラスに__wakeupマジックメソッドを追加する方法です。このメソッドはオブジェクトがデシリアライズされるたびに自動で呼び出され、デシリアライズ時にオブジェクトのすべてのプロパティが破棄されるようにします。以下に例を示します。

modules/Import/sources/ImportFile.phpには__destructマジックメソッドがあり、安全でないデシリアライゼーションを通じてシステム上の任意のファイルを削除するために悪用される可能性があります。しかし、前述の__wakeupメソッドが存在するため、攻撃者はこの__destructメソッドを悪用できません。このチェックはSuiteCRMのコードベース全体で確認されました。
しかし、SuiteCRMの依存関係を詳しく調べたところ、任意のファイル削除に悪用できる__destructメソッドが見つかりました。SuiteCRMはzend-gdataライブラリを使用しており、その間接依存関係としてzf1/zend-httpが使われています。
zf1/zend-httpパッケージのStream.phpファイルには__destructメソッドがあり、呼び出されると、オブジェクトの$stream_nameプロパティに対して@unlink関数を実行します。このメソッド内の@unlink関数を利用してファイルを削除できます。この関数を呼び出すには、対象オブジェクトに$_cleanupプロパティも存在する必要があります。この__destructメソッドのコードを以下に示します。

このガジェットはすでに知られており、PHPGGCライブラリで利用できます。PHPGGCはunserialize()ペイロードのライブラリで、ユーザーが指定したパラメーターに基づいてPOPガジェットのペイロードを生成するスクリプトも含まれています。
注:SuiteCRMのインストール環境にすべての開発用依存関係もインストールされている場合は、脆弱なバージョンのPHP-CS-Fixerを利用して、zf1/zend-httpの代わりに任意のファイルを削除することもできます。このPOPガジェットをPHPGGCに組み込むプルリクエストはこちらです:https://github.com/ambionics/phpggc/pull/93。
アップロード機能の特定と悪用
CVE-2020-8801のアドバイザリでは、PHARデシリアライゼーションに利用できるパラメーターやSuiteCRM内の領域について説明されていますが、アドバイザリと、この問題を発見した研究者の関連ブログには、PHARファイルをどこにアップロードできるのか、また悪用可能なパスや場所についての記載がありません。さらに、SuiteCRMにアップロードされたファイルは拡張子が取り除かれ、ファイル名がランダムなUUIDに変更されることも確認されました。たとえば、ドキュメントとしてparty-parrot.pngをアップロードすると、51eea769-ce13-40bb-fb25-60197bf855aeという名前に変更されます。

しかし、SuiteCRMの機能をさらに詳しく調べたところ、Module LoaderからアップロードしたZIPファイルはこの動作の対象外であり、モジュールのZIPファイルは攻撃者が推測可能な任意の場所に.zip拡張子のままアップロード、保存されることが分かりました。これはPHARデシリアライゼーションの脆弱性を悪用するための要件を満たすのに利用できます。以下に例を示します。

アップロードされたexamplemodule.zipファイルは、次の場所に保存されます。manifest.phpファイル自体はmodule.zipディレクトリから取得され、同じ場所で参照できます。

任意のファイル削除を引き起こす
アップロードしたZIPファイルのパスが判明し、ファイル削除ガジェットが存在し、PHARデシリアライゼーションを引き起こす関数が利用できる場合、これらの条件を組み合わせてSuiteCRMのインストール環境を悪用できます。
PHPGGCを使うと、次のようにPHARファイルを作成できます。
上記のコマンドは、ZIP形式のPHARファイルを作成し、malicious.zipという名前で保存します。前述のzf1/zend-http POPガジェットのシリアライズ済みペイロードを挿入し、このシリアライズされたオブジェクトの$stream_nameプロパティを/tmp/test.txtに設定します。さらに、PHPGGCの-fオプションを使用すると、unserialize()の呼び出し直後にシリアライズされたオブジェクトが破棄されるようになり、ペイロードの信頼性を高めることができます。
注:このZIPファイルをアプリケーションに受け入れさせるには、example manifest.phpファイルもZIPに追加する必要があります。Linuxでは、次のように実行できます。
アップロード後、このファイルはupload/upgrades/moduleディレクトリに配置されます。

このファイルは、SuiteCRM管理パネルのバックアップセクションから呼び出せます。

ファイル削除からコード実行へ
ファイル削除を悪用すれば、アプリケーションのソースコードファイルを削除してサービス拒否攻撃を実行できます。また、.htaccessファイルを削除してアップロードされたファイルに直接アクセスすることで、SuiteCRMのインストール環境上で任意のコードを実行することも可能です。
.htaccessは分散設定ファイルで、Apacheがディレクトリごとの設定変更を処理するために使用します。SuiteCRMのインストール環境では、このファイルによって/uploadや/filesなどのディレクトリへの直接アクセスが禁止されています。PHARデシリアライゼーションを利用して、このファイルを削除できます。さらに、前述のmainfest.phpを使ってPHPコードを紛れ込ませ、実行させることができます。
Module Loader機能でモジュールをアップロードする際、SuiteCRMはmanifest.phpも含まれていることを想定しています。以下に例を示します。

デフォルトではSuiteCRMは、アップロードされたファイルにexecやsystemなどの不審な関数呼び出しが含まれていないことを確認します。制限対象の関数呼び出しの全一覧はこちらです:Module Loader Restrictions
このチェックでは「include」関数が考慮されていません。PHPのinclude関数を使うと、ファイル(デフォルトではローカルファイル)を読み込み、PHPコードとして実行できます。そのため、ユーザーパラメーターを受け取るinclude関数(include($_GET['p']);)をmanifestファイルに紛れ込ませ、SuiteCRMによって名前が変更されるアップロードファイルに悪意のあるPHPコードを含めて渡せば、コードを実行できます。
PHARデシリアライゼーションのまとめ
PHARデシリアライゼーションによるコード実行の流れをまとめると、次のとおりです。
1. SuiteCRMにファイルをアップロードする
通常のファイル保存機能を使って、SuiteCRMにファイルをアップロードします。この例では、次のファイルをshell.phpとしてアップロードします:<?php echo system($_GET["cmd"]); ?>

このファイルはアプリケーションによってeb7cde01-6190-9ca9-b81b-601998504b41に名前が変更され、/var/www/html/uploadsディレクトリに保存されます。このUIDはファイルのURLで確認できます。
2. ZIP形式のPHARをアップロードする
呼び出されると.htaccessファイルを削除し、PHPコードを紛れ込ませたmanifest.phpを含むZIP形式のPHARアーカイブをアップロードします。PHPGGCは次のように使用できます。
次のコードをmanifest.phpファイルに追加し、そのファイルをPHAR ZIPアーカイブに追加します。
![GETパラメータを使用したPHPのinclude文「include($_GET[123]);」がハイライト表示されたコードエディター](https://res.cloudinary.com/snyk/image/upload/f_auto,w_2560,q_auto/f_auto/q_auto/v1620399714/wordpress-sync/blog-suitecrm-phar-include.png)
このZIPアーカイブは、Module Loaderコンポーネントを使ってシステムにアップロードできます。
3. PHARデシリアライゼーションを引き起こす
maliciousmodule.zipファイルを呼び出して、PHARデシリアライゼーションを引き起こします。バックアップ領域を使ってPHARデシリアライゼーションを実行できます。アプリケーションに送信するHTTPリクエストは次のとおりです。
4. maliciousmoduleのmanifestにアクセスする
ディレクトリパスにアクセスし、アップロードしたshell.php.txtを読み込んで、malicousmoduleのmanifestファイルにアクセスします。.htaccessファイルが削除されているため、アップロードディレクトリにアクセスできるようになります。malcioiusmodule-manifest.phpには次のようにアクセスできます。
?123を指定し、shell.php.txtファイルを渡せるようになります。
読み込まれたshell.php.txtはコードとして解釈され、cmdパラメーターで指定した内容がシステムコマンドによって実行されます。

まとめ
まとめると、ユーザー入力を検証する際は、大文字と小文字を区別しないチェックにならないよう注意が必要です。さらに、デシリアライゼーションガジェットの使用を修正・防止する場合は、Composerのオートロード機能を通じてプロジェクトに読み込まれる間接依存関係も考慮する必要があります。Snyk Open SourceはPHPのスキャンにも対応しており、コードからデシリアライゼーションガジェットを削除した既知のライブラリをスキャンして報告できます。
