β οΈ Disclaimer:
I’m a cybersecurity student, this is a non-professional, personal blog post. My analysis may contain errors or oversights I’m still learning. If you spot any mistakes or have suggestions, please don’t hesitate to reach out!
1. Citizen Lab breaks the story#
On 12 June 2025, Citizen Lab published "First Forensic Confirmation of Paragon’s iOS Mercenary Spyware".
Their report links Paragon’s Graphite spyware to a zero-click iMessage payload observed on several European journalists’ phones.
Core facts:
- Victim received an Apple notification in April 2025.
- Delivery vector: Paragon’s Graphite spyware used a sophisticated iMessage zero-click attack
Citizen Lab’s timeline ends on 11 June 2025 UTC, when Apple ships iOS 18.3.1 and credits CVE-2025-43200 as the fix.
2. CVE-2025-43200 & Apple’s advisory#
In Apple’s security note for iOS 18.3.1 we can read:
Impact: A logic issue existed when processing a maliciously crafted photo or video shared via an iCloud Link. Apple is aware of a report that this issue may have been exploited in an extremely sophisticated attack against specific targeted individuals. Description: This issue was addressed with improved checks.
CVE-2025-43200 - Apple.
No file paths, no clue which binary changed. That’s where patch diffing comes in!
3. ipsw-diffs on 18.3 vs 18.3.1#
Blacktop’s automated diff shows 10 Mach-Os rebuilt in the hot-fix, but only three are in the Messages stack highlighted below:
- 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 | Why we picked it | High-level diff preview result |
---|---|---|
iMessage.imservice | Core plug-in that implements iMessage logic | Significant: new log string + verification |
SafetyMonitorMessages | Swift bundle that shows Communication-Safety pop-ups | no functional delta seen with diffing tool |
identityservicesd | IDS/Push daemon that hands traffic to Messages | no functional delta seen with diffing tool |
4. Getting inside 18.3 and 18.3.1#
As iOS 18, Apple added dmg.aea which make the diffing different than before, if like me you were used to basic dmg! That’s why I will provide a micro-guide for this. If you’re only interested in the technical analysis you can skip to step 5.
- Download the two iOS versions
ipsw download ipsw --device iPhone17,1 --build 22D63
ipsw download ipsw --device iPhone17,1 --build 22D72
- Extract filesystem
ipsw extract --dmg fs iPhone17,1_18.3_22D63_Restore.ipsw
ipsw extract --dmg fs iPhone17,1_18.3.1_22D72_Restore.ipsw
- Extract fcs-key and the 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
- Mount
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
- Copy needed files
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. Diffs inside iMessage.imservice#
First of all here’s the similarity:
radiff2 -s 18.3/iMessage 18.3.1/iMessage
similarity: 0.977
distance: 49654
Then we can dig deeper in the diff with IDA Pro and Diaphora script:

We can see a partial match for -[MessageServiceSession _reAttemptMessageDeliveryForGUID: β¦]
With a notable new log "Being requested to re-send a message that wasn't sent by me"

A closer look at the code shows what all this is for. The "_reAttemptMessageDeliveryForGUID:… “ method is used to decide whether a failed “iMessage” should be retried, or whether an error should be displayed.
Using Graph view we can clearly see the new checking:

