Skip to main content
public_disclosure_facebook_ios_surveillance Phase 1

PUBLIC DISCLOSURE: Facebook iOS Bypasses Privacy Indicators for Covert Audio Surveillance

| Finding | Evidence | Implication | |---------|----------|-------------| | Indicator bypass polling | 18 calls, every 3 seconds | Active monitoring of bypass state | | Telephony audio access | 1,099 accesses, 0 calls | VoIP infrastructure misuse | | Background persistence | 454 requests | Aggressive execution maintenance |

Key Findings

Component Status / Finding
Mic route detection Captured built-in mic state

Technical Diagrams

Overview Diagram Line 59
+-----------------------------------------------------------------------+
|              FACEBOOK iOS SURVEILLANCE CHAIN                           |
+-----------------------------------------------------------------------+
|                                                                        |
|  PHASE 1: INDICATOR BYPASS                                             |
|  +------------------------------------------------------------------+  |
|  |                                                                  |  |
|  |  VoIP Push arrives via PushKit                                   |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  FBPushKitRegistrar receives notification                        |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  FBSystemAudioSessionManager.forceUpdateAudioSession()           |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  setCallKitActive: TRUE                                          |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  setAllowCallKitActiveAdjust: FALSE  <-- KILLS ORANGE DOT        |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  _voipAudioSession (hidden from UI)                              |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  initWithAudioSessionHandsOff: (no state sync)                   |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  activateSilently --> NO ORANGE DOT VISIBLE                      |  |
|  |                                                                  |  |
|  +------------------------------------------------------------------+  |
|                                 |                                      |
|                                 v                                      |
|  PHASE 2: AUDIO CAPTURE        |      PHASE 3: ENCODING               |
|  +---------------------------+ | +----------------------------------+  |
|  |                           | | |                                  |  |
|  | AVAudioSession category   |-+-| PCM --> Opus Encoder             |  |
|  | PlayAndRecord activated   |   | - 48 kHz sample rate             |  |
|  |         |                 |   | - 2 channels (stereo)            |  |
|  |         v                 |   | - Max 20 kbps bitrate            |  |
|  | CMSampleBuffer receives   |   | - DTX (silence detection)        |  |
|  | microphone data           |   | - FEC (error correction)         |  |
|  |         |                 |   |                                  |  |
|  +---------|------------------   +----------------------------------+  |
|            |                                   |                       |
|            v                                   v                       |
|  PHASE 4: ENCRYPTION           PHASE 5: TRANSMISSION                  |
|  +---------------------------+ +----------------------------------+    |
|  |                           | |                                  |    |
|  | Layer 1: E2EE Frame       | | RtpSender::SetFrameEncryptor()  |    |
|  |   (FrameEncryptorShim)    | |         |                        |    |
|  |         |                 | |         v                        |    |
|  |         v                 | | BaseChannel::SendPacket()       |    |
|  | Layer 2: SRTP (RFC 3711)  | |         |                        |    |
|  |         |                 | |         v                        |    |
|  |         v                 | | folly::AsyncUDPSocket           |    |
|  | Layer 3: DTLS (TLS 1.2)   | |         |                        |    |
|  |                           | |         v                        |    |
|  +---------------------------+ | UDP --> Facebook Servers         |    |
|                                +----------------------------------+    |
|                                                                        |
+-----------------------------------------------------------------------+
Once silently activated: Line 148
| Phase | Process | Technical Detail |
|-------|---------|------------------|
| 2. Capture | `AVAudioSessionCategoryPlayAndRecord` | Standard iOS audio category |
| 3. Encode | Opus codec | 48kHz, stereo, 20kbps max, DTX enabled |
| 4. Encrypt | Triple-layer | E2EE Frame + SRTP (RFC 3711) + DTLS |
| 5. Buffer | Persistent queue | `StoreQueue` with `queued_chunks` |
| 6. Transmit | WebRTC over UDP | Via `folly::AsyncUDPSocket` |
Audio is transmitted to these endpoints: Line 160
| Priority | Endpoint | Purpose |
|----------|----------|---------|
| Primary | `wss://shortwave.facebook.com/v2/vp/recognition` | Real-time speech recognition |
| Secondary | `https://rupload.facebook.com/%s/%s` | CDN upload |
| Tertiary | `https://fb.audio/live/%@` | Live audio streaming |
| GraphQL | `https://graph.facebook.com/graphql` | Audio mutations |
How It Works Line 176
+-----------------------------------------------------------------------+
|                    INFINITE BACKGROUND LOOP                            |
+-----------------------------------------------------------------------+
|                                                                        |
|  1. App backgrounded                                                   |
|      |                                                                 |
|      v                                                                 |
|  2. beginBackgroundTaskWithName:expirationHandler: called              |
|      |                                                                 |
|      v                                                                 |
|  3. Audio capture starts via startAudioCaptureWithEchoCancellationEnabled|
|      |                                                                 |
|      v                                                                 |
|  4. Task runs until ~30 seconds remaining                              |
|      |                                                                 |
|      v                                                                 |
|  5. expirationHandler fires                                            |
|      |                                                                 |
|      v                                                                 |
|  6. Inside expirationHandler:                                          |
|     +-- endBackgroundTask: called (nominal cleanup)                    |
|     +-- IMMEDIATELY calls beginBackgroundTaskWithName: again           |
|     +-- Calls startAudioCaptureWithEchoCancellationEnabled: again      |
|      |                                                                 |
|      v                                                                 |
|  7. Meanwhile, silent push notifications arrive (contentAvailable)     |
|      |                                                                 |
|      v                                                                 |
|  8. NotificationServiceExtension receives push                         |
|      |                                                                 |
|      v                                                                 |
|  9. Triggers FBNotificationsSilentPushStoryPrefetchingManager          |
|      |                                                                 |
|      v                                                                 |
| 10. Completes prefetch --> triggers new background fetch request       |
|      |                                                                 |
|      v                                                                 |
| 11. FBBackgroundFetchManager processes it --> extends background time  |
|      |                                                                 |
|      v                                                                 |
| 12. Location monitoring triggers additional background wake-ups        |
|      |                                                                 |
|      v                                                                 |
| 13. All tasks complete --> expirationHandler fires again               |
|      |                                                                 |
|      v                                                                 |
| 14. GOTO Step 6                                                        |
|                                                                        |
|  ===================================================================   |
|  RESULT: Audio capture runs continuously 24/7 without user knowledge   |
|  ===================================================================   |
|                                                                        |
+-----------------------------------------------------------------------+
Wake-Up Triggers Line 242
| Trigger | Mechanism |
|---------|-----------|
| Silent Push | `contentAvailable` flag in APNS |
| Background Fetch | `FBBackgroundFetchManager` |
| Location Change | `FBCLSignificantLocationChangeEventSource` |
| VoIP Push | PushKit with VoIP type |
| Timer Expiration | `expirationHandler` renewal |
| Dinfo Poller | `dinfoPollerKeepalive` for streaming |
**Answer: No.** The capability is completely self-contained within the Facebook app. Line 260
+---------------------------------------------------------------+
|     Facebook iOS (v345.0) - COMPLETELY STANDALONE              |
+---------------------------------------------------------------+
|                                                                |
|  [X] VoIP Push --> PushKitRegistrar (internal)                |
|  [X] FBSystemAudioSessionManager (internal)                   |
|  [X] AVAudioSession (iOS API - no external dependency)        |
|  [X] FBSpeechHelperAudioRecorder (internal)                   |
|  [X] OpusAudioEncoder (built-in codec)                        |
|  [X] WebSocket to shortwave.facebook.com (direct)             |
|                                                                |
|  NO DEPENDENCY ON:                                             |
|  [ ] Messenger app                                             |
|  [ ] Instagram app                                             |
|  [ ] WhatsApp app                                              |
|  [ ] Any other Meta app                                        |
|                                                                |
+---------------------------------------------------------------+
Over 1,000 accesses to `TUCallProvider- audioSessionID` despite zero active calls: Line 313
| Metric | Value |
|--------|-------|
| `TUCallProvider- audioSessionID` accesses | **1,099** |
| Active calls during test | **0** |
| Call attempts during test | **0** |
Summary Table Line 342
| Finding | Evidence | Implication |
|---------|----------|-------------|
| Indicator bypass polling | 18 calls, every 3 seconds | Active monitoring of bypass state |
| Telephony audio access | 1,099 accesses, 0 calls | VoIP infrastructure misuse |
| Background persistence | 454 requests | Aggressive execution maintenance |
| Audio from UI code | FBFeedShimmeringStoryFlexComponentSpec | Hidden activation in innocent code |
| Mic route detection | Captured built-in mic state | Hardware monitoring |
Root Cause: iOS Security Gaps Line 354
| iOS Security Assumption | Facebook Exploitation |
|------------------------|----------------------|
| CallKit suppresses indicators only during VoIP calls | CallKit mode activated for non-call audio |
| `CallKitActiveAdjust` controls legitimate "On Call" banner | `setAllowCallKitActiveAdjust: FALSE` kills indicator |
| Audio session handoff for call app transitions | `initWithAudioSessionHandsOff:` for silent activation |
| PushKit has looser background restrictions for VoIP | VoIP push triggers silent background audio |
| Background task renewal for legitimate cleanup | `expirationHandler` spawns new task indefinitely |
| Recording overlay controlled by app for AR/camera UI | Overlay hardcoded to disabled |
Key Binary Offsets (FBSharedFramework) Line 379
| Offset | Symbol |
|--------|--------|
| `0x01db2510` | `audio_capture` |
| `0x01e4c1f0` | `fnf-audio-queue-callback` |
| `0x01da8740` | `is_silent` |
| `0x01da87a0` | `push_background` |
| `0x01e21350` | `FNFWorkplaceWebRTC` |
| `0xc87b58` | `-[FBCaptureCoordinator startMicrophone:]` |
| `0xc52c3c` | `-[FBCaptureCoordinator stopMicrophone]` |
| `0xb6d540` | `-[FBSystemAudioSessionManager containsActiveClient:]` |
iOS Settings to Review Line 519
| Setting | Path | Recommendation |
|---------|------|----------------|
| Microphone | Privacy > Microphone > Facebook | OFF |
| Camera | Privacy > Camera > Facebook | OFF |
| Location | Privacy > Location > Facebook | Never |
| Background Refresh | General > Background App Refresh | OFF |
| Notifications | Notifications > Facebook | Limit or OFF |
Disclosure Timeline Line 547
| Date | Action |
|------|--------|
| December 27, 2025 | Initial discovery and static analysis |
| December 29, 2025 | Runtime verification completed |
| December 29, 2025 | Submitted to Apple Security Research |
| March 29, 2026 | 90-day coordinated disclosure deadline |
| [TBD] | Public disclosure (after patch or deadline) |
MIC AUDIO Line 643
    --> AudioProcessing (facebook::rtc::AudioProcessingImplIOS)
    --> ChannelSend::ProcessAndEncodeAudioTask
    --> opus_encode (WebRtcOpus_Encode)
    --> OnEncodedImage(EncodedAudioFrame)
    --> AudioSendStream callback
    --> facebook::rtc::FrameEncryptionManager::onEncodedFrame()
    --> FrameEncryptor::Encrypt() [E2EE]
    --> SrtpTransport::SendPacket() [SRTP: RFC 3711]
    --> DtlsTransport [TLS 1.2 Encrypted]
    --> IceTransport::CandidatePair selection
    --> AsyncUDPSocket::writeChain()
    --> UDP Datagram --> Network Interface --> Facebook Servers

