
Dieses Projekt soll eine Reihe von Werkzeugen bereitstellen, um LoRaWAN-Pakete zu erstellen, zu parsen, zu senden, zu analysieren und zu knacken, um die Sicherheit einer LoRaWAN-Infrastruktur zu auditieren oder einem Pentest zu unterziehen.
IoT-Bereitstellungen wachsen stetig und ein Teil dieses signifikanten Wachstums besteht aus Millionen von LPWAN-Sensoren (Low-Power Wide-Area Network), die in Hunderten von Städten (Smart Cities) auf der ganzen Welt sowie in der Industrie und in Privathaushalten eingesetzt werden. Eine der am häufigsten verwendeten LPWAN-Technologien ist LoRa, für die LoRaWAN der Netzwerkstandard (MAC-Schicht) ist. LoRaWAN ist ein sicheres Protokoll mit integrierter Verschlüsselung, aber Implementierungsprobleme und Schwachstellen beeinträchtigen die Sicherheit der meisten aktuellen Bereitstellungen.
Dieses Projekt soll eine Reihe von Werkzeugen bereitstellen, um einen Satz von LoRaWAN-Paketen zu erstellen, zu parsen, zu senden, zu analysieren und zu knacken, um die Sicherheit einer LoRaWAN-Infrastruktur zu auditieren oder zu pentesten.
Im Folgenden die Struktur dieses Repositorys:
|-- tools
|-- UdpSender.py
|-- UdpProxy.py
|-- TcpProxy.py
|-- lorawan
|-- BruteForcer.py
|-- MicGenerator.py
|-- PacketCrafter.py
|-- PacketParser.py
|-- SessionKeysGenerator.py
|-- Loracrack (https://github.com/matiassequeira/Loracrack/tree/master)
|-- utils
|-- DevAddrChanger.py
|-- Fuzzer.py
|-- FileLogger.py
|-- auditing
|-- datacollectors
|-- MqttCollector.py
|-- UdpForwarderProxy.py
|-- analyzers
|-- LafProcessData.py
|-- bruteForcer
|-- LafBruteforcer.py
|-- keys
|-- dataanalysis
|-- LafPacketAnalysis.py
|-- printer
|-- LafPrinter.py
|-- db
|-- __init__.py
|-- Models.py
|-- Service.py
|-- lorawanwrapper
|-- LorawanWrapper.py
|-- utils
|-- jsonUnmarshaler.go
|-- lorawanWrapper.go
|-- micGenerator.go
|-- sessionKeysGenerator.go
|-- scripts
|-- gateway_channel_changer
|-- LoRa-GW-Installer.sh
|-- Continuous-Channel-Switch.sh
|-- LoRa-GW-Channel-Setup.sh
Wir bieten verschiedene Optionen, um Ihr LoRaWAN Auditing Framework zum Laufen zu bringen:
tools/ zu verwenden, um Probleme mit dem Docker-Port-Mapping zu vermeiden.localhost eine Verbindung zu Postgres herzustellen. Siehe Anweisungen unten zur Einrichtung von Docker.Diese Anweisungen verschaffen Ihnen eine Kopie des Projekts und seiner Abhängigkeiten auf Ihrem lokalen Rechner. Die folgenden Befehle gelten für eine Debian-basierte Umgebung:
Klonen Sie dieses Repository: git clone --recurse-submodules https://github.com/IOActive/laf.git
Installieren Sie python3:
sudo apt-get updatesudo apt-get install python3.6Laden Sie die Python-Abhängigkeiten herunter und installieren Sie sie:
sudo pip3 install paho-mqtt && sudo pip3 install sqlalchemy && sudo pip3 install psycopg2-binary &&sudo pip3 install python-dateutilSetzen Sie PYTHONPATH und ENVIRONMENT
cd laf && export PYTHONPATH=$(pwd) && export ENVIRONMENT='DEV'Installieren und richten Sie Golang ein:
cd ~/Downloadssudo tar -C /usr/local -xvzf YOUR_GOLANG_FILEexport PATH=$PATH:/usr/local/go/binUnd das war's!
Dieser Ansatz vermeidet die manuelle Installation von Abhängigkeiten und startet eine PostgreSQL-Datenbank, in der die Werkzeuge Pakete und Daten speichern. Container:
Schritte:
git clone https://github.com/IOActive/laf.gitcd laf/docker-compose up --builddocker exec -ti laf_tools_1 /bin/bashSie können Daten in der Datenbank über pgAdmin überprüfen:
Zuerst auf pgAdmin zugreifen:
Dann müssen Sie den Server hinzufügen:
Hier finden Sie eine Beschreibung der Verzeichnisse und der darin enthaltenen Werkzeuge / Funktionen.
Der Hauptzweck der in diesem Verzeichnis bereitgestellten Werkzeuge besteht darin, die Durchführung eines Penetrationstests einer LoRaWAN-Infrastruktur zu erleichtern.
Dieses Werkzeug ist dazu gedacht, Uplink-Pakete (an den Netzwerkserver oder Gateway-Bridge, je nach Infrastruktur) oder Downlink-Pakete (an den Packet-Forwarder) zu senden. Optional können Pakete gefuzzt und eine gültige MIC berechnet werden.
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
--lcl-port LCL_PORT Quellport, z.B. --lcl-port=623.
--timeout TIMEOUT Zeit in Sekunden zwischen jedem gesendeten Paket.
Standard ist 1s. In dieser Zeit wartet der Sender auf
Antworten.
--repeat Senden von Nachrichten mehrfach wiederholen.
--fuzz-out FUZZ_OUT [FUZZ_OUT ...]
Fuzz-Daten, die an den Zielport gesendet werden
(siehe Fuzzing-Modi in utils/fuzzer.py), z.B.
--fuzz-out 1 2.
--key KEY Geben Sie den Schlüssel (im Hex-Format, insgesamt 32
Zeichen / 16 Byte) ein, um Pakete zu signieren (neue
MIC berechnen und hinzufügen). Beachten Sie, dass für
JoinRequests der AppKey verwendet werden muss und für
Datenpakete der NwkSKey. Dies kann von diesem Programm
nicht vorab überprüft werden. z.B.
00112233445566778899AABBCCDDEEFF
-a DEVADDR, --devaddr DEVADDR
Geräteadresse, die imitiert werden soll, im Hex-Format
(insgesamt 8 Zeichen), z.B. AABB0011.
--fcnt FCNT Der Frame Counter, der im angegebenen Datenpaket
gesetzt werden soll. Dies funktioniert nicht bei einem
JoinRequest/JoinAccept, da diese Pakete keinen fCnt
haben.
Erforderliche Argumente:
--dst-ip DST_IP Ziel-IP, z.B. --dst-ip 192.168.3.101.
--dst-port DST_PORT Zielport, z.B. --dst-port 623.
--data DATA UDP-Paket. Es können auch weitere Pakete im "data"
Array am Ende dieses Skripts hinzugefügt werden. Das
Paket muss ein Byte-String sein (Sie müssen doppelte
Anführungszeichen escapen). ***BEISPIEL*** mit dem
packet_forwarder-Format: --data "b'\x02\xe67\x00\xb8\'\xeb\xff\xfez\x80
\xdb{\"rxpk\":[{\"tmst\":2749728315,\"chan\":0,\"rfch\
":0,\"freq\":902.300000,\"stat\":1,\"modu\":\"LORA\",\
"datr\":\"SF7BW125\",\"codr\":\"4/5\",\"lsnr\":9.5,\"r
ssi\":-76,\"size\":23,\"data\":\"AMQAAAAAhQAAAgAAAAAAA
ACH9PRMJi4=\"}]}'" ***BEISPIEL*** mit dem Gatevice
[GV]-Format, das im Sofortmodus, in BW125 und bei
Freq 902.3 sendet: "b'{\"tx_mode\": 0, \"freq\": 902.3,
\"rfch\": 0, \"modu\": 16, \"datarate\": 16,
\"bandwidth\":3, \"codr\": 1, \"ipol\":false,
\"size\": 24, \"data\":
\"QOOL8AGA6AMCnudJqz3syCkeooCvqbSn\", \"class\": 2}'"
Beispiel:
Um ein einzelnes Paket alle 2 Sekunden an (localhost, 10001) von Port 10000 zu senden, mit zufälligem Fuzzing von MIC und Fcounter:
python3 UdpSender.py --lcl-port 10000 --dst-ip 127.0.0.1 --dst-port 10001 --timeout 2 --fuzz-out 4 5 --data "b'\x02\xe67\x00\xb8\'\xeb\xff\xfez\x80\xdb{\"rxpk\":[{\"tmst\":2749728315,\"chan\":0,\"rfch\":0,\"freq\":902.300000,\"stat\":1\"modu\":\"LORA\",\"datr\":\"SF7BW125\",\"codr\":\"4/5\",\"lsnr\":9.5,\"rssi\":-76,\"size\":23,\"data\":\"AMQAAAAAhQAAAgAAAAAAAACH9PRMJi4=\"}]}'"
Dieser UDP-Proxy ist hauptsächlich dazu gedacht, zwischen einer Reihe von Gateways (Packet-Forwardern) und einem Netzwerkserver oder Gateway-Bridge platziert zu werden, je nach der zu bewertenden Infrastruktur. Er bietet auch die Möglichkeit, Daten in der gewünschten Richtung (Uplink oder Downlink) zu fuzzen.
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
--collector-port COLLECTOR_PORT
Port des Packet-Forwarder-Datensammlers, z.B.
--collector-port 1701. Siehe
auditing/datacollectors/PacketForwarderCollector.py
--collector-ip COLLECTOR_IP
IP des Packet-Forwarder-Datensammlers. Standard ist
localhost. z.B. --collector-ip 192.168.1.1. Siehe
auditing/datacollectors/PacketForwarderCollector.py
--fuzz-in FUZZ_IN [FUZZ_IN ...]
Fuzz-Daten, die an den Zielport gesendet werden, in
den angegebenen Modi (siehe Fuzzing-Modi in
utils/fuzzer.py), z.B. --fuzz-in 1 2 ...
--fuzz-out FUZZ_OUT [FUZZ_OUT ...]
Fuzz-Daten, die an den (Quell-)Port gesendet werden,
in den angegebenen Modi (siehe Fuzzing-Modi in
utils/fuzzer.py), z.B. --fuzz-out 1 2 ...
-k KEY, --key KEY Geben Sie einen Geräte-AppSKey (im Hex-Format,
insgesamt 32 Zeichen / 16 Byte) ein, um dessen
FRMPayload zu entschlüsseln und als Klartext
auszugeben. Sie können auch den AppKey eingeben, wenn
Sie einen bestimmten Join Accept entschlüsseln
möchten. z.B. 00112233445566778899AABBCCDDEEFF
-p PATH, --path PATH Dateipfad, in dem die Daten gespeichert werden sollen.
Wenn nicht angegeben, werden die Daten nicht
gespeichert.
--no-log UDP-Pakete nicht in der Konsole ausgeben.
--no-parse PHYPayload nicht parsen. Wenn diese Option ausgewählt
ist, werden die Golang-Bibliotheken aus
/lorawanwrapper/ nicht importiert (Kompilieren der
Golang-Bibliotheken ist nicht erforderlich).
Erforderliche Argumente:
--port PORT Der lokale Port zum Lauschen, z.B. --port 623.
--dst-ip DST_IP Ziel-Host-IP, z.B. --dst-ip 192.168.3.101.
--dst-port DST_PORT Ziel-Host-Port, z.B. --dst-port 623.
Beispiel:
Um Pakete, die auf Port 1234 empfangen werden, an (localhost, 1235) zu senden und umgekehrt. Auf dem Port empfangene Pakete werden gefuzzt (devNonce wird zufällig geändert) und an (localhost, 1235) weitergeleitet.
python3 UdpProxy.py --port 1234 --dst-ip 127.0.0.1 --dst-port 1235 --fuzz-in 9
Dieser TCP-Proxy ist hauptsächlich dazu gedacht, zwischen dem Netzwerkserver und einem MQTT-Broker platziert zu werden. Er bietet auch die Möglichkeit, Daten zu fuzzen.
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
--fuzz-in FUZZ_IN [FUZZ_IN ...]
Fuzz-Daten, die an den Zielport gesendet werden, in den
angegebenen Modi (siehe Fuzzing-Modi in utils/fuzzer.py)
Erforderliche Argumente:
--lcl-port LCL_PORT Der lokale Port zum Lauschen, z.B. --lcl-port=623.
--dst-ip DST_IP Ziel-Host-IP, z.B. --dst-ip=192.168.3.101.
--dst-port DST_PORT Ziel-Host-Port, z.B. --dst-port=623.
Beispiel:
Daten von (localhost, 1884) und (localhost, 1883) senden und empfangen.
python3 TcpProxy.py --lcl-port 1884 --dst-ip 127.0.0.1 --dst-port 1883
Dieses Verzeichnis enthält eine Reihe von Skripten zum Parsen, Erstellen, Brute-Forcen usw. von LoRaWAN-Paketen.
Dieses Skript empfängt einen JoinAccept oder JoinRequest im Base64-Format und versucht, dessen AppKey mit einer Reihe möglicher Schlüssel zu entschlüsseln, die entweder in einer Datei bereitgestellt oder dynamisch generiert werden können.
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
-k KEYS, --keys KEYS Datei, die eine Liste von Schlüsseln enthält, getrennt
durch \n. Standardmäßig wird
/auditing/analyzers/bruteForcer/keys.txt verwendet.
--dont-generate Wählen Sie diese Option, wenn Sie keine Schlüssel
dynamisch mit den folgenden Kombinationen generieren
möchten: 1- Kombiniere das erste Byte mit den letzten
fünfzehn Bytes. z.B. AABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
2- Kombiniere gerade und ungerade Bytepositionen
gleichmäßig. z.B. AABBAABBAABBAABBAABBAABBAABBAABB
3- Die ersten 14 Bytes auf 00 setzen und die letzten 2
kombinieren. z.B. 0000000000000000000000000000BA01
Erforderliche Argumente:
-a ACCEPT, --accept ACCEPT
Join Accept im Base64-Format zum Brute-Forcen. z.B. -a
IHvAP4MXo5Qo6tdV+Yfk08o=
-r REQUEST, --request REQUEST
Join Request im Base64-Format zum Brute-Forcen. z.B.
-r AMQAAAAAhQAAAgAAAAAAAADcYldcgbc=
Beispiel:
Einen JoinRequest mit einem Schlüsselsatz aus my-keys.txt knacken und zusätzlich ca. 200.000 weitere Schlüssel dynamisch generieren.
python3 BruteForcer.py -a IHvAP4MXo5Qo6tdV+Yfk08o= -r AMQAAAAAhQAAAgAAAAAAAADcYldcgbc= -k ./my-keys.txt
Dieses Skript empfängt ein PHYPayload-Paket im Base64-Format und einen Schlüssel, der je nach Pakettyp der NwkSKey oder der AppKey sein kann, und generiert die neue MIC.
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
--jakey JAKEY [Nur für JoinAccept]. Geben Sie den Schlüssel ein, der
zuvor zum Verschlüsseln des JoinAccepts verwendet
wurde (im Hex-Format, insgesamt 32 Zeichen / 16
Byte). Dies kann von diesem Programm nicht vorab
überprüft werden. z.B.
00112233445566778899AABBCCDDEEFF. Ein gültiger
Schlüssel für den JoinAccept
"IB1scNmwJRA32RfMbvwe3oI=" ist
"f5a3b185dfe452c8edca3499abcd0341"
Erforderliche Argumente:
-d DATA, --data DATA Base64-Daten, die signiert werden sollen. z.B. -d
AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
-k KEY, --key KEY Geben Sie den neuen Schlüssel (im Hex-Format,
insgesamt 32 Zeichen / 16 Byte) ein, um Pakete zu
signieren (neue MIC berechnen und hinzufügen).
Beachten Sie, dass für JoinRequest/JoinAccept der
AppKey und für Datenpakete der NwkSKey verwendet
werden muss. Dies kann von diesem Programm nicht
vorab überprüft werden. z.B.
00112233445566778899AABBCCDDEEFF
Beispiel:
Das angegebene PHYPayload mit dem AppKey 00112233445566778899AABBCCDDEEFF signieren.
python3 MicGenerator.py -d AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU= -k 00112233445566778899AABBCCDDEEFF
Dieses Skript empfängt ein LoRaWAN-JSON-Paket und wandelt es in Base64 um. Es macht das Gegenteil von packetParser.py, sodass die Ausgabe dieses Skripts dort und umgekehrt verwendet werden kann.
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
-k KEY, --key KEY Geben Sie einen Geräte-AppSKey oder AppKey (im
Hex-Format, insgesamt 32 Zeichen / 16 Byte) ein, um
die FRMPayload oder einen Join Accept zu
verschlüsseln. z.B. F5A3B185DFE452C8EDCA3499ABCD0341
--nwkskey NWKSKEY Geben Sie den Netzwerksitzungsschlüssel ein, wenn Sie
ein Datenpaket mit einer gültigen MIC generieren möchten.
Erforderliche Argumente:
-j JSON, --json JSON JSON-Objekt zum Parsen. z.B. -j '{"mhdr":
{"mType":"JoinRequest","major":"LoRaWANR1"},"macPayloa
d":{"joinEUI":"55d239ac716f234d","devEUI":"b827eb891cf
50003","devNonce":51639},"mic":"7005c4a5"}'
Beispiel:
Einen JoinRequest-PHYPayload in Base64 aus dem angegebenen JSON mit den darin übergebenen Werten erhalten.
python3 PacketCrafter.py -j '{"mhdr":{"mType":"JoinRequest","major":"LoRaWANR1"},"macPayload":{"joinEUI":"55d239ac716f234d","devEUI":"b827eb891cf50003","devNonce":51639},"mic":"7005c4a5"}'
Dieses Skript parst und gibt einen einzelnen LoRaWAN-PHYPayload-Datensatz im Base64-Format aus. Es macht das Gegenteil von packetCrafter.py, sodass die Ausgabe dieses Skripts dort und umgekehrt verwendet werden kann.
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
-k KEY, --key KEY Geben Sie einen Geräte-AppKey oder AppSKey ein, je
nachdem, welches Paket entschlüsselt werden soll
(Join Accept oder Datenpaket). Muss im Hex-Format
sein, insgesamt 32 Zeichen / 16 Byte. z.B.
00112233445566778899AABBCCDDEEFF
Erforderliche Argumente:
-d DATA, --data DATA Base64-Daten, die geparst werden sollen. z.B. -d
AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
Beispiel:
Den JoinRequest im JSON-Format aus dem obigen Beispiel erhalten.
python3 PacketParser.py -d AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
Dieses Skript empfängt einen JoinAccept und einen JoinRequest im Base64-Format sowie einen AppKey, um die Sitzungsschlüssel zu generieren. Ein Beispiel für die Verwendung:
Optionale Argumente:
-h, --help Zeigt diese Hilfemeldung an und beendet.
Erforderliche Argumente:
-a JACCEPT, --jaccept JACCEPT
JoinAccept-Payload in Base64
-r JREQUEST, --jrequest JREQUEST
JoinRequest-Payload in Base64
-k KEY, --key KEY Geben Sie einen Geräte-AppKey (im Hex-Format,
insgesamt 32 Zeichen / 16 Byte) ein. z.B.
00112233445566778899AABBCCDDEEFF
Beispiel:
Die AppSKey und NwkSKey mit den folgenden Join-Daten erhalten.
python3 SessionKeysGenerator.py -r AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU= -a IB1scNmwJRA32RfMbvwe3oI= -k f5a3b185dfe452c8edca3499abcd0341
Dies sind Hilfsfunktionen, die von UdpSender.py und UdpProxy.py verwendet werden. In Fuzzer.py können Sie die implementierten Fuzzing-Modi sehen.
Der allgemeine Zweck dieses Verzeichnisses besteht darin, LoRaWAN-Pakete zu sammeln und verschiedene Aspekte des Datenverkehrs zu analysieren sowie einen Satz von Schlüsseln zu testen, um den AppKey per Brute-Force zu knacken.
Dieses Verzeichnis enthält eine Reihe von Skripten, die LoRaWAN-Pakete von verschiedenen Quellen (z. B. Gateway Packet-Forwarder, The Things Network usw.) empfangen und in Dateien mit einem Standardformat speichern. Diese Dateien sollten später vom Skript /auditing/analyzers/LafProcessData.py abgerufen werden, um verschiedene Unterwerkzeuge auszuführen.
Dieses Skript verbindet sich mit dem MQTT-Broker, ruft alle Topics ab und speichert Nachrichten in einer Datei im angegebenen Feld. Der Dateiname setzt sich aus dem Datum zusammen, an dem dieses Skript gestartet wurde.
Optionale Argumente:-h, --help zeigt diese Hilfemeldung an und beendet das Programm --collector-id COLLECTOR_ID Die ID des Datenkollektors. Diese ID wird mit den in der Datenbank gespeicherten Paketen verknüpft. z.B. --id 1 --organization-id ORGANIZATION_ID Die ID des Datenkollektors. Diese ID wird mit den in der Datenbank gespeicherten Paketen verknüpft. z.B. --id 1 --topics TOPICS [TOPICS ...] Liste der Themen, die Sie abonnieren möchten, getrennt durch Leerzeichen. Falls nichts angegeben, wird standardmäßig "#" verwendet.
Erforderliche Argumente:
--ip IP MQTT-Broker-IP, z.B. --ip 192.168.3.101.
--port PORT MQTT-Broker-Port, z.B. --port 623.
Beispiel:
Stellen Sie eine Verbindung zum MQTT-Broker mit IP 200.200.200.200 auf dem Standardport (1883) her.
python3 GenericMqttCollector.py --ip 200.200.200.200 --port 1883
Dieses Skript verbindet sich mit einem loraserver.io MQTT-Broker und speichert Nachrichten in der Datenbank. Sie müssen eine eindeutige collectorID angeben und können die Themen angeben, die Sie abonnieren möchten.
Optionale Argumente:
-h, --help zeigt diese Hilfemeldung an und beendet das Programm
--port PORT MQTT-Broker-Port, z.B. --port 623. Standard 1883.
--collector-id COLLECTOR_ID
Die ID des Datenkollektors. Diese ID wird mit den
in der Datenbank gespeicherten Paketen verknüpft. z.B. --id 1
--organization-id ORGANIZATION_ID
Die ID des Datenkollektors. Diese ID wird mit den
in der Datenbank gespeicherten Paketen verknüpft. z.B. --id 1
--topics TOPICS [TOPICS ...]
Liste der Themen, die Sie abonnieren möchten, getrennt durch
Leerzeichen. Falls nichts angegeben, wird standardmäßig "#" verwendet.
Erforderliche Argumente:
--ip IP MQTT-Broker-IP, z.B. --ip 192.168.3.101.
Dieses Skript empfängt UDP-Pakete vom UDP-Proxy im Gateway Packet_Forwarder-Format und persistiert sie.
Optionale Argumente:
-h, --help zeigt diese Hilfemeldung an und beendet das Programm
--collector-id COLLECTOR_ID
Die ID des Datenkollektors. Diese ID wird mit den
in der Datenbank gespeicherten Paketen verknüpft. z.B. --id 1
--organization-id ORGANIZATION_ID
Die ID des Datenkollektors. Diese ID wird mit den
in der Datenbank gespeicherten Paketen verknüpft. z.B. --id 1
Erforderliche Argumente:
-n NAME, --name NAME Eindeutige Zeichenfolgenkennung des Datenkollektors. z.B.
--name semtech_collector
-p PORT, --port PORT Port, auf dem auf UDP-Pakete gelauscht werden soll. --port 1702.
Beispiel:
Daten zwischen einem Gateway, das an den lokalen Port 1700 sendet, und einem Netzwerk-xserver, der auf (localhost, 1701) lauscht, aufzeichnen. Speichern Sie die Daten im Verzeichnis ./.
python3 PacketForwarderCollector.py --name semtech_collector --port 1700
Dieses Skript liest aus einer Datei oder aus Dateien oder von der Standardeingabe und führt verschiedene Unterwerkzeuge aus. Abhängig von der ausgewählten Option können Sie eine Analyse des LoRaWAN-Datenverkehrs durchführen, versuchen, den AppKey per Brute-Force zu knacken oder alle empfangenen Pakete zu parsen. Diese Optionen können kombiniert werden.
Optionale Argumente:
Dieses Skript ruft Pakete aus der Datenbank ab und führt verschiedene Unterwerkzeuge aus.
Dann speichert jedes Unterwerkzeug Ausgabedaten in der Datenbank. Siehe jede Option für
weitere Informationen.
optionale Argumente:
-h, --help zeigt diese Hilfemeldung an und beendet das Programm
-a, --analyze Sammelt und analysiert verschiedene Aspekte des Datenverkehrs. Wenn
Bruteforcer (-b) aktiviert ist, werden die Ergebnisse
korreliert
-b, --bforce Versucht, die AppKeys mit JoinRequests und
JoinAccepts Payloads per Brute-Force zu knacken
-k KEYS, --keys KEYS [Bruteforcer] Dateipfad zur Schlüsseldatei. Wenn nicht angegeben,
wird "bruteForcer/keys.txt" verwendet
--no-gen [Bruteforcer] Keine Schlüssel generieren, nur Schlüssel aus Dateien
versuchen
-p, --parse Parst die PHYPayload in lesbare Informationen
--from-id FROM_ID Paket-ID, ab der die Verarbeitung beginnen soll.
--to-id TO_ID Letzte zu verarbeitende Paket-ID.
Beispiel:
Verarbeiten Sie Pakete in der Datenbank ab Paket-ID 1000, führen Sie eine Datenverkehrsanalyse durch und versuchen Sie, AppKeys aus my-keys.txt zu knacken, generieren Sie jedoch nicht dynamisch weitere Schlüssel.
python3 LafProcessData.py -a -b -k my-keys.txt --no-gen --from-id 1000
Diese Skripte stellen die von LafProcessData.py orchestrierte Funktionalität bereit. Nachfolgend die Warnungen, die von LafPacketAnalysis.py und LafBruteForcer.py implementiert werden:
Dieses Verzeichnis enthält eine Reihe von Wrappern für die Bibliothek https://github.com/brocaar/lorawan/, die in Golang geschrieben ist. Diese Funktionen werden von den Tools implementiert.
Hier finden Sie eine Reihe von Skripten, die zur Automatisierung verschiedener Aufgaben dienen. Stellen Sie sicher, dass Sie ihnen bei Bedarf Ausführungsberechtigung erteilen (chmod +x your_script für Linux/MacOS).
Richten Sie Ihr Gateway einfach ein und wechseln Sie seine Kanäle zu Sniffing-Zwecken. Weitere Informationen zur Verwendung finden Sie in der README in diesem Verzeichnis.
Dieses Skript wird verwendet, um alle notwendigen Softwarepakete auf einem Raspberry PI zu installieren, um ein LoRaWAN-Gateway in Verbindung mit einem angeschlossenen LoRa-Konzentrator (iC980-SPI, RHF0M301-SPI, RAK831-SPI oder einem anderen durch manuelle Einrichtung) aufzubauen.
Da es nicht möglich ist zu wissen, auf welchen Frequenzen LoRa-Geräte arbeiten, haben wir ein Skript erstellt, das die Kanäle von Gateways aus den US915- und EU868-Frequenzbändern zu Sniffing-Zwecken umschalten kann. Obwohl es professionelle und teure Gateways gibt, die 32 oder 64 Kanäle unterstützen, unterstützen die meisten Gateways bis zu 8 Kanäle. Dieses Skript ist für den Einsatz auf dieser Art von Gateways gedacht.
Zumindest im US915-Frequenzband werden die ersten 8 Kanäle am häufigsten genutzt. Es gibt jedoch bekannte Implementierungen, die eine andere Kanalgruppe verwenden, wie zum Beispiel The Things Network, das die zweite Gruppe (8-15) von Kanälen für die Uplink-Kommunikation nutzt.
Derzeit unterstützen wir keine anderen Frequenzbänder, aber mit wenigen Änderungen an diesen Skripten können Sie dies selbst tun :).
TODO
Dieses Projekt ist unter der BSD-3-Clause-Lizenz lizenziert.
export GOPATH="$HOME/go"Kompilieren Sie die Go-Bibliothek:
cd laf/lorawanwrapper/utilsgo build -o lorawanWrapper.so -buildmode=c-shared jsonUnmarshaler.go lorawanWrapper.go micGenerator.go sessionKeysGenerator.go hashGenerator.goJe nachdem, welche Datenbank Sie verwenden möchten:
a. PostgreSQL: Befolgen Sie die Anweisungen unter 'LAF mit Docker installieren' bis zum 3. Schritt.
b. SQLite:
cd laf/auditing/db__init__.py mit Ihrem bevorzugten Texteditor und kommentieren Sie die Zeilen für Postgres aus (DB-Verbindung und Umgebungsvariablen) und entfernen Sie den Kommentar der Zeile für SQLite.| ID | Titel | Analyzer | Risikostufe | Beschreibung | Empfohlene Maßnahme |
|---|
| LAF-001 | DevNonce wiederholt | LafPacketAnalysis.py | Niedrig | DevNonces für jedes Gerät sollten zufällig genug sein, um nicht zu kollidieren. Wenn derselbe DevNonce in vielen Nachrichten wiederholt wurde, kann daraus geschlossen werden, dass ein Gerät einem Replay-Angriff ausgesetzt ist. Ein Angreifer hat also eine JoinRequest erfasst und versucht, sie erneut an das Gateway zu senden. | Überprüfen Sie, wie DevNonces generiert werden: Die Funktion, die sie generiert, sollte mit einer Zufallsbibliothek implementiert werden. Zudem muss sichergestellt werden, dass der Server historische DevNonces überprüft (sie sollten in der Datenbank gespeichert sein), um keine alte gültige JoinRequest, die zuvor vom Gerät gesendet wurde, zu akzeptieren und damit eine neue Sitzung zu generieren. |
| LAF-002 | DevEUIs teilen sich dieselbe DevAddr | LafPacketAnalysis.py | Info | Zwei verschiedenen Geräten könnte dieselbe DevAddr zugewiesen worden sein. Dies stellt keine Sicherheitsbedrohung dar. | Wenn das Gerät over the air aktiviert (OTAA) ist: Überprüfen Sie die Logik zur Zuweisung von DevAddrs und stellen Sie sicher, dass der Server nicht dieselbe DevAddr verschiedenen Geräten zuweist. Wenn das Gerät durch Personalisierung aktiviert (ABP) ist: Überprüfen Sie die DevAddr, die in der Firmware eines Geräts konfiguriert ist, und stellen Sie sicher, dass sie im LoRaWAN-Netzwerk eindeutig ist. |
| LAF-003 | Join-Replay | TODO | Mittel | Ein doppeltes Join-Request-Paket wurde erkannt, was bedeuten könnte, dass der LoRaWAN-Server einem Replay-Angriff ausgesetzt ist. Ein Angreifer, der ein früheres Join-Request-Paket erfasst hat, sendet es erneut an den LoRaWAN-Server, um zu versuchen, eine neue Sitzung zu generieren. | Überprüfen Sie, wie DevNonces generiert werden: Die Funktion, die sie generiert, sollte mit einer Zufallsbibliothek implementiert werden. Zudem muss sichergestellt werden, dass der Server historische DevNonces überprüft (sie sollten in der Datenbank gespeichert sein), um keine alte gültige JoinRequest, die zuvor vom Gerät gesendet wurde, zu akzeptieren und damit eine neue Sitzung zu generieren. |
| LAF-004 | Uplink-Datenpaket-Replay | TODO | Mittel | Ein doppeltes Uplink-Paket wurde erkannt, was bedeuten könnte, dass der LoRaWAN-Server einem Replay-Angriff ausgesetzt ist. Ein Angreifer, der ein Uplink-Paket (vom Gerät gesendet) erfasst hat, sendet es erneut an den LoRaWAN-Server. | Bei over the air aktivierten (OTAA) Geräten: Stellen Sie sicher, dass die Sitzungsschlüssel nach jedem Geräte-Reset oder Zählerüberlauf neu generiert werden, um Auswirkungen dieses Angriffs zu vermeiden. Bei durch Personalisierung aktivierten (ABP) Geräten ab LoRaWAN v1.0.* kann nichts getan werden, um einen Replay-Angriff zu verhindern, außer das Gerät auf OTAA umzustellen. |
| LAF-005 | Downlink-Datenpaket-Replay | TODO | Hoch | Ein doppeltes Downlink-Paket wurde erkannt. Der Server reagiert auf einen Replay-Angriff oder generiert einen atypischen Datenverkehr zu Geräten | Überprüfen Sie die Server-Logs und stellen Sie sicher, dass die zuvor empfohlenen Maßnahmen umgesetzt wurden |
| LAF-006 | Mögliches ABP-Gerät (Zähler zurückgesetzt und kein Join) | LafPacketAnalysis.py | Hoch | Wenn der Zähler zurückgesetzt wurde (wieder auf 0), die DevAddr gleich bleibt und kein vorheriger Join-Prozess erkannt wurde, könnte dies bedeuten, dass das Gerät durch Personalisierung (ABP) aktiviert ist. Die Implementierung von ABP-Geräten wird nicht empfohlen, da kein Join-Prozess durchgeführt wird, was bedeutet, dass die Sitzungsschlüssel für immer gleich bleiben. Ein Gerät, das seine Sitzungsschlüssel nicht ändert, ist anfällig für verschiedene Angriffe wie Lauschangriffe oder Replay. | Alle durch Personalisierung aktivierten (ABP) Geräte sollten nach Möglichkeit durch over the air aktivierte (OTAA) Geräte ersetzt werden. Die Implementierung von ABP-Geräten wird nicht empfohlen. |
| LAF-007 | Kleinerer Zähler als erwartet empfangen (anders als 0) | LafPacketAnalysis.py | Mittel | Wenn ein Angreifer ein Paar Sitzungsschlüssel erhält (durch Diebstahl des AppKey bei OTAA-Geräten oder des AppSKey/NwkSKey bei ABP-Geräten), könnte er gefälschte gültige Daten an den Server senden. Damit der Server gefälschte Nachrichten akzeptiert, muss der FCnt (Frame Counter) der Nachricht höher sein als der FCnt der letzten gesendeten Nachricht. In einem Szenario, in dem das ursprüngliche gefälschte Gerät weiterhin Nachrichten sendet, würde der Server (gültige) Nachrichten verwerfen, da sie einen kleineren FCnt hätten. Wenn also Nachrichten mit einem kleineren FCnt als vom LoRaWAN-Server erwartet empfangen werden, kann daraus geschlossen werden, dass eine parallele Sitzung aufgebaut wurde. | Wenn das Gerät ein over the air aktiviertes Gerät (OTAA) ist, ändern Sie seinen AppKey, da dieser wahrscheinlich kompromittiert wurde. Wenn es durch Personalisierung aktiviert ist, ändern Sie seinen AppSKey und NwkSKey. Stellen Sie zudem sicher, dass der LoRaWAN-Server aktualisiert ist und keine doppelten Nachrichten akzeptiert. |
| LAF-008 | Passwort mit JoinRequest geknackt | LafBruteforcer.py | Hoch | Es war möglich, eine JoinRequest-Nachricht mit einem bekannten AppKey zu entschlüsseln. | Verwenden Sie andere AppKeys als die von Herstellern bereitgestellten oder verwenden Sie zufälligere Schlüssel. |
| LAF-009 | Passwort geknackt | LafBruteforcer.py | Hoch | Der AppKey des Geräts wurde gefunden, indem ein bekannter oder nicht-zufälliger String ausprobiert wurde. Er wurde mit einem Paar von Join-Nachrichten (Request und Accept) entschlüsselt. | Verwenden Sie einen zufälligen Schlüsselgenerator für den AppKey anstatt der von Herstellern bereitgestellten. Setzen Sie außerdem nicht denselben AppKey für mehr als ein Gerät und generieren Sie AppKeys nicht mit einer vorhersagbaren Logik (z.B. inkrementelle Werte, bestimmte Bytes umdrehen usw.). |
| LAF-010 | Gateway-Standort geändert | LafPacketAnalysis.py | Mittel | Wenn das Gateway seinen Standort nicht ändern sollte. Es könnte gestohlen, bewegt worden sein oder ein gefälschtes Gateway könnte versuchen, das legitime Gateway zu imitieren. | Stellen Sie sicher, dass das Gateway nicht manipuliert wurde, weder physisch noch logisch. |