メインコンテンツへスキップ

Graphite(Paragon spyware)の iMessage ゼロクリックバグと、Apple がどう塞いだか

·4 分· loading · loading · ·
Jaybird1291
著者
Jaybird1291
目次
⚠️ この文章は ChatGPT で作成しました(日本語はまだ N4 レベルです)。

⚠️ 免責事項:
自分はサイバーセキュリティを学んでいる学生で、これはプロではない個人ブログ記事です。分析には誤りや見落としが含まれる可能性があります(まだ勉強中です)。もし間違いに気づいたり、改善案があれば気軽に連絡してください。

1. Citizen Lab が報道を出す
#

2025 年 6 月 12 日、Citizen Lab は "First Forensic Confirmation of Paragon’s iOS Mercenary Spyware" を公開した。

このレポートは Paragon の Graphite スパイウェアを、複数の欧州ジャーナリストの端末で観測された iMessage のゼロクリック・ペイロード と結び付けている。

コアとなる事実:

  • 被害者は 2025 年 4 月に Apple から通知を受け取っている。
  • 侵入経路: Paragon の Graphite スパイウェアは高度な iMessage ゼロクリック攻撃を用いた。

Citizen Lab のタイムラインは、Apple が iOS 18.3.1 をリリースし、修正として CVE-2025-43200 をクレジットした 2025 年 6 月 11 日(UTC)で終わっている。


2. CVE-2025-43200 と Apple のアドバイザリ
#

iOS 18.3.1 の Apple セキュリティノート にはこう書かれている:

Impact: iCloud Link 経由で共有された、悪意をもって細工された写真または動画を処理する際にロジック上の問題が存在した。Apple は、この問題が特定の標的個人に対する極めて高度な攻撃で悪用された可能性があるという報告を認識している。
Description: この問題は、チェックを改善することで対処した。
CVE-2025-43200 - Apple.

ファイルパスも、どのバイナリが変わったかのヒントもない。だからこそ patch diffing の出番。


3. 18.3 vs 18.3.1 の ipsw-diffs
#

Blacktop の自動差分 では、このホットフィックスで 10 個の Mach-O が再ビルドされているのが分かる。ただし Messages スタック周りで関係がありそうなのは、以下でハイライトした 3 つ:

  • iMessage (System/Library/Messages/PlugIns/iMessage.imservice/iMessage)
  • SafetyMonitor (System/Library/Messages/iMessageApps/SafetyMonitorMessages.bundle/SafetyMonitorMessages)
  • identityservicesd (System/Library/PrivateFrameworks/IDS.framework/identityservicesd.app/identityservicesd)
Binaryこれを選んだ理由差分ツールのハイレベル結果
iMessage.imserviceiMessage ロジックを実装する中核プラグイン大きい差分: 新しいログ文字列 + 検証処理
SafetyMonitorMessagesCommunication Safety のポップアップを出す Swift バンドルdiff ツール上は機能差分が見えない
identityservicesdIDS/Push デーモン(Messages にトラフィックを渡す)diff ツール上は機能差分が見えない

4. iOS 18.3 と 18.3.1 の中身に入る
#

iOS 18 から Apple は dmg.aea を導入していて、従来の「素の dmg」と同じノリでは差分が取りにくくなった(自分も、ふつうの dmg 前提で作業してたので詰まった)。なので、ここはミニガイドを残す。技術分析だけ見たい人は step 5 に飛ばして OK。

  1. 2 つの iOS バージョンをダウンロード
ipsw download ipsw --device iPhone17,1 --build 22D63
ipsw download ipsw --device iPhone17,1 --build 22D72

  1. ファイルシステムを抽出
ipsw extract --dmg fs  iPhone17,1_18.3_22D63_Restore.ipsw
ipsw extract --dmg fs  iPhone17,1_18.3.1_22D72_Restore.ipsw
  1. fcs-key と dmg を抽出
ipsw extract --fcs-key iPhone17,1_18.3_22D63_Restore.ipsw

ipsw fw aea --pem '044-59182-075.dmg.aea.pem' '044-59515-074.dmg.aea' --output extracted/
# もう片方も同様に
  1. マウント
