Security Research

Fleet Discovery Report

Bird/Spin Micromobility API Vulnerability Assessment — UC Davis Campus

Date: April 8–9, 2026 Platform: TowerWatch Classification: Public

Executive Summary

A vulnerability in Bird/Spin's /scan API endpoint allows systematic enumeration of micromobility vehicle fleets worldwide. By exploiting sequential hardware serial numbers, we identified 78.4% of the UC Davis campus fleet in under six hours using seven machines in parallel — bypassing all privacy protections designed into the GBFS standard.

538
Vehicles Identified
78.4%
Fleet Coverage
6 hrs
Discovery Window
7
Machines Parallel
4
ID Types Accepted
0
Rate Limits (Cellular)
Key finding: The /scan endpoint — designed for rider QR code scanning — accepts raw serial numbers and returns persistent vehicle identities, GPS coordinates, battery levels, and IMEI numbers. This single endpoint bypasses every privacy mechanism in the GBFS specification.

The Challenge — GBFS Privacy by Design

The General Bikeshare Feed Specification (GBFS) v2.3 was intentionally designed to prevent vehicle tracking. Every time a client polls the free_bike_status.json endpoint, all vehicle IDs are rotated. The specification explicitly states that persistent identity must not be derivable from public feeds.

What GBFS provides per poll:

  • Temporary bike_id — rotated every poll cycle
  • GPS coordinates (real, but not linkable across polls)
  • Battery level (real, but not linkable)
  • Vehicle type (generic, e.g., "scooter")

On the UC Davis campus, GBFS shows 673 vehicles on any given poll — but zero are trackable across consecutive polls. Every vehicle gets a new random ID. This is the privacy guarantee that riders and regulators expect.

The question was: can persistent identity be recovered despite these protections?

Discovery Timeline

The path from zero persistent identities to 538 identified vehicles unfolded over several phases, each building on the previous.

Phase Method Vehicles Found Cumulative Time
1 GBFS observation 0 (rotating IDs) 0 Ongoing
2 Physical QR scan 19 19 3 hours
3 Barcode brute force (9M range) 7 26 4 hours
4 IMEI scanning (TAC ranges) 3 29 2 hours
5 Serial number V1 +271 300 82 min
6 Serial V2–V4 (expanded ranges) +140 440 48 min
7 Multi-machine fast+sweep +98 538 45 min

The Vulnerability — /scan Endpoint

Bird/Spin's rider app uses a /scan endpoint to resolve physical QR codes to vehicle records. This endpoint was designed for proximity-based identification but accepts raw identifiers over the network with no proximity verification.

What makes /scan dangerous:

  • Accepts 4 identifier types: barcode, IMEI, serial_number, sticker_id
  • Serial numbers are hardware-stamped and sequential within manufacturing batches
  • Returns the persistent bird_id UUID — the master identity key
  • Returns real-time GPS, battery, model, IMEI, fleet assignment
  • No proximity check, no CAPTCHA, no anomaly detection on sequential scans

The serial number format follows a predictable hardware pattern:

// Serial number format "A1S0C2117C0378" │ │ │ │ │ │ │ │ │ └── unit (0378) — sequential within batch │ │ │ └───── separator (C) │ │ └────────── batch (2117) — manufacturing batch │ └───────────── variant (0C) — hardware variant └──────────────── maker (A1S) — manufacturer code

Request / Response:

POST /scan Authorization: Bearer [rider_jwt_token] Content-Type: application/json { "serial_number": "A1S0C2117C0378", "mode": "rider" }
// Response (sanitized) { "bird_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "code": "8KL9M2Z", "serial_number": "A1S0C2117C0378", "imei": "861234567890123", "model": "nbmax23", "vehicle_class": "scooter", "fleet_id": "davis-ca", "location": { "latitude": 38.5382, "longitude": -121.7617 }, "battery_level": 72, "locked": false, "born_at": "2024-03-15T08:22:00Z" }
Critical: A single API call returns enough data to persistently track any vehicle — including its real-time GPS position, hardware IMEI, and manufacturing timestamp. This endpoint was never meant for bulk enumeration, but nothing prevents it.

ID Virtualization — The Anti-Scraping Layer

Bird has implemented meaningful anti-scraping protections on most endpoints. The /bird/nearby endpoint generates entirely fabricated vehicle identities per session — different IDs, codes, models, and battery ranges for every JWT token. Only GPS coordinates and approximate battery levels reflect reality.

GBFS feeds rotate all identifiers per poll. These are genuine privacy protections that work as designed.

But /scan bypasses all of it. It returns the real persistent identity — the one identifier that ties everything together.

Endpoint Vehicle IDs GPS Battery Model IMEI Rate Limited
/bird/nearby Fabricated Real Approx Fabricated N/A Per-token
GBFS feeds Rotated Real Real Generic N/A Public
/scan Real + Persistent Real Real Real Real Subnet-only

Serial Number Analysis

Hardware serial numbers follow a structured format that reveals manufacturing batch, hardware variant, and sequential unit number. This structure is what makes enumeration possible.

A1SMaker
0CVariant
2117Batch
CSep
0378Unit
Segment Values Found Meaning
Maker A1S, A1B, N4Z, A0S A1S = scooters, A1B = e-bikes, N4Z = alt scooters, A0S = nbs90l model
Variant 0A0F, 4A4F, 5B, WE, 01 Hardware revision / radio configuration
Batch 20402150+ Manufacturing batch (Davis fleet: 2047–2135)
Separator C Fixed delimiter
Unit 00010600 Sequential unit within batch (variable max)

