
Reverse-Engineering der oBike-Protokollkommunikation (BLE und HTTP)
Dieses Dokument bietet eine Analyse der oBike-Kommunikationsprotokolle mit Stand Januar 2018.
Die Ergebnisse wurden auf der insomni'hack-Sicherheitskonferenz 2019 präsentiert:
sowie auf der AREA41-Sicherheitskonferenz 2018:
Das oBike-Schloss besteht aus einem TI-CC2541-Mikrocontroller, einem energieoptimierten System-on-a-Chip (SoC), das für Bluetooth-Low-Energy-Anwendungen (BLE) verwendet wird. Das Schloss selbst besitzt keine IP-Konnektivität; es nutzt die 3G/4G-Verbindung des Mobilgeräts, um mit dem oBike-Backend zu kommunizieren. Das Schloss kommuniziert über BLE mit der oBike-App auf dem Mobilgerät. Die Protokollnachrichten werden dann über eine REST-API an das oBike-Backend weitergeleitet.
Das Schloss besitzt kein eigenes GPS-Modul. Daher ist die an das Backend gemeldete Position immer die des Mobilgeräts, nicht die des oBike selbst.``` GPS | +---------+ +---------+ +----------+ | oBike | | Mobile | | oBike | | Lock | +--- BLE ---> | Device | +--- HTTPS ---> | Backend | +---------+ +---------+ +----------+
## Entsperrsequenz```
oBike Lock (BLE) Mobile Device (HTTPS) oBike Backend
------------+------------------------+---------------------------+--------------
| | |
| [1] hello(lat, lng) | |
| <--------------------- | |
Generate | | |
32bit | [2] keySource | |
Challenge | ---------------------> | [3] unlockPass(keySource) |
| | ========================> | Compute
| | | Response
| [5] | [4] encKey, keys |
| sendKeys(encKey, keys) | <======================== |
!Unlock | <--------------------- | |
Bike! | | |
| | |
Generate | | |
Acknowledge | [6] macKey, index | [7] |
Message | ---------------------> | lockMessage(macKey,index) |
| | ========================> | Register
| | | Ride (start
| | | billing)
Schritte:
hello-Nachricht senden und Koordinaten an das Schloss übertragen.keySource empfangen, einen 32-Bit-Wert, der die Anzahl der
Millisekunden seit dem Einschalten des Chips darstellt (Little Endian).keySource über den REST-Aufruf unlockPass an das oBike-Backend senden.encKey (Schlüsselindex) und einen 128-Bit-Schlüsselwert in keys empfangen.encKey (auf 96 Bits gekürzt) und den index senden (entspricht
encKey). Zu diesem Zeitpunkt entsperrt sich das Fahrrad.macKey und index empfangen, eine Bestätigung, dass das Entsperren
erfolgreich war.lockMessage mit den entsprechenden Werten (macKey und
index) senden. Zu diesem Zeitpunkt registriert das oBike-Backend die Fahrt und beginnt
die Abrechnung.Die in den folgenden Abschnitten beschriebenen BLE-Protokollkomponenten sind im
Python-Modul obike.ble_client implementiert. Zusätzlich ist ein Scanner zur Erkennung von oBike-
BLE-Werbeanzeigen in obike.ble_scanner.py implementiert.
6774 0D 86 59AEB6...3931 FD | | | | | | | | | +-- Check byte | | | +----------------- Payload | | +--------------------- Command type | +------------------------- Length of payload in bytes +------------------------------- Command Signature ('gt')
Sowohl eingehende als auch ausgehende Nachrichten beginnen immer mit der Signatur `\x67\x74` (ASCII `gt`).
Die Länge der Nutzlast ist die Anzahl der Bytes ohne Header/Trailer.
Das Protokoll unterstützt verschiedene Nachrichtentypen, die durch ein Byte identifiziert werden. Die beiden höchstwertigen Bits definieren die Richtung der Nachricht:```
0x86 1000 0101 mobile -> obike
0x46 0100 0101 obike -> mobile
Das Prüfbyte wird durch XOR-Verknüpfung des Befehlstyps und der Nutzlastbytes berechnet:``` check_byte = cmdtype ^ b[0] ^ b[1] ^ ... ^ b[N-1]
Die maximale PDU-Größe beträgt 19 Bytes (Header + Payload + Trailer). Nachrichten,
die diese Größe überschreiten, werden fragmentiert.
### BLE getLockRecord/deleteLockRecord
Befehlstyp: `6`
Diese Nachrichten werden verwendet, um den „lock record“ zu verwalten, einen vom
Chip gespeicherten Datensatz, der Informationen von der letzten Fahrt enthält,
wie memberid, Zeitstempel, oBike-Kennung, Koordinaten usw.
Ohne Payload aufgerufen, dient der Befehl zum Abrufen des gespeicherten
Lock-Records:```
00000000 67 74 00 86 86 |gt...|
Wenn kein Sperrdatensatz verfügbar ist, antwortet das Schloss mit einer leeren Nutzlast.``` 00000000 67 74 00 46 46 |gt.FF|
Ansonsten enthält die Antwort des Schlosses mehrere Werte aus der letzten Fahrt, im
folgenden Format:```
00000000 67 74 46 46 00 00 01 23 45 67 59 9d 72 2a 44 31 |gtFF...#d2Y.r*D1|
00000010 39 33 36 42 33 31 37 2a 72 9d 59 00 34 37 2e 33 |936B317*r.Y.47.3|
00000020 37 32 37 36 30 00 00 00 30 38 2e 35 33 30 38 34 |72760...08.53084|
00000030 32 32 00 00 87 76 f3 7a 8c be 90 f8 4b a4 fa 00 |22...v.z....K...|
00000040 2e ae e3 dc 91 00 00 00 a9 01 8b |...........|
0004 000001234567 member-id (explicitly coded in decimal, value is: 1234567)
000a 599d722a UNIX timestamp, 08/23/2017 @ 12:16pm
000e 443139333642333137 obike identifier (D1936B317) this corresponds to the MAC address without the first 3 hex digits, in this case: D4:3D:19:36:B3:17
0017 2a729d59 same UNIX timestamp, little endian
001b 00 transaction type
001c 34372e333732373630000000 latitude (47.372760)
0028 30382e353330383432320000 longitude (08.5308422)
0034 8776f37a8cbe90f84ba4fa002eaee3dc mackey (128 bits)
0044 91 key index