Signal's Contact Discovery automatically sends your contact list...

@v12sec
V12@v12sec
16 views Aug 26, 2026 ~3 min read
Advertisement
1
Signal's Contact Discovery automatically sends your contact list information to an SGX enclave in the cloud.

V12 broke into that enclave and leaked the key, allowing the server host to decrypt everything.

Two separate critical bugs: arbitrary read and RCE.

Here's how. 🧵
0:17
2
@signalapp's Contact Discovery Service (CDSI) lets you securely discover which of your contacts are on Signal.

It's on by default, and the app sends your full contacts list every 48h.

Queries run inside an SGX enclave, so even Signal's servers can't see them.

SGX encrypts the enclave's memory, so the host can't read the queries directly, and uses ORAM to hide the access pattern.
3
While SGX protects enclave memory, the host still controls the machine around it. The host invokes enclave calls, schedules threads, and manages page tables.

As ORAM is expensive, the database is split into shards, each serviced by an enclave worker.

A malicious host can use page faults to pause enclave threads at critical points, enabling fine-grained deterministic exploitation of data races.
4
Critical #1: duplicate shard workers.

A shard queues a lookup, then a wait. With one worker per shard, consuming the wait proves the lookup finished. But the enclave doesn't prevent us from spawning a duplicate:

1. Worker A starts a lookup.
2. Worker B consumes the wait.
3. The query frees its result buffer.
4. Worker A resumes and writes 56 bytes into freed memory.

We groom the heap to reuse the freed memory as a protobuf workspace, replacing a pointer and length with host-chosen values. The protobuf encoder in a normal client response path then copies out the bytes we selected.
5
This gives us an arbitrary read, which we use to extract the Noise responder private key, allowing the server to impersonate the secure enclave. The server can then simply decrypt all incoming traffic meant for the enclave.

This allows the owner of the untrusted server to steal all the phone numbers in your contacts.
Media image
6
Critical #2: client-handle TOCTOU.

CDSI hands the host opaque pointers to client state. When performing operations, it authenticates these client pointers by checking a secret canary, then separately CASing a state field.

We used our host control primitive to pause between the two, free the client, groom the heap, and reuse its address for our own object. The enclave validates the original client, but uses our replacement.
7
We craft the replacement, overwriting a function pointer and its argument to enter Open Enclave's register restore path with a host-crafted register context. This yields full control over enclave registers and allows code execution within the trusted context.

Our POC makes the pwned enclave memcpy its secrets out to host memory.

This again allows the server to impersonate the enclave, decrypting all incoming contact information.
8
Both POCs were validated on the Azure SGX machine type used by Signal, on the exact enclave binary used in prod.

For each, we extracted the private key, derived its public key, and confirmed that it matched the attested value.
Media image
9
Signal fixed both critical issues: the enclave now enforces one worker per shard, and client validation/acquisition is atomic.

Full technical write-up, report and POCs: v12.sh/blog/signal
10
This bug was found by @rt1rz using V12.

V12 is our autonomous AI hacker.

Find bugs like this in your code: v12.sh
Actions
What You Can Do
  • Export as PDF or Markdown
  • Batch Export to Notion
  • Bookmark & Highlight
  • LinkedIn & Instagram Carousel Maker
Create Free Account

Includes 7-day Premium trial

Advertisement