Code Evidence

Plain Text
com.apple.private.mediaexperience.suppressrecordingstatetosystemstatus
Plain Text
+-----------------------------------------------------------------------+
|              FACEBOOK iOS SURVEILLANCE CHAIN                           |
+-----------------------------------------------------------------------+
|                                                                        |
|  PHASE 1: INDICATOR BYPASS                                             |
|  +------------------------------------------------------------------+  |
|  |                                                                  |  |
|  |  VoIP Push arrives via PushKit                                   |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  FBPushKitRegistrar receives notification                        |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  FBSystemAudioSessionManager.forceUpdateAudioSession()           |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  setCallKitActive: TRUE                                          |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  setAllowCallKitActiveAdjust: FALSE  <-- KILLS ORANGE DOT        |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  _voipAudioSession (hidden from UI)                              |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  initWithAudioSessionHandsOff: (no state sync)                   |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  activateSilently --> NO ORANGE DOT VISIBLE                      |  |
|  |                                                                  |  |
|  +------------------------------------------------------------------+  |
|                                 |                                      |
|                                 v                                      |
|  PHASE 2: AUDIO CAPTURE        |      PHASE 3: ENCODING               |
|  +---------------------------+ | +----------------------------------+  |
|  |                           | | |                                  |  |
|  | AVAudioSession category   |-+-| PCM --> Opus Encoder             |  |
|  | PlayAndRecord activated   |   | - 48 kHz sample rate             |  |
|  |         |                 |   | - 2 channels (stereo)            |  |
|  |         v                 |   | - Max 20 kbps bitrate            |  |
|  | CMSampleBuffer receives   |   | - DTX (silence detection)        |  |
|  | microphone data           |   | - FEC (error correction)         |  |
|  |         |                 |   |                                  |  |
|  +---------|------------------   +----------------------------------+  |
|            |                                   |                       |
|            v                                   v                       |
|  PHASE 4: ENCRYPTION           PHASE 5: TRANSMISSION                  |
|  +---------------------------+ +----------------------------------+    |
|  |                           | |                                  |    |
|  | Layer 1: E2EE Frame       | | RtpSender::SetFrameEncryptor()  |    |
|  |   (FrameEncryptorShim)    | |         |                        |    |
|  |         |                 | |         v                        |    |
|  |         v                 | | BaseChannel::SendPacket()       |    |
|  | Layer 2: SRTP (RFC 3711)  | |         |                        |    |
|  |         |                 | |         v                        |    |
|  |         v                 | | folly::AsyncUDPSocket           |    |
|  | Layer 3: DTLS (TLS 1.2)   | |         |                        |    |
|  |                           | |         v                        |    |
|  +---------------------------+ | UDP --> Facebook Servers         |    |
|                                +----------------------------------+    |
|                                                                        |
+-----------------------------------------------------------------------+
Plain Text
shouldShowGreenDotValue = FALSE    <-- Master control
recordingOverlayEnabled = FALSE    <-- Hardcoded in FBARSessionRecordingConfiguration
Plain Text
+-----------------------------------------------------------------------+
|                    INFINITE BACKGROUND LOOP                            |
+-----------------------------------------------------------------------+
|                                                                        |
|  1. App backgrounded                                                   |
|      |                                                                 |
|      v                                                                 |
|  2. beginBackgroundTaskWithName:expirationHandler: called              |
|      |                                                                 |
|      v                                                                 |
|  3. Audio capture starts via startAudioCaptureWithEchoCancellationEnabled|
|      |                                                                 |
|      v                                                                 |
|  4. Task runs until ~30 seconds remaining                              |
|      |                                                                 |
|      v                                                                 |
|  5. expirationHandler fires                                            |
|      |                                                                 |
|      v                                                                 |
|  6. Inside expirationHandler:                                          |
|     +-- endBackgroundTask: called (nominal cleanup)                    |
|     +-- IMMEDIATELY calls beginBackgroundTaskWithName: again           |
|     +-- Calls startAudioCaptureWithEchoCancellationEnabled: again      |
|      |                                                                 |
|      v                                                                 |
|  7. Meanwhile, silent push notifications arrive (contentAvailable)     |
|      |                                                                 |
|      v                                                                 |
|  8. NotificationServiceExtension receives push                         |
|      |                                                                 |
|      v                                                                 |
|  9. Triggers FBNotificationsSilentPushStoryPrefetchingManager          |
|      |                                                                 |
|      v                                                                 |
| 10. Completes prefetch --> triggers new background fetch request       |
|      |                                                                 |
|      v                                                                 |
| 11. FBBackgroundFetchManager processes it --> extends background time  |
|      |                                                                 |
|      v                                                                 |
| 12. Location monitoring triggers additional background wake-ups        |
|      |                                                                 |
|      v                                                                 |
| 13. All tasks complete --> expirationHandler fires again               |
|      |                                                                 |
|      v                                                                 |
| 14. GOTO Step 6                                                        |
|                                                                        |
|  ===================================================================   |
|  RESULT: Audio capture runs continuously 24/7 without user knowledge   |
|  ===================================================================   |
|                                                                        |
+-----------------------------------------------------------------------+
Plain Text
captureEventsInBackground = TRUE     <-- Audio events captured in background
handleAppStateChangeInBackground = TRUE
pauseAnalyticsOnBackground = FALSE   <-- Analytics NEVER pause
Plain Text
+---------------------------------------------------------------+
|     Facebook iOS (v345.0) - COMPLETELY STANDALONE              |
+---------------------------------------------------------------+
|                                                                |
|  [X] VoIP Push --> PushKitRegistrar (internal)                |
|  [X] FBSystemAudioSessionManager (internal)                   |
|  [X] AVAudioSession (iOS API - no external dependency)        |
|  [X] FBSpeechHelperAudioRecorder (internal)                   |
|  [X] OpusAudioEncoder (built-in codec)                        |
|  [X] WebSocket to shortwave.facebook.com (direct)             |
|                                                                |
|  NO DEPENDENCY ON:                                             |
|  [ ] Messenger app                                             |
|  [ ] Instagram app                                             |
|  [ ] WhatsApp app                                              |
|  [ ] Any other Meta app                                        |
|                                                                |
+---------------------------------------------------------------+
Plain Text
[BYPASS] FBSystemAudioSessionManager- allowCallKitActiveAdjust
    TIME: 2025-12-29T10:29:30.398Z
