Урок 5. Счётчик кадров: replay против ребута
Урок 5. Счётчик кадров: replay-атака против ребута лисы
Паспорт урока
| Параметр | Значение |
|---|---|
| Место в модуле | FoxHunter, модуль 2 «Безопасность протокола», урок 5 из 6 |
| Длительность | 45 мин |
| Возраст | 13–16 |
| Оборудование | Лиса, охотник, фальшлиса с заряженным CAPTURED_PACKET |
| Предварительные знания | Урок 4: подпись, модель угроз |
| Предмет | Информатика, информационная безопасность |
[RAW] и вписать 9 байт в CAPTURED_PACKET в скетче фальшлисы. Без этого урок не состоится. Сделать накануне, не на занятии.Образовательные цели
| № | Цель | Блум | Миллер | Чем доказывается |
|---|---|---|---|---|
| 5.1 | Объяснять, почему подпись не защищает от повтора | понимать | знает как | Ответ: подпись верна, а пакет чужой |
| 5.2 | Различать «честный ребут» и «replay-атаку» по динамике счётчика | анализировать | знает как | Разбор: застывший против растущего |
| 5.3 | Обосновывать требование нескольких пакетов для ресинхронизации | оценивать | знает как | Ответ про RESYNC_N |
| 5.4 | Проектировать правило принятия решения при неоднозначных данных | создавать | показывает как | Собственный алгоритм различения |
Сценарий занятия (45 минут)
| Время | Блок | Что делает педагог | Что делают ученики |
|---|---|---|---|
| 0–5 | Retrieval | Мини-квиз по уроку 4 | Пишут, разбор |
| 5–10 | Вызов | «Подпись есть. Можно ли всё же обмануть?» | Выдвигают гипотезы |
| 10–18 | Атака 2 | Запускает replay: подпись верна, но отвергнут | Наблюдают, объясняют |
| 18–28 | Кульминация | Перезагружает настоящую лису — её приняли | Формулируют различие |
| 28–38 | Теория | Монотонный счётчик, ресинхронизация | Разбирают алгоритм |
| 38–45 | Практика и ДЗ | Задачи | Решают |
Блок 1. Вызов (5–10 мин)
Постановка
«На прошлом уроке мы защитились подписью: подделать пакет без ключа нельзя. Вопрос: можно ли обмануть охотника, не подделывая ничего?»
Дать классу 3 минуты на гипотезы. Если никто не догадался, подсказка:
«А обязательно ли злоумышленнику создавать пакет? Что ещё можно сделать с пакетом, который уже летал в эфире?»
Ожидаемый ответ: записать и послать снова.
Ключевая мысль
Подпись подтверждает: «этот пакет когда-то создал владелец ключа». Она ничего не говорит о том, когда. Записанный пакет остаётся математически безупречным вечно.
Блок 2. Атака 2: повтор (10–18 мин)
Демонстрация
Педагог запускает фальшлису в режиме атаки 2. Она шлёт заранее пойманный настоящий пакет — байт в байт, без изменений.
Что увидят дети
[SEC] REJECT ... SIG:OK FCNT:REPLAYОбратить внимание класса на важное: SIG:OK. Подпись прошла проверку! Пакет подлинный — и всё равно отвергнут.
Разговор
«Подпись верна. Охотник признаёт: этот пакет действительно создала настоящая лиса. И всё-таки не принимает. Почему?»
| Гипотеза | Реакция |
|---|---|
| «Он видит, что пакет повторяется» | Верно. Но по какому признаку? |
| «Данные одинаковые» | А если бы менялось напряжение батареи? Нужен более надёжный признак |
| «Счётчик не растёт» | Точный ответ. Записать на доску |
Что показать в мониторе
Вывести рядом два потока:
| Настоящая лиса | Фальшлиса (replay) |
|---|---|
fcnt = 1050 | fcnt = 127 |
fcnt = 1051 | fcnt = 127 |
fcnt = 1052 | fcnt = 127 |
| растёт | застыл |
Блок 3. Кульминация урока (18–28 мин)
Это методически сильнейший момент всего модуля. Провести его аккуратно, не торопясь.
Постановка ловушки
«Итак, правило простое: счётчик должен расти. Если пришёл пакет со счётчиком меньше запомненного — это атака, отвергаем. Согласны?»
Класс обычно соглашается.
«Хорошо. А теперь я просто выключу и включу настоящую лису.»
Что произойдёт
После перезагрузки fcnt настоящей лисы обнуляется и начинается с малых значений: 1, 2, 3… Охотник помнит 1052.
По только что принятому правилу настоящую лису надо отвергнуть. Но она честная!
Постановка проблемы
На доску две ситуации:
| Replay-атака | Ребут настоящей лисы | |
|---|---|---|
| Подпись | верна | верна |
fcnt меньше запомненного | да | да |
| Кто это на самом деле | злоумышленник | честная лиса |
«Данные выглядят одинаково. Как отличить?»
Разгадка
Дать классу подумать 3–4 минуты. Направляющий вопрос:
«Посмотрите не на одно значение, а на несколько подряд. Что делает счётчик в каждом случае?»
| Replay-атака | Ребут |
|---|---|
| 127, 127, 127, 127… | 1, 2, 3, 4… |
| застыл | растёт |
Формулировка, которую надо получить от детей:
Различает не значение счётчика, а его динамика. Атака переигрывает один и тот же пакет — счётчик стоит. Перезагруженная лиса продолжает работать — счётчик растёт, просто с нуля.
Один и тот же механизм — монотонный счётчик — отвечает на два разных вопроса, если смотреть на него во времени, а не в моменте.
Блок 4. Теория (28–38 мин)
4.1. Базовый уровень (13–14 лет)
Через нумерацию писем.
Друг присылает тебе письма и нумерует их: №1, №2, №3. Ты помнишь последний номер. Приходит письмо №2 — ты уже такое получал, значит кто-то подсунул копию старого.
Но однажды друг переехал и начал нумерацию заново: №1, №2, №3. Первое такое письмо выглядит как копия. А потом ты видишь: номера растут. Копия так себя не ведёт — она всегда одна и та же.
4.2. Углублённый уровень (14–16 лет)
Шаг 1. Монотонный счётчик кадров.
fcnt — 4-байтовое число, увеличивающееся на единицу с каждым отправленным пакетом. Приёмник хранит последнее принятое значение и применяет правило:
Это защита от повтора: записанный пакет несёт старое значение, и правило его отсекает.
Шаг 2. Проблема перезагрузки.
Счётчик хранится в оперативной памяти. При сбросе питания он обнуляется. Возможные решения:
| Решение | Плюс | Минус |
|---|---|---|
Хранить fcnt в энергонезависимой памяти | переживает ребут | износ памяти при частой записи |
| Разрешить сброс на сервере | просто | открывает дыру для replay |
| Распознавать ребут по динамике | не требует памяти | сложнее логика, есть окно уязвимости |
В нашем стенде выбран третий путь — он же самый поучительный.
Шаг 3. Алгоритм ресинхронизации.
принят пакет, подпись OK
если fcnt > сохранённого:
принять, обновить сохранённый
иначе:
# подозрительно: replay или ребут?
если это первый такой пакет:
запомнить кандидата, счётчик_подряд = 1
иначе если fcnt == предыдущий_кандидат + 1:
счётчик_подряд += 1
если счётчик_подряд >= RESYNC_N:
[RESYNC] — это ребут, принять лису обратно
иначе:
[SEC] REJECT ... FCNT:REPLAYШаг 4. Зачем требовать несколько пакетов подряд.
Одного растущего пакета мало. Злоумышленник, записавший два последовательных пакета лисы, может проигрывать их по очереди — счётчик будет «расти» на единицу. Требование RESYNC_N подряд растущих значений поднимает планку: атакующему нужно записать целую серию.
Это не абсолютная защита, а повышение стоимости атаки — типичный и честный приём в безопасности.
Честная оговорка для сильных учеников. Логика ресинхронизации по определению создаёт окно уязвимости: если злоумышленник запишет RESYNC_N последовательных пакетов, он сможет заставить приёмник принять свою серию.
Промышленное решение — хранение счётчика в энергонезависимой памяти плюс проверка на стороне сервера. В LoRaWAN сброс счётчика разрешают только для отладочных ABP-устройств и явно считают это ослаблением защиты.
Признание ограничений собственного решения — обязательная часть инженерной работы.
Шаг 5. Где встречается тот же механизм.
| Система | Как называется | Назначение |
|---|---|---|
| LoRaWAN | FCntUp / FCntDown | защита кадров от повтора |
| TLS (HTTPS) | sequence number | защита записей от повтора |
| IPsec | anti-replay window | скользящее окно принятых номеров |
| Платёжные системы | номер транзакции | защита от повторного списания |
| Автомобильные брелоки | rolling code | защита от записи и повтора сигнала |
Блок 5. Практические задачи (38–45 мин)
Уровень A (13–14 лет)
A1. Охотник напечатал SIG:OK FCNT:REPLAY. Что это значит?
Ответ
Подпись верна — пакет действительно был когда-то создан настоящей лисой. Но счётчик кадров не вырос, значит это запись старого пакета, воспроизведённая заново. Охотник отвергает.
A2. Почему подпись не спасает от повтора?
Ответ
Подпись подтверждает авторство и целостность, но не содержит информации о времени. Записанный пакет остаётся подлинным — злоумышленнику не нужно ничего подделывать, он просто повторяет чужое.
A3. Лиса перезагрузилась, её счётчик пошёл с единицы. Почему охотник не считает её атакой?
Ответ
Охотник смотрит не на одно значение, а на несколько подряд. У перезагруженной лисы счётчик растёт (1, 2, 3), у повтора — стоит на месте. Увидев несколько растущих значений, охотник понимает, что это честный перезапуск, и ресинхронизируется.
Уровень B (14–15 лет)
B1. Охотник помнит fcnt = 500. Приходят пакеты со значениями 3, 4, 5, 6 (подпись верна во всех). Как поступит охотник при RESYNC_N = 3?
Решение
fcnt = 3: меньше сохранённого 500 → подозрение, запоминается кандидат, счётчик подряд = 1;fcnt = 4: равен кандидат + 1 → счётчик подряд = 2;fcnt = 5: → счётчик подряд = 3, достигнутRESYNC_N→[RESYNC], лиса принята, сохранённое значение обновляется на 5;fcnt = 6: больше сохранённого 5 → принимается обычным образом.
Вывод: охотник опознал перезагрузку за три пакета, то есть менее чем за секунду при BEACON_MS = 300.
B2. Почему нельзя принять правило «принимать любой fcnt, если подпись верна»?
Ответ
Тогда replay-атака проходит полностью: записанный пакет имеет верную подпись и будет принят сколько угодно раз. Злоумышленник сможет имитировать присутствие лисы там, где её нет, — то есть увести детей в ложном направлении.
Подпись и счётчик закрывают разные угрозы, и нужны оба.
B3. Злоумышленник записал два последовательных пакета (fcnt = 100 и 101) и проигрывает их по очереди. Пройдёт ли он проверку при RESYNC_N = 3?
Решение
Последовательность его передач: 100, 101, 100, 101, 100…
- 100 → кандидат, подряд = 1
- 101 → равно 100 + 1, подряд = 2
- 100 → не равно 101 + 1, цепочка рвётся, счётчик сбрасывается
Порог RESYNC_N = 3 не достигается никогда. Атака не проходит.
Но если бы он записал три пакета (100, 101, 102), цепочка достигла бы трёх и ресинхронизация сработала бы. Это и есть окно уязвимости, о котором сказано выше.
Уровень C (15–16 лет)
C1. Предложите улучшение схемы ресинхронизации, повышающее стойкость без энергонезависимой памяти. Оцените, что теряется.
Разбор
Возможные направления:
- Увеличить
RESYNC_N(до 10–20). Атакующему нужна длинная запись. Теряем: время восстановления после честного ребута растёт. - Требовать роста в течение временного окна. Проверять не только последовательность, но и интервал прихода (300 мс). Записанная серия, проигрываемая с другим темпом, не пройдёт. Теряем: усложнение, чувствительность к потерям пакетов.
- Добавить метку времени в пакет. Приёмник отвергает пакеты старше N секунд. Теряем: нужны синхронные часы у лисы и охотника.
- Челлендж-ответ. Охотник посылает случайное число, лиса его подписывает. Полностью закрывает replay. Теряем: маяк перестаёт быть односторонним, лисе нужен приёмник, растут энергопотребление и сложность.
Правильный вывод: вариант 4 — самый стойкий и самый дорогой. Односторонний маяк принципиально уязвимее двустороннего протокола. Выбор определяется ценой атаки и стоимостью защиты, а не стремлением к абсолютной защите.
C2. Счётчик fcnt — 4-байтовый. Через сколько времени он переполнится при BEACON_MS = 300? Что произойдёт при переполнении?
Решение
Максимум 4-байтового беззнакового числа: 2³² − 1 ≈ 4,295 · 10⁹.
При 300 мс на пакет: 1 / 0,3 ≈ 3,33 пакета в секунду.
При переполнении счётчик обнулится, и приёмник воспримет это как ребут — сработает механизм ресинхронизации, лиса будет принята. То есть переполнение обрабатывается корректно, хоть и не специально.
Замечание: 41 год делает проблему практически неактуальной. Но в системах с частой передачей (например, каждые 10 мс) счётчик переполнился бы за год, и это уже требует обработки. Правильный инженерный рефлекс — посчитать срок переполнения, а не полагаться на «наверное, хватит».
C3. Сравните защиту от replay в нашем стенде и в LoRaWAN. Что промышленный протокол делает иначе?
Разбор
| Аспект | Наш стенд | LoRaWAN |
|---|---|---|
| Счётчик | fcnt, 4 байта, в RAM | FCntUp, сессионный |
| Хранение | теряется при сбросе питания | на сервере; на устройстве — в EEPROM (в проде) |
| Проверка | на приёмнике (охотник) | на сетевом сервере |
| Ресинхронизация | по динамике, RESYNC_N подряд | только явным разрешением «disable frame-counter validation» |
| Подпись | 1 байт XOR | 4 байта AES-CMAC |
| Направление | односторонний маяк | двусторонний, есть даунлинк |
Ключевое отличие: LoRaWAN не пытается «умно распознать» ребут — он считает сброс счётчика ослаблением защиты и требует явного разрешения администратора. Наш стенд идёт на компромисс ради простоты, и это честно задокументировано.
Урок: промышленные протоколы часто отказываются от изящных эвристик в пользу строгих правил, потому что эвристика — это всегда окно для атаки.
Ответы на вопросы, которые прозвучат
| Вопрос ученика | Как отвечать |
|---|---|
| «Почему просто не сохранять счётчик в память?» | Так и делают в реальных системах. Но флеш-память имеет ограниченный ресурс записи (десятки тысяч циклов), а лиса шлёт три пакета в секунду — память износится очень быстро. Нужны хитрые схемы записи (см. ДЗ уровня C) |
| «А если злодей запишет много пакетов?» | Тогда он сможет пройти ресинхронизацию — и это честное ограничение нашей схемы. В проде счётчик хранят надёжно, а сброс запрещают |
| «Можно ли добавить время в пакет?» | Да, хорошее решение. Но требует синхронных часов у лисы и охотника — в нашем стенде их нет |
| «Что если лиса потеряет пакеты — счётчик перескочит?» | Ничего страшного: правило требует роста, а не «ровно на единицу». Пропуски нормальны и как раз показывают потери в эфире |
| «Брелок от машины так же работает?» | Да, это тот же механизм — rolling code. Ранние брелоки без него вскрывались записью и повтором |
| «А если злодей ретранслирует сигнал вживую?» | Отличный вопрос — счётчик при этом растёт честно, и защита не срабатывает. Это relay-атака, от неё защищаются измерением времени распространения сигнала. У нас такой защиты нет |
Раздаточный лист (для печати)
Урок 5. Счётчик кадров
Задача урока: одинаковые на вид данные — разные причины. Как различить?
Replay-атака Ребут настоящей лисы Подпись fcntменьше запомненногоДинамика fcntРешение охотника Правило приёмника:
принять, если fcnt > сохранённого иначе: смотреть, РАСТЁТ ли он в следующих пакетах RESYNC_N подряд растущих -> ребут, принять застыл -> атака, отвергнутьЗадание. Охотник помнит
fcnt = 500,RESYNC_N = 3. Заполните:
Пришло Больше 500? Растёт подряд? Решение охотника 3 4 5 6 Где ещё применяется тот же механизм:
- ________________________
- ________________________
- ________________________
Главный вывод урока своими словами:
______________________________________________________
______________________________________________________
Домашнее задание
Обязательная часть
Врайтап по рубрике А. Обязательный пункт: опишите момент, когда правило «счётчик должен расти» оказалось недостаточным, и как его починили.
Уровень A
Найти три системы, где что-то нумеруется по порядку для защиты от повтора (чеки, билеты, номера транзакций, талоны). Описать, что произойдёт при повторении номера.
Уровень B
Придумать и описать сценарий атаки на охотника, который не закрывается ни подписью, ни счётчиком. Объяснить, почему существующие механизмы не помогают.
Ориентир для педагога
Ожидаемые ответы: глушение (злоумышленник забивает эфир помехой — подпись и счётчик бесполезны); relay-атака (злоумышленник принимает сигнал лисы и мгновенно переизлучает из другого места — счётчик растёт честно, подпись верна, но координата ложная). Второй ответ особенно ценен: это реальная угроза, от которой защищаются измерением времени распространения сигнала.
Уровень C
Маяк имеет энергонезависимую память с ресурсом 100 000 циклов записи. Нужно хранить счётчик так, чтобы памяти хватило на 10 лет при 3,33 пакета в секунду. Предложите схему записи и обоснуйте расчётом.
Ориентир для педагога
Расчёт объёма задачи:
Пакетов за 10 лет: 3,33 · 10 · 365 · 24 · 3600 ≈ 1,05 · 10⁹.
Если писать каждый пакет — 10⁹ записей при ресурсе 10⁵. Не хватает в 10 000 раз.
Требуется: не менее 10 501 пакета на одну запись.
| Шаг записи N | Записей за 10 лет | Хватает? |
|---|---|---|
| 1 000 | 1 050 149 | нет |
| 10 000 | 105 015 | нет (на грани) |
| 100 000 | 10 501 | да, с запасом ×10 |
Схема решения: записывать счётчик не каждый пакет, а раз в N = 100 000. При старте после сброса питания прочитать сохранённое значение и сразу прибавить N — тогда новый счётчик заведомо больше любого, который был реально использован. Цена: после каждого ребута «теряется» до 100 000 номеров, но их запас (2³²) это позволяет — хватит на 43 000 перезагрузок.
Дополнительное улучшение (для самых сильных): wear leveling — писать не в одну ячейку, а по кругу в несколько, что умножает ресурс на их количество.
Это реальная инженерная задача: именно так решают проблему в промышленных LoRaWAN-узлах.
Итог урока: что записать в журнал
Вывод урока 5
- Подпись подтверждает авторство, но не время. Записанный пакет остаётся подлинным.
- Защита от повтора — монотонный счётчик кадров: приёмник принимает только растущие значения.
- Ребут честной лисы выглядит как атака: счётчик тоже становится малым.
- Различает динамика: у атаки счётчик застыл, у ребута — растёт. Смотреть надо на серию, а не на одно значение.
- Требование
RESYNC_Nподряд растущих значений повышает стоимость атаки, но не делает её невозможной — это честный компромисс. - Тот же механизм работает в LoRaWAN, TLS, IPsec, платёжных системах и автомобильных брелоках.
Связь с курсом
| Что родилось на этом уроке | Где станет инструментом |
|---|---|
| Защита от replay | PowerSentry урок 7 (FCnt в LoRaWAN) |
| «Смотреть на динамику, а не на значение» | RFCurtain (тренд против порога), BeaconRadio (свежесть фикса) |
| Различение похожих ситуаций по контексту | BeaconRadio урок 5 (спуфинг: HDOP хороший, но ГЛОНАСС ноль) |
| Повышение стоимости атаки вместо абсолютной защиты | Защита выпускного проекта |
Чек-лист педагога перед уроком
- Накануне: пойман реальный пакет, байты вписаны в
CAPTURED_PACKET - Проверено: атака 2 даёт
SIG:OK FCNT:REPLAY - Проверено: перезагрузка настоящей лисы даёт
[RESYNC]и приём - Монитор порта настроен показывать
fcntкаждого пакета - Раздаточный лист распечатан
- Продуман момент кульминации: не выдать разгадку раньше времени