Tor 0day: Краш Tor-сети

Tor 0day: Краш Tor-сети

5∆17Ø

За последние 3,75 года мой Tor-сервер подвергся широкому спектру DoS-атак. Большинство из этих атак осуществлялись с помощью обычных агрессивных ботов, и их легко было бы отфильтровать в клирнете, но сеть Tor не похожа на клирнет. В остальных случаях атаки были распределённые - DDoS - и их целью явно было вывести мой сервер в оффлайн.

За все это время я нашёл несколько способов свести ущерб к минимуму и поддерживать свой сервер в рабочем состоянии. Мне казалось, что все эти атаки были довольно мощными, но только до момента с атакой в феврале 2020-го, которую я неоднократно упоминал в серии "Tor 0day".

Не знаю, кто стоял за этим нападением и какие инструменты он или они использовали. Существуют инструменты с открытыми исходниками для проведения DDoS-атак в Tor, например, Stringer-Tor, но я сталкивался со Stringer и эта встреча не стала для меня проблемой. Атака в феврале 2020-го имела другую сигнатуру, так что это точно не был какой-то известный софт.

Для меня атака началась в понедельник, третьего февраля 2020-го года, в 03:10:37 (в 10:10:37 по Гринвичу). Я знаю, что не был первой целью - меня атаковали в феврале, но, по крайней мере, полдюжины других Tor-сервисов подвергались таким атакам еще в январе. Я почти уверен, что причина нападения заключалась в том, что мне хватило наглости оставить запись в своём блоге о деанонимизации цепочек Tor - после публикации этой записи я получил два письма с угрозами, а за ними последовала DDoS-атака. Но я не думаю, что люди, стоящие за деанонимизацией, и человек, стоящий за DDoS-атакой, были связаны между собой, поскольку попытки деанона продолжались еще неделю после атаки.

Поскольку я не знаю ни человека, который меня атаковал, ни инструмента, которым он пользовался, единственное, что я могу изучить и о чём могу рассказать - это технические детали того, как атака выглядела с моей стороны. В этой записи блога я расскажу о том, как это выглядело, о шагах по снижению ущерба, которые я предпринял, и о конечных результатах.

Борьба с DDoS

В клирнете есть три основных способа борьбы с атаками типа «отказ в обслуживании»:

Вариант №1: Фильтрация

Если есть возможность определить сигнатуру атаки - значит, можно отфильтровать её, заблокировать. Если фильтрация будет происходить на моём сервере, это может означать, что под раздачу попадет мой маршрутизатор. Если фильтрация будет происходить на моём маршрутизаторе, то велика вероятность, что весь удар на себя примет предыдущая точка перехода (previous next hop). Выходит, чем ближе блокировка вредоносного трафика к источнику этого трафика, тем эффективнее блокировка. В своей предыдущей записи в блоге я упоминал, что есть люди, которые пытаются идентифицировать и фильтровать DDoS-атаки прежде, чем они перейдут между основными операторами связи.

У Tor имеется несколько официальных вариантов защиты от DDoS-атак. В моих предыдущих записях в блоге были описаны некоторые неофициальные решения и внесенные изменения в исходный код, которые могут помочь снизить ущерб.

Вариант 2: Косвенные сервисы

Некоторые типы фильтрации более сложны. Проверка JS-куками или SYN-куки могут предотвращать прямые атаки на сервис. В клирнете SYN-куки являются стандартом де-факто для предотвращения TCP SYN-спуфинга. Но Tor более уязвим на уровне приложений, а не на транспортном, и компенсировать его лучше именно там.

Хорошим примером такой компенсации является EndGame. Это внешняя система фильтрации, которая защищает внутренний сервер. Она используется рядом даркнет-маркетплейсов, а также Dread (Dread - это, по сути, клон Reddit в даркнете). Но Endgame защищает только внутренний сервер, внешние сервисы не входят в зону его ответственности. Вот что говорят сами создатели:

EndGame не идеален. Он не может защитить от атак на входные узлы (проект Tor должен будет добавить POW на входных узлах, чтобы исправить это). Но он действительно обеспечивает хорошую защиту и масштабирование, что позволяет вашим серверам быть гораздо устойчивее несмотря на то, какие атаки будут на них нацелены.