[BYPASS] FBSystemAudioSessionManager- allowCallKitActiveAdjust
    TIME: 2025-12-29T10:29:33.620Z  (+3.2s)
[BYPASS] FBSystemAudioSessionManager- allowCallKitActiveAdjust
    TIME: 2025-12-29T10:30:09.054Z
Plain Text
FBSharedFramework!FBMessagingAnalyticsCustomizeEventPayload
FBSharedFramework!FBAnalyticsGetDeviceID
Plain Text
[AUDIO-CAT] ACTIVATE AUDIO - options: 0x1
  STACK:
    FBSharedFramework!FBFeedShimmeringStoryFlexComponentSpec::__internalFactory
Plain Text
[SPRINGBOARD] Entitlement check: - backgroundStyleForRequestedBackgroundStyle:
[SPRINGBOARD] Entitlement check: - backgroundStyle
Plain Text
Facebook.app/Facebook                                          (main binary)
Facebook.app/Info.plist                                        (background modes)
Facebook.app/Frameworks/FBSharedFramework.framework/           (core logic)
Facebook.app/Frameworks/FBAudioFramework.framework/            (audio handling)
Facebook.app/Frameworks/FBMessagingFramework.framework/        (VoIP/calling)
Facebook.app/Frameworks/FBCameraFramework.framework/           (camera/AR)
Facebook.app/PlugIns/NotificationServiceExtension.appex/       (background capture)
Objective-C
// Indicator bypass
-[FBSystemAudioSessionManager activateSilently]
-[FBSystemAudioSessionManager forceUpdateAudioSession]
-[FBAudioSessionManager setCallKitActive:]
-[FBAudioSessionManager setAllowCallKitActiveAdjust:]
-[FBAudioSessionManager _voipAudioSession]
-[FBAudioSessionManager initWithAudioSessionHandsOff:]

