Оптимизация повторной передачи пакетов в глобальных сетях (WAN) десятилетиями оставалась ключевой задачей сетевой инженерии. На протяжении раннего этапа развития интернета протокол TCP опирался исключительно на механизм кумулятивных подтверждений (RFC 793). Если отдельный сегмент в окне передачи терялся из-за помех или переполнения очередей маршрутизатора, отправитель был вынужден повторно передавать потерянный пакет и все последующие данные, впустую расходуя пропускную способность канала.
Для устранения этой неэффективности Инженерный совет интернета (IETF) стандартизировал опцию TCP Selective Acknowledgment (SACK) в спецификации RFC 2018. SACK позволяет получателю точно указывать непрерывные диапазоны успешно принятых данных, давая отправителю возможность точечно передавать только недостающие байты.
Однако реализация математических стандартов в системном коде ядра операционной системы нередко влечет неочевидные уязвимости. В середине 2019 года исследователи безопасности раскрыли группу критических дефектов в сетевом стеке ядра Linux, центральным из которых стал CVE-2019-11477 (получивший широкую известность как SACK Panic). Манипулируя параметром максимального размера сегмента (MSS) и формируя специально созданные блоки SACK, удаленный злоумышленник мог спровоцировать 16-битное целочисленное переполнение внутренней очереди буферов сокетов (sk_buff), вызывая мгновенный сбой операционной системы.
Анализ природы SACK Panic показывает, как транспортные оптимизации взаимодействуют с подсистемами распределения памяти ядра и почему сквозное туннелирование L3 по протоколу RiceGuard служит надежным экраном от атак на уровне сегментации.
+-------------------------------------------------------------------------+
| КУМУЛЯТИВНЫЙ ACK ПРОТИВ SELECTIVE ACK (RFC 2018) |
+-------------------------------------------------------------------------+
| СЦЕНАРИЙ: Пакеты 1, 2, 4, 5 доставлены; Пакет 3 утерян в транзите. |
| 1. RFC 793 КУМУЛЯТИВНЫЙ ACK: Приемник шлет ACK=3. Отправитель повторно |
| передает пакеты 3, 4 и 5. Трафик расходуется впустую. |
| 2. RFC 2018 ОПЦИЯ SACK: Приемник шлет ACK=3 + SACK [4-6]. Отправитель |
| повторно передает ТОЛЬКО Пакет 3. Нулевые потери пропускной способности.|
+-------------------------------------------------------------------------+
Эволюция подтверждений TCP: Кумулятивный ACK против SACK (RFC 2018)
При классическом кумулятивном подтверждении 32-битное поле номера подтверждения в 20-байтном заголовке TCP сообщает порядковый номер следующего ожидаемого байта. Это гарантирует, что все предшествующие октеты успешно приняты и проверены. При потере нескольких несмежных сегментов отправитель встает перед дилеммой: повторить отправку одного сегмента и ждать RTT или отправить все окно заново.
Стандарт RFC 2018 решил эту проблему, выделив специальную область в опциях заголовка TCP (Option Kind 5). Получатель передает до четырех непрерывных блоков данных, полученных не по порядку. Каждый блок задается двумя 32-битными числами:
- Левая граница (Left Edge): Начальный порядковый номер изолированного полученного блока.
- Правая граница (Right Edge): Номер, следующий сразу за последним байтом этого блока.
Опираясь на эти дескрипторы, передающий узел восполняет пробелы точечно, не сбрасывая конвейер передачи.
Структура опции SACK: Форматы блоков и граничные дескрипторы
Заголовок TCP отводит под опции не более 40 байт. Базовая опция SACK занимает 2 байта: тип (Kind 5) и длина (Length). Каждый последующий блок SACK требует 8 байт (два 32-битных указателя границ).
Поскольку в соединениях часто активны временные метки TCP Timestamps (RFC 7323, занимающие 10 байт), доступное пространство опций сокращается:
- Без меток времени: 40 байт - 2 байта = 38 байт, что позволяет передать максимум 4 блока SACK (32 байта).
- С метками времени: 40 байт - 10 байт - 2 байта = 28 байт, что вмещает максимум 3 блока SACK (24 байта).
В сочетании с Duplicate SACK (D-SACK, RFC 2883) первый блок может информировать о дубликатах сегментов, позволяя анализировать сетевые аномалии и ложные таймауты.
Управление неупорядоченными сегментами в ядре Linux: Очередь sk_buff
В ядре Linux каждый сетевой пакет представляется структурой буфера сокета — struct sk_buff (skb). Для активного сокета TCP ядро поддерживает очередь повторной передачи, организуя объекты sk_buff в связанный список.
При использовании аппаратных механизмов разгрузки Generic Segmentation Offload (GSO) ядро не создает отдельные пакеты по 1500 байт. Вместо этого формируются суперпакеты размером до 64 КБ, которые сетевая карта или программный планировщик разделяет на кадры MTU перед физической отправкой в среду передачи.
Механика фрагментации буфера сокета (skb) и расход оперативной памяти
Для учета того, сколько физических кадров представляет один буфер GSO, ядро использует счетчик gso_segs в управляющей структуре skb_shared_info:
struct skb_shared_info {
__u16 gso_size;
__u16 gso_segs;
struct sk_buff *frag_list;
};
Архитектурная уязвимость заложена в типе данных: поле gso_segs объявлено как 16-битное беззнаковое целое (__u16), диапазон значений которого ограничен интервалом от 0 до 65 535.
Когда входящий пакет подтверждает через блок SACK лишь небольшую часть данных внутри суперпакета GSO, ядро не может удалить весь буфер. Оно вызывает функцию tcp_fragment(), которая разбивает существующий sk_buff на части: подтвержденную и оставшуюся в ожидании.
+-------------------------------------------------------------------------+
| ФРАГМЕНТАЦИЯ SK_BUFF В ЯДРЕ LINUX |
+-------------------------------------------------------------------------+
| 1. Буфер GSO (32 КБ): Данные: 32 КБ | gso_segs: 23 сегмента |
| 2. Приход блока SACK: Ядро делит skb через tcp_fragment() |
| 3. Атака Micro-MSS: MSS=48 байт дробит очередь на тысячи мелких |
| структур skb -> ПЕРЕПОЛНЕНИЕ gso_segs |
+-------------------------------------------------------------------------+
Механизм уязвимости SACK Panic (CVE-2019-11477): 16-битное целочисленное переполнение
Суть CVE-2019-11477 заключается в обработке фрагментированной очереди при попытке ядра оптимизировать структуры перед повторной отправкой.
Ядро стремится объединить смежные сегменты с помощью функции tcp_shift_skb_data(). Она смещает данные в головной буфер и увеличивает его счетчик gso_segs на количество объединенных сегментов:
/* net/ipv4/tcp_input.c */
tcp_skb_pcount_add(prev, pcount);
Поскольку gso_segs — 16-битная переменная, при превышении значения 65 535 происходит переполнение и значение сбрасывается в ноль.
Вскоре после этого стек ядра выполняет внутреннюю проверку инвариантов в tcp_fragment():
BUG_ON(tcp_skb_pcount(skb) == 0);
Макрос BUG_ON() служит аварийным выключателем: при истинности условия ядро аварийно останавливает систему для предотвращения повреждения памяти (Kernel Panic). Злоумышленник на неаутентифицированном TCP-соединении мог обрушить сервер за считанные секунды.
Роль параметров Micro-MSS в усилении истощения буферов ядра
Как атакующий мог довести счетчик gso_segs до 65 535 без передачи гигабайтов трафика? Ключом стало манипулирование опцией Maximum Segment Size (MSS) при установлении TCP-соединения.
В штатных условиях MSS составляет 1400–1460 байт. Однако RFC 793 допускает согласование экстремально малых величин. Нижней границей в стеке Linux являлось значение 48 байт (размер заголовка IPv4 плюс минимальные опции TCP).
При MSS в 48 байт каждый сегмент несет минимальный объем полезных данных. Буфер размером 32 КБ разделяется на:
$$ ext{gso_segs} = rac{32,768 ext{ байт}}{48 ext{ байт/сегмент}} pprox 682 ext{ сегмента}$$
Передавая специально оформленные блоки SACK, подтверждающие крошечные чередующиеся участки, атакующий заставлял ядро дробить очередь на тысячи микробуферов sk_buff. При их последующем слиянии счетчик gso_segs лавинообразно превышал 65 535, вызывая мгновенный сбой.
Замедление SACK и деградация системных ресурсов (CVE-2019-11478 и CVE-2019-11479)
В рамках исследования стека SACK были обнаружены еще две уязвимости отказа в обслуживании:
- CVE-2019-11478 (SACK Sluggishness): Искусственная фрагментация очереди отправки. Перебор связанных списков с десятками тысяч элементов
sk_buffпри таймаутах повторной передачи загружал процессор на 100%. - CVE-2019-11479 (Истощение ресурсов через сверхмалый MSS): Принуждение сервера к передаче данных с MSS 48 байт увеличивало число генерируемых пакетов в 30 раз, исчерпывая память очередей.
Асимметричная нагрузка на процессор при переборе списков в непропатченном ядре
Вычислительная асимметрия атаки была колоссальной. Клиент генерировал трафик объемом в несколько килобайт. Сервер-жертва, напротив, выделял мегабайты памяти ядра (kmalloc), выполнял тяжелые циклы перебора списков и перегружал процессор обработкой системных прерываний (ksoftirqd).
Для инфраструктурных шлюзов и серверов с тысячами одновременных сессий подобная атака приводила к отказу в обслуживании без срабатывания порогов обнаружения объемного DDoS.
Защита от уязвимостей SACK через туннелирование Уровня 3 и RiceGuard
Прямое взаимодействие узлов через публичный интернет делает их транспортные стеки доступными для анализа и направленных атак.
Архитектура туннелирования L3 сервиса VPNRice на базе протокола RiceGuard устраняет эту уязвимость за счет изоляции транспортного уровня:
- Инкапсуляция в UDP с аутентификацией: RiceGuard помещает весь пользовательский трафик в фиксированные датаграммы UDP, защищенные шифрованием ChaCha20-Poly1305. Внешние транзитные узлы видят только монолитный UDP-поток и не могут внедрять вредоносные опции TCP.
- Фильтрация MSS на шлюзах: Шлюзы RiceGuard применяют жесткое ограничение MSS на периметре. Любая внутренняя попытка установить аномально малый MSS (< 536 байт) нормализуется или отклоняется, предотвращая микрофрагментацию.
- Изоляция сетевого состояния: Внешний туннель замыкается на защищенных узлах, экранируя клиентские устройства от прямого зондирования TCP-стека.
Пользователи могут развернуть клиентское приложение через странице загрузки VPN, изолируя соединения от аномалий транспортного уровня.
+-------------------------------------------------------------------------+
| ЗАЩИТА L3-ТУННЕЛИРОВАНИЯ RICEGUARD |
+-------------------------------------------------------------------------+
| БЕЗ ЗАЩИТЫ: [Атакующий] ── MSS=48 + Вредоносный SACK ──> [Сбой ядра] |
| С ЗАЩИТОЙ: [Атакующий] ── Пакеты micro-MSS ──> [Шлюз RiceGuard] |
| ├─ Нормализация MSS |
| ├─ Очистка опций |
| └─ UDP-туннель Noise |
| └─> [Узел защищен] |
+-------------------------------------------------------------------------+
Усиление безопасности ядра и инспекция пакетов: Настройка Sysctl и MSS Clamping
Сетевые администраторы могут защитить инфраструктуру через параметры sysctl и правила фильтрации брандмауэра.
1. Отключение SACK (Временная мера при авариях)
Если оперативное обновление ядра невозможно, обработку SACK можно деактивировать:
# Проверка текущего статуса SACK (1 = включено, 0 = выключено)
sysctl net.ipv4.tcp_sack
# Деактивация обработки SACK
sudo sysctl -w net.ipv4.tcp_sack=0
Компромисс: Отключение устраняет риск сбоя ядра, но снижает скорость на нестабильных каналах из-за возврата к кумулятивной схеме.
2. Установка минимального порога MSS
В современных ядрах параметр tcp_min_snd_mss запрещает согласование опасных величин MSS:
# Минимальный разрешенный MSS 536 байт (по стандарту RFC 879)
sudo sysctl -w net.ipv4.tcp_min_snd_mss=536
3. Нормализация MSS на сетевом шлюзе через Iptables
Маршрутизаторы периметра должны блокировать пакеты с аномальными параметрами MSS:
# Сброс входящих SYN-пакетов с MSS меньше 536 байт
sudo iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1:535 -j DROP
# Автоматическая нормализация MSS под Path MTU
sudo iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
4. Диагностика трафика с помощью Tcpdump
Сетевые инженеры могут отслеживать подозрительные сегменты командой:
# Перехват пакетов TCP SYN со значением MSS ниже 500 байт
sudo tcpdump -nn -i eth0 'tcp[tcpflags] & tcp-syn != 0 and tcp[20:2] < 500'
Сравнение архитектур: Стандартный TCP, уязвимый SACK и защищенный туннель
Характеристики сетевых моделей при возникновении потерь данных и атак:
| Эксплуатационный показатель | Стандартный TCP RFC 793 | Непропатченный SACK (RFC 2018) | Защищенный туннель RiceGuard |
|---|---|---|---|
| Скорость восстановления | Низкая (Повтор всего окна) | Высокая (Точечные повторы) | Оптимальная (Восстановление L3 UDP) |
| Уязвимость к сбою ядра | Отсутствует | Критическая (Сбой CVE-2019-11477) | Полная защита (MSS Clamping) |
| Нагрузка CPU при атаке | Минимальная | Критическая (Фрагментация очередей) | Незначительная (Аппаратный криптопайплайн) |
| Потребление памяти RAM | Фиксированная очередь | Взрывное (Микро-skb) | Строго предсказуемое |
| Модификация заголовков | Уязвимо для манипуляций | Опции могут сбрасываться | Исключена (Сквозная аутентификация) |
| Пропускная способность WAN | Падает до 60% при потерях | Высокая | Максимальная устойчивость |
Архитектурное исправление в ядре Linux: Очереди на красно-черных деревьях (RB-Tree)
Устранение SACK Panic привело к переработке сетевой подсистемы ядра под руководством Эрика Дюмазе:
- Контроль переполнения счетчиков: В функцию
tcp_shifted_skb()добавлены строгие проверки, исключающие переходgso_segsчерез нулевое значение. - Переход на красно-черные деревья (RB-Tree): Очередь неупорядоченных сегментов переведена со списков на красно-черные деревья (
struct rb_node). Это снизило вычислительную сложность поиска с $O(N)$ до $O(\log N)$, нейтрализовав угрозу CVE-2019-11478. - Фиксированный аппаратный порог MSS: В стек внедрено жесткое ограничение, запрещающее функции
tcp_fragment()дробить буферы ниже 48 байт независимо от внешних запросов.
Эти доработки сохранили производительность SACK, полностью устранив риск истощения системных ресурсов.
Контрольный список сетевого инженера по защите инфраструктуры TCP SACK
Для предотвращения инцидентов с истощением сетевой памяти рекомендуется выполнить следующие шаги:
- Аудит версий ядер серверов: Убедитесь, что рабочие системы используют обновления с исправлением уязвимости (ядра Linux 4.19.52+, 5.1.11+ или пакеты LTS-дистрибутивов).
- Настройка периметральной фильтрации MSS: Сконфигурируйте шлюзы и балансировщики на отсечение входящих TCP-сессий с MSS ниже 536 байт.
- Проверка системных переменных Sysctl: Убедитесь, что параметр
net.ipv4.tcp_min_snd_mssустановлен в значение 536. - Изоляция трафика удаленных сотрудников: Переведите рабочие станции на туннелирование L3 RiceGuard для изоляции транспортных параметров от внешних сетей.
- Мониторинг программных прерываний SoftIRQ: Настройте оповещения на аномальный рост процессорного времени
ksoftirqdдля своевременного обнаружения попыток исчерпания ресурсов. - Применение защиты SYN-Cookies: Активируйте параметр
net.ipv4.tcp_syncookies=1для предотвращения переполнения таблиц состояний на этапе рукопожатия.
Комплексное сочетание системных параметров безопасности и надежного шифрованного туннелирования обеспечивает высокую скорость передачи данных и устойчивость инфраструктуры к направленным атакам.