Проект Tor годами игнорировал защиту от DDoS-атак для Tor-сервисов. Фактически, только к четвёртой части этой серии "Tor 0day" проект Tor предложил хоть какие-то контрмеры. К этому моменту у ребят из проекта Tor было шесть месяцев, чтобы заметить, что серия "Tor 0day" существует и о чем в ней говорится. Их решения ориентированы на косвенное использование: анонимные токены и POW (Proof-Of-Work). К сожалению, эти решения не влияют на основную причину, по которой DDoS-атака в феврале 2020-го стала возможна.

Вариант № 3: Увеличение доступных ресурсов

Если пропускная способность вашего канала 1Гбит/с, то увеличьте её до 2 Гбит/с. Если ваш сервер не вывозит количество подключений, то добавьте ещё несколько серверов. Если ваш сервер будет оборудован мощнее, чем у атакующего, то он не сможет нанести вам существенный вред.

Большинство крупных Tor-сервисов используют OnionBalance в качестве балансировщика нагрузки. Распределяя вычислительную нагрузку, он увеличивает доступные ресурсы ваших серверов за предел возможностей большинства атакующих. В качестве примера, DarkDotFail опубликовал в Twitter скриншот загрузки своего сервера во время DDoS-атаки:

Здесь видно, как ядра справляются с атакой в 500Мбит трафика с помощью OnionBalance, и это всего лишь один из их серверов.

Конечно, увеличение ресурсов означает увеличение эксплуатационных расходов, но и DDoS-атака может привести к тому, что сервер упадет в оффлайн из-за экстремальных нагрузок. Как отметил DarkDotFail:

сердечное "спасибо!" нашим донорам, мы не смогли бы противостоять атакам без мощных серверов.

Меньше - значит больше

Мой Tor-сервис оставался в рабочем состоянии во время множества сетевых атак. Однако я не использую дополнительные сервисы и ресурсы. Фактически, мой сервер - это небольшая виртуалка на одном ЦП, 1 Гб ОЗУ и пропускной способностью в 20 Мбит/с. Во время нормальной пиковой нагрузки мой ЦП загружен на 50%. Мои эксплуатационные расходы составляют около одного бакса в месяц, и эта сумма не менятся даже с учетом атак. Мой сервер выдерживает DDoS-атаки используя другую концепцию: перенаправление. Я перенаправляю атаку на атакующего. Я заставляю злоумышленника обходиться самому себе дороже (вычислительное время, задержки в сети и т.д.), чем это обходится мне.

Например, те изменения, которые я внес в код, позволяют мне идентифицровать сигнатуру атаки (фильтрация и профилирование), а затем закрывают туннель Tor. Это перекладывает вычислительную нагрузку на атакующего; ему нужно потратить время на восстановление туннеля, прежде чем он сможет снова добраться до меня. В общем, я закрываю туннели быстрее, чем он может открыть новые.

Атака в феврале 2020 года

Как я уже отмечал, атака началась 3 февраля 2020 года в 03:10:37. Оповещение с сервера пришло в 6:25 утра, на него сыпались соединения через Tor с постоянной скоростью около 300 соединений в секунду и всё это в течение нескольких часов.

На моем сервере соединения проходят через сеть к демону tor. Демон tor перенаправляет соединения на мой веб-сервер (nginx) через локальный сокет. Затем nginx вызывает некоторый PHP-код для выполнения профилирования. Все это обычно занимает долю секунды, но не в этот раз.

Патч №1: я вывел в оффлайн свой сервер nginx, чтобы его перенастроить, увеличил количество пользователей и количество открытых соединений на одного пользователя. Я также увеличил количество соединений между nginx и интерпретатором php. С этими изменениями мой веб-сервер перестал падать под нагрузкой.

Пропуская нагрузку через себя

Хотя мой сервер после настройки смог справиться с такой высокой нагрузкой, узлы цепочки Tor с ней справиться не смогли. К сожалению, атака была настолько мощная, что мой защитный узел и демон tor отвалились.

Если бы я попробовал переподключиться к тому же защитному узлу, который отвалился, я не смог бы установить цепочку Tor - процесс остановился бы на "Bootstrapped 90%: создание цепочки Tor".

Если бы я принудительно сменил защитный узел, то атака произошла бы в то время, когда демон tor регистрировался на серверах каталога скрытых сервисов (hidden service directory - hsdir). Злоумышленник мог работать с несколькими серверами hsdir. Тем не менее, он, вероятно, кэшировал информацию и мониторил серверы hsdir, чтобы найти меня в момент перерегистрации.