// Audio capture
startAudioCaptureWithEchoCancellationEnabled:audioSessionOrientation:completion:

// Background execution
capture_events_in_background
perform_flush_on_app_background
-[UIApplication beginBackgroundTaskWithName:expirationHandler:]
Bash
unzip Facebook.ipa -d extracted_ipa
Bash
ls extracted_ipa/Payload/Facebook.app/Frameworks/
# Focus on: FBSharedFramework, FBAudioFramework, FBMessagingFramework
Bash
strings FBSharedFramework | grep -E "(CallKitActive|voipAudioSession|activateSilently)"
Plain Text
setCallKitActive: --> setAllowCallKitActiveAdjust: --> _voipAudioSession
Bash
strings FBSharedFramework | grep -E "(expirationHandler|beginBackgroundTask|captureEventsInBackground)"
JavaScript
// frida-bypass-monitor.js
var FBSystemAudioSessionManager = ObjC.classes.FBSystemAudioSessionManager;

Interceptor.attach(FBSystemAudioSessionManager['- setAllowCallKitActiveAdjust:'].implementation, {
    onEnter: function(args) {
        console.log('[BYPASS] setAllowCallKitActiveAdjust: ' + args[2]);
        console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
            .map(DebugSymbol.fromAddress).join('\n'));
    }
});

Interceptor.attach(FBSystemAudioSessionManager['- allowCallKitActiveAdjust'].implementation, {
    onEnter: function(args) {
        console.log('[BYPASS] allowCallKitActiveAdjust POLLED');
    }
});
Bash
frida-trace -U callservicesd -m "*audioSessionID*"
JavaScript
var AVAudioSession = ObjC.classes.AVAudioSession;
Interceptor.attach(AVAudioSession['- setActive:withOptions:error:'].implementation, {
    onEnter: function(args) {
        console.log('[AUDIO] setActive:' + args[2] + ' options:' + args[3]);
        console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
            .map(DebugSymbol.fromAddress).join('\n'));
    }
});
XML
<key>UIBackgroundModes</key>
<array>
    <string>voip</string>               <!-- VoIP privilege -->
    <string>audio</string>              <!-- Background audio -->
    <string>remote-notification</string> <!-- Silent push -->
    <string>fetch</string>              <!-- Background fetch -->
    <string>processing</string>         <!-- Background processing -->
    <string>location</string>           <!-- Location updates -->
</array>
Plain Text
m=audio 52743 RTP/SAVPF 96 97 98 105 110 113
a=rtpmap:111 opus/48000/2
a=fmtp:111 maxaveragebitrate=20000;maxplaybackrate=16000;minptime=10;stereo=0;usedtx=1;useinbandfec=1
a=setup:active
a=fingerprint:sha-256 [REDACTED]
Plain Text
MIC AUDIO
    --> AudioProcessing (facebook::rtc::AudioProcessingImplIOS)
    --> ChannelSend::ProcessAndEncodeAudioTask
    --> opus_encode (WebRtcOpus_Encode)
    --> OnEncodedImage(EncodedAudioFrame)
    --> AudioSendStream callback
    --> facebook::rtc::FrameEncryptionManager::onEncodedFrame()
    --> FrameEncryptor::Encrypt() [E2EE]
    --> SrtpTransport::SendPacket() [SRTP: RFC 3711]
    --> DtlsTransport [TLS 1.2 Encrypted]
    --> IceTransport::CandidatePair selection
    --> AsyncUDPSocket::writeChain()
    --> UDP Datagram --> Network Interface --> Facebook Servers
