A Trip to Ancient BABYLON: Unearthing a 2017 Pegasus Persistence Exploit

Bill Marczak (Senior Researcher · Citizen Lab), Daniel Roethlisberger (Fellow · Citizen Lab)

REcon 2025 · Day 2 · Main Track · Reverse Engineering

Overview

In mid-2024, Citizen Lab researchers Bill Marczak and Daniel Roethlisberger stumbled onto something unusual on VirusTotal: an old sample of NSO Group's Pegasus spyware, calibrated to a specific victim

Watch on YouTube

Visual summary for A Trip to Ancient BABYLON: Unearthing a 2017 Pegasus Persistence Exploit by Bill Marczak, Daniel Roethlisberger
Visual summary for A Trip to Ancient BABYLON: Unearthing a 2017 Pegasus Persistence Exploit by Bill Marczak, Daniel Roethlisberger

Key moments

  1. 1:35 Pegasus capabilities: remote microphone, camera, and data access
  2. 4:44 Detection: Apple's threat notification system
  3. 7:54 BABYLON mechanism: JavaScript exploit running at boot
  4. 14:13 Exploitation target: iOS dynamic linker shared cache
  5. 17:27 ROP chain: loading exploit chain for persistence installation
  6. 23:43 Persistence chain: launchd spawns BABYLON, BABYLON spawns Pegasus
  7. 26:54 Forensic puzzle: analyzing why dictation was disabled

A Trip to Ancient BABYLON: Unearthing a 2017 Pegasus Persistence Exploit

Speakers: Bill Marczak, Senior Researcher, Citizen Lab; Daniel Roethlisberger, Fellow, Citizen Lab

Conference: REcon 2025

YouTube: https://www.youtube.com/watch?v=ZlopMtjsVRw

Overview

In mid-2024, Citizen Lab researchers Bill Marczak and Daniel Roethlisberger stumbled onto something unusual on VirusTotal: an old sample of NSO Group's Pegasus spyware, calibrated to a specific victim device, running on iOS 10.3.1 for iPhone 7. What followed was a multi-month reverse engineering expedition that uncovered a sophisticated iOS persistence exploit — one that abuses NSKeyedArchiver deserialization in locationd, defeats ASLR for iOS 10, and chains through system services to ultimately load the Pegasus implant. The talk is a detailed walkthrough of the static analysis, emulation infrastructure, and forensic detective work required to fully reconstruct the exploit — including gadget chain analysis, a 130-system-call emulation layer, and an accidental window into possible exploit sharing between distinct state-sponsored threat actors.

Background

▶ Watch: Pegasus capabilities: remote microphone, camera, and data access (1:35)

NSO Group is among the most prominent vendors in the mercenary spyware industry, selling its Pegasus product to government customers who use it to target the phones of dissidents, journalists, human rights activists, and (contrary to the marketing pitch) foreign heads of state. Citizen Lab has documented Pegasus infections across governments spanning every continent, including cases that led to UK court findings against the Amir of Dubai and indications that French President Macron and UK Prime Minister Boris Johnson were among targets.

Tracking Pegasus infections has grown substantially harder over time. In 2016, a human rights activist in the UAE (Ahmed Mansour) received a suspicious SMS link, forwarded it to Citizen Lab, and clicking it in a controlled environment yielded a chain of three zero-day exploits delivering Pegasus. The shift since then has been toward zero-click exploits — remote interactionless attacks where targets neither click anything nor notice any sign of compromise. Detection now requires proactive log analysis and, increasingly, threat notifications from Apple.

Persistence on iOS Is Hard

