
Ce projet a pour but de fournir une série d'outils pour fabriquer, parser, envoyer, analyser et casser un ensemble de paquets LoRaWAN afin d'auditer ou de tester la sécurité d'une infrastructure LoRaWAN.
Les déploiements IoT ne cessent de croître et une partie de cette croissance significative est composée de millions de capteurs LPWAN (réseau à faible consommation et longue portée) déployés dans des centaines de villes (Smart Cities) à travers le monde, ainsi que dans les industries et les foyers. L'une des technologies LPWAN les plus utilisées est LoRa, dont LoRaWAN est le standard réseau (couche MAC). LoRaWAN est un protocole sécurisé avec chiffrement intégré, mais les problèmes d'implémentation et les faiblesses affectent la sécurité de la plupart des déploiements actuels.
Ce projet vise à fournir une série d'outils pour fabriquer, analyser, envoyer, analyser et casser un ensemble de paquets LoRaWAN afin d'auditer ou de tester la sécurité d'une infrastructure LoRaWAN.
Ci-dessous, la structure de ce dépôt :
|-- 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
Nous proposons différentes options pour mettre en place votre Cadre d'Audit LoRaWAN :
tools/, afin d'éviter les problèmes de mappage de ports avec Docker.localhost. Voir les instructions ci-dessous pour configurer Docker.Ces instructions vous permettront d'obtenir une copie du projet et de ses dépendances sur votre machine locale. Les commandes ci-dessous sont pour un environnement basé sur Debian :
Clonez ce dépôt : git clone --recurse-submodules https://github.com/IOActive/laf.git
Installez python3 :
sudo apt-get updatesudo apt-get install python3.6Téléchargez et installez les dépendances Python :
sudo pip3 install paho-mqtt && sudo pip3 install sqlalchemy && sudo pip3 install psycopg2-binary &&sudo pip3 install python-dateutilDéfinissez PYTHONPATH et ENVIRONMENT
cd laf && export PYTHONPATH=$(pwd) && export ENVIRONMENT='DEV'Installez et configurez golang :
cd ~/Downloadssudo tar -C /usr/local -xvzf VOTRE_FICHIER_GOLANGexport PATH=$PATH:/usr/local/go/binexport GOPATH="$HOME/go"Compilez la bibliothèque go :
cd laf/lorawanwrapper/utilsgo build -o lorawanWrapper.so -buildmode=c-shared jsonUnmarshaler.go lorawanWrapper.go micGenerator.go sessionKeysGenerator.go hashGenerator.goSelon la base de données que vous souhaitez utiliser :
a. PostgreSQL : Suivez les instructions 'Installer LAF en utilisant Docker' jusqu'à la 3ème étape.
b. SQLite :
cd laf/auditing/db__init__.py avec votre éditeur de texte préféré et commentez les lignes à utiliser avec Postgres (connexion DB et variables d'environnement) et décommentez la ligne à utiliser avec sqlite.Et voilà !
Cette approche évite de gérer l'installation des dépendances et démarre une base de données PostgreSQL où les outils enregistrent les paquets et les données. Conteneurs :
Étapes :
git clone https://github.com/IOActive/laf.gitcd laf/docker-compose up --builddocker exec -ti laf_tools_1 /bin/bashVous pouvez vérifier les données dans la base de données en utilisant pgAdmin :
Tout d'abord, accédez à pgAdmin :
Ensuite, vous devez ajouter le serveur :
Voici la description des répertoires et des outils/fonctions qu'ils contiennent.
L'objectif principal des outils fournis dans ce répertoire est de faciliter l'exécution d'un test d'intrusion sur une infrastructure LoRaWAN.
Cet outil est destiné à envoyer des paquets montants (vers le serveur réseau ou la passerelle bridge, selon l'infrastructure) ou des paquets descendants (vers le packet-forwarder). Optionnellement, les paquets peuvent être fuzzés et un MIC valide peut être calculé.
Arguments optionnels :
-h, --help show this help message and exit
--lcl-port LCL_PORT Source port, eg. --lcl-port=623.
--timeout TIMEOUT Time in seconds between every packet sent. Default is
1s. In this time, the sender will listen for replies.
--repeat Send message/s multiple times
--fuzz-out FUZZ_OUT [FUZZ_OUT ...]
Fuzz data sent to dest port (see fuzzing modes in
utils/fuzzer.py), eg. --fuzz-out 1 2.
--key KEY Enter the key (in hex format, a total of 32 characters
/ 16 bytes) to sign packets (calculate and add a new
MIC). Note that for JoinRequests it must be the
AppKey, and the NwkSKey for Data packets. This cannot
be validated beforehand by this program. eg.
00112233445566778899AABBCCDDEEFF
-a DEVADDR, --devaddr DEVADDR
DeviceAddress to impersonate, given in hex format (8
characters total), eg. AABB0011.
--fcnt FCNT The frame counter to be set in the given data packet.
This wouldn't work in a JoinRequest/JoinAccept since
this packets don't have a fCnt
Arguments obligatoires :
--dst-ip DST_IP Destination ip, eg. --dst-ip 192.168.3.101.
--dst-port DST_PORT Destination port, eg. --dst-port 623.
--data DATA UDP packet. It can also be added more packets in
"data" array at the end of this script. The packet
must be a byte string (you will have to escape double
quotes). ***EXAMPLE*** with the 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=\"}]}'" ***EXAMPLE*** using the gatevice
[GV] format sending in inmediate mode, in BW125 and
freq 902.3 is "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}'"
Exemple :
Pour envoyer un seul paquet toutes les 2 secondes vers (localhost, 10001) depuis le port 10000 en fuzzant aléatoirement le MIC et le 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=\"}]}'"
Ce proxy UDP est principalement destiné à être placé entre une série de passerelles (packet_forwarders) et un serveur réseau ou une passerelle bridge selon l'infrastructure évaluée. Il offre également la possibilité de fuzzer les données dans la direction souhaitée (montante ou descendante)
Arguments optionnels :
-h, --help show this help message and exit
--collector-port COLLECTOR_PORT
Packet forwarder data collector port, eg. --collector-
port 1701. See
auditing/datacollectors/PacketForwarderCollector.py
--collector-ip COLLECTOR_IP
Packet forwarder data collector ip. Default is
localhost. eg. --collector-ip 192.168.1.1. See
auditing/datacollectors/PacketForwarderCollector.py
--fuzz-in FUZZ_IN [FUZZ_IN ...]
Fuzz data sent to dst-port in the given modes (see
fuzzing modes in utils/fuzzer.py), eg. --fuzz-in 1 2
...
--fuzz-out FUZZ_OUT [FUZZ_OUT ...]
Fuzz data sent to (source) port in the given modes
(see fuzzing modes in utils/fuzzer.py), eg. --fuzz-out
1 2 ...
-k KEY, --key KEY Enter a device AppSKey (in hex format, a total of 32
characters / 16 bytes) to decrypt its FRMPayload and
print it in plain text. You can also enter the AppKey
if you wish to decrypt a given Join Accept. eg.
00112233445566778899AABBCCDDEEFF
-p PATH, --path PATH Filepath where to save the data. If not given, data
will not be saved.
--no-log Do not print UDP packages into console
--no-parse Do not parse PHYPayload. If this option is selected,
Golang librarys from /lorawanwrapper/ won't be
imported (golang libs compiling is not required)
Arguments obligatoires :
--port PORT The local port to listen, eg. --port 623.
--dst-ip DST_IP Destination host ip, eg. --dst-ip 192.168.3.101.
--dst-port DST_PORT Destination host port, eg. --dst-port 623.
Exemple :
Pour envoyer les paquets reçus sur le port 1234 vers (localhost, 1235) et vice versa. Les paquets reçus sur le port seront fuzzés (le devNonce sera changé aléatoirement) et transmis à (localhost, 1235).
python3 UdpProxy.py --port 1234 --dst-ip 127.0.0.1 --dst-port 1235 --fuzz-in 9
Ce proxy TCP est principalement destiné à être placé entre le serveur réseau et un courtier MQTT. Il offre également la possibilité de fuzzer les données.
Arguments optionnels :
-h, --help show this help message and exit
--fuzz-in FUZZ_IN [FUZZ_IN ...]
Fuzz data sent to dst-port in the given modes (see
fuzzing modes in utils/fuzzer.py)
Arguments obligatoires :
--lcl-port LCL_PORT The local port to listen, eg. --lcl-port=623.
--dst-ip DST_IP Destination host ip, eg. --dst-ip=192.168.3.101.
--dst-port DST_PORT Destination host port, eg. --dst-port=623.
Exemple :
Envoyer et recevoir des données depuis (localhost, 1884) vers (localhost, 1883)
python3 TcpProxy.py --lcl-port 1884 --dst-ip 127.0.0.1 --dst-port 1883
Ce répertoire contient une série de scripts pour analyser, fabriquer, bruteforcer, etc., les paquets LoRaWAN.
Ce script reçoit un JoinAccept ou un JoinRequest en Base64 et tente de déchiffrer son AppKey avec un ensemble de clés possibles qui peuvent être fournies dans un fichier ou être générées à la volée.
Arguments optionnels :
-h, --help show this help message and exit
-k KEYS, --keys KEYS File containing a list of keys, separated by \n. Will
use /auditing/analyzers/bruteForcer/keys.txt by
default
--dont-generate Select this options if you don't want to generate keys
on the fly with the following combinations: 1- Combine
the first byte and the last fifteeen bytes. eg.
AABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 2- Combine even and
odd bytes position equally. eg.
AABBAABBAABBAABBAABBAABBAABBAABB 3- The first 14 bytes
in 00 and combine the last 2. eg.
0000000000000000000000000000BA01
Arguments obligatoires :
-a ACCEPT, --accept ACCEPT
Join Accept in Base64 format to be bruteforced. eg. -a
IHvAP4MXo5Qo6tdV+Yfk08o=
-r REQUEST, --request REQUEST
Join Request in Base64 format to be bruteforced. eg.
-r AMQAAAAAhQAAAgAAAAAAAADcYldcgbc=
Exemple :
Cracker un JoinRequest avec un ensemble de clés provenant de my-keys.txt et également générer environ 200000 clés supplémentaires dynamiquement.
python3 BruteForcer.py -a IHvAP4MXo5Qo6tdV+Yfk08o= -r AMQAAAAAhQAAAgAAAAAAAADcYldcgbc= -k ./my-keys.txt
Ce script reçoit un paquet PHYPayload en Base64 et une clé qui peut être la NwkSKey ou l'AppKey selon le type de paquet et génère le nouveau MIC.
Arguments optionnels :
-h, --help show this help message and exit
--jakey JAKEY [JoinAccept ONLY]. Enter the key used to encrypt the
JoinAccept previously (in hex format, a total of 32
characters / 16 bytes). This cannot be validated
beforehand by this program. eg.
00112233445566778899AABBCCDDEEFF. A valid key sample
for the JoinAccept "IB1scNmwJRA32RfMbvwe3oI=" is
"f5a3b185dfe452c8edca3499abcd0341"
Arguments obligatoires :
-d DATA, --data DATA Base64 data to be signed. eg. -d
AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
-k KEY, --key KEY Enter the new key (in hex format, a total of 32
characters / 16 bytes) to sign packets (calculate and
add a new MIC). Note that for JoinRequest/JoinAccept
it must be the AppKey, and the NwkSKey for Data
packets. This cannot be validated beforehand by this
program. eg. 00112233445566778899AABBCCDDEEFF
Exemple :
Signer le PHYPayload donné avec l'AppKey 00112233445566778899AABBCCDDEEFF.
python3 MicGenerator.py -d AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU= -k 00112233445566778899AABBCCDDEEFF
Ce script reçoit un paquet LoRaWAN au format JSON et le transforme en Base64. Il fait l'inverse de packetParser.py, donc la sortie de ce script peut être utilisée ici et vice versa.
Arguments optionnels :
-h, --help show this help message and exit
-k KEY, --key KEY Enter a device AppSKey or AppKey (in hex format, a
total of 32 characters / 16 bytes) to encrypt the
FRMPayload or a Join Accept. eg.
F5A3B185DFE452C8EDCA3499ABCD0341
--nwkskey NWKSKEY Enter the network session key if you'd like to
generate a data packet with a valid MIC.
Arguments obligatoires :
-j JSON, --json JSON JSON object to parse. eg. -j '{"mhdr":
{"mType":"JoinRequest","major":"LoRaWANR1"},"macPayloa
d":{"joinEUI":"55d239ac716f234d","devEUI":"b827eb891cf
50003","devNonce":51639},"mic":"7005c4a5"}'
Exemple :
Obtenir un PHYPayload JoinRequest en Base64 à partir du JSON donné avec les valeurs transmises.
python3 PacketCrafter.py -j '{"mhdr":{"mType":"JoinRequest","major":"LoRaWANR1"},"macPayload":{"joinEUI":"55d239ac716f234d","devEUI":"b827eb891cf50003","devNonce":51639},"mic":"7005c4a5"}'
Ce script analyse et affiche un seul PHYPayload LoRaWAN en Base64. Il fait l'inverse de packetCrafter.py, donc la sortie de ce script peut être utilisée ici et vice versa.
Arguments optionnels :
-h, --help show this help message and exit
-k KEY, --key KEY Enter a device AppKey or AppSKey depending on the
packet to be decrypted (join accept or data packet).
Must be in hex format, a total of 32 characters / 16
bytes. eg. 00112233445566778899AABBCCDDEEFF
Arguments obligatoires :
-d DATA, --data DATA Base64 data to be parsed. eg. -d
AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
Exemple :
Obtenir le JoinRequest au format JSON à partir de l'exemple ci-dessus.
python3 PacketParser.py -d AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
Ce script reçoit un JoinAccept et un JoinRequest en Base64, ainsi qu'une AppKey pour générer les clés de session. Un exemple d'utilisation :
Arguments optionnels :
-h, --help show this help message and exit
Arguments obligatoires :
-a JACCEPT, --jaccept JACCEPT
JoinAccept payload in base64
-r JREQUEST, --jrequest JREQUEST
JoinRequest payload in base64
-k KEY, --key KEY Enter a device AppKey (in hex format, a total of 32
characters / 16 bytes). eg.
00112233445566778899AABBCCDDEEFF
Exemple :
Obtenir l'AppSKey et la NwkSKey avec les données de jonction suivantes.
python3 SessionKeysGenerator.py -r AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU= -a IB1scNmwJRA32RfMbvwe3oI= -k f5a3b185dfe452c8edca3499abcd0341
Ce sont des fonctions auxiliaires utilisées par UdpSender.py et UdpProxy.py. Dans Fuzzer.py, vous pouvez voir les modes de fuzzing implémentés.
L'objectif général de ce répertoire est de collecter des paquets LoRaWAN et d'analyser différents aspects du trafic, ainsi que de tester un ensemble de clés pour tenter de bruteforcer l'AppKey.
Ce répertoire contient un ensemble de scripts qui reçoivent des paquets LoRaWAN de différentes sources (par exemple, gateway packet_forwarder, The Things Network, etc.) et les enregistrent dans des fichiers, avec un format standard. Ces fichiers doivent ensuite être récupérés par le script /auditing/analyzers/LafProcessData.py pour exécuter différents sous-outils.
Ce script se connecte au courtier MQTT, récupère tous les sujets et enregistre les messages dans un fichier dans le champ spécifié. Le nom du fichier est composé de la date à laquelle ce script a été démarré.
Arguments optionnels :-h, --help affiche ce message d'aide et quitte --collector-id COLLECTOR_ID L'ID du dataCollector. Cet ID sera associé aux paquets sauvegardés dans la BDD. ex. --id 1 --organization-id ORGANIZATION_ID L'ID du dataCollector. Cet ID sera associé aux paquets sauvegardés dans la BDD. ex. --id 1 --topics TOPICS [TOPICS ...] Liste le(s) topic(s) auxquels vous voulez vous abonner, séparés par des espaces. Si rien n'est donné, le défaut sera '#'.
Arguments requis :
--ip IP Adresse IP du broker MQTT, ex. --ip 192.168.3.101.
--port PORT Port du broker MQTT, ex. --port 623.
Exemple :
Se connecter au broker MQTT avec l'IP 200.200.200.200 sur le port par défaut (1883).
python3 GenericMqttCollector.py --ip 200.200.200.200 --port 1883
Ce script se connecte à un broker mqtt loraserver.io et sauvegarde les messages dans la BDD. Vous devez spécifier un collectorID unique et vous pouvez spécifier les topics auxquels vous souhaitez vous abonner.
Arguments optionnels :
-h, --help affiche ce message d'aide et quitte
--port PORT Port du broker MQTT, ex. --port 623. Défaut 1883.
--collector-id COLLECTOR_ID
L'ID du dataCollector. Cet ID sera associé aux paquets sauvegardés dans la BDD. ex. --id 1
--organization-id ORGANIZATION_ID
L'ID du dataCollector. Cet ID sera associé aux paquets sauvegardés dans la BDD. ex. --id 1
--topics TOPICS [TOPICS ...]
Liste le(s) topic(s) auxquels vous voulez vous abonner, séparés par des espaces. Si rien n'est donné, le défaut sera '#'.
Arguments requis :
--ip IP Adresse IP du broker MQTT, ex. --ip 192.168.3.101.
Ce script reçoit des paquets UDP du proxy UDP au format packet_forwarder de la gateway et les persiste.
Arguments optionnels :
-h, --help affiche ce message d'aide et quitte
--collector-id COLLECTOR_ID
L'ID du dataCollector. Cet ID sera associé aux paquets sauvegardés dans la BDD. ex. --id 1
--organization-id ORGANIZATION_ID
L'ID du dataCollector. Cet ID sera associé aux paquets sauvegardés dans la BDD. ex. --id 1
Arguments requis :
-n NAME, --name NAME Chaîne identifiant unique du Data Collector. ex.
--name semtech_collector
-p PORT, --port PORT Port d'écoute des paquets UDP. --port 1702.
Exemple :
Enregistre les données entre une gateway envoyant sur le port local 1700 et un serveur réseau écoutant sur (localhost, 1701). Sauvegarde les données dans le répertoire ./.
python3 PacketForwarderCollector.py --name semtech_collector --port 1700
Ce script lit depuis un fichier ou des fichiers ou stdin et exécute différents sous-outils. Selon l'option sélectionnée, vous pouvez exécuter une analyse du trafic LoRaWAN, tenter de bruteforcer la AppKey, ou analyser tous les paquets reçus. Ces options peuvent être combinées.
Arguments optionnels :
Ce script lit les paquets depuis la BDD et exécute différents sous-outils.
Ensuite, chaque sous-outil sauvegardera les données de sortie dans la BDD. Voyez chaque option pour plus d'informations.
arguments optionnels :
-h, --help affiche ce message d'aide et quitte
-a, --analyze Collecte et analyse différents aspects du trafic. Si
Bruteforcer (-b) est activé, les résultats seront
corrélés
-b, --bforce Tente de bruteforcer les AppKeys avec les payloads
JoinRequests et JoinAccepts
-k KEYS, --keys KEYS [Bruteforcer] Chemin du fichier de clés. Si non fourni,
"bruteForcer/keys.txt" sera utilisé
--no-gen [Bruteforcer] Ne pas générer de clés, seulement essayer les clés
des fichiers
-p, --parse Analyse le PHYPayload en informations lisibles
--from-id FROM_ID ID du paquet à partir duquel commencer le traitement.
--to-id TO_ID Dernier ID de paquet à traiter.
Exemple :
Traite les paquets dans la BDD à partir de l'ID de paquet 1000, exécute une analyse de trafic, et tente de casser les AppKeys fournies dans my-keys.txt, mais ne génère pas dynamiquement plus de clés.
python3 LafProcessData.py -a -b -k my-keys.txt --no-gen --from-id 1000
Ces scripts fournissent les fonctionnalités orchestrées par LafProcessData.py. Ci-dessous, les alertes implémentées par LafPacketAnalysis.py et LafBruteForcer.py:
| ID | Titre | Analyseur | Niveau de risque | Description | Action recommandée |
|---|---|---|---|---|---|
| LAF-001 | DevNonce répété | LafPacketAnalysis.py | Faible | Les DevNonces de chaque appareil doivent être suffisamment aléatoires pour ne pas entrer en collision. Si le même DevNonce a été répété dans de nombreux messages, on peut déduire qu'un appareil est victime d'une attaque par rejeu. C'est-à-dire qu'un attaquant qui a capturé une JoinRequest tente de la renvoyer à la gateway. | Vérifiez comment les DevNonces sont générés : la fonction qui les génère doit être implémentée en utilisant une bibliothèque aléatoire. De plus, vous devez vous assurer que le serveur vérifie les DevNonces historiques (ils doivent être persistés en BDD), afin de ne pas accepter une ancienne JoinRequest valide précédemment envoyée par l'appareil et ainsi générer une nouvelle session. |
| LAF-002 | DevEUIs partageant la même DevAddr | LafPacketAnalysis.py | Information | Deux appareils différents peuvent avoir reçu la même DevAddr. Ce n'est pas une menace de sécurité. | Si l'appareil est activé par liaison radio (OTAA) : Vérifiez la logique utilisée pour attribuer les DevAddrs, et assurez-vous que le serveur n'attribue pas la même DevAddr à différents appareils. Si l'appareil est activé par personnalisation (ABP) : Vérifiez que la DevAddr configurée dans le firmware de l'appareil est unique dans le réseau LoRaWAN. |
| LAF-003 | Rejeu de Join | TODO | Moyen | Un paquet de join request dupliqué a été détecté, ce qui peut impliquer que le serveur LoRaWAN est victime d'une attaque par rejeu. C'est-à-dire qu'un attaquant qui a pu capturer un précédent paquet de join request le renvoie au serveur LoRaWAN, afin de tenter de générer une nouvelle session. | Vérifiez comment les DevNonces sont générés : la fonction qui les génère doit être implémentée en utilisant une bibliothèque aléatoire. De plus, vous devez vous assurer que le serveur vérifie les DevNonces historiques (ils doivent être persistés en BDD), afin de ne pas accepter une ancienne JoinRequest valide précédemment envoyée par l'appareil et ainsi générer une nouvelle session. |
| LAF-004 | Rejeu de paquets de données montantes | TODO | Moyen | Un paquet montant dupliqué a été détecté, ce qui peut impliquer que le serveur LoRaWAN est victime d'une attaque par rejeu. C'est-à-dire qu'un attaquant qui a pu capturer un paquet montant (envoyé par l'appareil) le renvoie au serveur LoRaWAN. | Sur les appareils activés par liaison radio (OTAA) : Assurez-vous que les clés de session sont régénérées après chaque réinitialisation de l'appareil ou débordement du compteur pour éviter tout effet de cette attaque. Avec les appareils activés par personnalisation (ABP) de LoRaWAN v1.0.*, rien ne peut être fait pour empêcher une attaque par rejeu, à part passer l'appareil en OTAA. |
Ce répertoire fournit un ensemble de wrappers pour la bibliothèque https://github.com/brocaar/lorawan/, qui est écrite en Golang. Ces fonctions sont implémentées par les outils.
Vous trouverez ici une série de scripts destinés à automatiser différentes tâches. Assurez-vous de leur donner la permission d'exécution si nécessaire (chmod +x votre_script pour Linux/MacOS).
Configurez facilement votre gateway et changez ses canaux à des fins d'écoute. Pour plus d'informations sur la façon de les utiliser, vous pouvez consulter le readme dans ce répertoire.
Ce script est utilisé pour installer tous les paquets logiciels nécessaires sur un Raspberry PI pour construire une gateway LoRaWAN en conjonction avec un concentrateur LoRa connecté (iC980-SPI, RHF0M301-SPI, RAK831-SPI ou tout autre par configuration manuelle).
Comme il n'est pas possible de savoir sur quelles fréquences les appareils LoRa fonctionnent, nous avons créé un script qui peut changer les canaux des gateways des bandes de fréquences US915 et EU868 à des fins d'écoute. Bien qu'il existe des gateways professionnelles et coûteuses qui supportent 32 ou 64 canaux, la plupart des gateways supportent jusqu'à 8 canaux. Ce script est destiné à fonctionner sur ce type de gateways.
Au moins dans la bande de fréquences US915, les 8 premiers canaux sont les plus utilisés. Mais il existe des implémentations bien connues qui utilisent un autre groupe de canaux, comme par exemple The Things Networks, qui utilise le second groupe (8-15) de canaux pour la communication montante.
Actuellement, nous ne supportons pas d'autres bandes de fréquences, mais avec quelques modifications de ces scripts, vous pourriez le faire par vous-même :).
TODO
Ce projet est sous licence BSD-3-Clause.
| LAF-005 | Rejeu de paquets de données descendantes | TODO | Élevé | Un paquet descendant dupliqué a été détecté. Le serveur répond à une attaque par rejeu ou génère un trafic atypique vers les appareils | Vérifiez les journaux du serveur et assurez-vous que les actions recommandées précédentes sont mises en œuvre |
| LAF-006 | Appareil ABP possible (réinitialisation du compteur et absence de join) | LafPacketAnalysis.py | Élevé | Si le compteur a été réinitialisé (revenu à 0), la DevAddr reste la même, et aucun processus de Join antérieur n'a été détecté, cela peut indiquer que l'appareil est activé par personnalisation (ABP). L'implémentation d'appareils ABP est déconseillée car aucun processus de Join n'est effectué, ce qui signifie que les clés de session restent identiques pour toujours. Un appareil qui ne change pas ses clés de session est sujet à diverses attaques telles que l'écoute ou le rejeu. | Tous les appareils activés par personnalisation (ABP) devraient être remplacés par des appareils activés par liaison radio (OTAA) si possible. L'implémentation d'appareils ABP est déconseillée. |
| LAF-007 | Compteur reçu plus petit que prévu (différent de 0) | LafPacketAnalysis.py | Moyen | Si un attaquant obtient une paire de clés de session (pour avoir volé la AppKey dans les appareils OTAA ou la AppSKey/NwkSKey dans les appareils ABP), il/elle pourrait envoyer de fausses données valides au serveur. Pour que le serveur accepte des messages usurpés, il faut que le FCnt (compteur de trame) du message soit supérieur au FCnt du dernier message envoyé. Dans un scénario où l'appareil usurpé légitime continue d'envoyer des messages, le serveur commencerait à rejeter les messages (valides) car ils auraient un FCnt plus petit. Ainsi, lorsque des messages avec une valeur de FCnt plus petite que celle attendue par le serveur LoRaWAN sont reçus, il est possible de déduire qu'une session parallèle a été établie. | Si l'appareil est activé par liaison radio (OTAA), changez sa AppKey car elle a probablement été compromise. S'il est activé par personnalisation, changez sa AppSKey et NwkSKey. De plus, assurez-vous que le serveur LoRaWAN est à jour et n'accepte pas les messages dupliqués. |
| LAF-008 | Mot de passe cracké avec JoinRequest | LafBruteforcer.py | Élevé | Il a été possible de déchiffrer un message JoinRequest en utilisant une AppKey connue. | Utilisez des AppKeys différentes de celles fournies par les vendeurs ou utilisez des clés plus aléatoires. |
| LAF-009 | Mot de passe cracké | LafBruteforcer.py | Élevé | La AppKey de l'appareil a été trouvée en essayant une chaîne bien connue ou non aléatoire. Elle a été déchiffrée en utilisant une paire de messages join (Request et Accept). | Utilisez un générateur de clé aléatoire pour la AppKey au lieu d'utiliser celles fournies par les vendeurs. De plus, n'attribuez pas la même AppKey à plus d'un appareil et ne générez pas les AppKeys avec une logique prévisible (par exemple, valeurs incrémentales, inversion de certains octets, etc.) |
| LAF-010 | La gateway a changé d'emplacement | LafPacketAnalysis.py | Moyen | Si la gateway n'est pas censée changer d'emplacement. Elle peut avoir été volée, déplacée, ou une fausse gateway peut tenter d'usurper la gateway légitime. | Assurez-vous que la gateway n'a pas été compromise, physiquement ou logiquement. |