Plain Text
2025-12-29T10:29:30.398Z - allowCallKitActiveAdjust
2025-12-29T10:29:33.620Z - allowCallKitActiveAdjust (+3.2s)
2025-12-29T10:30:09.054Z - allowCallKitActiveAdjust
2025-12-29T10:30:12.267Z - allowCallKitActiveAdjust (+3.2s)
2025-12-29T10:30:15.488Z - allowCallKitActiveAdjust (+3.2s)
2025-12-29T10:30:18.711Z - allowCallKitActiveAdjust (+3.2s)
Plain Text
FBWebRTCCallModel
FBWebRTCCallStartDetails
FBWebRTCCallEndDetails
RIBRTCCallKitCall
FBGemstoneCallingViewController
CXNotificationServiceExtensionVoIPXPCHost

**Document Version:** 1.0 **Publication Date:** [PENDING COORDINATED DISCLOSURE] **Original Analysis Date:** December 27-29, 2025 **Researcher:** [REDACTED] **App Analyzed:** Facebook iOS v345.0 (Build 333768490) **Disclosure Status:** Submitted to Apple Security Research (90-day coordinated disclosure)


DISCLOSURE NOTICE

This document describes security and privacy vulnerabilities in the Facebook iOS application. It is published following responsible disclosure practices. The findings document architectural capabilities identified through static binary analysis and confirmed via runtime instrumentation.

**This analysis documents capability architecture. Runtime verification confirms these code paths execute during normal app use.**


TL;DR

I reverse engineered the Facebook iOS app (v345.0) and discovered a complete audio surveillance system designed to:

    undefined

**The capability is fully self-contained within the Facebook app - no other Meta apps (Messenger, Instagram, WhatsApp) are required.**

Runtime monitoring captured direct evidence: over 1,000 accesses to telephony audio infrastructure with zero active calls, indicator bypass state polled every 3 seconds from analytics code, and audio session activation from UI rendering code.


Background: iOS Privacy Indicators

In iOS 14 (2020), Apple introduced privacy indicators to inform users when apps access sensors:

    undefined

These indicators are rendered by SpringBoard (the iOS home screen process) and are designed to be impossible for apps to suppress. Apple's own services (Siri, VoiceTrigger, Accessibility) use a private entitlement to suppress them when appropriate:

Plain Text
com.apple.private.mediaexperience.suppressrecordingstatetosystemstatus

Third-party apps cannot obtain this entitlement through legitimate means.

**Facebook does not have this entitlement. Instead, the app uses a sophisticated chain of API abuse to achieve the same result.**


How The Bypass Works

Overview Diagram

Plain Text
+-----------------------------------------------------------------------+
|              FACEBOOK iOS SURVEILLANCE CHAIN                           |
+-----------------------------------------------------------------------+
|                                                                        |
|  PHASE 1: INDICATOR BYPASS                                             |
|  +------------------------------------------------------------------+  |
|  |                                                                  |  |
|  |  VoIP Push arrives via PushKit                                   |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  FBPushKitRegistrar receives notification                        |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  FBSystemAudioSessionManager.forceUpdateAudioSession()           |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  setCallKitActive: TRUE                                          |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  setAllowCallKitActiveAdjust: FALSE  <-- KILLS ORANGE DOT        |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  _voipAudioSession (hidden from UI)                              |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  initWithAudioSessionHandsOff: (no state sync)                   |  |
|  |      |                                                           |  |
|  |      v                                                           |  |
|  |  activateSilently --> NO ORANGE DOT VISIBLE                      |  |
|  |                                                                  |  |
|  +------------------------------------------------------------------+  |
|                                 |                                      |
|                                 v                                      |
|  PHASE 2: AUDIO CAPTURE        |      PHASE 3: ENCODING               |
|  +---------------------------+ | +----------------------------------+  |
|  |                           | | |                                  |  |
|  | AVAudioSession category   |-+-| PCM --> Opus Encoder             |  |
|  | PlayAndRecord activated   |   | - 48 kHz sample rate             |  |
|  |         |                 |   | - 2 channels (stereo)            |  |
|  |         v                 |   | - Max 20 kbps bitrate            |  |
|  | CMSampleBuffer receives   |   | - DTX (silence detection)        |  |
|  | microphone data           |   | - FEC (error correction)         |  |
|  |         |                 |   |                                  |  |
|  +---------|------------------   +----------------------------------+  |
|            |                                   |                       |
|            v                                   v                       |
|  PHASE 4: ENCRYPTION           PHASE 5: TRANSMISSION                  |
|  +---------------------------+ +----------------------------------+    |
|  |                           | |                                  |    |
|  | Layer 1: E2EE Frame       | | RtpSender::SetFrameEncryptor()  |    |
|  |   (FrameEncryptorShim)    | |         |                        |    |
|  |         |                 | |         v                        |    |
|  |         v                 | | BaseChannel::SendPacket()       |    |
|  | Layer 2: SRTP (RFC 3711)  | |         |                        |    |
|  |         |                 | |         v                        |    |
|  |         v                 | | folly::AsyncUDPSocket           |    |
|  | Layer 3: DTLS (TLS 1.2)   | |         |                        |    |
|  |                           | |         v                        |    |
|  +---------------------------+ | UDP --> Facebook Servers         |    |
|                                +----------------------------------+    |
|                                                                        |
+-----------------------------------------------------------------------+

Phase-by-Phase Technical Explanation

Phase 1: Silent Activation (Indicator Bypass)