Я мало что мог сделать, чтобы защитить серверы hsdir, поэтому смена защитных узлов просто увеличивала для меня количество недоступных защитных узлов.

С каждым новым защитным узлом мой демон tor регистрировался на серверах hsdir, запускался в течение нескольких минут, а затем зависал. Демон был запущен, но не отвечал на новые запросы на подключение. Я решил проверить исходный код демона и определить, почему он виснет. (Отладка плохо документированного спагетти-кода tor заняла несколько часов.)

От багов до DDoS-атак

Основная проблема заключалась в исходном коде демона tor. В частности, он неверно вычисляет количество доступных соединений для запросов на подключение. Ошибка обнаружена в исходном коде файла src/lib/net/socket.c в функции to_accept_socket_with_extensions. Ошибка происходит из-за get_n_open_sockets и accept4:

В этом случае get_n_open_sockets() возвращает неправильное число и accept4 падает. И, хотя accept4 упал, Tor не очищает распределение сокетов.

Патч №2: я остановил демона Tor, а чтобы количество соединений не исчерпалось, изменил максимальное значение в src/lib/net/socket.c:

Заданное по умолчанию значение определяет максимум 1024 одновременно открытых сокета. Это абсолютно неправильно. Каждый входящий сокет объединяется с выходящим, но код демона tor проверяет только сетевые соединения, а не соединения, ретранслируемые на веб-сервер. Значение переменной max_socket должно быть уменьшено вдвое - до 512. Если у вас есть командный порт - всегда должен быть по крайней мере 1 сокет для него. Кроме того, stdin, stdout и stderr тоже нужны сокеты. Поэтому получаем что-то вроде:

Как сказано в комменте от создателей Tor "измените это значение с помощью дескриптора max file". (В комментарии к исходному коду Tor нет функции с именем "set_max_file_sockets()", вместо нее есть "set_max_sockets()".) Эта функция используется в src/lib/process/restrict.c, где она сбрасывает значение по умолчанию на еще более неподходящее.

Исходный код Tor обновляет значение с помощью getrlimit для RLIMIT_NOFILE. Как отмечено в руководстве для getrlimit, RLIMIT_NOFILE возвращает "максимальное число дескрипторов, которое может быть открыто этим процессом". Это значение определяется ядром. На моём сервере у меня есть дефолтное значение Linux - 4096. (Разработчики Tor зарепортили 1048544 на своем сервере.) Однако это не учитывает количество используемых в настоящее время сокетов.

