Ловушка (тарпит) для входящих SSH-соединений
Не секрет, что интернет — очень враждебная среда. Как только вы поднимаете сервер, он мгновенно подвергается массированным атакам и множественным сканированиям. На примере ханипота от безопасников можно оценить масштаб этого мусорного трафика. Фактически, на среднем сервере 99% трафика может быть вредоносным.
Tarpit — это порт-ловушка, который используется для замедления входящих соединений. Если сторонняя система подключается к этому порту, то быстро закрыть соединение не получится. Ей придётся тратить свои системные ресурсы и ждать, пока соединение не прервётся по таймауту, или вручную разрывать его.
Чаще всего тарпиты применяют для защиты. Технику впервые разработали для защиты от компьютерных червей. А сейчас её можно использовать, чтобы испортить жизнь спамерам и исследователям, которые занимаются широким сканированием всех IP-адресов подряд (примеры на Хабре: Австрия, Украина).
Одному из сисадминов по имени Крис Веллонс, видимо, надоело наблюдать за этим безобразием — и он написал маленькую программку Endlessh, тарпит для SSH, замедляющий входящие соединения. Программа открывает порт (по умолчанию для тестирования указан порт 2222) и притворяется SSH-сервером, а на самом деле устанавливает бесконечное соединение с входящим клиентом, пока тот не сдастся. Это может продолжаться несколько дней или больше, пока клиент не отвалится.
Установка утилиты:
$ make
$ ./endlessh &
$ ssh -p2222 localhost
Правильно реализованный тарпит отнимет у злоумышленника больше ресурсов, чем у вас. Но дело даже не в ресурсах. Автор пишет, что программа вызывает привыкание. Прямо сейчас в его ловушке 27 клиентов, некоторые из них подключены в течение недель. На пике активности в ловушке сидели 1378 клиентов в течение 20 часов!
В рабочем режиме сервер Endlessh нужно ставить на обычный порт 22, куда массово стучатся хулиганы. Стандартные рекомендации по безопасности всегда советуют переместить SSH на другой порт, что сразу на порядок сокращает размер логов.
Крис Веллонс говорит, что его программа эксплуатирует один абзац из спецификации RFC 4253 на протокол SSH. Немедленно после установления соединения TCP, но перед применением криптографии обе стороны должны послать идентификационную строку. И там же приписка: «Сервер МОЖЕТ послать другие строки данных перед отправкой строки с версией». И нет ограничения на объём этих данных, просто каждую строку нужно начинать с SSH-
.
Именно этим занимается программа Endlessh: она отправляет бесконечный поток случайно сгенерированных данных, которые соответствуют RFC 4253, то есть отправка перед идентификацией, а каждая строка начинается с SSH-
и не превышает 255 символов, включая символ окончания строки. В общем, всё по стандарту.
По умолчанию программа ожидает 10 секунд между отправками пакетов. Это предотвращает отключение по таймауту, так что клиент будет сидеть в ловушке вечно.
Поскольку отправка данных осуществляется до применения криптографии, программа исключительно простая. В ней не нужно внедрять никаких шифров и поддержку множества протоколов.
Автор постарался, чтобы утилита потребляла минимум ресурсов и работала абсолютно незаметно на машине. В отличие от современных антивирусов и других «систем безопасности», она не должна тормозить компьютер. Ему удалось минимизировать и трафик, и потребление памяти за счёт чуть более хитрой программной реализации. Если бы он просто запускал отдельный процесс на новое соединение, то потенциальные злоумышленники могли бы провести DDoS-атаку, открыв множество соединений для исчерпания ресурсов на машине. По одному потоку на соединение — тоже не лучший вариант, потому что ядро будет тратить ресурсы на управление потоками.
Поэтому Крис Веллонс выбрал для Endlessh самый легковесный вариант: однопоточный сервер poll(2)
, где клиенты в ловушке практически не потребляют лишних ресурсов, не считая объекта сокета в ядре и ещё 78 байт для отслеживания в Endlessh. Чтобы не выделять буферы получения и отправки для каждого клиента, Endlessh открывает сокет прямого доступа и напрямую транслирует пакеты TCP, игнорируя почти весь стек TCP/IP операционной системы. Входящий буфер вообще не нужен, потому что входящие данные нас не интересуют.
Автор говорит, что на момент своей программы не знал о существовании питоновского asycio и других тарпитов. Если бы он знал об asycio, то свою утилиту мог бы реализовать всего в 18-ти строчках на Python:
import asyncio
import random
async def handler(_reader, writer):
try:
while True:
await asyncio.sleep(10)
writer.write(b'%x\r\n' % random.randint(0, 2**32))
await writer.drain()
except ConnectionResetError:
pass
async def main():
server = await asyncio.start_server(handler, '0.0.0.0', 2222)
async with server:
await server.serve_forever()
asyncio.run(main())
Asyncio идеально подходит для написания тарпитов. Например, такая ловушка на много часов подвесит Firefox, Chrome или другого клиента, который пытается подключиться к вашему HTTP-серверу:
import asyncio
import random
async def handler(_reader, writer):
writer.write(b'HTTP/1.1 200 OK\r\n')
try:
while True:
await asyncio.sleep(5)
header = random.randint(0, 2**32)
value = random.randint(0, 2**32)
writer.write(b'X-%x: %x\r\n' % (header, value))
await writer.drain()
except ConnectionResetError:
pass
async def main():
server = await asyncio.start_server(handler, '0.0.0.0', 8080)
async with server:
await server.serve_forever()
asyncio.run(main())
Тарпит — отличный инструмент для наказания интернет-хулиганов. Правда, есть некоторый риск, наоборот, привлечь их внимание к необычному поведению конкретного сервера. Кто-то может подумать о мести и нацеленной DDoS-атаке на ваш IP. Впрочем, пока таких случаев не было, и тарпиты отлично работают.