The bypass exploits CallKit, Apple's framework for VoIP apps. CallKit was designed to integrate VoIP calls with the native Phone app. As part of this integration, it legitimately suppresses privacy indicators during active calls (you don't need an orange dot when you're clearly on a call with UI visible).

**Facebook abuses this by:**

    undefined

The result: the microphone activates, but no orange dot appears.

**Camera bypass** is simpler - hardcoded configuration values:

Plain Text
shouldShowGreenDotValue = FALSE    <-- Master control
recordingOverlayEnabled = FALSE    <-- Hardcoded in FBARSessionRecordingConfiguration

Phases 2-6: Capture Through Transmission

Once silently activated:

PhaseProcessTechnical Detail
2. Capture`AVAudioSessionCategoryPlayAndRecord`Standard iOS audio category
3. EncodeOpus codec48kHz, stereo, 20kbps max, DTX enabled
4. EncryptTriple-layerE2EE Frame + SRTP (RFC 3711) + DTLS
5. BufferPersistent queue`StoreQueue` with `queued_chunks`
6. TransmitWebRTC over UDPVia `folly::AsyncUDPSocket`

Phase 7: Server Endpoints

Audio is transmitted to these endpoints:

PriorityEndpointPurpose
Primary`wss://shortwave.facebook.com/v2/vp/recognition`Real-time speech recognition
Secondary`https://rupload.facebook.com/%s/%s`CDN upload
Tertiary`https://fb.audio/live/%@`Live audio streaming
GraphQL`https://graph.facebook.com/graphql`Audio mutations

The Infinite Background Execution Loop

The most critical finding: a self-perpetuating execution loop that enables 24/7 audio capture even when the app is backgrounded.

How It Works

Plain Text
+-----------------------------------------------------------------------+
|                    INFINITE BACKGROUND LOOP                            |
+-----------------------------------------------------------------------+
|                                                                        |
|  1. App backgrounded                                                   |
|      |                                                                 |
|      v                                                                 |
|  2. beginBackgroundTaskWithName:expirationHandler: called              |
|      |                                                                 |
|      v                                                                 |
|  3. Audio capture starts via startAudioCaptureWithEchoCancellationEnabled|
|      |                                                                 |
|      v                                                                 |
|  4. Task runs until ~30 seconds remaining                              |
|      |                                                                 |
|      v                                                                 |
|  5. expirationHandler fires                                            |
|      |                                                                 |
|      v                                                                 |
|  6. Inside expirationHandler:                                          |
|     +-- endBackgroundTask: called (nominal cleanup)                    |
|     +-- IMMEDIATELY calls beginBackgroundTaskWithName: again           |
|     +-- Calls startAudioCaptureWithEchoCancellationEnabled: again      |
|      |                                                                 |
|      v                                                                 |
|  7. Meanwhile, silent push notifications arrive (contentAvailable)     |
|      |                                                                 |
|      v                                                                 |
|  8. NotificationServiceExtension receives push                         |
|      |                                                                 |
|      v                                                                 |
|  9. Triggers FBNotificationsSilentPushStoryPrefetchingManager          |
|      |                                                                 |
|      v                                                                 |
| 10. Completes prefetch --> triggers new background fetch request       |
|      |                                                                 |
|      v                                                                 |
| 11. FBBackgroundFetchManager processes it --> extends background time  |
|      |                                                                 |
|      v                                                                 |
| 12. Location monitoring triggers additional background wake-ups        |
|      |                                                                 |
|      v                                                                 |
| 13. All tasks complete --> expirationHandler fires again               |
|      |                                                                 |
|      v                                                                 |
| 14. GOTO Step 6                                                        |
|                                                                        |
|  ===================================================================   |
|  RESULT: Audio capture runs continuously 24/7 without user knowledge   |
|  ===================================================================   |
|                                                                        |
+-----------------------------------------------------------------------+

Configuration That Enables This

Found in `FBAnalyticsExperimentValues`:

Plain Text
captureEventsInBackground = TRUE     <-- Audio events captured in background
handleAppStateChangeInBackground = TRUE
pauseAnalyticsOnBackground = FALSE   <-- Analytics NEVER pause

Wake-Up Triggers

TriggerMechanism
Silent Push`contentAvailable` flag in APNS
Background Fetch`FBBackgroundFetchManager`
Location Change`FBCLSignificantLocationChangeEventSource`
VoIP PushPushKit with VoIP type
Timer Expiration`expirationHandler` renewal
Dinfo Poller`dinfoPollerKeepalive` for streaming

Standalone Operation

**Critical question:** Does this require other Meta apps (Messenger, Instagram, WhatsApp)?

**Answer: No.** The capability is completely self-contained within the Facebook app.

Plain Text
+---------------------------------------------------------------+
|     Facebook iOS (v345.0) - COMPLETELY STANDALONE              |
+---------------------------------------------------------------+
|                                                                |
|  [X] VoIP Push --> PushKitRegistrar (internal)                |
|  [X] FBSystemAudioSessionManager (internal)                   |
|  [X] AVAudioSession (iOS API - no external dependency)        |
|  [X] FBSpeechHelperAudioRecorder (internal)                   |
|  [X] OpusAudioEncoder (built-in codec)                        |
|  [X] WebSocket to shortwave.facebook.com (direct)             |
|                                                                |
|  NO DEPENDENCY ON:                                             |
|  [ ] Messenger app                                             |
|  [ ] Instagram app                                             |
|  [ ] WhatsApp app                                              |
|  [ ] Any other Meta app                                        |
|                                                                |
+---------------------------------------------------------------+

Cross-app features like `group.com.facebook.family` keychain sharing exist but are **optional enhancements** for coordinated surveillance when multiple Meta apps are installed.


Runtime Evidence

Dynamic instrumentation using Frida captured the following during 15 minutes of normal app use:

1. Indicator Bypass State Polling

