WhisperPair-PoC and Research

GitHub

demo

Executive Summary

Google Fast Pair was designed to make Bluetooth pairing seamless: tap a notification and you're connected. But what happens when that seamless experience becomes a security liability? WhisperPair-PoC is a security research tool that exposes two critical vulnerability classes affecting millions of Bluetooth accessories: unauthorized pairing bypass and Find My Device Network tracking exploitation.

This post details the technical internals of WhisperPair-PoC, the protocol weaknesses it exploits, and what this means for the Bluetooth accessory ecosystem.


Table of Contents

  1. The Vulnerabilities
  2. Fast Pair Protocol Primer
  3. How WhisperPair-PoC Works
  4. The Attack Surface
  5. Detection Methodology
  6. Exploitation Capabilities
  7. Remediation Guidance
  8. Ethical Considerations

The Vulnerabilities

CVE-2025-36911: Fast Pair Pairing Mode Bypass

The Google Fast Pair specification explicitly states:

"If the optional Public Key field is present: If the device is not in pairing mode, ignore the write and exit."

This is the critical security gate. Devices should only respond to Key-Based Pairing requests when the user has explicitly put the device into pairing mode (typically by holding a button). This ensures user intent, so you can't pair with someone's earbuds while they're wearing them.

The Problem: Many manufacturers skip this check entirely. They process pairing requests regardless of pairing mode state, enabling:

Find Hub Account Key Exposure

Google's Find My Device Network (FMDN) allows tracking Bluetooth accessories via the crowd-sourced Android device network. This requires an Account Key, a 16-byte symmetric key linking the device to a Google account.

The Problem: The Account Key characteristic often accepts writes without authentication:


Fast Pair Protocol Primer

Before diving into the exploitation, let's understand the legitimate Fast Pair flow:

Advertisement Phase

┌─────────────────────────────────────────────────────────────┐
│                    BLE Advertisement                         │
├─────────────────────────────────────────────────────────────┤
│  Service UUID: 0xFE2C (Fast Pair)                           │
│  Service Data:                                               │
│    [Pairing Mode]   → 3 bytes: Model ID only                │
│    [Not Pairing]    → 4+ bytes: 0x00 + Account Key Filter   │
└─────────────────────────────────────────────────────────────┘

The advertisement format reveals pairing state:

Key-Based Pairing Handshake

