Wireshark & nRF-Sniffer
BLE-Pakete aufzeichnen, filtern und analysieren
Was ist Wireshark?
Wireshark ist ein freier Netzwerkprotokoll-Analyzer, der den gesamten Datenverkehr auf einer Netzwerkschnittstelle mitschneidet und jedes Paket byte-genau aufschlüsselt. Ursprünglich für kabelgebundene Netzwerke entwickelt, versteht Wireshark heute über 3.000 Protokolle — darunter alle BLE-Schichten von der Physical Layer bis zu GATT.
Allein kann Wireshark BLE nicht empfangen: Der eingebaute Bluetooth-Adapter eines Computers gibt keinen Zugriff auf rohe Pakete. Erst die Kombination mit dem nRF52840 USB Dongle und der nRF-Sniffer-Firmware 4.1.1 von Nordic Semiconductor macht Wireshark zu einem vollständigen BLE-Sniffer. Die Firmware kommuniziert über ein sogenanntes Extcap-Plugin mit Wireshark — ein Python-Skript, das als Brücke zwischen der USB-Hardware und dem Analyzer fungiert.
Passiver Sniffer — kein Eingriff in die Kommunikation
Der nRF-Sniffer empfängt BLE-Pakete passiv, ohne selbst zu senden. Die Zielgeräte bemerken die Aufzeichnung nicht. Das unterscheidet ihn von einem aktiven Man-in-the-Middle-Proxy und reduziert das rechtliche Risiko bei autorisierten Analysen.
Installation
Abhängigkeiten
Du benötigst vier Komponenten, die du in dieser Reihenfolge installierst:
# 1. Wireshark 4.6.1 oder neuer
sudo apt install wireshark
# Während der Installation: "Ja" bei der Frage nach nicht-root Captures
# 2. Python-Abhängigkeit für das Extcap-Plugin
pip3 install pyserial
# 3. Serielle Schnittstelle freigeben (ohne sudo)
sudo usermod -a -G dialout $USER
# Abmelden und neu anmelden, damit die Gruppe aktiv wirdLade dann das nRF-Sniffer-Paket von Nordic herunter: nordicsemi.com/Products/Development-tools/nRF-Sniffer-for-Bluetooth-LE
Das ZIP enthält die Firmware-Datei sniffer_pca10059_nrf52840_4.1.1.hex und das Extcap-Plugin.
Firmware flashen
# Dongle in den DFU-Modus versetzen:
# Knopf gedrückt halten -> in USB-Port stecken -> Knopf loslassen
# Rote LED blinkt langsam = DFU-Modus aktiv
# nRF Connect for Desktop installieren (enthält Programmer-App)
# https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-Desktop
# Firmware flashen via Programmer-App:
# Gerät auswählen -> HEX-Datei laden -> Write klicken
# Gelbe LED leuchtet dauerhaft = Sniffer-Firmware aktivExtcap-Plugin einrichten
# Persönliches Extcap-Verzeichnis ermitteln
wireshark -G folders 2>&1 | grep "Personal Extcap"
# Typisch: /home/<user>/.config/wireshark/extcap/
# Plugin-Dateien kopieren
cp nrf_sniffer_for_bluetooth_le/extcap/* ~/.config/wireshark/extcap/
chmod +x ~/.config/wireshark/extcap/nrf_sniffer_for_bluetooth_le.pyNach einem Wireshark-Neustart erscheint der Dongle als Schnittstelle "nRF Sniffer for Bluetooth LE" in der Capture-Liste.
Erste Schritte: LED-Strip-Traffic aufzeichnen
Dieses Beispiel zeigt den kompletten Ablauf einer BLE-Capture-Session anhand eines Smart-LED-Strips.
Wichtigste Regel: Starte die Aufzeichnung immer bevor die App eine Verbindung herstellt. Nur so erfasst du den SMP-Pairing-Handshake — den einmaligen Schlüsselaustausch beim Verbindungsaufbau.
Reihenfolge:
1. Wireshark öffnen
2. "nRF Sniffer for Bluetooth LE" doppelklicken -> Aufzeichnung startet
3. LED-Strip einschalten
4. App auf dem Smartphone öffnen
5. App mit dem Strip verbinden lassen
6. Farbe ändern, Helligkeit anpassen, Effekte aktivieren
7. Verbindung trennen
8. Aufzeichnung stoppen (Strg+E)
9. PCAP speichern: File -> Save As
Sobald die Aufzeichnung läuft, siehst du zunächst Advertising-Pakete des Strips. Nach dem Verbindungsaufbau durch die App erscheinen GATT-Pakete. Jede Farbänderung erzeugt mindestens einen GATT-Write-Request.
Filtere jetzt auf die eigentlichen Steuerbefehle:
btatt.opcode == 0x12
Für einen typischen LED-Strip bleiben nach diesem Filter 60–70 Pakete übrig — alle im Klartext, mit direkt lesbaren RGB-Werten.
Gerät gezielt auswählen
In den Capture-Optionen der nRF-Sniffer-Schnittstelle (Zahnrad-Icon neben dem Interface) kannst du das Zielgerät nach BLE-Adresse oder Gerätename einschränken. Starte erst ohne Filter, um die Advertising-Pakete aller Geräte zu sehen, und notiere die Adresse des Zielgeräts.
Wichtigste Features
Display-Filter vs. Capture-Filter
Wireshark unterscheidet zwei grundlegend verschiedene Filtertypen:
| Typ | Wann aktiv | Syntax | Wirkung |
|---|---|---|---|
| Capture-Filter | Während der Aufzeichnung | BPF-Syntax | Nicht-matching Pakete werden verworfen — unwiderruflich |
| Display-Filter | Nach der Aufzeichnung | Wireshark-Syntax | Nicht-matching Pakete werden nur ausgeblendet, bleiben in der PCAP |
Für BLE-Analysen gilt: Immer Display-Filter verwenden. Ein Capture-Filter könnte wichtige Pakete (z. B. SMP-Handshake) dauerhaft löschen, bevor du ihren Wert erkennst.
BLE-spezifische Display-Filter
Die wichtigsten Filter für BLE-Sicherheitsanalysen:
| Filter | Beschreibung |
|---|---|
btatt.opcode == 0x12 | GATT Write Requests (Steuerbefehle) |
btatt.opcode == 0x52 | GATT Write Commands (ohne Bestätigung) |
| `btatt.opcode == 0x12 | |
btatt.opcode == 0x1b | Handle Value Notifications (Gerät -> App) |
btatt.opcode == 0x0a | Read Requests |
btatt.opcode == 0x0b | Read Responses |
btatt.handle == 0x000e | Spezifischen GATT-Handle filtern |
btatt.value.length == 16 | 16-Byte-Payloads (AES-Blockgröße) |
btsmp | SMP-Pairing-Protokoll |
btle.advertising_header | Nur Advertising-Pakete |
btle && btatt | Gesamte ATT-Schicht |
Filter lassen sich mit && (AND), || (OR) und ! (NOT) kombinieren:
# Alle Schreiboperationen ohne Bestätigung, 16 Bytes lang
btatt.opcode == 0x52 && btatt.value.length == 16
# Alles außer Advertising-Paketen
!btle.advertising_header.pdu_type == 0x00
BLE-Verbindungsstream verfolgen
Wireshark kann alle Pakete einer einzelnen BLE-Verbindung zusammenfassen. Klicke mit der rechten Maustaste auf ein GATT-Paket und wähle Follow → Bluetooth ATT Stream. Das öffnet ein Fenster, das ausschließlich die ATT-Kommunikation dieser Verbindung zeigt — ohne Advertising-Pakete anderer Geräte.
Einfärbungsregeln für BLE-Pakete
Farbmarkierungen erleichtern die visuelle Mustererkennung erheblich. Gehe zu View → Coloring Rules → + und lege folgende Regeln an:
| Farbe | Filter | Bedeutung |
|---|---|---|
| Hellblau | btatt.opcode == 0x12 | Write Requests (App -> Gerät) |
| Hellgrün | btatt.opcode == 0x1b | Notifications (Gerät -> App) |
| Gelb | btsmp | Pairing-Handshake |
| Orange | btatt.opcode == 0x52 | Write Commands |
Gefilterte Pakete als neue PCAP exportieren
Wenn du mit einem Display-Filter nur die relevanten Pakete siehst, exportiere diese als eigene Datei:
File -> Export Specified Packets
-> "Displayed" auswählen
-> Als neue PCAP-Datei speichern
Die exportierte Datei enthält nur die sichtbaren Pakete — ideal für Berichte oder die Weitergabe an Kollegen ohne irrelevante Hintergrunddaten.
Fortgeschritten
Lua-Dissektoren für proprietäre Protokolle
Wenn ein Gerät ein eigenes binäres Protokoll in den GATT-Payloads verwendet (wie der LED-Strip mit seinem 7e...ef-Rahmen), kann Wireshark dieses Protokoll nicht automatisch dekodieren. Du kannst jedoch einen eigenen Lua-Dissektor schreiben:
-- led_strip_dissector.lua
-- Speichern unter: ~/.config/wireshark/plugins/led_strip.lua
local led_proto = Proto("ledstrip", "LED Strip Protocol")
local f_start = ProtoField.uint8("ledstrip.start", "Start Byte", base.HEX)
local f_len = ProtoField.uint8("ledstrip.length", "Length", base.DEC)
local f_opcode = ProtoField.uint8("ledstrip.opcode", "Opcode", base.HEX)
local f_red = ProtoField.uint8("ledstrip.red", "Red", base.DEC)
local f_green = ProtoField.uint8("ledstrip.green", "Green", base.DEC)
local f_blue = ProtoField.uint8("ledstrip.blue", "Blue", base.DEC)
led_proto.fields = { f_start, f_len, f_opcode, f_red, f_green, f_blue }
function led_proto.dissector(buffer, pinfo, tree)
if buffer:len() < 4 then return end
pinfo.cols.protocol = "LED-Strip"
local subtree = tree:add(led_proto, buffer(), "LED Strip Command")
subtree:add(f_start, buffer(0, 1))
subtree:add(f_len, buffer(1, 1))
subtree:add(f_opcode, buffer(2, 1))
if buffer:len() >= 8 and buffer(2,1):uint() == 0x05 then
subtree:add(f_red, buffer(4, 1))
subtree:add(f_green, buffer(5, 1))
subtree:add(f_blue, buffer(6, 1))
end
end
-- Dissektor für GATT-Handle 0x000e registrieren
local btatt_table = DissectorTable.get("btatt.handle")
btatt_table:add(0x000e, led_proto)Nach dem Neustart von Wireshark dekodiert der Dissektor alle Pakete auf Handle 0x000e automatisch.
tshark für Kommandozeilen-Analyse
tshark ist die Kommandozeilen-Version von Wireshark. Es ermöglicht automatisierte Analysen und ist ideal für Scripting:
# Alle Write-Requests aus einer PCAP extrahieren
tshark -r capture.pcap \
-Y "btatt.opcode == 0x12" \
-T fields \
-e frame.number \
-e btatt.handle \
-e btatt.value \
> write_requests.txt
# Einzigartige GATT-Handles auflisten
tshark -r capture.pcap \
-Y "btatt" \
-T fields \
-e btatt.handle | sort -u
# Paketlängenverteilung der Write-Requests
tshark -r capture.pcap \
-Y "btatt.opcode == 0x12" \
-T fields -e frame.len | sort | uniq -c | sort -rnBLE-Verkehr entschlüsseln
Wenn der SMP-Pairing-Handshake mit Legacy Pairing (nicht LE Secure Connections) aufgezeichnet wurde und der Temporary Key bekannt ist (bei "Just Works": TK = 0), kann Wireshark den Long-Term-Key ableiten und den gesamten verschlüsselten Verkehr entschlüsseln.
Bei "Just Works"-Pairing ist TK = 0 (128 Bit Nullen). Wireshark kann den Short-Term Key (STK) und daraus den Long-Term Key (LTK) direkt aus den SMP-Paketen im Capture ableiten. Das Tool crackle automatisiert diesen Vorgang: crackle -i capture.pcap -o decrypted.pcap extrahiert den LTK und erzeugt einen entschlüsselten Mitschnitt. Falls du den LTK manuell eintragen willst:
Edit -> Preferences -> Protocols -> BTLE
-> "Long Term Key" eintragen (als Hex, ohne Leerzeichen)
Bei LE Secure Connections (ECDH-basiert) ist passives Entschlüsseln ohne den privaten Schlüssel mathematisch nicht möglich — der Diffie-Hellman-Schlüsselaustausch verhindert das konstruktionsbedingt.
Payload-Muster mit tshark erkennen
Ein einzelner tshark-Befehl reicht, um zu prüfen, ob Payloads verschlüsselt oder Klartext sind. Verschlüsselte Daten haben hohe Entropie und keine erkennbaren Bytemuster. Klartext-Steuerbefehle zeigen dagegen Wiederholungen:
# Alle Payload-Bytes eines Handles extrahieren und Bytefrequenz zählen
tshark -r capture.pcap \
-Y "btatt.opcode == 0x52 && btatt.handle == 0x000e" \
-T fields -e btatt.value | tr ':' '\n' | sort | uniq -c | sort -rn | head -20
# Startbyte-Verteilung prüfen (einheitliches Startbyte = Klartext-Protokoll)
tshark -r capture.pcap \
-Y "btatt.opcode == 0x52" \
-T fields -e btatt.value | cut -c1-2 | sort | uniq -c | sort -rnWenn das häufigste Byte 7e ist und immer am Anfang steht, hast du ein Klartext-Protokoll mit festem Framing — wie beim LED-Strip.
Statistische Analyse
Wireshark enthält eingebaute Statistik-Werkzeuge:
- Statistics → IO Graph: Paketrate über Zeit — Spitzen zeigen intensive Aktivität (z. B. Firmware-Update)
- Statistics → Packet Lengths: Verteilung der Paketlängen — eine Häufung bei 16 oder 32 Bytes deutet auf AES-Blockgrößen hin
- Statistics → Protocol Hierarchy: Zeigt alle vorhandenen Protokollschichten und ihren Anteil
Mehrere PCAP-Dateien zusammenführen
Wenn du mehrere Sessions aufgezeichnet hast, führe sie vor der Analyse zusammen:
# mergecap ist Teil von Wireshark
mergecap -w combined.pcap session1.pcap session2.pcap session3.pcap
# Mit tshark überprüfen
tshark -r combined.pcap -q -z io,stat,0Häufige Probleme
nRF Sniffer erscheint nicht als Schnittstelle
Prüfe, ob das Python-Skript ausführbar ist: ls -la ~/.config/wireshark/extcap/ sollte nrf_sniffer_for_bluetooth_le.py mit x-Bit zeigen. Falls nicht: chmod +x ~/.config/wireshark/extcap/nrf_sniffer_for_bluetooth_le.py. Starte Wireshark danach neu.
Keine Pakete sichtbar, obwohl Gerät aktiv ist
Stelle sicher, dass der Dongle die Sniffer-Firmware trägt (gelbe LED dauerhaft). Prüfe mit ls /dev/ttyACM*, ob der Dongle als serielles Gerät erkannt wird. Falls /dev/ttyACM0 existiert, aber Wireshark keinen Zugriff hat: Gruppen-Mitgliedschaft prüfen mit groups $USER — dialout muss enthalten sein.
Nur verschlüsselte Pakete, kein Klartext
Die BLE-Verbindung nutzt Link-Layer-Verschlüsselung. Bei Legacy Pairing und aufgezeichnetem Handshake: Long-Term-Key unter Edit → Preferences → Protocols → BTLE eintragen. Bei LE Secure Connections hilft nur die App-Layer-Analyse via JADX.
Berechtigungsfehler beim Start
sudo wireshark ist keine Dauerlösung. Die korrekte Lösung ist sudo usermod -a -G wireshark $USER, gefolgt von einer Neuanmeldung. Wireshark installiert beim Setup normalerweise die Gruppe wireshark mit setuid-Rechten auf dumpcap.
Capture als Root vermeiden
Das Starten von Wireshark als Root öffnet eine Angriffsfläche, weil Wireshark Hunderte von Netzwerkdissektoren mit Fremddaten ausführt. Nutze stattdessen die wireshark-Gruppe oder dialout-Gruppe für den Zugriff auf /dev/ttyACM0.