Ловушка (тарпит) для входящих 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. Впрочем, пока таких случаев не было, и тарпиты отлично работают.



    GlobalSign
    177,00
    Компания
    Поделиться публикацией

    Комментарии 63

      0
      А текст вообще вычитывался перед публикацией? Или подразумевается, что до некоторых с первого раза не доходит? :-)
        0
        я не глухой слепой, я не слепой. Поэтому не нужно мне повторять все по два раза, по два раза ))))
        +2

        Отличная статья, с удовольствием прочитал ее дважды :)
        А если серьезно, то реально попробую эту технику использовать

          +3
          Против ленивых хацкеров сработает, а неленивые доработают скрипты
            0
            Для таких fail2ban придумали и даже с экспоненциальной системой бана.
            +5
            Вещь нужная, но не не питоне. То что автор ее на C написал позволит ее запустить даже на OpenWRT. Но еще лучше ее как модуль/правил в для примера в iptables. Как здесь ruunix.ru/597-tarpit-lovushka-v-iptables.html
              +3
              Добавлю для Ubuntu/Debian:

              sudo apt install xtables-addons-dkms
              iptables -t raw -A PREROUTING -p tcp --dport 22 -j NOTRACK
              iptables -A INPUT -p tcp --dport 22 -j TARPIT

              Понадобятся хедеры ядра и компилятор, если ещё не стоят.
              +13
              К сожалению вещь просто веселая (и классная), но никак не полезная. И никто не будет дорабатывать скрипты. Эти сканеры работают во-первых массово, во-вторых их цели ну настолько кривые устройства, где ssh сервера реализованы такими Ляо на коленке, весь этот сонм криво настроенного IoT, что среди них девайсов со странным поведением — 50%. Вообще никто не заметит что одно соединение кривое, их у сканера будут тысячи и сами по себе без специализированного тарпита через жопу.
                +2
                Port knocking, имхо, удобнее и безопаснее. К тому же дорабатывать нужно только скрипты клиентов.
                  +2
                  ну о чём вы, какой port knocking? Конечно же лучше потратить десяток килобайт памяти спамера ценой отдельного сервиса у себя)
                    0
                    т.е. тарпит не сервис, а что-то такое ангельское эфемерное, не заметное для системы… :) под ддосом весьма нехило может ресурсов пожрать.
                    порт-кнокинг имхо гораздо надежнее. без ключа не открыт ни один лишний порт, и одного лишнего движения.

                    есть еще пинг-кнокинг под некоторые системы прощее стучатся получается.
                  +1

                  Вот не понимаю, зачем.
                  Вам шашечки или ехать?
                  У вас цель: обезопасить сервер или сделать мир лучше?
                  У меня, как правило, первая.
                  Поэтому sshd + blacklistd + ipfw ОТЛИЧНО справляются с данной задачей (по опыту, куда лучше fail2ban и sshguard).
                  И порт не меняю: а смысл?
                  Из-за того, что кто-то куда-то может пару раз постучать, корячиться и всегда помнить, что при ssh — одним способом порт указывать, при scp — другим?.. А скрипты: когда в одном проекте один порт, а в другом — он занят приложением?..
                  Безопасности добавли на ноль целых и ничего десятых, зато неудобства — на все 100 :)

                    +3

                    Для желающих тут же погрузиться в поиск: blacklistd хорош, но FreeBSD-only. Ждём, как говорится, e-buildов

                      +5
                      по опыту, куда лучше fail2ban и sshguard

                      Аргументировано.
                      На собственном опыте могу сказать, что "по опыту" как правило значит чуть более чем ничего. Кхе… кхе :)


                      На самом деле главный аргумент приспешников blacklistd ("не нужно парсить лог") "разбивается" единственным примером:
                      Демон sshd (при включенном UseBlacklist) отправит инфу (authentication success/failure) на blacklistd сокет, и… запишет это в лог. Ну а blacklistd прочитает её из сокета и распарсит.
                      Чем оно в принципе отличается от "распарсивания" fail2ban тех же строчек прочитанных из лога, никто так и не смог мне внятно объяснить.


                      Кроме того, fail2ban тоже это давно умеет, а в новых версиях уже официальным протоколом и для сообщения об отдельных неудачных попытках, т.е. достаточно отправить ему на сокет:


                      # отовсюду (симулируем простейший "маринад"):
                      f2b_attempt_msg="(lp0\nS'set'\np1\naS'%s'\np2\naS'attempt'\np3\naS'%s'\np4\na.<F2B_END_COMMAND><F2B_CLOSE_COMMAND>"
                      printf "$f2b_attempt_msg" sshd 192.0.2.1 | nc -q 0 -U '/run/fail2ban/fail2ban.sock'
                      # из питона:
                      s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM);
                      s.connect('/run/fail2ban/fail2ban.sock'); 
                      s.send(pickle.dumps(['set', 'sshd', 'attempt', '192.0.2.2', 'unknown user'], 0) + b"<F2B_END_COMMAND>"); s.close()
                      # ну и из шела:
                      fail2ban-client set <jail> attempt <IP>  [<failure-message1> ... <failure-messageN>]
                        0
                        Демон sshd (при включенном UseBlacklist) отправит инфу (authentication success/failure) на blacklistd сокет, и… запишет это в лог.

                        настраиваемо.


                        Ну а blacklistd прочитает её из сокета и распарсит.
                        Чем оно в принципе отличается от "распарсивания" fail2ban тех же строчек прочитанных из лога, никто так и не смог мне внятно объяснить.

                        лучше уж хотя бы тем, что не нужно в системе держать питон ради единственного fail2ban.
                        размер используемой памяти — очевидно же, не в пользу fail2ban.
                        и решение в случае sshd+blacklistd+ipfw получается "искаропки".

                          0

                          "искаропки" оно было бы, если бы sshd умел всё то сам (это однако противоречило бы философии *nix).

                        +1
                        Сделать мир лучше — тоже неплохая цель, особенно если требует всего несколько килобайт на сервере.
                          0

                          Это Вы так про питон пошутили? :)

                            0

                            питон во многих сборках Linux есть по-умолчанию. Не во всех, естественно, но устанавливать специально его чаще всего не требуется.

                          0
                          Не понял, какое неудобство от смены порта? Просто один раз написать пару строчек в ssh config? Менять порт рекомендуют наряду с другими мероприятиями по усилению безопасности. Ясное дело, что OpenSSH (обычно эта реализация ssh) дофига безопасный сам по себе, но нежеланных гостей все равно лучше не ждать с вывеской «Добро пожаловать, назовите пароль».
                            0
                            наверное, когда куча машин, логиниться хочется со всех на все, а puppet еще не настроили, тогда неудобно
                            0
                            В принципе можно и шашечки и ехать: вместо того, чтобы банить — отправлять в ловушку.
                            +5
                            отправляет бесконечный поток случайно сгенерированных данных

                            А как же стоимость исходящего трафика?
                              0
                              А там трафика и нет. 1 байт (1 пакет) в 10 секунд
                              +1
                              Подскажите, а какой смысл при работе Endlessh отправлять именно рандомные данные в строке с «SSH-»? Может быть можно ещё немного упростить программу?
                                +1
                                Чтоб враги сразу не догадались и заморочились поиском способа «расшифровки»?
                                  +3
                                  Так это до криптографии. Автор же писал, что можно отправлять любые данные до строки с версией. А просто так вещать не пойми кому свой рандом тоже не безопасно. Если генератор некачественный, то со стороны противника можно осуществлять анализ и это теоретически поспособствует другому вектору атак.
                                    +1
                                    Я ж не просто так взял в кавычки, я имел в виду: пускай потенциальный взломщик сидит и ломает голову, что же это такое, тратит ресурсы и время. А реальную криптографию даже в виду не имел.
                                      0
                                      Лучше как раз взять самый некачественный программный псевдогенератор (PRNG(x)), и пусть хоть сколько ломают себе голову. Потому как в системе и openssl используются совсем другие.
                                  +2
                                  59348 2465K TARPIT tcp — eth0 * 0.0.0.0/0 0.0.0.0/0 tcp multiport dports 20,22,25,80,135,139,5900,8080,8443 -j TARPIT --tarpit

                                  Это не сделает мир лучше прямо сразу, но при относительно массовом применении намного увеличит порог вхождения как для реальных жуликов, так и для бестолковых пионэров с кривыми ручками. И реально сократит мусорный трафик.

                                    +2
                                    С одной стороны да, а с другой — тарпнутые (пойманные в ловушку) соединения висят в conntrack и не протухают, так как тарпит не дает. Например, если тарпить на слабых роутерах, то из-за того, что контрэк распух — у вас снизится (и реально снижается!) скорость коммутации пакетов (табличку долго просматривать приходится). Очень этим грешили роутеры типа DIR-100, когда запуск torrent и последующее выключение забивали контрэк так, что до провайдера пинг подскакивал до сотен. Просто потому что соединения плодились, а не полоса была занята. Сейчас с этим получше, но в любом случае проблема остается.
                                    Опять же, можно написать (уже написано примерно во времена появления TARPIT в iptables, но гуглить лень) генератор пакетов, который навешает у вас TARPIT соединений, которые вы сами же будете поддерживать и забивать свой контрэк, а со стороны хакера это не будут соединения, а просто пара пакетов с железяки, которая даже не знает что такое TCP.
                                    Решение есть: на все порты, которые -j TARPIT делать еще -t raw -j NOTRACK (ну или совсем отказаться от conntrack)

                                    Но такие сложности уже напоминают ситуацию — обе стороны наелись гов.. мыла, и остались при своем

                                    Но все же в целом с вами соглашусь: использовать -j TARPIT, если уж хочется именно потроллить ботнеты (ведь сканят именно ботнеты, вручную мало что сканят), куда практичнее, чем запускать демона на питоне или даже С, симулирующего ssh
                                      +1
                                      iptables -t raw -A PREROUTING -p tcp --dport 22 -j NOTRACK
                                      iptables -A INPUT -p tcp --dport 22 -j TARPIT


                                      UPD: увидел, что вы уже написали про NOTRACK. Но всё равно пусть коммент висит, лишним не будет. Тратить conntrack на мусорные соединения действительно как-то не очень.
                                    +1
                                    DenyHosts еще лучше.
                                    +1
                                    Запустил питоновскую версию, добавив чуть логов. Несколько часов не было ни одного подключения, потом подзабыл об этом. Сейчас смотрю — 39 клиентов уже подключалось.
                                      +2
                                      Добавил еще немного логов, чтобы удобнее отслеживать длительность коннектов.
                                      В результате, 6 из 7 подключений отваливаются через 30 секунд, седьмое протянулось на 17 минут и 30 секунд.
                                        +1
                                        Если хотите больше подопытных, перенесите хонейпот на порт 445 или 23. А если 80 свободен, то перенесите его туда, поскольку там вся тусовка.
                                      +1
                                      Не очень понятно зачем это нужно. Повесить на 22 порт, а реальный sshd поднять на рандомном? И наказывать кулхацкеров?
                                        +4
                                        1378 бесполезных соединений на сервере висящих в течение 20 часов против ботнета который сканирует порты на уязвимости, за счет ресурсов зараженных пользователей, не совсем похоже на правильный подход. Всегда считал что лучше минимизировать свои затраты, чем сделать кому то хуже чем себе. Ханипоты нужны для обнаружения и предотвращения атак, но никак не для наказывания, замучаятесь наказывать.
                                          +1
                                          Я кстати в недоумении. 3 дня пытаюсь достучаться до поддержки avito и dns-shop по поводу их систем защиты. И в одном и в другом месте необычайная тупость технической поддержки.
                                          Дословный ответ: Наша система работает отлично, а вам не надо подключатся через VPN к нашему сайту.

                                          Ответ от других еще более глобален:
                                          image
                                            0
                                            Давно известно, что авито блокирует доступ к некоторым ресурсам сайта с адресов прокси и прочих левых диапазонов. И ещё википедия не даёт статьи редактировать.
                                              0
                                              В Википедии это вынужденная мера, чтобы затруднить многократную реинкарнацию заблокированного вандала. Добросовестному участнику с положительным вкладом при необходимости выдают технический флаг исключения из IP-блокировок, позволяющий редактировать с заблокированного IP-адреса, а также через прокси, VPN и Tor; 24–25 августа 2015 года в связи с тогдашними событиями активным участникам (более трёхсот) выдали такой технический флаг, а 27 августа его почти у всех забрали за ненадобностью.
                                          +2
                                          Python без отступов в форматировании работать не будет. Поправьте форматирование.
                                            +1
                                            Мне кажется, на продакшене правильный «хонейпот» должен привести к INPUT DROP.
                                              +1
                                              DROP сразу же говорит о том, что порт открыт и трафик фильтруется.
                                              Правильнее
                                              -A INPUT -p tcp -m tcp -j REJECT --reject-with tcp-reset
                                              -A INPUT -p udp -m udp -j REJECT --reject-with icmp-port-unreachable

                                              Так это выглядит закрытыми (не открытыми приложениями) портами, и в скане nmap, к примеру, порты будут 'closed', а не 'FILTERED'.
                                                –2
                                                Дроп ни о чём не говорит, а вот пришедший в ответ резет явно говорит о фильтрации.
                                                  +1
                                                  Вы заблуждаетесь и плохо себе представляете функционирование сетей. Всё именно так, как я сказал.
                                                  Когда кто-то обращается к хосту на порт, который не «слушает» ни одно приложение (порт closed), он (хост) отвечает ICMP-сообщением «icmp-port-unreachable» («Порт недоступен»). Если недоступен весь хост, то предыдущий роутер отправляет icmp-host-unreachable («Узел недоступен»).
                                                  Т. е. при нормальном функционировании сети пакеты не должны пропадать «в никуда» и бесследно. Таким образом, отсутствие ответа (и, к примеру, «повисшая» в стадии установки соединения TCP-сессия, как в примере) как раз говорит о фильтрации, и именно так и показывает это nmap. О чём прямо написано в «man nmap»:
                                                  «The state is either open, filtered, closed, or unfiltered. Open means that an application on the target machine is listening for connections/packets on that port. Filtered means that a firewall, filter, or other network obstacle is blocking the port so that Nmap cannot tell whether it is open or closed. Closed ports have no application listening on them… »

                                                  Так что, прежде чем что-то безапелляционно утверждать, — проверяйте себя.
                                                    +2
                                                    Стоит учитывать, что такое поведение характерно только для хоста, который торчит всеми открытыми портами непосредственно в Интернет. Если хост находится за брандмауэром (как частный случай, брандмауэр включен на самом хосте), в котором по умолчанию стоит deny all (DROP), то обычным поведением будет как раз DROP.
                                                  +1
                                                  Если используется INPUT DROP, то атакующий видит FILTERED для всех портов, вне зависимости от того, открыт порт или нет. Используя REJECT, атакующий видит то же самое, только вместо FILTERED, отображается CLOSED. Однако есть проблема: REJECT, в отличии от DROP, не обрывает соединение, а всё же отвечает на запрос.

                                                  Учитывая то, что ни в одном из этих случаев атакующий не может знать, используется тот или иной порт на целевой машине, то правильнее всего использовать DROP, а не заставить машину отвечать на ненужные запросы.
                                                0
                                                А для ftp ничего такого нет?
                                                Беглый поиск не дал результата.
                                                  +1
                                                  Для FTP в наше время есть переключение протокола на SFTP с авторизацией по ключу и скорейшему забыванию про существование FTP.
                                                    0
                                                    В своём блоге автор приводит пример для веб-сервера и говорит, что решение «идеально подходит» для создания подобных ловушек. Если немного его поправить, думаю, для FTP тоже должно работать.
                                                    +4
                                                    Небольшое замечание:

                                                    > И там же приписка: «Сервер МОЖЕТ послать другие строки данных перед отправкой строки с версией». И нет ограничения на объём этих данных, просто каждую строку нужно начинать с SSH-.

                                                    По поводу начала строки RFC говорит прямо противоположное: The server MAY send other lines of data before sending the version string. Each line SHOULD be terminated by a Carriage Return and Line Feed. Such lines MUST NOT begin with «SSH-»

                                                    > Именно этим занимается программа Endlessh: она отправляет бесконечный поток ..., а каждая строка начинается с SSH-

                                                    Чтобы убедиться что это не так достаточно сделать telnet localhost 2222
                                                    Или посмотреть в исходник.
                                                    static int
                                                    randline(char *line, int maxlen, unsigned long s[1])
                                                    {
                                                        int len = 3 + rand16(s) % (maxlen - 2);
                                                        for (int i = 0; i < len - 2; i++)
                                                            line[i] = 32 + rand16(s) % 95;
                                                        line[len - 2] = 13;
                                                        line[len - 1] = 10;
                                                        if (memcmp(line, "SSH-", 4) == 0)
                                                            line[0] = 'X';
                                                        return len;
                                                    }
                                                    </code>
                                                    
                                                    Странно, что никто этого не заметил. 
                                                    
                                                      0
                                                      А разве if (memcmp(line, "SSH-", 4) == 0) не проверяет, что строка начинается с «SSH-» и если да, то line[0] = 'X'; заменяет первый символ на «X»? То есть, такая строка становится «XSH-».
                                                        +2
                                                        Именно так. Для того чтобы выполнить требование RFC: lines MUST NOT begin with «SSH-»
                                                          0
                                                          Спасибо. Оказывается, это ошибка перевода. В оригинале автор пишет:
                                                          There is no limit on the number of lines, just that these lines must not begin with “SSH-“
                                                      0
                                                      localhost не является аргументом. Запускается лишь для tcp6, на гх есть репорт на эту тему.
                                                        0
                                                        Спасибо, познавательно! Главное — чтобы было весело.
                                                          0
                                                          скан портов разве не найдет настоящий порт ssh?
                                                            0
                                                            Если целью злоумышленников является один сервер, то они могут просканировать все его порты (все 216). Если же злоумышленники сканируют диапазон IP-адресов, то у каждого адреса они будут проверять несколько стандартных портов, или даже только 22 порт — потому что как бы много ресурсов ни было у злоумышленников, эти ресурсы не бесконечны. Впрочем, использование нестандартного порта — не единственная необходимая мера предосторожности; в частности, надо отключить SSH-доступ по паролю (оставить только по ключу).

                                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                          Самое читаемое