Шрифт:
Закладка:
1. Перед тем как приступать к реальной атаке, необходимо выявить наличие соединения между двумя пользователями в интернете.
2. Затем нужно выяснить, какие номера портов использовать.
3. Наконец, необходимо узнать порядковые номера.
Это довольно сложная задача для того, кто находится на другом конце интернета, но все-таки ее можно выполнить. Спустя десятилетия после атаки Митника на Суперкомпьютерный центр Сан-Диего исследователи в области информационной безопасности выявили новую уязвимость, позволившую им реализовать внемаршрутный взлом TCP на популярных системах Linux. Они описали свою атаку в статье «Внемаршрутный взлом TCP: глобальное ограничение скорости опасно» (весьма удачное название, как мы увидим далее). Мы рассмотрим этот случай, поскольку он хорошо демонстрирует, что утечка секретной информации может произойти косвенным образом.
По иронии судьбы проведение этой атаки стало возможным благодаря новой функции, которая должна была, наоборот, повысить, а не снизить степень защиты системы. Как уже упоминалось, внемаршрутное внедрение данных было очень сложной задачей. Злоумышленник должен был угадать номера портов и порядковые номера, а это трудно сделать методом прямого подбора. Тем не менее это возможно, учитывая, что точный порядковый номер не требовался — достаточно было обеспечить отправку данных в пределах допустимого окна. То есть с некоторой (очень малой) долей вероятности злоумышленники могли сбросить соединение или внедрить данные в существующие соединения. В августе 2010 года для решения этой проблемы было выпущено новое расширение протокола TCP (RFC 5961).
Спецификация RFC 5961 изменила то, как TCP принимал сегменты SYN, RST и обычные сегменты данных. Описанная уязвимость имелась только в Linux, поскольку лишь в этой системе RFC 5961 была реализована должным образом. Чтобы понять, как изменился протокол TCP с новым расширением, нужно разобраться, как он работал до этого. Сначала выясним, как TCP принимал сегменты SYN. До выхода RFC 5961 при получении SYN для уже существующего соединения TCP отбрасывал этот пакет, если он находился за пределами допустимого окна, и сбрасывал соединение, если он находился в его пределах. Это объяснялось следующим образом. При получении SYN TCP предполагал, что другой абонент произвел перезапуск, а значит, существующее соединение утратило актуальность. Такое поведение было не слишком разумным, ведь чтобы сбросить соединение, злоумышленнику достаточно было получить лишь один сегмент SYN с порядковым номером, попадающим в окно получателя. Вместо этого RFC 5961 предложила не сбрасывать соединение сразу, а сначала отправлять подтверждение запроса (challenge ACK) очевидному источнику сегмента SYN. Если пакет пришел от реального удаленного узла, значит, этот узел действительно потерял предыдущее соединение и теперь устанавливает новое. В этом случае при получении подтверждения запроса он отправит пакет RST с корректным порядковым номером. Этого не смогут сделать злоумышленники, поскольку они не получат подтверждение запроса.
То же самое относится и к сегментам RST. В исходной версии TCP хосты удаляли RST-пакеты, если они не попадали в допустимое окно, в противном случае они сбрасывали соединение. Чтобы усложнить сброс чужого соединения, в RFC 5961 было предложено немедленно прерывать его, только если порядковый номер в RST точно совпадает с номером, с которого начинается окно получателя (то есть со следующим ожидаемым порядковым номером). Когда этот номер не совпадает с ожидаемым, но находится в пределах допустимого окна, хост не разрывает соединение и отсылает подтверждение запроса. Если мы имеем дело с реальным отправителем, то он передаст RST-пакет с корректным порядковым номером.
Наконец, для сегментов данных протокол 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 подтверждений запроса в секунду, из