Endlessh — фиктивный SSH-сервер для борьбы с перебором паролей

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

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

Для предотвращения обрыва соединения по таймауту используется особенность протокола SSH, который допускает отправку сервером произвольного числа строк с информацией о сервере до вывода строки «SSH-«, сигнализирующей о начале обмена данными для аутентификации. После подключения клиента, Endlessh периодически отправляет клиенту случайные строки без вывода приглашения «SSH-«, что мешает удалённой стороне завершить соединение по таймауту. Таким образом соединение может оставаться открытым длительное время (несколько дней) и сеанс SSH-клиента оказывается заблокированным.

Установка Endlessh

Клонируем endlessh из репозитория GitHub с помощью команды:

git clone https://github.com/skeeto/endlessh

После установки перейдите во вновь созданный каталог с помощью команды:

cd endlessh

Скомпилируйте endlessh с помощью команды:

make

Установите endlessh с помощью команды:

sudo make install

Настройка

По умолчанию endlessh может работать только на портах выше 1024. Вносим изменения в служебный файл systemd.

sudo nano /etc/systemd/system/endlessh.service

В этом файле удалите символы # на следующей строке:

#AmbientCapabilities=CAP_NET_BIND_SERVICE

Также нужно закомментировать строку:

PrivateUsers=true

Сохраните и закройте файл.

Далее запускаем команду:

sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/endlessh

Откройте файл конфигурации endlessh с помощью команды:

sudo nano /etc/endlessh/config

Меняем порт с 2222 на 22.

Если вы обнаружите, что в этом файле ничего нет, вставьте следующее:

The port on which to listen for new SSH connections.
Port 22
The endless banner is sent one line at a time. This is the delay
in milliseconds between individual lines.
Delay 10000
The length of each line is randomized. This controls the maximum
length of each line. Shorter lines may keep clients on for longer if
they give up after a certain number of bytes.
MaxLineLength 32
Maximum number of connections to accept at a time. Connections beyond
these are not immediately rejected but will wait in the queue.
MaxClients 4096
Set the detail level for the log.
0 = Quiet
1 = Standard, useful log messages
2 = Very noisy debugging information
LogLevel 0
Set the family of the listening socket
0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)
4 = Use IPv4 only
6 = Use IPv6 only
BindFamily 0

Сохраните и закройте файл.

Не забудьте, что SSH должен быть настроен на порт отличный от 22

Запускам сервис:

sudo systemctl start endlessh
sudo systemctl enable endlessh

Смена порта SSH

  • Для смены порта SSH выполните следующую команду
    sed -i 's/#Port 22/Port $PORTYOUWANT/' /etc/ssh/sshd_config
  • Перезапустим SSH сервис для смены порта
    systemctl restart ssh
  • Проверяем, что порт SSH сменился
    netstat -tulpn |grep $PORTYOUWANT

Изменяем правила Firewall

  • вывести пронумерованный список UFW ufw status numbered
  • Удалите старые правила для 22 порта SSH ufw delete $RULENUMBER
  • Добавьте новые правила для  нового порта SSH ufw allow from $YOURIP to any port $PORTYOUWANT
  • Добавляем новые правила для 22 порта EndleSSH ufw allow from any to any port 22

Принцип действия

По умолчанию программа ожидает 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())

Добавить комментарий