Aller au contenu

Le bug zero-click d'iMessage du spyware Paragon de Graphite et comment Apple l'a patché

·8 mins· loading · loading · ·
IOS CVE-2025-43200 Patch-Diffing Graphite Paragon
Sommaire

⚠️ Attention :
Je suis un étudiant en cybersécurité. Ce post est personnel et non-professionnel. Mon analyse peut contenir des erreurs ou des imprécisions, je suis encore en apprentissage. Si vous constatez des erreurs ou si vous avez des suggestions, n’hésitez pas à me contacter !

1. Citizen Lab révèle l’affaire
#

Le 12 juin 2025, Citizen Lab publie le rapport “First Forensic Confirmation of Paragon’s iOS Mercenary Spyware”.

Le rapport associe le spyware Graphite de Paragon à une attaque zero-click iMessage ciblant plusieurs journalistes européens.

Faits principaux :

  • Une victime a reçu une threat notif d’Apple en avril 2025.
  • Vecteur d’attaque : utilisation d’une attaque sophistiquée zero-click via iMessage.

Tout ça se termine le 11 juin 2025 (UTC), date à laquelle Apple publie iOS 18.3.1 avec la CVE-2025-43200 pour la correction.


2. CVE-2025-43200 & note d’Apple
#

La note de sécurité d’Apple pour iOS 18.3.1 indique :

Impact : Un problème logique existait lors du traitement d’une photo ou d’une vidéo malicieusement conçue partagée via un lien iCloud. Apple est au courant d’un rapport selon lequel ce problème aurait été exploité dans une attaque extrêmement sophistiquée contre des individus ciblés.
Description : Ce problème a été résolu par une vérification améliorée.
CVE-2025-43200 - Apple.

Aucun chemin de fichier ni indication du binaire modifié. C’est là que le patch diffing entre en jeu !


3. ipsw-diffs entre 18.3 et 18.3.1
#

Le diff automatisé de Blacktop montre 10 Mach-Os modifiés, dont trois seulement liés à iMessages :

  • 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)
BinairePourquoi ce choix ?Aperçu du diff
iMessage.imserviceMéthode interne d’iMessageImportant : nouvelle chaîne de log + vérification
SafetyMonitorMessagesPop-ups de sécurité de communicationAucun delta fonctionnel vu avec le diffing tool
identityservicesdDaemon IDS/Push acheminant le trafic vers MessagesAucun delta fonctionnel vu avec le diffing tool

4. Récupération de 18.3 et 18.3.1
#

Depuis iOS 18, Apple a ajouté les dmg.aea, rendant l’analyse différent si comme moi vous étiez habitué au dmg basique. Voici un micro-guide (si vous êtes uniquement intéressé à l’analyse technique passé à la partie 5.).

  1. Téléchargement des deux versions d’iOS
ipsw download ipsw --device iPhone17,1 --build 22D63
ipsw download ipsw --device iPhone17,1 --build 22D72

  1. Extraction du filesystem
ipsw extract --dmg fs  iPhone17,1_18.3_22D63_Restore.ipsw
ipsw extract --dmg fs  iPhone17,1_18.3.1_22D72_Restore.ipsw
  1. Extraction de la fcs-key et du 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/
# do this for the other one too
  1. Montage
ipsw mount fs --pem-db extracted/fcs-keys.json ../iPhone17,1_18.3_22D63_Restore.ipsw

If like me this doesn’t work for any reasons:

sudo apfs-fuse -o allow_other,uid=1001,gid=1001 extracted/044-59515-074.dmg /mnt/ios_old
  1. Copie des fichiers nécessaires
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. Différences dans iMessage.imservice
#

Premièrement voici la similarité :

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

Ensuite nous pouvons creuser plus profondément avec IDA Pro et Diaphora script :

Nous pouvons voir un “partial match” pour-[MessageServiceSession _reAttemptMessageDeliveryForGUID: …].

Avec un nouveau log notable "Being requested to re-send a message that wasn't sent by me".

En analysant le code, on comprend à quoi sert tout cela. La méthode "_reAttemptMessageDeliveryForGUID:…" sert à décider si un message “iMessage” en échec doit être retenté ou s’il faut afficher une erreur.

En utilisant la Graph view nous pouvons clairement voir la nouvelle vérification :

Voici le changement important :

  • Une nouvelle vérification de l’auteur
//  APRÈS 18.3.1 - Bloque le renvoi des messages étrangers.
if (![message isFromMe]) {                       // message authored by someone else
    os_log_info(MessageServiceLog,
                "Being requested to re-send a message that wasn't sent by me");
    return;                                      // bail = exploit dies
}

Pourquoi ? - On peut supposer que la chaîne zéro-clic de Paragon a créé une trame de contrôle “renvoi” pointant vers une GUID dans la DB de discussion de la victime (où is_from_me == 0). La protection unique ci-dessus rejette cette requête.

Nous pouvons voir ces informations dans la SMS.db:

CVE-2025-43200 est un correctif logique d’une seule ligne:
“Ne renvoie que les messages que tu as réellement écrits.”
L’exploit a fonctionné car cette invariant évidente n’était jamais appliquée dans l’helper de renvoi. Le correctif d’Apple consiste à :

  1. if (!msg.isFromMe) return;
  2. Une instruction os_log pour le triage / log.