Here’s the important change:
- New authorship gate:
// AFTER 18.3.1 - NEW blocks resend of foreign messages
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
}
Why? - We can assume that Paragon’s zero-click chain forged a “resend” control frame that pointed to a GUID in the victim’s chat DB (where is_from_me == 0
). The single guard above rejects that request.
You can see these informations in the SMS.db:
CVE-2025-43200 is a one-line logic patch:
“Only retry messages you actually wrote.”
The exploit succeeded because that obvious invariant was never enforced in the resend helper. Apple’s fix consists of:
if (!msg.isFromMe) return;
- An
os_log
statement for triage.
18.3 (vulnerable) 18.3.1 (patched)
βββββββββββββββββββββ ββββββββββββββββββββββββββββββββββ
β β¦ 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. How this maps to CVE-2025-43200#
The attacker:
- Injected or replayed a specially-crafted iMessage “resend” request that points to an existing GUID in the victim’s local SQLite chat DB.
- Because 18.3 lacked the
isFromMe
test, the helper method_reAttemptMessageDeliveryForGUID:β¦
happily burned a retry credit and re-sent the foreign message (or its attachment) to the attacker-controlled handle.
Patch 18.3.1 closes that hole by insisting the GUID’s isFromMe
bit is true. The message must originate from the local user. Any forged request that references someone-else’s message now trips the new log entry and bails early.
7. Attack - plausible reconstruction#
So far I haven’t found a compelling, end-to-end scenario that shows how CVE-2025-43200 fits into the cases documented by Citizen Lab. If you have alternative ideas-or artefacts I’ve missed-please get in touch; I’d be keen to investigate further.
My first hypothesis was that the bug might provide a stealthy exfiltration channel. On closer inspection that seems unlikely: the primitive can forward only those attachments that already live inside the Messages sandbox. It would not, by itself, let an attacker pull arbitrary data such as Signal or WhatsApp databases.
My latest theory (investigations continue!) is as follows:
- The attacker sends an image or photo containing an inactive payload that targets a parser.
- Blastdoor does not remove the payload (this is not its role) and the malicious attachment is saved on the target phone.
- The attacker abuses
_reAttemptMessageDeliveryForGUID:...
to get the target to resend the malicious attachment on its own, passing it through the parser and exploiting it outside the sandbox.

Facts:
- The emitter’s phone previews the attachments (https://mysk.blog/2020/10/25/link-previews/)
- Inactive payloads to bypass the Blastdoor sandbox have already been seen several times :
- FORCEDENTRY (CVE-2021-30860)
- BLASTPASS (CVE-2023-41064 + 41061)
- Triangulation…
- schemes based on the work of Samuel GroΓ & Ian Beer (A Brief History of iMessage Exploitation & A Look at iMessage in iOS 14)
Questions:
- have I misunderstood the iMessage pipeline?
- is the pipeline still up to date?
- is the return part as it should be?
- if it works in 2 phases, it means that the target receives a suspicious notification - is this okay for attacker’s OPSEC?
- maybe it’s in 1 phase, sending the malicious attachment + message forwarding at the same time?
- how does the attacker find the GUID of the malicious message to send it back (
_reAttemptMessageDeliveryForGUID:...
)?- Would the attacker need another vuln to exfiltrate the GUID? Not the first time we’ve seen something like this? (cf. How to catch a wild triangle where we see
SELECT guid FROM attachment WHERE uti == "http://com.apple.watchface"....
)?
- Would the attacker need another vuln to exfiltrate the GUID? Not the first time we’ve seen something like this? (cf. How to catch a wild triangle where we see
- if there’s all of that, why a patch only on
isFromMe
? The patch only talks about “logic checks” and “This issue was addressed with improved checks”, which is consistent with what was found. Where does “maliciously crafted photo or video shared via an iCloud Link” fit in (cf. Apple security updates notes)? Maybe “malicious craft” = doing it in 1 phase?
8. Forensic thoughts#
Since I don’t have the CitizenLab compromised iPhones data everything here is hypothesis or purely logical deductions.
1. Unified log queries that surface CVE-2025-43200 activity:
What to look for | Why it matters | |
---|---|---|
"re-send a message that wasn't sent by me" | New os_log string introduced only in iOS 18.3.1+; its presence means the device blocked a forged resend attempt. | |
_reAttemptMessageDeliveryForGUID stack traces (pre-patch) | On vulnerable 18.3/18.2.1 devices you may still see crash or fault logs that name this selector if the exploit mis-fires. |
Tip: unified logs roll after ~7 days on-device; always pull a full sysdiagnose immediately.
2. Chat database (sms.db) artefacts:
/* 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;
- A duplicate GUID flipping from
is_from_me
= 0 β 1 without user action strongly suggests this was abused. - Cross-reference the GUID with the attachments table.
3. IDS & Message-delivery traces:
- Location (rooted dump or iOS Full Filesystem):
/private/var/mobile/Library/Logs/CrashReporter/DiagnosticLogs/ids.*
- Grep for:
"resend-request"
"guid"
values that also appear in sms.db duplicates
These plaintext IDS control-frames often survive in the diagnostic logs even when the main unified log has rolled.