
Этот проект предназначен для предоставления набора инструментов для создания, разбора, отправки, анализа и взлома набора пакетов LoRaWAN с целью аудита или тестирования на проникновение безопасности инфраструктуры LoRaWAN.
Развертывания IoT продолжают расти, и значительная часть этого роста состоит из миллионов LPWAN-сенсоров (маломощные сети с большим радиусом действия), развернутых в сотнях городов (Умные города) по всему миру, а также в промышленности и домах. Одной из наиболее используемых LPWAN-технологий является LoRa, для которой LoRaWAN является сетевым стандартом (MAC-уровень). LoRaWAN — это безопасный протокол со встроенным шифрованием, но проблемы реализации и уязвимости влияют на безопасность большинства текущих развертываний.
Этот проект предоставляет набор инструментов для создания, разбора, отправки, анализа и взлома набора пакетов LoRaWAN с целью аудита или пентеста безопасности инфраструктуры LoRaWAN.
Ниже представлена структура этого репозитория:
|-- 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
Мы предоставляем различные варианты для запуска вашей среды аудита LoRaWAN:
tools/, чтобы избежать проблем с пробросом портов Docker.localhost. Инструкции по настройке Docker ниже.Эти инструкции помогут вам получить копию проекта и его зависимостей на вашем локальном компьютере. Команды ниже предназначены для окружения на основе Debian:
Клонируйте этот репозиторий: git clone --recurse-submodules https://github.com/IOActive/laf.git
Установите python3:
sudo apt-get updatesudo apt-get install python3.6Загрузите и установите зависимости Python:
sudo pip3 install paho-mqtt && sudo pip3 install sqlalchemy && sudo pip3 install psycopg2-binary &&sudo pip3 install python-dateutilУстановите PYTHONPATH и ENVIRONMENT
cd laf && export PYTHONPATH=$(pwd) && export ENVIRONMENT='DEV'Установите и настройте golang:
cd ~/Downloadssudo tar -C /usr/local -xvzf YOUR_GOLANG_FILEexport PATH=$PATH:/usr/local/go/binВот и всё!
Этот подход позволяет избежать установки зависимостей и запускает базу данных PostgreSQL, где инструменты сохраняют пакеты и данные. Контейнеры:
Шаги:
git clone https://github.com/IOActive/laf.gitcd laf/docker-compose up --builddocker exec -ti laf_tools_1 /bin/bashВы можете проверить данные в БД с помощью pgAdmin:
Сначала откройте pgAdmin:
Затем добавьте сервер:
Здесь приведено описание директорий и инструментов/функций внутри них.
Основная цель инструментов в этой папке — облегчить выполнение пентеста инфраструктуры LoRaWAN.
Этот инструмент предназначен для отправки восходящих пакетов (на сетевой сервер или gatewayBridge, в зависимости от инфраструктуры) или нисходящих пакетов (на packet-forwarder). Опционально пакеты могут быть фаззированы, а также может быть вычислен корректный MIC.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
--lcl-port LCL_PORT Исходный порт, например --lcl-port=623.
--timeout TIMEOUT Время в секундах между отправкой каждого пакета. По умолчанию 1с. За это время отправитель будет ожидать ответы.
--repeat Отправить сообщение несколько раз
--fuzz-out FUZZ_OUT [FUZZ_OUT ...]
Фаззить данные, отправляемые на целевой порт (см. режимы фаззинга в utils/fuzzer.py), например --fuzz-out 1 2.
--key KEY Укажите ключ (в шестнадцатеричном формате, всего 32 символа / 16 байт) для подписи пакетов (вычисление и добавление нового MIC). Обратите внимание, что для JoinRequests это должен быть AppKey, а для Data-пакетов — NwkSKey. Это не может быть проверено заранее этой программой. Например: 00112233445566778899AABBCCDDEEFF
-a DEVADDR, --devaddr DEVADDR
DeviceAddress для подмены, заданный в шестнадцатеричном формате (всего 8 символов), например AABB0011.
--fcnt FCNT Счетчик кадров, который будет установлен в данном пакете данных. Это не сработает для JoinRequest/JoinAccept, так как эти пакеты не имеют fCnt
Обязательные аргументы:
--dst-ip DST_IP IP-адрес назначения, например --dst-ip 192.168.3.101.
--dst-port DST_PORT Порт назначения, например --dst-port 623.
--data DATA UDP-пакет. Также можно добавить больше пакетов в массив "data" в конце этого скрипта. Пакет должен быть байтовой строкой (вам нужно экранировать двойные кавычки). ***ПРИМЕР*** с форматом packet_forwarder: --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=\"}]}'" ***ПРИМЕР*** с использованием формата gatevice [GV] в немедленном режиме, BW125 и частоте 902.3: "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}'"
Пример:
Отправлять один пакет каждые 2 секунды на (localhost, 10001) с порта 10000, фаззируя случайно MIC и 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=\"}]}'"
Этот UDP-прокси в основном предназначен для размещения между серией шлюзов (packet_forwarders) и сетевым сервером или gatewayBridge, в зависимости от оцениваемой инфраструктуры. Он также предлагает возможность фаззить данные в нужном направлении (восходящем или нисходящем).
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
--collector-port COLLECTOR_PORT
Порт сбора данных от packet forwarder, например --collector-port 1701. См. auditing/datacollectors/PacketForwarderCollector.py
--collector-ip COLLECTOR_IP
IP-адрес коллектора данных packet forwarder. По умолчанию localhost. Например --collector-ip 192.168.1.1. См. auditing/datacollectors/PacketForwarderCollector.py
--fuzz-in FUZZ_IN [FUZZ_IN ...]
Фаззить данные, отправляемые на dst-port в заданных режимах (см. режимы фаззинга в utils/fuzzer.py), например --fuzz-in 1 2 ...
--fuzz-out FUZZ_OUT [FUZZ_OUT ...]
Фаззить данные, отправляемые на (исходный) порт в заданных режимах (см. режимы фаззинга в utils/fuzzer.py), например --fuzz-out 1 2 ...
-k KEY, --key KEY Укажите устройство AppSKey (в шестнадцатеричном формате, всего 32 символа / 16 байт) для расшифровки его FRMPayload и вывода в открытом тексте. Также можно указать AppKey, если вы хотите расшифровать данный Join Accept. Например: 00112233445566778899AABBCCDDEEFF
-p PATH, --path PATH Путь к файлу для сохранения данных. Если не указан, данные сохраняться не будут.
--no-log Не выводить UDP-пакеты в консоль
--no-parse Не парсить PHYPayload. Если выбрана эта опция, библиотеки Golang из /lorawanwrapper/ не будут импортироваться (компиляция библиотек golang не требуется)
Обязательные аргументы:
--port PORT Локальный порт для прослушивания, например --port 623.
--dst-ip DST_IP IP-адрес хоста назначения, например --dst-ip 192.168.3.101.
--dst-port DST_PORT Порт хоста назначения, например --dst-port 623.
Пример:
Отправлять пакеты, полученные на порте 1234, на (localhost, 1235) и обратно. Пакеты, полученные на порте, будут фаззированы (devNonce будет случайно изменен) и перенаправлены на (localhost, 1235).
python3 UdpProxy.py --port 1234 --dst-ip 127.0.0.1 --dst-port 1235 --fuzz-in 9
Этот TCP-прокси в основном предназначен для размещения между сетевым сервером и MQTT-брокерами. Он также предлагает возможность фаззить данные.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
--fuzz-in FUZZ_IN [FUZZ_IN ...]
Фаззить данные, отправляемые на dst-port в заданных режимах (см. режимы фаззинга в utils/fuzzer.py)
Обязательные аргументы:
--lcl-port LCL_PORT Локальный порт для прослушивания, например --lcl-port=623.
--dst-ip DST_IP IP-адрес хоста назначения, например --dst-ip=192.168.3.101.
--dst-port DST_PORT Порт хоста назначения, например --dst-port=623.
Пример:
Отправлять и получать данные между (localhost, 1884) и (localhost, 1883):
python3 TcpProxy.py --lcl-port 1884 --dst-ip 127.0.0.1 --dst-port 1883
Эта папка содержит набор скриптов для разбора, создания, брутфорса и т.д. пакетов LoRaWAN.
Этот скрипт принимает JoinAccept или JoinRequest в Base64 и пытается расшифровать его AppKey с помощью набора возможных ключей, которые могут быть предоставлены в файле или сгенерированы на лету.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
-k KEYS, --keys KEYS Файл, содержащий список ключей, разделенных \n. По умолчанию будет использоваться /auditing/analyzers/bruteForcer/keys.txt
--dont-generate Выберите эту опцию, если вы не хотите генерировать ключи на лету со следующими комбинациями: 1- Объединить первый байт и последние пятнадцать байт. Например: AABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 2- Объединить четные и нечетные позиции байтов одинаково. Например: AABBAABBAABBAABBAABBAABBAABBAABB 3- Первые 14 байт равны 00 и объединить последние 2. Например: 0000000000000000000000000000BA01
Обязательные аргументы:
-a ACCEPT, --accept ACCEPT
Join Accept в формате Base64 для брутфорса. Например: -a
IHvAP4MXo5Qo6tdV+Yfk08o=
-r REQUEST, --request REQUEST
Join Request в формате Base64 для брутфорса. Например:
-r AMQAAAAAhQAAAgAAAAAAAADcYldcgbc=
Пример:
Взломать JoinRequest с набором ключей из my-keys.txt и также сгенерировать около 200000 ключей динамически.
python3 BruteForcer.py -a IHvAP4MXo5Qo6tdV+Yfk08o= -r AMQAAAAAhQAAAgAAAAAAAADcYldcgbc= -k ./my-keys.txt
Этот скрипт принимает PHYPayload в Base64 и ключ, который может быть NwkSKey или AppKey в зависимости от типа пакета, и генерирует новый MIC.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
--jakey JAKEY [Только для JoinAccept]. Укажите ключ, использованный для шифрования JoinAccept ранее (в шестнадцатеричном формате, всего 32 символа / 16 байт). Это не может быть проверено заранее этой программой. Например: 00112233445566778899AABBCCDDEEFF. Пример корректного ключа для JoinAccept "IB1scNmwJRA32RfMbvwe3oI=" это "f5a3b185dfe452c8edca3499abcd0341"
Обязательные аргументы:
-d DATA, --data DATA Данные в Base64 для подписи. Например: -d
AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
-k KEY, --key KEY Укажите новый ключ (в шестнадцатеричном формате, всего 32 символа / 16 байт) для подписи пакетов (вычисление и добавление нового MIC). Обратите внимание, что для JoinRequest/JoinAccept это должен быть AppKey, а для Data-пакетов — NwkSKey. Это не может быть проверено заранее этой программой. Например: 00112233445566778899AABBCCDDEEFF
Пример:
Подписать данный PHYPayload с помощью AppKey 00112233445566778899AABBCCDDEEFF.
python3 MicGenerator.py -d AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU= -k 00112233445566778899AABBCCDDEEFF
Этот скрипт принимает JSON-пакет LoRaWAN и преобразует его в Base64. Он выполняет обратное действие по сравнению с packetParser.py, поэтому вывод одного скрипта может быть использован в другом и наоборот.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
-k KEY, --key KEY Укажите устройство AppSKey или AppKey (в шестнадцатеричном формате, всего 32 символа / 16 байт) для шифрования FRMPayload или Join Accept. Например: F5A3B185DFE452C8EDCA3499ABCD0341
--nwkskey NWKSKEY Укажите сетевой сессионный ключ, если вы хотите сгенерировать пакет данных с корректным MIC.
Обязательные аргументы:
-j JSON, --json JSON JSON-объект для разбора. Например: -j '{"mhdr":
{"mType":"JoinRequest","major":"LoRaWANR1"},"macPayloa
d":{"joinEUI":"55d239ac716f234d","devEUI":"b827eb891cf
50003","devNonce":51639},"mic":"7005c4a5"}'
Пример:
Получить PHYPayload JoinRequest в Base64 из заданного JSON с переданными значениями.
python3 PacketCrafter.py -j '{"mhdr":{"mType":"JoinRequest","major":"LoRaWANR1"},"macPayload":{"joinEUI":"55d239ac716f234d","devEUI":"b827eb891cf50003","devNonce":51639},"mic":"7005c4a5"}'
Этот скрипт разбирает и выводит один PHYPayload LoRaWAN в Base64. Он выполняет обратное действие по сравнению с packetCrafter.py, поэтому вывод одного скрипта может быть использован в другом и наоборот.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
-k KEY, --key KEY Укажите устройство AppKey или AppSKey в зависимости от типа пакета для расшифровки (join accept или data packet). Должен быть в шестнадцатеричном формате, всего 32 символа / 16 байт. Например: 00112233445566778899AABBCCDDEEFF
Обязательные аргументы:
-d DATA, --data DATA Данные в Base64 для разбора. Например: -d
AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
Пример:
Получить JoinRequest в формате JSON из примера выше.
python3 PacketParser.py -d AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU=
Этот скрипт принимает JoinAccept и JoinRequest в Base64, а также AppKey для генерации сессионных ключей. Пример использования:
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
Обязательные аргументы:
-a JACCEPT, --jaccept JACCEPT
JoinAccept в формате base64
-r JREQUEST, --jrequest JREQUEST
JoinRequest в формате base64
-k KEY, --key KEY Укажите устройство AppKey (в шестнадцатеричном формате, всего 32 символа / 16 байт). Например:
00112233445566778899AABBCCDDEEFF
Пример:
Получить AppSKey и NwkSKey с помощью следующих данных соединения.
python3 SessionKeysGenerator.py -r AE0jb3GsOdJVAwD1HInrJ7i3yXAFxKU= -a IB1scNmwJRA32RfMbvwe3oI= -k f5a3b185dfe452c8edca3499abcd0341
Это вспомогательные функции, используемые UdpSender.py и UdpProxy.py. В Fuzzer.py можно увидеть реализованные режимы фаззинга.
Общая цель этой папки — сбор пакетов LoRaWAN и анализ различных аспектов трафика, а также попытка подбора ключей для брутфорса AppKey.
Эта папка содержит набор скриптов, которые принимают пакеты LoRaWAN из различных источников (например, шлюз packet_forwarder, The Things Network и т.д.) и сохраняют их в файлы в стандартном формате. Эти файлы должны быть позже обработаны скриптом /auditing/analyzers/LafProcessData.py для выполнения различных под-инструментов.
Этот скрипт подключается к MQTT-брокеру, получает все топики и сохраняет сообщения в файл в указанном поле. Имя файла состоит из даты запуска этого скрипта.
Необязательные аргументы:-h, --help показать это справочное сообщение и выйти --collector-id COLLECTOR_ID Идентификатор сборщика данных. Этот ID будет связан с пакетами, сохранёнными в БД. например --id 1 --organization-id ORGANIZATION_ID Идентификатор сборщика данных. Этот ID будет связан с пакетами, сохранёнными в БД. например --id 1 --topics TOPICS [TOPICS ...] Список тем, на которые вы хотите подписаться, разделённых пробелами. Если ничего не указано, по умолчанию будет "#".
Обязательные аргументы:
--ip IP IP-адрес MQTT-брокера, например --ip 192.168.3.101.
--port PORT Порт MQTT-брокера, например --port 623.
Пример:
Подключиться к MQTT-брокеру с IP 200.200.200.200 на порту по умолчанию (1883).
python3 GenericMqttCollector.py --ip 200.200.200.200 --port 1883
Этот скрипт подключается к MQTT-брокеру loraserver.io и сохраняет сообщения в БД. Необходимо указать уникальный ID сборщика, а также можно указать темы, на которые вы хотите подписаться.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
--port PORT Порт MQTT-брокера, например --port 623. По умолчанию 1883.
--collector-id COLLECTOR_ID
Идентификатор сборщика данных. Этот ID будет
связан с пакетами, сохранёнными в БД. например --id 1
--organization-id ORGANIZATION_ID
Идентификатор сборщика данных. Этот ID будет
связан с пакетами, сохранёнными в БД. например --id 1
--topics TOPICS [TOPICS ...]
Список тем, на которые вы хотите подписаться, разделённых
пробелами. Если ничего не указано, по умолчанию будет "#".
Обязательные аргументы:
--ip IP IP-адрес MQTT-брокера, например --ip 192.168.3.101.
Этот скрипт принимает UDP-пакеты от UDP-прокси в формате packet_forwarder шлюза и сохраняет их.
Необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
--collector-id COLLECTOR_ID
Идентификатор сборщика данных. Этот ID будет
связан с пакетами, сохранёнными в БД. например --id 1
--organization-id ORGANIZATION_ID
Идентификатор сборщика данных. Этот ID будет
связан с пакетами, сохранёнными в БД. например --id 1
Обязательные аргументы:
-n NAME, --name NAME Уникальная строковая метка сборщика данных. например
--name semtech_collector
-p PORT, --port PORT Порт для прослушивания UDP-пакетов. --port 1702.
Пример:
Записать данные между шлюзом, отправляющим на локальный порт 1700, и сетевым xserver, прослушивающим (localhost, 1701). Сохранить данные в каталог ./.
python3 PacketForwarderCollector.py --name semtech_collector --port 1700
Этот скрипт читает данные из файла (файлов) или stdin и запускает различные вспомогательные инструменты. В зависимости от выбранной опции можно выполнить анализ LoRaWAN-трафика, попытаться подобрать AppKey методом грубой силы или разобрать все полученные пакеты. Эти опции можно комбинировать.
Необязательные аргументы:
Этот скрипт извлекает пакеты из БД и запускает различные вспомогательные инструменты.
Затем каждый вспомогательный инструмент сохраняет выходные данные в БД. См. каждую опцию для
получения дополнительной информации.
необязательные аргументы:
-h, --help показать это справочное сообщение и выйти
-a, --analyze Собрать и проанализировать различные аспекты трафика. Если
активирован BruteForcer (-b), результаты будут
скоррелированы.
-b, --bforce Попытаться подобрать AppKeys с помощью JoinRequests и
JoinAccepts payloads.
-k KEYS, --keys KEYS [Bruteforcer] Путь к файлу с ключами. Если не указан,
будет использоваться "bruteForcer/keys.txt".
--no-gen [Bruteforcer] Не генерировать ключи, только пробовать ключи из
файлов.
-p, --parse Разобрать PHYPayload в читаемую информацию.
--from-id FROM_ID ID пакета, с которого начать обработку.
--to-id TO_ID ID последнего обрабатываемого пакета.
Пример:
Обработать пакеты в БД, начиная с ID пакета 1000, выполнить анализ трафика и попытаться взломать AppKeys из файла my-keys.txt, но не генерировать динамически дополнительные ключи.
python3 LafProcessData.py -a -b -k my-keys.txt --no-gen --from-id 1000
Эти скрипты предоставляют функциональность, управляемую LafProcessData.py. Ниже приведены оповещения, реализованные в LafPacketAnalysis.py и LafBruteForcer.py:
Этот каталог содержит набор обёрток для библиотеки https://github.com/brocaar/lorawan/, написанной на Golang. Эти функции реализуются инструментами.
Здесь вы найдёте ряд скриптов, предназначенных для автоматизации различных задач. При необходимости дайте им права на выполнение (chmod +x your_script для Linux/MacOS).
Простая настройка шлюза и переключение его каналов для целей сниффинга. Для получения дополнительной информации об их использовании вы можете прочитать readme в этом каталоге.
Этот скрипт используется для установки всех необходимых пакетов программного обеспечения на Raspberry PI для создания шлюза LoRaWAN в сочетании с подключённым концентратором LoRa (iC980-SPI, RHF0M301-SPI, RAK831-SPI или любым другим при ручной настройке).
Поскольку невозможно узнать, на каких частотах работают устройства LoRa, мы создали скрипт, который может переключать каналы шлюзов из частотных диапазонов US915 и EU868 для целей сниффинга. Хотя существуют профессиональные и дорогие шлюзы, поддерживающие 32 или 64 канала, большинство шлюзов поддерживают до 8 каналов. Этот скрипт предназначен для работы на таких шлюзах.
По крайней мере, в частотном диапазоне US915 первые 8 каналов используются наиболее часто. Но есть известные реализации, использующие другую группу каналов, например The Things Networks, которые используют вторую группу (8-15) каналов для uplink-связи.
В настоящее время мы не поддерживаем другие частотные диапазоны, но с небольшими изменениями этих скриптов вы сможете сделать это самостоятельно :).
TODO
Этот проект лицензирован по лицензии BSD-3-Clause.
export GOPATH="$HOME/go"Скомпилируйте библиотеку go:
cd laf/lorawanwrapper/utilsgo build -o lorawanWrapper.so -buildmode=c-shared jsonUnmarshaler.go lorawanWrapper.go micGenerator.go sessionKeysGenerator.go hashGenerator.goВ зависимости от того, какую БД вы хотите использовать:
a. PostgreSQL: Следуйте инструкциям «Установка LAF с помощью Docker» до 3-го шага.
b. SQLite:
cd laf/auditing/db__init__.py с помощью вашего текстового редактора, закомментируйте строки для использования с PostgreSQL (подключение к БД и переменные окружения) и раскомментируйте строку для использования с sqlite.| ID | Заголовок | Анализатор | Уровень риска | Описание | Рекомендуемые действия |
|---|
| LAF-001 | DevNonce повторён | LafPacketAnalysis.py | Низкий | DevNonce для каждого устройства должны быть достаточно случайными, чтобы не было коллизий. Если один и тот же DevNonce повторялся во многих сообщениях, можно сделать вывод, что устройство подвергается атаке повторного воспроизведения. То есть злоумышленник захватил JoinRequest и пытается снова отправить его шлюзу. | Проверьте, как генерируются DevNonce: функция их генерации должна использовать случайную библиотеку. Кроме того, убедитесь, что сервер проверяет исторические DevNonce (они должны сохраняться в БД) и не принимает старый валидный JoinRequest, ранее отправленный устройством, чтобы не генерировать новую сессию. |
| LAF-002 | DevEUI, использующие один и тот же DevAddr | LafPacketAnalysis.py | Информация | Два разных устройства могут получить один и тот же DevAddr. Это не является угрозой безопасности. | Если устройство активируется по воздуху (OTAA): проверьте логику назначения DevAddr и убедитесь, что сервер не назначает одинаковый DevAddr разным устройствам. Если устройство активируется по персонализации (ABP): проверьте DevAddr, настроенный в прошивке устройства, чтобы он был уникальным в сети LoRaWAN. |
| LAF-003 | Повторное воспроизведение Join | TODO | Средний | Обнаружен дубликат пакета join request, что может означать, что сервер LoRaWAN подвергается атаке повторного воспроизведения. То есть злоумышленник, возможно, захватил предыдущий пакет join request и снова отправляет его серверу LoRaWAN, чтобы попытаться создать новую сессию. | Проверьте, как генерируются DevNonce: функция их генерации должна использовать случайную библиотеку. Кроме того, убедитесь, что сервер проверяет исторические DevNonce (они должны сохраняться в БД) и не принимает старый валидный JoinRequest, ранее отправленный устройством, чтобы не генерировать новую сессию. |
| LAF-004 | Повторное воспроизведение uplink-пакетов данных | TODO | Средний | Обнаружен дубликат uplink-пакета, что может означать, что сервер LoRaWAN подвергается атаке повторного воспроизведения. То есть злоумышленник, возможно, захватил uplink-пакет (отправленный устройством) и снова отправляет его серверу LoRaWAN. | Для устройств с активацией по воздуху (OTAA): убедитесь, что сессионные ключи перегенерируются после каждого сброса устройства или переполнения счётчика, чтобы избежать влияния этой атаки. Для устройств с активацией по персонализации (ABP) в LoRaWAN v1.0.* ничего нельзя сделать для предотвращения атаки повторного воспроизведения, кроме перевода устройства на OTAA. |
| LAF-005 | Повторное воспроизведение downlink-пакетов данных | TODO | Высокий | Обнаружен дубликат downlink-пакета. Сервер отвечает на атаку повторного воспроизведения или генерирует нетипичный трафик к устройствам. | Проверьте серверные логи и убедитесь, что реализованы предыдущие рекомендуемые действия. |
| LAF-006 | Возможное ABP-устройство (сброс счётчика и отсутствие join) | LafPacketAnalysis.py | Высокий | Если счётчик был сброшен (вернулся к 0), DevAddr остался тем же, и не обнаружено предыдущего процесса Join, это может означать, что устройство активировано по персонализации (ABP). Реализация ABP-устройств не рекомендуется, поскольку не выполняется процесс join, что означает, что сессионные ключи остаются неизменными навсегда. Устройство, которое не меняет свои сессионные ключи, подвержено различным атакам, таким как перехват или повторное воспроизведение. | Все устройства с активацией по персонализации (ABP) должны быть заменены на устройства с активацией по воздуху (OTAA), если это возможно. Реализация ABP-устройств не рекомендуется. |
| LAF-007 | Получен меньший счётчик, чем ожидалось (отличен от 0) | LafPacketAnalysis.py | Средний | Если злоумышленник получает пару сессионных ключей (например, украв AppKey в устройствах OTAA или AppSKey/NwkSKey в устройствах ABP), он сможет отправлять поддельные валидные данные на сервер. Чтобы сервер принял поддельные сообщения, требуется, чтобы FCnt (счётчик кадров) сообщения был выше FCnt последнего отправленного сообщения. В сценарии, где исходное поддельное устройство продолжает отправлять сообщения, сервер начнёт отбрасывать (валидные) сообщения, так как их FCnt будет меньше. Следовательно, когда принимаются сообщения со значением FCnt меньше ожидаемого сервером LoRaWAN, можно сделать вывод, что установлена параллельная сессия. | Если устройство активировано по воздуху (OTAA), измените его AppKey, так как он, вероятно, скомпрометирован. Если оно активировано по персонализации, измените его AppSKey и NwkSKey. Кроме того, убедитесь, что сервер LoRaWAN обновлён и не принимает дублирующиеся сообщения. |
| LAF-008 | Пароль взломан с помощью JoinRequest | LafBruteforcer.py | Высокий | Удалось расшифровать сообщение JoinRequest, используя известный AppKey. | Используйте другие AppKeys, отличные от предоставленных поставщиками, или используйте более случайные ключи. |
| LAF-009 | Пароль взломан | LafBruteforcer.py | Высокий | AppKey устройства был найден путём перебора с использованием известных или неслучайных строк. Удалось расшифровать с помощью пары сообщений join (Request и Accept). | Используйте генератор случайных ключей для AppKey вместо использования ключей от поставщиков. Кроме того, не устанавливайте одинаковый AppKey для нескольких устройств и не генерируйте AppKeys по предсказуемой логике (например, инкрементные значения, перестановка определённых байтов и т.д.). |
| LAF-010 | Шлюз изменил местоположение | LafPacketAnalysis.py | Средний | Если шлюз не должен менять своё местоположение. Возможно, он был украден, перемещён или поддельный шлюз пытается выдать себя за легитимный. | Убедитесь, что шлюз не был скомпрометирован ни физически, ни логически. |