Traditional persistence on iOS — launching a binary on reboot — is architecturally blocked by Apple's Secure Boot chain, code signing, and numerous cryptographic attestation layers documented extensively in Apple's Platform Security Guide. In practice, threat actors fall back on several strategies:

  • Re-exploitation on reboot: Detect the reboot remotely and fire the zero-click exploit again (documented in Kaspersky's Operation Triangulation report).
  • JavaScript re-exploit on boot: Pegasus itself used JavaScript exploit code re-executed at boot time.
  • iOS Shortcuts abuse: Predator used shortcut automations in 2021 to silently open WebKit in the background on startup.

The Babylon sample represents a different approach: a persistence exploit that survives reboots by injecting an exploit blob into a file that locationd (the location services daemon) reads at startup — effectively re-exploiting the device silently every time it reboots.

Key Findings

▶ Watch: BABYLON mechanism: JavaScript exploit running at boot (7:54)

The central discovery is a fully reconstructed iOS 10.3.1 persistence exploit targeting locationd via an NSKeyedArchiver deserialization vulnerability — specifically a type confusion bug in PersonNameComponentsFormatter. The exploit:

  • Bypasses ASLR by brute-force enumeration of all possible shared cache slide values, exploiting the iOS 10 ASLR range for iPhone 7 (exactly 17,925 possible slides)
  • Uses jump-oriented programming (JOP) chains rather than return-oriented programming (ROP), with gadgets ending in branch-to-register instructions
  • Chains through CrashReporter / sis_diagnose to gain additional entitlements for patching AMFI (Apple Mobile File Integrity)
  • Ultimately loads the Pegasus implant loader (GoldenGate or Tower binaries)
  • Was silently patched by Apple in iOS 10.3.3, with the bug having been introduced in iOS 9

A secondary finding concerns possible exploit code sharing between NSO Group's Pegasus and at least two other distinct threat actor groups (dubbed "Shadowbird" and "Sunbird"), visible through shared typos and implementation strings.

Technical Deep Dive

▶ Watch: Exploitation target: iOS dynamic linker shared cache (14:13)

The Sample and Starting Files

The VirusTotal upload included two files: Babylon (an ad hoc-signed iOS Mach-O binary) and Genesis (a compressed property list / .plist file). The sample was accompanied by a database referencing domains previously tracked as part of NSO Group's infection infrastructure, confirming its Pegasus provenance.

Babylon operates in two primary modes:

  • Babylon mode: Sabotages diagnostics (prevents crash telemetry from reaching Apple), patches AMFI to disable code-signing checks, and spawns either GoldenGate or Tower from disk.
  • Persian mode: Also sabotages diagnostics, then loads the Genesis .plist to construct the exploit blob from it, inserting the result into /var/mobile/Library/Caches/com.apple.locationd/calibration_data.plist under the key stride_calibration_data_bins. It also sets a modified_untether entry — a jailbreaking community term for a persistence mechanism that survives reboots.

Reconstructing the Exploit Blob from Genesis

Genesis contains fix-up values, a range, an ROP/JOP stub, and a template for the exploit blob. The code that builds the exploit from these ingredients was difficult to understand directly: it performs unusual calculations, operates on unusual data structures, and tunes a particular constant in a non-obvious way. After study, the researchers recognized that the code was building and rebasing many small copies of a small JOP chain — the pattern for breaking ASLR via pre-computed slide tables.

The team lifted this code into Objective-C and reconstructed the exploit blob mechanically, without fully understanding the underlying vulnerability at this stage.

Decoding the NSKeyedArchiver Object Hierarchy

The exploit blob is an NSKeyedArchiver archive — a serialized Objective-C object graph. Decoded with appropriate tooling, the top-level structure is an array containing:

  • A Date
  • A Formatter
  • A Bundle
  • An OFTagSet
  • Arrays
  • Another Formatter
  • A hash table containing approximately 300 string proxies

The last four string proxies are critical: two contain buffers constructed by Babylon's code, and two contain large JOP chains (~200 KB each).

Identifying the Vulnerability via Emulation

Without a physical iPhone 7 running iOS 10.3.1 (difficult to source in 2024) and having ruled out help from Apple, Corellium, or Cellebrite, the team built an emulation environment using Unicorn. The goal was to map locationd and the dynamic linker into memory, let the linker perform library initialization, and then watch the exploit execute.

This required implementing approximately 130 system call stubs, covering:

  • VFS operations
  • Virtual memory management (including ASLR)
  • Mach IPC
  • A largely complete libdispatch (supporting full asynchronous dispatch queue execution)
  • MIG and XPC (enabling simulation of higher-level system services)

With this emulation layer in place, the team could watch locationd load its user.plist calibration file and trigger exploitation. The emulator revealed:

  1. Exploitation occurs entirely within Foundation's deserialization code — not in locationd's own logic afterward.
  2. The vulnerable classes are Set, Array, PersonNameComponentsFormatter, and a hash table.

The Type Confusion Bug

The deserialization sequence is:

  1. A Set deserializes its constituent Array objects and checks them for equality.
  2. Array equality checking calls isEqual:toFormatter: on a PersonNameComponentsFormatter object with the hash table as the other argument.
  3. The code does not check the type of the argument, so it performs a direct (unsafe) access to a private field offset of the hash table.
  4. Whatever value is stored at that offset in the hash table is misinterpreted as an object pointer.
  5. A local selector is called on this dangling/fabricated pointer.
  6. The Objective-C runtime resolves the call by walking the method cache of the object's class — but instead of the real method cache, it walks a fake cache embedded in the exploit's hash table allocations.

ASLR Defeat via Fake Method Cache

The fake method cache is the ASLR-breaking mechanism. iOS 10's ASLR for the shared cache (the file containing all system dylibs, mapped into every process address space) on iPhone 7 has exactly 17,925 possible slides within the shared region. The exploit populates the fake cache with 17,924 entries — one per possible slide value — each containing the selector address recalculated for that slide plus a small JOP chain rebased to that slide. The Objective-C runtime performs a linear search through the cache entries, finds the one matching the actual current slide, and begins executing the JOP chain instead of the expected method implementation.

(The off-by-one — 17,924 entries instead of 17,925 — means there is a 1/17,925 chance of failure, producing a crash loop in locationd. As the researchers noted: "NSO Group, if you're watching this, I have a trade offer.")

The two fake cache memory allocations happen to land in adjacent memory regions due to iOS 10's magazine allocator behavior given the specific object hierarchy — a heap-grooming aspect that was deterministic given the exploit's structure.

The Full Exploitation Chain

  1. locationd loads user.plist, triggering deserialization of the exploit blob.
  2. JOP chains 0 and 1 execute within Foundation's deserialization code.
  3. Chain 1 XPCs over to sis_diagnose, exploiting the same deserialization vulnerability via an XPC endpoint that sis_diagnose exposed at that time for deserializing NSKeyedArchiver archives from XPC requests.
  4. sis_diagnose's entitlements allow patching AMFID to accept ad hoc-signed Mach-O binaries.
  5. Chain 2 spawns Babylon.
  6. Babylon (with privileged entitlements) patches AMFI more thoroughly, enabling Pegasus to inject into other processes.
  7. Babylon spawns the Pegasus implant loader (GoldenGate or Tower).

Apple silently fixed the underlying bug in iOS 10.3.3. Subsequent iOS security improvements — CoreTrust (iOS 12), Pointer Authentication Codes (PAC), and many others — would have broken multiple additional aspects of this persistence chain even if the deserialization bug remained.

Demo / Proof of Concept

▶ Watch: ROP chain: loading exploit chain for persistence installation (17:27)

The primary proof-of-concept in this talk is the emulation itself: the researchers demonstrated watching live emulated execution of locationd being exploited, tracing gadget-by-gadget through the JOP chains, observing the fake method cache lookup in action, and following the full chain through to Babylon's execution. Binary Ninja was used for static analysis and gadget disassembly; Unicorn served as the emulation backbone.

Defensive Implications

▶ Watch: Persistence chain: launchd spawns BABYLON, BABYLON spawns Pegasus (23:43)

For Apple and iOS Security

This research demonstrates that parser/deserialization vulnerabilities are dual-use: they serve not only for initial access (as documented extensively in zero-click attack chains targeting iMessage parsing) but also for persistence, enabling stealthy re-exploitation on every reboot without any interactive trigger. The practical implication is that burning a deserialization bug on a persistence mechanism is high-risk for the attacker (it exposes the exploit to detection on every reboot), so attackers may prefer cloud-token theft or zero-click re-exploitation instead — but when persistence is used, it can be invisible to the user.

Apple's fix in iOS 10.3.3 addressed the bug at the source. Later improvements (CoreTrust, PAC, improved sandbox restrictions around iMessage parsing) layered additional defenses that would have disrupted this chain even without the direct fix.

For Threat Intelligence and Attribution

The exploit code sharing finding has meaningful implications for attribution methodology. The 2017 Pegasus sample and the Sunbird threat actor shared:

  1. Identical diagnostic sabotage code, including a typo: allowDictation was set to false instead of allowDiagnosticSubmission (corrected in a 2018 Pegasus sample).
  2. The string user_aside.plist — an intermediate filename used during persistence installation — a non-obvious implementation choice not derived from any public documentation.

Shadowbird (a Middle East-focused actor) had allowDictation set to false on infected devices with no presence of Pegasus or Sunbird implants, suggesting exposure to the same original buggy code without necessarily using the same implant.

The conclusion: at least the persistence exploit source code, and possibly the diagnostic sabotage code, was shared between multiple threat actors — potentially via an exploit broker or shared development arrangement. Artifacts like binary-level implementation strings can fingerprint shared code lineage, but are not sufficient to uniquely identify a specific threat actor.

Key Takeaways

  • Deserialization vulnerabilities power both initial access and persistence on iOS. The same NSKeyedArchiver attack surface that underlies many zero-click campaigns was exploited here to achieve silent reboot persistence via locationd.
  • ASLR on older iOS devices was exhaustively defeated by pre-computing fake method cache entries for every possible shared cache slide — 17,924 entries covering essentially all valid slide values on iPhone 7 / iOS 10.
  • Emulation at scale is achievable for static samples without physical hardware: with ~130 system call stubs and working libdispatch, MIG, and XPC implementations, Citizen Lab was able to watch the full exploitation in action.
  • Exploit code sharing may be more common than previously documented. The same typo appearing in Pegasus 2017 and Sunbird's diagnostic sabotage code — alongside shared implementation strings — points to a supply chain for exploit components that crosses organizationally distinct threat actor groups.
  • Attribution must carefully distinguish between exploit code (potentially shared) and implant code (more likely actor-specific). Strings like GoldenGate, Tower, and Babylon are better fingerprints of the Pegasus implant than low-level exploit implementation details that may be shared.

About the Speaker(s)

▶ Watch: Forensic puzzle: analyzing why dictation was disabled (26:54)

Bill Marczak is a Senior Researcher at Citizen Lab at the University of Toronto's Munk School of Global Affairs and Public Policy. He has been instrumental in tracking NSO Group's Pegasus spyware since its first public documentation in 2016 and has co-authored numerous reports on state-sponsored surveillance targeting civil society. Daniel Roethlisberger is a Fellow at Citizen Lab and a deep technical contributor to Citizen Lab's malware analysis and reverse engineering work, including the construction of the emulation environment used in this research.

Reviews

Dr. Zero (Offensive Security Researcher) — MUST SEE

Citizen Lab unearthed a 2017 Pegasus persistence exploit from a VirusTotal ghost, built a 130-syscall iOS emulator from scratch to understand it, and dropped attribution evidence suggesting NSO's exploit code crossed organizational lines — this is what elite conference research looks like.

Heather Calloway (CISO) — MUST SEE

Citizen Lab reverse-engineered a Pegasus persistence exploit from 2017 and in doing so exposed a supply chain for state-sponsored surveillance tools that the industry has been systematically underestimating — this is the governance story, not the technical one.

→ Top-rated talks at REcon 2025

All talks from REcon 2025