Seeker (Phone)                              Provider (Accessory)
      │                                              │
      │───── GATT Connect ──────────────────────────>│
      │                                              │
      │───── Discover Services ─────────────────────>│
      │<──── Service: 0xFE2C ────────────────────────│
      │                                              │
      │───── Enable Notifications (0xFE2C1234) ─────>│
      │                                              │
      │───── Write Key-Based Pairing Request ───────>│
      │      [16-byte encrypted block]               │
      │      [64-byte ECDH Public Key] (optional)    │
      │                                              │
      │      ┌────────────────────────────────────┐  │
      │      │ SECURITY CHECK:                    │  │
      │      │ If Public Key present AND          │  │
      │      │ device NOT in pairing mode:        │  │
      │      │   → IGNORE and EXIT                │  │
      │      │ Else:                              │  │
      │      │   → Process request                │  │
      │      └────────────────────────────────────┘  │
      │                                              │
      │<──── Notification: Encrypted Response ───────│
      │      [Provider's BR/EDR Address]             │
      │                                              │
      │═══════ Bluetooth Classic Pairing ═══════════>│

The vulnerability occurs when devices skip the "SECURITY CHECK" box entirely.


How WhisperPair-PoC Works

WhisperPair-PoC is a Python-based security research tool built on top of the Bleak BLE library. It operates in several phases:

Architecture Overview

┌────────────────────────────────────────────────────────────────┐
│                        WhisperPair-PoC                          │
├────────────────────────────────────────────────────────────────┤
│  CLI Layer                                                      │
│  ├── Argument parsing (--target-name, --scan-duration)         │
│  ├── TargetPolicy construction                                  │
│  └── REPL initialization                                        │
├────────────────────────────────────────────────────────────────┤
│  Discovery Engine                                               │
│  ├── BLE scanning via Bleak                                    │
│  ├── Advertisement parsing                                      │
│  ├── Protocol detection (Fast Pair, FMDN, Swift Pair)          │
│  └── Device fingerprinting (Model ID, OUI lookup)              │
├────────────────────────────────────────────────────────────────┤
│  Check Engines                                                  │
│  ├── FastPairCheckEngine (passive advertisement analysis)      │
│  ├── FastPairBypass (active CVE-2025-36911 testing)           │
│  ├── FindHubCheckEngine (Account Key status detection)         │
│  └── RiskScorer (composite vulnerability assessment)           │
├────────────────────────────────────────────────────────────────┤
│  Connection Manager                                             │
│  ├── GATT connect with MTU negotiation                         │
│  ├── Service/characteristic discovery                          │
│  ├── Read/Write/Notify operations                              │
│  └── Error handling and retry logic                            │
├────────────────────────────────────────────────────────────────┤
│  Exploitation Modules                                           │
│  ├── ring_device() - Trigger locator sound                     │
│  ├── set_account_key() - Write Account Key                     │
│  └── Response parsing (BR/EDR address extraction)              │
└────────────────────────────────────────────────────────────────┘

Core Components

1. Discovery Engine (discovery.py)

The scanner uses Bleak's detection callbacks to capture BLE advertisements:

async def _detection_callback(
    self, device: BLEDevice, advertisement_data: AdvertisementData
) -> None:
    """Process each detected BLE advertisement."""
    discovered = DiscoveredDevice(
        address=device.address,
        name=device.name or advertisement_data.local_name,
        rssi=advertisement_data.rssi,
        advertisement=self._convert_advertisement(advertisement_data),
        first_seen=datetime.now(UTC),
        last_seen=datetime.now(UTC),
    )
    self._devices[device.address] = discovered

For each device, the tool extracts:

2. Pairing Mode Inference (fastpair.py)

Pairing mode is determined by analyzing the device's advertisement:

def _analyze_service_data(self, evidence: FastPairEvidence) -> None:
    """Analyze Fast Pair service data to infer pairing mode."""
    data = evidence.service_data_bytes
    
    # 3 bytes = Model ID only = Pairing Mode (discoverable)
    if len(data) == 3:
        evidence.inferred_pairing_mode = PairingModeState.IN_PAIRING_MODE
        return
    
    # 4+ bytes with version 0x00 = Not in pairing mode
    if data[0] == 0x00:
        akd_byte = data[1]
        akd_type = akd_byte & 0x0F  # Lower 4 bits
        # Type 0x00 = Show UI, Type 0x02 = Hide UI
        # Both indicate NOT in pairing mode
        evidence.inferred_pairing_mode = PairingModeState.NOT_IN_PAIRING_MODE

This is crucial: if a device is determined to be not in pairing mode, and it still responds to the pairing request, it's vulnerable.

3. Vulnerability Detection (fastpair_attack.py)

The core vulnerability test sends Key-Based Pairing requests and monitors for responses:

async def check_vulnerability(self, device: DiscoveredDevice) -> BypassCheckResult:
    """Test if device responds to pairing requests when not in pairing mode."""
    
    # Enable notifications to receive response
    await self.connection_manager.start_notify(
        KEY_BASED_PAIRING_CHAR,
        self._notification_handler,
    )
    
    # Build and send request (multiple strategies)
    for strategy in self.strategies:
        request, flags = self._build_strategy_request(device.address, strategy)
        await self.connection_manager.write_characteristic(
            KEY_BASED_PAIRING_CHAR,
            request,
        )
        
        # Wait for response
        try:
            await asyncio.wait_for(
                self._response_received.wait(),
                timeout=self.response_timeout,
            )
            # Response received = VULNERABLE
            return BypassCheckResult(result=BypassResult.VULNERABLE, ...)
        except asyncio.TimeoutError:
            # No response = Device correctly ignored request
            continue
    
    return BypassCheckResult(result=BypassResult.NOT_VULNERABLE, ...)

The tool implements multiple request strategies because different devices respond to different flag combinations:

Strategy Flags Description
RAW_KBP 0x11 INITIATE_BONDING | EXTENDED_RESPONSE
WITH_PUBLIC_KEY 0x11 80-byte request with ECDH public key
RETROACTIVE 0x0A Bypasses some manufacturer checks
EXTENDED 0x10 For newer device firmware

4. Response Parsing

When a vulnerable device responds, the tool extracts the BR/EDR (Bluetooth Classic) address:

def _parse_response(self, response_data: bytes) -> str | None:
    """Extract provider's BR/EDR address from response."""
    
    # Strategy 1: Standard response (type 0x01)
    if response_data[0] == 0x01:
        return self._extract_address(response_data, offset=1)
    
    # Strategy 2: Extended response (type 0x02)
    if response_data[0] == 0x02:
        addr_count = response_data[2]
        return self._extract_address(response_data, offset=3)
    
    # Strategy 3: Brute force pattern matching
    for offset in range(len(response_data) - 5):
        addr = self._extract_address(response_data, offset)
        if self._is_valid_mac(addr):
            return addr

This BR/EDR address could be used to initiate Bluetooth Classic pairing (though WhisperPair-PoC stops at detection).


The Attack Surface

What Makes Devices Vulnerable?

Research identified common implementation failures:

1. Missing Pairing Mode Check

The most common issue: manufacturers simply don't implement the check:

// VULNERABLE: No pairing mode check
void handle_kbp_write(uint8_t* data, size_t len) {
    if (len >= 80 && has_public_key(data)) {
        // Should check: if (!is_in_pairing_mode()) return;
        process_pairing_request(data);  // Processes regardless
    }
}

2. Unprotected Account Key Writes

The Account Key characteristic should require authentication:

// VULNERABLE: No authentication required
void handle_account_key_write(uint8_t* key, size_t len) {
    if (len == 16) {
        store_account_key(key);  // Accepts any key from anyone
    }
}

// SECURE: Verify caller knows existing key
void handle_account_key_write_secure(uint8_t* encrypted_key, size_t len) {
    if (!verify_encrypted_with_existing_key(encrypted_key)) {
        return;  // Reject unauthorized writes
    }
    store_account_key(decrypt(encrypted_key));
}

3. Advertisement Leakage

Devices advertising Fast Pair service data when not in pairing mode reveal:


Detection Methodology

WhisperPair-PoC uses a layered detection approach:

Layer 1: Passive Advertisement Analysis

No connection required. The tool analyzes what the device broadcasts:

┌─────────────────────────────────────────────────────────────┐
│  Passive Checks                                              │
├─────────────────────────────────────────────────────────────┤
│  ✓ Fast Pair service UUID present (0xFE2C)                  │
│  ✓ FMDN service UUID present (0xFD44)                       │
│  ✓ Pairing mode inferred from service data length           │
│  ✓ Model ID extracted (when in pairing mode)                │
│  ✓ Account Key Filter detected (when not in pairing mode)   │
│  ✓ Address type analysis (static vs. random)                │
└─────────────────────────────────────────────────────────────┘

Result: "Device advertising Fast Pair data while NOT in pairing mode"
        → Potential gating violation (needs active test to confirm)

Layer 2: Active GATT Probing

Requires connection. The tool interacts with GATT services:

┌─────────────────────────────────────────────────────────────┐
│  Active Checks                                               │
├─────────────────────────────────────────────────────────────┤
│  1. Connect to device via BLE                               │
│  2. Discover Fast Pair service (0xFE2C)                     │
│  3. Locate Key-Based Pairing characteristic (0xFE2C1234)    │
│  4. Enable notifications                                     │
│  5. Write Key-Based Pairing request                         │
│  6. Wait for response (with timeout)                        │
│  7. Response received → VULNERABLE                          │
│     Timeout/Rejected → NOT VULNERABLE                       │
└─────────────────────────────────────────────────────────────┘

Layer 3: Risk Scoring

A composite risk score is computed:

Signal Score Meaning
Gating violation (passive) +2 Advertising FP when not in pairing mode
Bypass confirmed (active) +2 Responded to KBP when not in pairing mode
FMDN without Account Key +2 Trackable via Find My network
Static BLE address +1 Device is persistently trackable
Address rotation observed -1 Privacy-preserving behavior

Risk Levels:


Exploitation Capabilities

WhisperPair-PoC includes controlled exploitation features to demonstrate impact:

1. Device Ringing (ring command)

Triggers the FMDN Beacon Actions characteristic to play the locator sound:

REDACTED FOR RELEASE

Impact: Attacker can harass victim by repeatedly triggering sound, or use it to locate a device they want to steal.

2. Account Key Manipulation (set account-key command)

Writes a new Account Key to the device:

REDACTED FOR RELEASE

Impact:

What Is Not Implemented

WhisperPair-PoC intentionally stops short of full exploitation:

Capability Implemented Reason
Vulnerability detection ✅ Yes Core purpose
BR/EDR address extraction ✅ Yes Evidence collection
Bluetooth Classic pairing ❌ No Requires platform-specific code
HFP audio hijacking ❌ No Beyond scope of BLE tool
Persistent implant ❌ No Malicious capability

Remediation Guidance

For Device Manufacturers

  1. Implement the pairing mode check:
if (has_public_key(request) && !is_in_pairing_mode()) {
    return;  // Ignore request per spec
}
  1. Require authentication for Account Key writes:

    • Encrypt new key with existing key
    • Verify before storing
  2. Minimize advertisement exposure:

    • Only advertise Fast Pair UUID when in pairing mode
    • Use rotating addresses when possible
  3. Firmware update mechanism:

    • Push fixes to affected devices in the field

For End Users

  1. Keep firmware updated on Bluetooth accessories
  2. Factory reset if you suspect compromise
  3. Be cautious with devices that auto-pair without pressing a button
  4. Check Find My Device settings periodically

Intended Use

This tool is intended for:


Conclusion

WhisperPair-PoC demonstrates that the "seamless" Fast Pair experience comes with security tradeoffs that many manufacturers fail to address. The pairing mode check is a single conditional that separates a secure device from a vulnerable one, yet it's frequently omitted.

Fast Pair's trust model assumes devices will enforce user intent. When they don't, attackers get silent pairing, tracking capabilities, and denial-of-service vectors against victims' devices.

This tool aims to help the ecosystem identify and remediate these issues, making Bluetooth accessories safer for everyone.


References


WhisperPair-PoC is released for authorized security research only. No responsibility is assumed for misuse of this tool.



Tags: CVE, BLE, PoC, Featured

← Back home