Наконец, для сегментов данных протокол TCP старого образца проводил две проверки. Сначала он проверял порядковый номер, и если тот находился в пределах допустимого окна, он также проверял номер подтверждения, который считался действительным при попадании в определенный (огромный) интервал. Мы обозначим порядковый номер первого неподтвержденного байта как FUB, а номер следующего байта, подлежащего отправке, как NEXT. В этом случае действительны все пакеты с номерами подтверждения в диапазоне [FUB – 2GB, NEXT], или половина всего пространства номеров подтверждения. Злоумышленники просто не могут не воспользоваться такой ситуацией! Более того, если номер подтверждения также находился в пределах окна, прежний TCP обрабатывал данные и производил стандартное продвижение окна. Вместо этого спецификация RFC 5961 предписывает принимать те пакеты, у которых номер подтверждения находится внутри окна (приблизительно), и отправлять подтверждение запроса в случае пакетов, попадающих в окно [FUB – 2GB, FUB – MAXWIN], где MAXWIN — самое большое из когда-либо объявленных узлом окон.
Разработчики RFC 5961 быстро поняли, что это расширение может порождать очень много подтверждений запроса, и предложили ограничить количество подтверждений. В реализации для Linux это означало отправку не более 100 подтверждений запроса в секунду в рамках всех соединений. То есть некая глобальная переменная, общая для всех соединений, должна была отслеживать количество переданных подтверждений. Когда оно достигало 100, отправка прекращалась до окончания 1-секундного интервала (что бы при этом ни происходило).
Как бы хорошо это ни звучало, здесь кроется одна проблема. Единственная глобальная переменная здесь отражает общее состояние, что может стать побочным каналом для хорошо продуманной атаки. Рассмотрим первое препятствие, стоящее перед злоумышленниками, — вопрос о наличии соединения между двумя пользователями. Как вы помните, подтверждение запроса высылается в трех случаях:
1. В сегменте SYN указаны правильные IP-адреса отправителя и получателя и номера портов; при этом неважно, каким будет порядковый номер.
2. Порядковый номер сегмента RST находится в пределах допустимого окна.
3. В сегменте данных номер подтверждения также находится в пределах окна для запросов.
Допустим, злоумышленники хотят выяснить, имеется ли соединение между пользователем с адресом 130.37.20.7 и веб-сервером (с портом получателя 80) с адресом 37.60.194.64. Поскольку взломщикам не нужен точный порядковый номер, им достаточно лишь подобрать номер порта отправителя. Для этого они подключаются к веб-серверу и отправляют 100 пакетов RST подряд, в ответ на что сервер отправляет 100 подтверждений запроса (либо меньше, если часть таких подтверждений уже была отправлена до этого, что, впрочем, маловероятно). После 100 пакетов RST злоумышленники отправляют поддельный сегмент SYN, выдавая себя за клиента с адресом 130.37.20.7 с помощью подобранного номера порта. Если номер подобран неверно, то ничего не происходит, и взломщикам все равно приходит 100 подтверждений. Но если он подобран правильно, мы получаем первый сценарий, при котором сервер отсылает подтверждение запроса реальному клиенту. Поскольку сервер может отправлять только 100 подтверждений запроса в секунду, из них злоумышленники получат только 99. То есть, посчитав полученные подтверждения, они могут не только выявить наличие соединения между двумя хостами, но и определить используемый клиентом номер порта источника (скрытый). Конечно, для этого потребуется довольно много попыток, но это вполне осуществимо. Кроме того, существуют методы, позволяющие повысить эффективность этого процесса.
Получив номер порта, злоумышленники могут перейти к следующему этапу атаки: подбору порядкового номера и номера подтверждения. Это делается аналогичным образом. В случае порядкового номера хакеры снова отсылают 100 подлинных пакетов RST (побуждая сервер к отправке подтверждений запросов) и дополнительный поддельный RST с правильными IP-адресами и уже известными номерами портов, а также подобранным порядковым номером. Если подобранный номер попадает в окно, мы получаем второй сценарий. То есть злоумышленники могут определить, правильно ли они подобрали номер, подсчитав количество полученных подтверждений запроса.
Наконец, в случае номера подтверждения они повторяют тот же трюк, отправляя в дополнение к 100 RST пакет данных, у которого все поля содержат корректные данные, за исключением подобранного номера подтверждения. После этого у злоумышленников имеется вся информация, необходимая для сброса соединения или внедрения данных.
Внемаршрутный взлом TCP хорошо демонстрирует три момента. Во-первых, мы понимаем, насколько сложными могут быть сетевые атаки. Во-вторых, это прекрасный пример сетевой атаки по побочным каналам (side-channel attack). С ее помощью хакеры получают важную информацию по обходному каналу. В данном случае они узнают все нужные им параметры подключения путем подсчета, казалось бы, никак не связанных с этим вещей. В-третьих, внемаршрутный взлом TCP показывает, что главная причина, из-за которой атаки по побочным каналам стали возможными, — хранение общего состояния в глобальной переменной. Уязвимости побочных каналов очень часто встречаются и в программном, и в аппаратном обеспечении, и всегда коренным источником проблем является совместное использование какого-нибудь важного ресурса. Конечно, мы и так это знали, ведь такое разделение ресурсов идет вразрез с базовой идеей Зальцера и Шредера о необходимости минимизировать количество общих механизмов, о которой мы говорили в начале главы. В сфере безопасности представление о том, что с другими нужно делиться, не соответствует действительности!
Прежде чем перейти к следующей теме (нарушение работы и отказ в обслуживании), следует отметить, что внедрение данных не относится к числу методов, представляющих исключительно теоретический интерес; он также активно используется на практике. После разоблачений, сделанных Эдвардом Сноуденом (Edward Snowden) в 2013 году, стало известно, что американское Агентство национальной безопасности (National Security Agency, NSA) проводило массовую слежку за гражданами. Одним из направлений этой деятельности было проведение сложной сетевой атаки Quantum. Когда определенные пользователи подключались к популярным сервисам (Twitter, Gmail или Facebook), их перенаправляли на специальные серверы путем внедрения пакетов. Затем эти серверы взламывали компьютеры жертв для получения полного доступа к хранящейся на них информации. Конечно, NSA полностью отрицает это. Оно готово отрицать сам факт своего существования. Иногда «NSA» в шутку расшифровывают как «No Such Agency» («нет такого агентства»).
8.2.4. Нарушение работы
Попытки сделать систему недоступной называются атаками типа «отказ в обслуживании». При этом жертве отправляется такой объем данных, с которым она не справляется и перестает отвечать на запросы. Существует несколько причин, по которым компьютер может прекратить реагировать:
1. Аварийный отказ. Отправленный злоумышленником контент вызывает аварийный отказ или зависание компьютера жертвы. Примером такой атаки является «пинг смерти», упомянутый ранее.
2. Алгоритмическая сложность. Злоумышленник отправляет такие данные, обработка которых требует больших вычислительных затрат (на уровне алгоритмов). Предположим, сервер позволяет клиентам отсылать расширенные поисковые запросы. В этом случае атака за счет алгоритмической сложности может сводиться к отправке ряда сложных регулярных выражений, что приведет к ситуации с наихудшим временем поиска.
3. Переполнение данными. Злоумышленник закидывает жертву таким гигантским количеством запросов или ответов, что система не справляется с ними. Часто, хотя и не всегда, это приводит к аварийному отказу компьютера жертвы.
Атаки переполнения данными стали серьезной головной болью многих организаций, ведь на сегодняшний день провести крупномасштабную DoS-атаку очень просто и дешево. За небольшую денежную сумму можно арендовать сеть ботов с тысячами компьютеров и атаковать любой адрес. Если данные отправляются с большого количества компьютеров из разных уголков мира, то это распределенная атака типа «отказ в обслуживании», или DDoS-атака (Distributed Denial-of-Service). Устроить DDoS-атаку может даже далекий от техники человек с помощью существующих в интернете «стрессоров» или «бутеров» — специализированных сервисов, предлагающих для этой цели удобный интерфейс.
Переполнение SYN-пакетами
В старые добрые времена DDoS-атаки были достаточно простыми. Например, можно было использовать множество взломанных компьютеров, чтобы обеспечить переполнение SYN-пакетами. Все эти компьютеры отправляли на сервер TCP-сегменты SYN, в которых часто были подделаны данные об отправителе. Пока сервер отправлял ответы SYN/ACK, никакой другой компьютер не мог завершить «рукопожатие» TCP. Таким образом, сервер оставался в зависшем состоянии, а это довольно затратно. Каждый хост может поддерживать лишь ограниченное количество полуоткрытых TCP-соединений и уже не принимает новые соединения по достижении этого предела.
Существует много способов защиты от переполнения SYN-пакетами. Например, можно просто отбрасывать полуоткрытые соединения после достижения предела, отдавая предпочтение новым, или уменьшить время жизни принятых SYN-пакетов. Очень элегантное и простое решение, которое сегодня поддерживает большинство существующих систем, сводится к использованию файлов SYN cookie, уже упоминавшихся в главе 6. Специальный алгоритм устанавливает изначальный порядковый номер таким образом, чтобы серверу не нужно было запоминать никакие параметры подключения до получения третьего пакета в «тройном рукопожатия». Как вы помните, размер порядкового номера составляет 32 бита. При использовании файлов SYN cookie сервер выбирает изначальный порядковый номер так:
1. Первые 5 бит отводятся под рез