ipsw mount fs --pem-db extracted/fcs-keys.json ../iPhone17,1_18.3_22D63_Restore.ipsw

もし自分みたいに何らかの理由でうまくいかない場合:

sudo apfs-fuse -o allow_other,uid=1001,gid=1001 extracted/044-59515-074.dmg /mnt/ios_old
  1. 必要なファイルをコピー
cp System/Library/PrivateFrameworks/IDS.framework/identityservicesd.app/identityservicesd ~/Documents/CVE-2025-43200/18.3.1/

cp System/Library/Messages/PlugIns/iMessage.imservice/iMessage ~/Documents/CVE-2025-43200/18.3.1/

cp System/Library/Messages/iMessageApps/SafetyMonitorMessages.bundle/SafetyMonitorMessages ~/Documents/CVE-2025-43200/18.3.1/


5. iMessage.imservice 内の差分
#

まず類似度:

radiff2 -s 18.3/iMessage 18.3.1/iMessage 
similarity: 0.977
distance: 49654

次に、IDA Pro と Diaphora script で差分を深掘りする:

-[MessageServiceSession _reAttemptMessageDeliveryForGUID: …] に部分一致が見える。

そして、新しく追加されたログ "Being requested to re-send a message that wasn't sent by me" が目立つ。

コードをもう少し読むと、これが何のためかが見えてくる。"_reAttemptMessageDeliveryForGUID:… “ メソッドは、「送信に失敗した iMessage をリトライするか、それともエラー表示にするか」を決めるために使われている。

Graph view にすると、新しいチェックが明確に見える:

重要な変更点はこれ:

  • 新しい “author(作成者)” ゲート:
//  AFTER 18.3.1 - NEW 他人のメッセージの再送をブロックする
if (![message isFromMe]) {                       // 自分が書いたメッセージではない
    os_log_info(MessageServiceLog,
                "Being requested to re-send a message that wasn't sent by me");
    return;                                      // ここで中断 = exploit が死ぬ
}

なぜ? —— Paragon のゼロクリックチェーンは、おそらく “resend” のコントロールフレームを偽造して、被害者の chat DB(is_from_me == 0)にある GUID を指すようにしたのだと思う。この 1 行のガードで、その要求は拒否される。

この情報は SMS.db でも確認できる:

CVE-2025-43200 は、1 行のロジックパッチと言っていい。
自分が書いたメッセージだけをリトライする」。
これまで exploit が成立していたのは、再送ヘルパー内でその当たり前の不変条件が強制されていなかったから。Apple の修正は:

  1. if (!msg.isFromMe) return;
  2. トリアージ向けの os_log 追加
18.3(脆弱)               18.3.1(修正)
┌───────────────────┐      ┌────────────────────────────────┐
│ … ルックアップ …  │      │ … 同じ …                       │ 
age-limit check   │      │ age-limit check(変更なし)     │ 
│ ──────────────────│      │────────────────────────────────│
author check なし │      │ if (!message.isFromMe) {       │ ◄─ NEW
retry ロジック    │      │     log "...not sent by me..." |
└───────────────────┘      │     return;}retry ロジック(変更なし)     │
                           └────────────────────────────────┘

6. これが CVE-2025-43200 にどう対応するか
#

攻撃者は:

  1. 被害者端末のローカル SQLite chat DB 内の GUID を指す、特別に細工された iMessage の “resend” リクエストを注入/リプレイした。
  2. 18.3 では isFromMe チェックが無かったため、ヘルパーメソッド _reAttemptMessageDeliveryForGUID:… はリトライ枠を消費して 他人のメッセージ(または添付)を再送してしまう(送信先は攻撃者が制御するハンドル)。

18.3.1 のパッチは、この穴を isFromMe == true の強制で塞ぐ。GUID が ローカルユーザー発のメッセージでなければならない。他人のメッセージを参照する偽造要求は、新しいログに引っかかって 即リターンする。


7. 攻撃の「あり得そうな」再構成
#

現時点では、Citizen Lab が記述したケースに CVE-2025-43200 をきれいに当てはめられる、説得力のあるエンドツーエンドのシナリオは見つけられていない。別のアイデアや、見落としているアーティファクトがあれば連絡してほしい。継続して調べたい。

