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 wird

Lade 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 aktiv

Extcap-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.py

Nach 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:

TypWann aktivSyntaxWirkung
Capture-FilterWährend der AufzeichnungBPF-SyntaxNicht-matching Pakete werden verworfen — unwiderruflich
Display-FilterNach der AufzeichnungWireshark-SyntaxNicht-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:

FilterBeschreibung
btatt.opcode == 0x12GATT Write Requests (Steuerbefehle)
btatt.opcode == 0x52GATT Write Commands (ohne Bestätigung)
`btatt.opcode == 0x12
btatt.opcode == 0x1bHandle Value Notifications (Gerät -> App)
btatt.opcode == 0x0aRead Requests
btatt.opcode == 0x0bRead Responses
btatt.handle == 0x000eSpezifischen GATT-Handle filtern
btatt.value.length == 1616-Byte-Payloads (AES-Blockgröße)
btsmpSMP-Pairing-Protokoll
btle.advertising_headerNur Advertising-Pakete
btle && btattGesamte 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:

FarbeFilterBedeutung
Hellblaubtatt.opcode == 0x12Write Requests (App -> Gerät)
Hellgrünbtatt.opcode == 0x1bNotifications (Gerät -> App)
GelbbtsmpPairing-Handshake
Orangebtatt.opcode == 0x52Write 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 -rn

BLE-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 -rn

Wenn 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,0

Hä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 $USERdialout 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.

Wissens-Check