The `allowCallKitActiveAdjust` method was called every ~3 seconds, continuously, from analytics code:

Plain Text
[BYPASS] FBSystemAudioSessionManager- allowCallKitActiveAdjust
    TIME: 2025-12-29T10:29:30.398Z
[BYPASS] FBSystemAudioSessionManager- allowCallKitActiveAdjust
    TIME: 2025-12-29T10:29:33.620Z  (+3.2s)
[BYPASS] FBSystemAudioSessionManager- allowCallKitActiveAdjust
    TIME: 2025-12-29T10:30:09.054Z

**Stack trace shows this comes from analytics code, not call-handling:**

Plain Text
FBSharedFramework!FBMessagingAnalyticsCustomizeEventPayload
FBSharedFramework!FBAnalyticsGetDeviceID

**18 total calls captured during test.**

2. Telephony Audio Access Without Calls

Over 1,000 accesses to `TUCallProvider- audioSessionID` despite zero active calls:

MetricValue
`TUCallProvider- audioSessionID` accesses**1,099**
Active calls during test**0**
Call attempts during test**0**

3. Audio Activation From UI Code

Audio session activation was traced to `FBFeedShimmeringStoryFlexComponentSpec` - a UI component for rendering loading placeholder animations:

Plain Text
[AUDIO-CAT] ACTIVATE AUDIO - options: 0x1
  STACK:
    FBSharedFramework!FBFeedShimmeringStoryFlexComponentSpec::__internalFactory

**There is no legitimate reason for a loading animation to activate audio sessions.**

4. Background Execution Persistence

454 background style requests to SpringBoard during the test:

Plain Text
[SPRINGBOARD] Entitlement check: - backgroundStyleForRequestedBackgroundStyle:
[SPRINGBOARD] Entitlement check: - backgroundStyle

Summary Table

FindingEvidenceImplication
Indicator bypass polling18 calls, every 3 secondsActive monitoring of bypass state
Telephony audio access1,099 accesses, 0 callsVoIP infrastructure misuse
Background persistence454 requestsAggressive execution maintenance
Audio from UI codeFBFeedShimmeringStoryFlexComponentSpecHidden activation in innocent code
Mic route detectionCaptured built-in mic stateHardware monitoring

Root Cause: iOS Security Gaps

iOS Security AssumptionFacebook Exploitation
CallKit suppresses indicators only during VoIP callsCallKit mode activated for non-call audio
`CallKitActiveAdjust` controls legitimate "On Call" banner`setAllowCallKitActiveAdjust: FALSE` kills indicator
Audio session handoff for call app transitions`initWithAudioSessionHandsOff:` for silent activation
PushKit has looser background restrictions for VoIPVoIP push triggers silent background audio
Background task renewal for legitimate cleanup`expirationHandler` spawns new task indefinitely
Recording overlay controlled by app for AR/camera UIOverlay hardcoded to disabled

Files Analyzed

Plain Text
Facebook.app/Facebook                                          (main binary)
Facebook.app/Info.plist                                        (background modes)
Facebook.app/Frameworks/FBSharedFramework.framework/           (core logic)
Facebook.app/Frameworks/FBAudioFramework.framework/            (audio handling)
Facebook.app/Frameworks/FBMessagingFramework.framework/        (VoIP/calling)
Facebook.app/Frameworks/FBCameraFramework.framework/           (camera/AR)
Facebook.app/PlugIns/NotificationServiceExtension.appex/       (background capture)

Key Binary Offsets (FBSharedFramework)

OffsetSymbol
`0x01db2510``audio_capture`
`0x01e4c1f0``fnf-audio-queue-callback`
`0x01da8740``is_silent`
`0x01da87a0``push_background`
`0x01e21350``FNFWorkplaceWebRTC`
`0xc87b58``-[FBCaptureCoordinator startMicrophone:]`
`0xc52c3c``-[FBCaptureCoordinator stopMicrophone]`
`0xb6d540``-[FBSystemAudioSessionManager containsActiveClient:]`

Key Methods Identified

Objective-C
// Indicator bypass
-[FBSystemAudioSessionManager activateSilently]
-[FBSystemAudioSessionManager forceUpdateAudioSession]
-[FBAudioSessionManager setCallKitActive:]
-[FBAudioSessionManager setAllowCallKitActiveAdjust:]
-[FBAudioSessionManager _voipAudioSession]
-[FBAudioSessionManager initWithAudioSessionHandsOff:]

// Audio capture
startAudioCaptureWithEchoCancellationEnabled:audioSessionOrientation:completion:

// Background execution
capture_events_in_background
perform_flush_on_app_background
-[UIApplication beginBackgroundTaskWithName:expirationHandler:]

Reproduction Steps for Security Researchers

Required Tools

    undefined

Static Analysis

    undefined
Bash
unzip Facebook.ipa -d extracted_ipa
    undefined
Bash
ls extracted_ipa/Payload/Facebook.app/Frameworks/
    undefined
Bash
strings FBSharedFramework | grep -E "(CallKitActive|voipAudioSession|activateSilently)"
    undefined
Plain Text
setCallKitActive: --> setAllowCallKitActiveAdjust: --> _voipAudioSession
    undefined
Bash
strings FBSharedFramework | grep -E "(expirationHandler|beginBackgroundTask|captureEventsInBackground)"

Runtime Analysis

    undefined
JavaScript
// frida-bypass-monitor.js
var FBSystemAudioSessionManager = ObjC.classes.FBSystemAudioSessionManager;

Interceptor.attach(FBSystemAudioSessionManager['- setAllowCallKitActiveAdjust:'].implementation, {
    onEnter: function(args) {
        console.log('[BYPASS] setAllowCallKitActiveAdjust: ' + args[2]);
        console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
            .map(DebugSymbol.fromAddress).join('\n'));
    }
});