18.3 (vulnerable)          18.3.1 (patché)
┌───────────────────┐      ┌────────────────────────────────┐
│ … look-ups …      │      │ … same …                       │ 
age-limit check   │      │ age-limit check (unchanged)│ ──────────────────│      │────────────────────────────────│
NO author check   │      │ if (!message.isFromMe) {       │ ◄─ NEW
retry logic       │      │     log "...not sent by me..." |
└───────────────────┘      │     return;}retry logic (unchanged)                           └────────────────────────────────┘

6. Cela dans CVE-2025-43200
#

L’attaquant :

  1. Injectait ou rejouait une requête iMessage “renvoi” spécialement conçue pointant vers un GUID existant dans la base de données SQLite locale de la victime.
  2. Comme iOS 18.3 ne vérifiait pas isFromMe, la méthode _reAttemptMessageDeliveryForGUID:... consommait un crédit de renvoi et réexpédiait le message étranger (ou sa pièce jointe) vers le contact contrôlé par l’attaquant.

Le correctif d’iOS 18.3.1 comble cette faille en exigeant que le bit isFromMe du GUID soit vrai. Le message doit impérativement provenir de l’utilisateur local ; toute requête falsifiée référant à un message tiers déclenche désormais la nouvelle entrée de log et interrompt immédiatement le processus.


7. Attaque - Reconstruction plausible
#

À ce jour, je n’ai pas identifié de scénario complet et convaincant montrant comment la CVE-2025-43200 s’insère exactement dans les cas documentés par Citizen Lab. Si vous avez d’autres idées ou des artefacts que j’aurais pu manquer, n’hésitez pas à me contacter.

Mon premier scénario envisageait que ce bug fournisse un canal d’exfiltration furtif. À y regarder de plus près, cela paraît peu probable : la primitive ne peut transmettre que des pièces jointes déjà présentes dans la sandbox de Messages. Elle ne permettrait pas, à elle seule, d’extraire des données arbitraires (bases Signal, WhatsApp, etc.).

Attention ! C’est une théorie ! Je n’ai pas l’exploit chain, je n’ai même pas un simple artéfacte.

Ma dernière théorie en date (les investigations continuent !) est la suivante :

  1. L’attaquant envoie une image ou photo contenant un payload inactif qui cible un parser
  2. Blastdoor n’élimine pas le payload (ce n’est pas son rôle) et la pièce jointe malveillante est sauvegardé sur le téléphone cible
  3. L’attaquant abuse de _reAttemptMessageDeliveryForGUID:... pour que la cible renvoie de lui même la pièce jointe malveillante, la faisant passer dans les parser et exploit hors sandbox

Faits :

Questions :

  • ai-je eu une mauvaise compréhension de la pipeline iMessage ?
  • la pipeline est-elle toujours à jour ?
  • la partie renvoie est-elle bien comme cela ?
  • si ça fonctionne en 2 phases, cela veut dire que la cible reçoit une notif suspecte - est-ce okay d’un point de vu OPSEC pour l’attaquant ?
  • peut-être que c’est en 1 phase, envoie de la pièce jointe malveillante + renvoi de message en même temps ?
  • comment l’attaquant trouve la GUID du message malveillant pour le renvoyer (_reAttemptMessageDeliveryForGUID:...) ?
    • Il faudrait une autre vuln pour exfiltrer le GUID ? Pas la première fois que l’on voit quelque chose comme cela ? (cf. How to catch a wild triangle ou l’on voit SELECT guid FROM attachment WHERE uti == "http://com.apple.watchface"....) ?
  • s’il y a tout ça pourquoi un patch uniquement sur le isFromMe ? Le patch parle bien uniquement de “logic check” et “This issue was addressed with improved checks.” ce qui colle bien avec ce qui a été trouvé. Quelle place au “maliciously crafted photo or video shared via an iCloud Link”? (cf. Apple security updates notes) ? Peut-être que le “malicious craft” = le fait de le faire en 1 phase ?

8. Forensique
#

Étant donné que je n’ai pas accès aux données des iPhone compromis par Citizen Lab, tout ce qui suit est purement hypothétique ou fondé sur des déductions logiques.

1. Requêtes de logs unifiés dévoilant l’activité CVE-2025-43200 :

À rechercherPourquoi c’est important
"re-send a message that wasn't sent by me"Nouvelle chaîne os_log introduite uniquement à partir d’iOS 18.3.1 ; sa présence indique que l’appareil a bloqué une tentative de renvoi falsifiée.
stack traces _reAttemptMessageDeliveryForGUID (pré-patch)Sur les appareils vulnérables (18.3/18.2.1), vous pouvez toujours trouver dans les logs de crash des références à ce sélecteur si l’exploit échoue.

Attention : les logs unifiés rotate au bout d’environ 7 jours sur l’appareil ; pensez à extraire un sysdiagnose complet immédiatement.

2. Artefacts dans la base de chat (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;
  • Un GUID dupliqué passant de is_from_me = 01 sans action de l’utilisateur suggère fortement un abus du mécanisme de renvoi.
  • Recouper ce GUID avec la table attachments.

3. Traces IDS & livraison de messages :

  • Emplacement (dump rooté ou système de fichiers complet iOS) : /private/var/mobile/Library/Logs/CrashReporter/DiagnosticLogs/ids.*
  • Rechercher dans les logs avec grep :
    • "resend-request"
    • valeurs de "guid" apparaissant également dans les doublons de sms.db

Ces trames de contrôle IDS en clair survivent souvent dans les logs de diagnostic même lorsque les logs unifiés ont été archivés.