最初の仮説は「ステルスな持ち出しチャネル」だった。ただ、よく見ると可能性は低い。できるのは Messages のサンドボックス内に既にある添付を転送することだけで、Signal や WhatsApp の DB みたいな任意のデータをそれ単体で引き抜けるわけではない。

Warning! ここからは推測です。完全な exploit チェーンも、サンプルも、アーティファクトも持っていません。

現時点の最新仮説(調査継続中)はこう:

  1. 攻撃者が、パーサを狙う「不活性(inactive)」なペイロードを含む画像/写真を送る。
  2. Blastdoor はペイロードを削除しない(役割的にそうではない)ため、悪性添付はターゲット端末に保存される。
  3. 攻撃者が _reAttemptMessageDeliveryForGUID:... を悪用して、ターゲットに悪性添付を自分で再送させる。これにより添付が再びパーサ経由になり、サンドボックス外で exploit される。

Facts:

  • 送信側端末は添付のプレビューを生成する(https://mysk.blog/2020/10/25/link-previews/)
  • Blastdoor サンドボックスを回避するための「不活性ペイロード」は過去にも何度も見てきた:
    • FORCEDENTRY (CVE-2021-30860)
    • BLASTPASS (CVE-2023-41064 + 41061)
    • Triangulation…
  • Samuel Groß と Ian Beer の仕事に基づくスキーム(A Brief History of iMessage Exploitation & A Look at iMessage in iOS 14

Questions:

  • iMessage のパイプライン理解を自分が誤っている?
  • このパイプラインは今も同じ?
  • return まわりの挙動は想定通り?
  • 2 フェーズ構成だと「怪しい通知」を受け取ることになるが、攻撃者の OPSEC 的に許容できる?
  • 1 フェーズ(悪性添付送信 + 同時に message forwarding)なのかも?
  • 攻撃者はどうやって悪性メッセージの GUID を見つけて送り返させる(_reAttemptMessageDeliveryForGUID:...)?
    • GUID を外に出すために別の vuln が要る? こういうのは初めてじゃない(例: How to catch a wild triangleSELECT guid FROM attachment WHERE uti == "http://com.apple.watchface"....)?
  • ここまで必要なら、なぜパッチが isFromMe だけ? Apple の説明は “logic checks” / “improved checks” なので整合はする。一方で “maliciously crafted photo or video shared via an iCloud Link”(Apple security updates notes)はどこにハマる? “maliciously crafted” = 1 フェーズでやること、という意味なのか?

8. フォレンジック観点のメモ
#

CitizenLab の侵害端末データを持っていないので、ここはすべて仮説か、ロジックベースの推論になる。

1. CVE-2025-43200 の痕跡を浮かせる unified log クエリ:

| 探すべきもの … | | ———————————————————–…————————————————————- | | "re-send a message that wasn't sent by me" …nce means the device blocked a forged resend attempt. | | _reAttemptMessageDeliveryForGUID stack traces (pre-pa…fault logs that name this selector if the exploit mis-fires. |

Tip: unified logs は端末上で ~7 日程度でロールする。なので、疑わしい端末を見つけたら まず sysdiagnose をフルで取るのが基本。

2. チャット DB(sms.db)のアーティファクト:

/*  Possible duplicates: same GUID appears as both inbound (is_from_me = 0)
    and outbound (is_from_me = 1) within a short window                */
SELECT guid, date, is_from_me, text
FROM message
WHERE guid IN (
    SELECT guid FROM message WHERE is_from_me = 0
)
ORDER BY date ASC;
  • ユーザー操作なしに、同じ GUID が is_from_me = 0 ➜ 1 にひっくり返っている(= duplicate GUID)なら、悪用された可能性が高い。
  • GUID を attachments テーブルと突き合わせる。

3. IDS と配信トレース:

  • 場所(rooted dump / iOS Full Filesystem の場合): /private/var/mobile/Library/Logs/CrashReporter/DiagnosticLogs/ids.*
  • Grep する文字列:
    • "resend-request"
    • sms.db の duplicates にも出てくる "guid"

こうした plaintext な IDS の制御フレームは、unified log がロールした後でも diagnostic logs 側に残っていることがある。