Конечно же, эта схема предельно упрощена. В реальной жизни при установлении
TCP-соединения может возникнуть множество различных ситуаций, однако именно
такая трехэтапная процедура начальных переговоров (handshaking) и используется
злоумышленниками для организации атаки.
SYN-наводнение начинается с того, что атакующий клиент передает серверу, выбранному
в качестве жертвы, SYN-пакет. Сервер, как и следует из описания в RFC-793, отвечает
передачей пакета SYN/ACK, записывает данные в очередь, переходит в состояние
SYN_RECEIVED, называемое "полуоткрытым" соединением, и ожидает получения
ACK-пакета. На этом нормальный ход процедуры установления соединения прекращается,
и сервер остается в состоянии SYN_RECEIVED. "Страдает" ли от этого
сервер? Конечно же, да. Во-первых, сведения о "полуоткрытом" соединении
хранятся в буфере, имеющем ограниченные размеры, а во-вторых, при переходе в
состояние SYN_RECEIVED в память записывается управляющий блок. Одним словом,
для поддержания "полуоткрытого" соединения выделяются ресурсы, а их
объем в системе отнюдь не бесконечен. Более того, в некоторых операционных системах,
например в ранних версиях SunOS, размер буфера для хранения сведений о "полуоткрытых"
соединениях был смехотворно мал (это неудивительно, ведь когда разрабатывались
такие системы, о SYN-наводнении еще ничего не было известно). Впоследствии выделенные
ресурсы освобождаются по тайм-ауту, но за это время атакующий клиент успевает
прислать новые SYN-пакеты. Передавая SYN-пакеты быстрее, чем закрываются "полуоткрытые"
соединения, атакующий компьютер может исчерпать ресурсы сервера, в результате
чего последний потеряет способность устанавливать TCP-соединения с реальными
клиентами.
Интересно, что внешне SYN-наводнение никак не проявляется, и системный администратор
поначалу его и не заметит. Ведь исходящие соединения исправно устанавливаются,
а пользователи локальной сети по-прежнему имеют возможность посылать и получать
данные из Internet. Факт длительного отсутствия свежей почты также не сразу
бросается в глаза. Через какое-то время кто-то все же обратит внимание на недоступность
"родного" сервера, в то время как обмен данными со всей Глобальной
Сетью происходит без проблем. Тут-то и выяснится, что сервер атакован и надо
срочно предпринимать необходимые меры защиты.
Конечно же, злоумышленник совсем не заинтересован в том, чтобы системный администратор
выявил источник атаки, поэтому в передаваемых SYN-пакетах он подменяет исходный
IP-адрес (это как раз и обеспечивается реализацией raw sockets).
Поскольку начальный обмен пакетами SYN и ACK -- неотъемлемая часть процедуры
установления соединения, противодействовать SYN-наводнению крайне трудно. Долгое
время защиты от этого вида атаки практически не существовало.
Доверяй, но проверяй
Занимаясь поисками средств, способных противодействовать атаке, Гибсон
заметил, что все сведения, необходимые для установления TCP-соединения, находятся
в составе ACK-пакета, передаваемого клиентом. Действительно, в нем содержатся
исходный порт и порт назначения, а также начальные номера последовательности
для клиента и для сервера (ISN_C и ISN_S). Следовательно, сервер может не выделять
ресурсы и не помещать информацию о запросе в очередь. Единственное, что ему
надо сделать при получении SYN-пакета -- отправить пакет SYN/ACK и "забыть"
о том, что кто-то попытался установить соединение.
Однако такой подход еще не решает проблемы, ведь вместо SYN-наводнения злоумышленнику
ничего не стоит организовать ACK-наводнение и заставить сервер выделять ресурсы
на поддержание уже не "полуоткрытых", а открытых соединений. Поэтому
Гибсон пошел дальше и разработал изящный способ идентификации клиента, который
он назвал методом зашифрованного маркера (encrypted token).
Как было сказано выше, в составе пакета SYN/ACK передается начальный номер последовательности
(ISN_S). Поскольку этот номер устанавливается сервером, то в нем, по предложению
Гибсона, должен быть зашифрован IP-адрес клиента. Если процедура соединения
проходит корректно, то, получив пакет SYN/ACK, клиент передает ACK-пакет, записав
в поле ACKN значение ISN_S+1. Расшифровав начальный номер последовательности,
сервер сравнивает его с адресом, указанным в IP-заголовке, и, если адреса совпадают,
устанавливает соединение. Если же адрес, зашифрованный в ISN_S, отличается от
IP-адреса, значит, имеет место фальсификация и, следовательно, предпринимается
попытка атаки.
Разрабатывая методику защиты, Гибсон уделил большое внимание шифрованию IP-адреса
клиента и использовал для этого систему кодирования RC5. Имеет ли значение выбор
того или иного алгоритма? Вряд ли. По-видимому, простейший код даст не менее
надежные результаты, чем "сверхмощный" шифр, ведь злоумышленнику неоткуда
взять информацию для декодирования. Зашифрованные данные придут не ему, а клиенту,
расположенному по фальшивому адресу, указанному в пакете. Даже если хакер предпримет
специальные меры для сбора информации, затратит время и ресурсы для подбора
ключа, он сможет воспользоваться полученными результатами лишь до первой перезагрузки
сервера. При перезагрузке будет выбран новый ключ, который придется взламывать
заново.
ISN_S -- не роскошь...
Описание решения, предложенного Гибсоном, будет неполным, если не упомянуть
об одной детали. Дело в том, что непосредственная замена ISN_S зашифрованным
IP-адресом противоречит одному из требований RFC-793. Начальный номер последовательности
не случайно включается в состав пакетов SYN и ACK. Он необходим для того, чтобы
"заблудившийся" пакет не нарушил нормального обмена данными.
При работе в Internet нередки случаи, когда клиент разрывает соединение с сервером,
а затем снова устанавливает его. В рамках нового соединения начинается обмен
данными, который может быть нарушен пакетом, отправленным в течение предыдущего
соединения, но "заблудившимся" в сети. Как выявить такой пакет? Ведь
IP-адреса компьютеров постоянны, а номера портов также могут остаться неизменными.
Для защиты от "заблудившихся" пакетов используются возрастающие начальные
номера последовательности. ISN_S и ISN_C должны выбираться так, чтобы содержимое
полей SEQ и ACKN гарантированно отличалось бы от соответствующих значений для
пакетов, которыми компьютеры обмениваются в рамках текущего соединения. Для
этого в каждой системе, предназначенной для поддержки TCP, содержится генератор
начального номера последовательности, каждые 4 микросекунды увеличивающий начальный
номер на единицу.
С проблемой выбора ISN_S удалось справиться очень просто. Получив SYN-пакет,
система Гибсона кодирует IP-адрес клиента и складывает его с начальным номером
последовательности, переданным клиентом. Полученное значение используется в
качестве ISN_S. Приняв ACK-пакет, система вычитает ISN_C из ISN_S, в результате
чего получает данные для расшифровки IP-адреса.
Следует признать, что, для того чтобы реализовать такую схему, придется затратить
определенные усилия, в частности необходимо существенно модифицировать средства
поддержки TCP. Однако изменения, предложенные Гибсоном, не затрагивают клиентов,
а вносятся только в состав серверных программ, поэтому провести требуемую модернизацию
вполне реально.
Новое -- хорошо забытое старое?
Оценив по достоинству решение Гибсона, обратимся к документу TCP
SYN Flooding and IP Spoofing Attacks. Среди рекомендаций, призванных снизить
негативные воздействия SYN-наводнения, пользователям Linux предлагается некое
дополнение к системе. В описании читаем следующее: "...поддержка TCP/IP-взаимодействия
осуществляется обычным образом до тех пор, пока очередь ["полуоткрытых"
соединений] не будет близка к заполнению. После этого вместо отправки пакетов
SYN/ACK сервер посылает SYN-cookie и ожидает ответа на него. При этом сервер
удаляет запись о принятом SYN-пакете из очереди...".
Указанный здесь механизм был разработан Дэном Бернстейном (Dan Bernstein) и
Эриком Шенком (Eric Schenk). На Web-странице cr.yp.to/syncookies.html один из
авторов данного подхода кратко описал принцип действия SYN-cookie. От обычного
SYN/ACK-пакета SYN-cookie отличается принципом формирования ISN_S.