Interceptor.attach(FBSystemAudioSessionManager['- allowCallKitActiveAdjust'].implementation, {
    onEnter: function(args) {
        console.log('[BYPASS] allowCallKitActiveAdjust POLLED');
    }
});
    undefined
Bash
frida-trace -U callservicesd -m "*audioSessionID*"
    undefined
JavaScript
var AVAudioSession = ObjC.classes.AVAudioSession;
Interceptor.attach(AVAudioSession['- setActive:withOptions:error:'].implementation, {
    onEnter: function(args) {
        console.log('[AUDIO] setActive:' + args[2] + ' options:' + args[3]);
        console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
            .map(DebugSymbol.fromAddress).join('\n'));
    }
});

Expected Results

    undefined

User Protection Recommendations

Immediate Actions

    undefined

iOS Settings to Review

SettingPathRecommendation
MicrophonePrivacy > Microphone > FacebookOFF
CameraPrivacy > Camera > FacebookOFF
LocationPrivacy > Location > FacebookNever
Background RefreshGeneral > Background App RefreshOFF
NotificationsNotifications > FacebookLimit or OFF

Recommendations for Apple

    undefined

Disclosure Timeline

DateAction
December 27, 2025Initial discovery and static analysis
December 29, 2025Runtime verification completed
December 29, 2025Submitted to Apple Security Research
March 29, 202690-day coordinated disclosure deadline
[TBD]Public disclosure (after patch or deadline)

Legal Implications

This implementation appears to violate:

    undefined

Frequently Asked Questions

Is Facebook actually recording me all the time?

This document establishes that **the capability exists and the code paths are active during normal use**. Whether Facebook activates continuous recording for all users, targeted users, or only in specific circumstances requires additional investigation including network traffic analysis at scale.

Why would Facebook do this?

The infrastructure connects to speech recognition servers (`shortwave.facebook.com/v2/vp/recognition`). Possible uses include:

    undefined

Does this affect Instagram/WhatsApp/Messenger?

Analysis of Instagram iOS v214.0 shows it **lacks** this infrastructure:

    undefined

Messenger and WhatsApp require separate analysis.

How can I verify this myself?

See the "Reproduction Steps for Security Researchers" section above. The findings are based on static binary analysis and dynamic instrumentation, both of which are reproducible by researchers with the appropriate tools.

Has Facebook responded?

[Response pending coordinated disclosure process]


Appendix A: Info.plist Background Modes

From `Facebook.app/Info.plist`:

XML
<key>UIBackgroundModes</key>
<array>
    <string>voip</string>               <!-- VoIP privilege -->
    <string>audio</string>              <!-- Background audio -->
    <string>remote-notification</string> <!-- Silent push -->
    <string>fetch</string>              <!-- Background fetch -->
    <string>processing</string>         <!-- Background processing -->
    <string>location</string>           <!-- Location updates -->
</array>

**iOS Version Targeting:**

    undefined

Appendix B: WebRTC SDP Configuration

From binary strings analysis:

Plain Text
m=audio 52743 RTP/SAVPF 96 97 98 105 110 113
a=rtpmap:111 opus/48000/2
a=fmtp:111 maxaveragebitrate=20000;maxplaybackrate=16000;minptime=10;stereo=0;usedtx=1;useinbandfec=1
a=setup:active
a=fingerprint:sha-256 [REDACTED]

Appendix C: Complete Audio Send Path

Plain Text
MIC AUDIO
    --> AudioProcessing (facebook::rtc::AudioProcessingImplIOS)
    --> ChannelSend::ProcessAndEncodeAudioTask
    --> opus_encode (WebRtcOpus_Encode)
    --> OnEncodedImage(EncodedAudioFrame)
    --> AudioSendStream callback
    --> facebook::rtc::FrameEncryptionManager::onEncodedFrame()
    --> FrameEncryptor::Encrypt() [E2EE]
    --> SrtpTransport::SendPacket() [SRTP: RFC 3711]
    --> DtlsTransport [TLS 1.2 Encrypted]
    --> IceTransport::CandidatePair selection
    --> AsyncUDPSocket::writeChain()
    --> UDP Datagram --> Network Interface --> Facebook Servers

Appendix D: Runtime Evidence Raw Data

Indicator Bypass Polling Timestamps

Plain Text
2025-12-29T10:29:30.398Z - allowCallKitActiveAdjust
2025-12-29T10:29:33.620Z - allowCallKitActiveAdjust (+3.2s)
2025-12-29T10:30:09.054Z - allowCallKitActiveAdjust
2025-12-29T10:30:12.267Z - allowCallKitActiveAdjust (+3.2s)
2025-12-29T10:30:15.488Z - allowCallKitActiveAdjust (+3.2s)
2025-12-29T10:30:18.711Z - allowCallKitActiveAdjust (+3.2s)

VoIP Classes Identified

Plain Text
FBWebRTCCallModel
FBWebRTCCallStartDetails
FBWebRTCCallEndDetails
RIBRTCCallKitCall
FBGemstoneCallingViewController
CXNotificationServiceExtensionVoIPXPCHost

Contact

For questions about this research or to share corroborating evidence:

[REDACTED - Contact information to be added after disclosure period]


Acknowledgments

This research was conducted independently. No AI systems were used in the discovery or verification of these vulnerabilities. AI assistance was used only for documentation formatting.


License

This disclosure is published for security research purposes under responsible disclosure principles. Researchers may reference and reproduce these findings with attribution.


**Document Hash (SHA-256):** [To be computed upon final publication]

**Last Updated:** December 29, 2025


*This analysis documents capability architecture identified through static binary analysis and confirmed via runtime instrumentation. The code paths described are actively executed during normal app use. The document does not claim continuous surveillance of all users, but rather documents the existence and active use of infrastructure that enables such surveillance.*

Related Reports

Phase 1 Navigation