Distribution of identified vehicles across manufacturing batches:

The Efficiency Breakthrough

The serial number approach is 1,667 times more efficient than barcode brute force, turning a 10-day operation into an 82-minute scan.

~10 days
Barcode Brute Force
9,000,000 candidates · 0.004% hit rate
~3 days
IMEI Scanning
1,400,000 candidates · 1.9% hit rate
82 min
Serial Scanning
16,200 candidates · 10% hit rate
Why it works: Barcodes are random 7-digit codes (10M possible). IMEIs have a known TAC prefix but 7 random digits (10M per TAC). Serial numbers are structured: ~15 maker+variant combos × ~100 batches × ~600 units = ~900,000 total, and real fleets cluster in a narrow batch range, reducing the effective search space to ~16,200.

Rate Limiting Discovery

Rate limiting on the /scan endpoint is subnet-dependent, not account-dependent. Requests from mobile carrier networks — the same networks that scooters themselves use — experience zero throttling.

Network Type Throughput Rate Limiting Notes
T-Mobile Cellular ~50 req/s None detected Same CGNAT as scooter fleet
University Ethernet ~10 req/s Moderate Sustained without 403s
Residential ISP ~8 req/s Moderate Occasional slowdowns
Azure Datacenter ~3 req/s Aggressive Frequent 403 responses
Implication: Bird's rate limiting is designed to block datacenter scraping but trusts mobile carrier IPs — the same IPs used by millions of legitimate riders. Any attacker on cellular has effectively unlimited access.

Multi-Machine Architecture

Seven machines across five different network types were deployed in parallel, with results merged via shared storage and HTTP file servers.

Local Mac
UC Davis Ethernet
Primary coordinator
~10 req/s sustained
Old Mac
T-Mobile Cellular
Star performer
~50 req/s, zero throttling
Windows VM
Residential Broadband
Steady contributor
~8 req/s
Codespace #1
Azure (West US 2)
Throttled but functional
~3 req/s
Codespace #2
Azure (West US 2)
Throttled but functional
~3 req/s
Deepnote
Cloud (GCP)
Supplementary
~5 req/s
Merge Controller
Local Mac
Dedup + reconcile
OneDrive / iCloud / HTTP sync
Sync pattern: Each machine writes discovered vehicles to a local JSON file. Files sync via OneDrive, iCloud, and HTTP file servers (python3 -m http.server). The merge controller on the local Mac deduplicates by bird_id UUID and produces the canonical fleet registry.

Fleet Composition

The UC Davis campus fleet consists primarily of scooters (nbmax23 model) with a smaller contingent of e-bikes.

Scooters (nbmax23) 474
E-bikes (nba80) 64
Unidentified 148

Total GBFS-visible fleet: 686 vehicles

Data Exposed Per Vehicle

Each successful /scan response returns the following fields for a single vehicle. Combined with polling, this enables continuous real-time tracking.

Bird ID (UUID)
f47ac10b-58cc-4372-a567-0e02b2c3d479
Barcode
8KL9M2Z
Serial Number
A1S0C2117C0378
IMEI
861234567890123
Model
nbmax23
Fleet ID
davis-ca
GPS Position
38.5382, -121.7617
Battery Level
72%
Locked Status
false (available)
Vehicle Class
scooter
Born At
2024-03-15T08:22:00Z
Persistent Tracking
bird_id never rotates
Privacy implication: With a persistent bird_id, an attacker can poll GBFS feeds and correlate GPS positions over time — effectively tracking individual vehicles (and by proxy, their riders) across the city. The GBFS ID rotation is meaningless once persistent identity is known.

Global Implications

This technique is not specific to UC Davis. It exploits hardware-level serial number patterns that are consistent across Bird/Spin's entire manufacturing pipeline.

  • Serial numbers are sequential across manufacturing batches — not randomized per deployment
  • The /scan endpoint is globally available and core to the rider app flow
  • Any city's fleet can be enumerated by scanning the relevant batch ranges
  • Batch numbers correlate with deployment dates — newer cities use higher batch numbers
  • No authentication upgrade stops this: /scan must remain accessible to every rider
  • Estimated global fleet exposure: millions of vehicles across hundreds of cities
200+
Cities with Bird/Spin
Hours
Per-City Enumeration Time
Millions
Vehicles Globally Exposed

Identified vs. Unknown Vehicles

Gold markers represent vehicles with confirmed persistent identity. Gray markers represent vehicles visible in GBFS but not yet linked to a hardware serial number.

-- Identified -- Unidentified

Recommendations for Bird/Spin

  1. Rate limit /scan by account, not by IP.
    A single rider account has no legitimate reason to scan more than a few vehicles per hour. Account-level rate limiting at 20 scans/hour would stop enumeration without affecting normal riders.
  2. Remove serial_number as an accepted scan input.
    The rider app uses QR barcodes, not serial numbers. There is no user-facing reason for the API to accept hardware serials. Remove this input type entirely.
  3. Add anomaly detection for sequential scanning patterns.
    Legitimate riders scan one vehicle at a time. Any account scanning serial numbers in sequence (incrementing unit numbers) is clearly automated. Flag and block these patterns in real time.
  4. Implement proof-of-proximity for /scan.
    Require the scanning device to be within Bluetooth or GPS range of the target vehicle. The scooters already have BLE radios — use them for proximity verification before returning identity data.
  5. Rotate the serial-to-barcode mapping server-side.
    Even if serial numbers must be accepted for warehouse operations, the mapping from serial to bird_id should rotate periodically, breaking persistent tracking from stale scans.