В моем баг-репорте HackerOne (#789065, представленном 4 февраля 2020 года) проект Tor пытался утверждать, что установленное мной значение ulimit было слишком низким, однако:

1. Я собираюсь следовать рекомендациям по настройке от разработчиков ядра Linux. Дефолтный лимит должен быть где-то между 1024 и 4096.

2. Количество сокетов - это параметр, который можно изменить. Разработчики ядра не рекомендуют увеличивать общее значение более чем на 10% от доступной оперативной памяти. К тому же, вы определенно не захотите выделять все доступные сокеты одному приложению.

3. Значение переменной, которая хранит количество подключений, при этом типе DDoS-атаки не имеет значения. Ваш демон tor в конечном итоге попытается выделить слишком много сокетов, потеряет счёт и перестанет отвечать на последующие запросы.

В этом смысл этой DDoS-атаки: она генерировала слишком много параллельных соединений, демон tor терял счёт и отваливался.

(Для тех, кому интересно, я создал быстрый тест sockettest.c, который отображает количество сокетов, доступных для каждого приложения и количество сокетов, используемых в настоящее время. Мой тест показал, что мой сервер имеет нижний предел 1024 и верхний 4096, а также, что в настоящее время используются 3 сокета для stdin, stdout и stderr - с учетом этого, количество доступных сокетов - 4093.)

Вместо того, чтобы исправлять get_n_open_sockets, вычисляя правильное значение, я сделал быстрый патч и установил точное значение max_sockets в 500.

Я не стал утруждаться и исправлять недостающую очистку сокетов (из-за чего accept4 крашился), потому что исправленная проверка max_sockets гарантирует, что accept4 не скрашится никогда.

Хорошие новости: мой демон tor больше не превышает лимит сокетов, и не игнорирует новые подключения. DDoS-атака больше не смогла положить мой слабенький сервер.

Плохая новость: защитный узел всё равно быстро умер, так как у него нет подобного патча.

Защита для защитника

Я искал возможность не прерывать соединение с защитным узлом, если он перегружен. Оказывается, такой вариант есть. В конфигурационном файле torrc можно указать скорость полосы пропускания (BandwidthRate, BandwidthBurst, MaxAdvertisedBandwidth, PerConnBWRate и PerConnBWBurst), а также максимальное количество одновременных подключений (HiddenServiceMaxStreams). Когда эти ограничения будут достигнуты, демон tor скажет защитному узлу замедлить или остановить новые соединения (запустить троттлинг).

Значения по умолчанию для BandwidthRate и BandwidthBurst равны 1 гигабайту в секунду. Да, демон tor по дефолту предполагает, что у вас есть соединение "1 Гбайт" - это 8 гигабит. Остальные настройки не имеют ограничений по умолчанию. С таким высоким дефолтным порогом ваш демон tor никогда не будет пытаться сбросить нагрузку.

Патч №3: я уменьшил значения для этих настроек, снизив ограничения пропускной способности. Я знаю, что это звучит нелогично, но снизить порог пропускной способности в соответствии с доступными ресурсами лучше, чем пытаться увеличить сетевые ресурсы (и затраты на них). Лучше позволить демону троттлить сетевой трафик, чем установить его предел до неограниченной пропускной способности.

Как пользователь Tor и владелец Tor-сервиса, я никогда не видел пропускной способности более 2 мегабит (Мбит, а не Мбайт). Поэтому я снизил скорость соединения до 4 Мбит. Так же я ограничил свой сервер до 20 Мбит/с, поэтому общую максимальную скорость подключения я установил на 15 Мбит/с.

Каков оказался результат? Во время DDoS-атаки троттлинг сработал, и мой защитный узел (1-й переход в цепочке) остался в строю. Скорость DDoS-атаки увеличилась до более чем 500 соединений в секунду, но и это не стало проблемой. Я все еще профилировал атаку в режиме реального времени и отключал злоумышленника быстрее, чем он мог соединиться. Постоянные пользователи всё ещё могли пользоваться моим сайтом и не видели никаких задержек.

Плохие новости: мои мосты (2-й переход в цепочке) начали падать. Это потому, что, насколько я могу судить, защитный узел не передает информацию о троттлинге по цепочке. Более того, большинство защитных узлов используют настройки по умолчанию "8 гигабит", когда в действительности у них подключение медленнее гигабита. Если бы каждый узел Tor (защитный узел, мост, входной узел и т. д.) правильно настроил регулирование пропускной способности, то это не стало бы проблемой.

С другой стороны, я могу изменить частоту смены защитного узла. Мосты не падали в течении 10-15 минут, а цепочка Tor менялась каждые 10. К тому времени, когда старая цепочка была готова упасть, я уже поднимал новую.

Конечно, если бы случайно цепочка циклически повторялась и выбирала ранее использованный мост, то кумулятивное воздействие от неправильного подсчета сокетов привело бы к сбою моста. Единственный способ восстановить его в таком случае - перезапустить.

Входные узлы

С этими тремя патчами мой веб-сервер, php-движок, демон tor и защитный узел стоически держались. В течение нескольких дней этой масштабной DDoS-атаки я думал, что отлично справляюсь. Потом движение просто остановилось. Насколько я мог судить, с моей стороны не было никаких проблем. Все соединения, казалось, работали, и все узлы на пути были функциональны. Но что-то было не в порядке...

Когда ваш клиент Tor подключается к Tor-сервису, он никогда не делает этого напрямую. При установлении соединения клиент сначала взаимодействует с сервером каталога скрытых служб (hsdir). Hsdir предоставляет список "входных узлов" - предварительно выбранных узлов, которые помогают согласовать маршрут соединения между вашим клиентом и моим сервером. Нападавший положил мои входные узлы, а без них никто не мог подключиться к моему сервису.

Патч №4: я проверил настройку HiddenServiceNumIntroductionPoints. По умолчанию сервер использует 3 точки входа. Допускается максимум 10 таких точек для сервисов Tor v2 и 20 для Tor v3. Я изменил свою конфигурацию и превысил дефолтное значение. Как только я это сделал, жизнь на сервере возродилась. (Этот вариант можно отнести к способу смягчения DDoS-атак № 3: увеличение доступных ресурсов. Но в данном случае увеличение количества входных узлов мне ничего не стоило.)

Конец атаки

Эта DDoS-атака была жесткой. Десятки Tor-сервисов были выведены из строя. Кроме Dread и некоторых маркетплейсов по продаже наркотиков, жертвами стали Tor-сервисы Facebook, BBC, ProPublica и Dark.fail. Но мой крошечный сервер жил.

В Твиттере я постоянно комментировал свои успехи. Отчасти это было сделано для того, чтобы показать, что мой сервер работает и информировать других людей о масштабах атаки, отчасти я провоцировал нападавшего на продолжение атаки. И да, я действительно хотел, чтобы он продолжал. И да, он следил за мной в Твиттере. У меня был только один человек, который критиковал меня за новости с фронта:

Мне удалось держать атакующего вовлеченным в процесс атаки в течение 9 дней, он применил три разных метода атаки. Это был отличный способ отладить и укрепить мой демон tor. Кроме того, мои друзья пытались отследить сервер командования и управления ботнетом (C&C). Мы также связались с правоохранительными органами на случай, если злоумышленник окажется в Соединенных Штатах (я не юрист, но мое неавторитетное понимание заключается в том, что 18 USC 1030(e)(11) и (g) делает DDoS-атаку противозаконной). Нам нужно было отследить злоумышленника, а для этого требовалось его постоянно провоцировать, чем я и занимался.

К сожалению, мы его не нашли. Знаете киношное клише, где копы отслеживают телефонный звонок, а плохой парень вешает трубку как раз перед тем, как копы его находят? Да, так оно и было. Мы сузили C&C до нескольких серверов, а геолокацию атакующего - до региона. Затем атака прекратилась.

Я не думаю, что он отвалил, потому что знал, что мы следим за ним. Мне кажется, что он остановился, потому что слишком много узлов Tor упало. К 12 февраля атака возобновилась с удвоенной силой. Наши наблюдатели оценили ее в ~760 Гбит/с, и нацеливалась она на несколько Tor-сервисов. Атака продолжалась всего несколько часов, но она начала ронять узлы по всей сети Tor. Я думаю, что атакующий понял, что большинство соединений его ботнета с Tor рвутся. Он остановился, потому что сам больше не мог подключаться к сети Tor.

Итоги

Шаги по уменьшению ущерба от этой DDoS-атаки требуют четыре простых патча:

  1. Добавьте вычислительной мощности своему серверу.
  2. Отредактируйте конфиг torrc и увеличьте количество входных узлов.
  3. Отредактируйте исходный код и установите количество доступных сокетов для демона tor в соответствии с реальностью.
  4. Отредактируйте конфиг torrc и установите пропускную способность сети демона tor на реалистичное значение.

Шаги 1 и 2 предназначены только для Tor-сервисов. Шаги 3 и 4 должны быть выполнены на каждом узле Tor (мост, защитный узел, входной и выходной узел) и каждой Tor-службе. Эти изменения позволяют вашему локальному демону tor отражать атаку обратно в сеть Tor и предотвращают коллапс вашей цепочки. Это позволяет поддерживать ваш сервис в рабочем состоянии, не увеличивая при этом ваши расходы.

Однако есть и побочный эффект. До тех пор, пока каждый узел сети Tor не будет правильно настроен, кто-то с небольшим ботнетом сможет заставить узлы по всей сети Tor зависнуть и выйти из строя. Более того, им не нужно атаковать чей-то чужой сервер (пожалуйста, не атакуйте мой сервер); они могут запустить свой собственный сервер, правильно его настроить, атаковать его и положить практически всю сеть узлов Tor. Да, правда: атака на ваш собственный (пропатченный) сервер посредством вашего собственного ботнета приведет к сбою узлов Tor. Если это будет продолжаться достаточно долго, то впоследствии вся сеть может Tor просто рухнуть.

Защита всей сети Tor требует патча на каждом отдельного узле Tor. Ребята из проекта Tor были проинформированы об этой атаке, уязвимости, воздействии и решении в феврале прошлого года. Прошло семь месяцев, а они так ничего и не сделали.


Переводчик: 5∆17Ø

Оригинал взят отсюда.

Report Page