CVE-2023-25828は、Black Duck KnowledgeBase™でBDSA-2023-0370として追跡されており、Pluck CMSにおける認証されたリモート・コード実行の脆弱性です。Pluckは、Webサイトのセットアップと管理に使用されるPHPベースのコンテンツ管理システム(CMS)です。シンプルで使いやすいPluck CMSは、個人のブログなどの小規模なWebサイトの運営に最適です。このソフトウェアは、2005年にCMSsystemという名前で最初にリリースされて以来、保守を重ね、2014年にGitHubに移行してから53個の星を獲得しています。この脆弱性は、Pluck CMSソースコードの監査中にSynopsys Cybersecurity Research Center(CyRC)のリサーチャーによって発見されました。
ソースコードのレビューは、ファイル・アップロード機能に特に注意を払って行われました。ファイルのアップロードは多くの種類のアプリケーションで危険を伴う可能性がありますが、特にPHPアプリケーションではリスクが高いため、この機能が最初に調査されました。
管理者は、Pluck CMSを使用して画像をアップロードできます。
図1:Pluck CMSでの画像のアップロード
この機能のソースコードを参照しても、明らかな脆弱性は見当たりませんが、MIMEタイプとファイル拡張子を許可リストと照合してチェックします。
スニペット1:data/inc/images.php
問題は、この許可リストの実装がすべてのファイル・アップロード機能に使用されているかどうかです。もう1つのファイル・アップロード機能、albumsモジュールの画像アップロードを調べてみましょう。この機能は、画像をアルバムに収集し、Webページに一度に追加できる点を除いて、画像のアップロード機能と同様です。
この機能に関連する関数は、data/modules/albums/albums.admin.phpファイルのalbums_page_admin_editalbum()です。
スニペット2:data/modules/albums/albums.admin.php
ファイルを送信すると、MIMEタイプが許可リストと照合してチェックされます。
次に、画像ファイル名に対する処理が実行されます。この処理では、‘ . ’文字の位置を判断し、‘ . ’文字が見つからなかった場合のためにファイル名と拡張子を分離するロジックを記述し、ファイル名に対して検索エンジンの最適化を実行して、完全な画像と画像サムネイルの絶対パスを構築します。
興味深いことに、このプロセスのどこにも、アプリケーションが許可リストを使用してファイル拡張子を制限する処理はありません。これは、アップロードされたファイルの内容を基盤となるWebサーバーで解釈して実行できるようにするファイル拡張子(.phpや.pharなど)を制限なく使用できるということなので、直ちに警告が発せられます。
ファイル拡張子と有効なMIMEタイプを使用してこの動作を悪用するさまざまな方法があります。Webシェルは、ファイルの内容を完全に置き換えたり、ファイルの内容に追加したり、画像のメタデータに含めたりすることができますが、この例の場合には、関数の後半にロジックがあるため、これらのいずれの手法も適用できません。
スニペット3:data/modules/albums.admin.php
前に計算された$fullimageパスを使用してSmartImageオブジェクトをインスタンス化します。サイズ変更も問題のように思えるかもしれませんが、高さと幅が同じ画像を指定することで回避できます。それでは、SmartImageコンストラクタの内容を見てみましょう。
スニペット4:data/inc/lib/SmartImage.class.php
__construct関数は、PHP-GDの組み込みのgetimagesize関数を使用して、関数によって割り当てられた定数から処理される画像の種類を識別します。この関数に$this->info[2]を介してアクセスし、別のPHP-GD組み込み関数imagecreatefrom*を使用して、定数の値に応じて画像を処理します。これらの関数は、ファイルに追加されたWebシェルや画像メタデータに挿入されたWebシェルなど、単純な方法で埋め込まれたWebシェルを破棄する正規化を実行します。
この時点で、CyRCのリサーチャーは、これらのimagecreatefrom*関数の動作を調査しました。まずimagecreatefromjpegに着目し、正規化後にも存続できる方法でWebシェルを埋め込むことが可能であることを示す先行研究を発見しました。Jellypegツールを使用すると、圧縮プロセス後に小さなコードが残っている場所を見つけることができます。imagecreatefromjpeg関数とimagejpeg関数によって処理された後もファイルのコンテンツ内にWebシェルが残っているオフセットをブルートフォース検索するという仕組みです。
このツールをデフォルト構成で実行すると、ペイロード<?=exec($_GET[“c”])?>が埋め込まれた有効な画像が見つかります。
図2: 埋め込まれたペイロード
このファイルにより、SmartImageコンストラクタのimagecreatefromjpegによって正規化が実行された後もWebシェルが存続できるようになります。有効なMIMEタイプと.phpファイル拡張子を使用してアップロードした場合、Webパス/pluck/data/settings/modules/albums/<album_name>/<file_name>でファイルに直接移動し、HTTPパラメータ‘c’でコマンドを渡すことによってRCE(リモート・コード実行)を実現できます。これにより、認証された攻撃者がアプリケーションの基盤となるWebサーバー上で任意のコマンドを実行できるようになるため、重大な脆弱性となります。
この脆弱性に対するパッチがバージョン4.7.16-dev5、コミット8aec080でリリースされています。これにより、標準の画像アップロード機能の拡張子許可リストの実装が複製されます。
スニペット5:data/modules/albums/albums.admin.php
ただし、このパッチにはエラーがあります。ハイライト表示された文字がぶら下がっているため、ファイルに構文エラーが発生します。このファイルがアプリケーション内の他のページに含まれている場合、エラーが発生し、albumsモジュールが完全に無効になります。この構文エラーを修正する別のパッチがバージョン4.7.17、コミットa52de9cでリリースされました。このパッチで脆弱性が緩和されます。
Jellypegの開発に尽力してくれたJinny Ramsmarkに感謝します。