Я случайно устроил DDoS-атаку на SSH-сервис своего университета: История с уроками
История IT-специалиста, случайно вызвавшего DDoS-атаку на университетский SSH-сервер. Узнайте, как это произошло и какие уроки можно извлечь для сетевой безопасности.
Я случайно устроил DDoS-атаку на SSH-сервис своего университета
Введение: Как случайная ошибка привела к крупному инциденту безопасности
Иногда маленькая ошибка может запустить цепную реакцию, последствия которой ощущаются по всей сети. История, которую я хочу рассказать, началась с простого скрипта для автоматизации рутинных задач и закончилась тем, что я по неосторожности устроил DDoS-атаку на SSH-сервер своего университета. Это был урок, который научил меня большему, чем любой учебник по сетевой безопасности. И сегодня я делюсь этой историей, чтобы помочь другим избежать подобных ситуаций.
Предыстория: Мои задачи и настройки SSH-сервера в университете
В университете я работал в исследовательской группе, нам часто приходилось обрабатывать большие объемы данных на удаленных серверах. Чтобы оптимизировать workflow, я решил настроить SSH-туннелирование для безопасного доступа к ресурсам.
Мой SSH-сервер был настроен на стандартном порту 22 с использованием ключей аутентификации для повышения безопасности. Я написал небольшой скрипт на Python, который автоматически подключался к нескольким серверам для сбора данных и выполнял команды параллельно. Казалось бы, ничего сложного — типичная задача для любого системного администратора.
Небольшая деталь, о которой я тогда не подумал — в университете было более 200 студентов и преподавателей, которые могли одновременно обращаться к тем же серверам через эту сеть.
Непреднамеренные действия: Что именно я сделал, что вызвало DDoS
В один из дней я решил доработать свой скрипт, добавив в него автоматическую повторную попытку подключения в случае сбоя. Казалось бы, разумное решение — чтобы скрипт не прерывался из-за временного сбоя сети. Вот как выглядела ключевая часть кода:
import paramiko
import time
def connect_and_execute():
while True: # Бесконечный цикл!
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('server.university.edu', username='myuser', key_filename='~/.ssh/id_rsa')
stdin, stdout, stderr = ssh.exec_command('data_collection_script.sh')
# ... обработка вывода
ssh.close()
break # Выход из цикла при успешном выполнении
except Exception as e:
print(f"Ошибка: {e}. Повторная попытка через 5 секунд...")
time.sleep(5)
Проблема была в том, что я забыл добавить ограничение на количество попыток. Когда скрипт обнаруживал сбой, он начинал бесконечно пытаться reconnect'ся, каждую отправляя запрос на сервер. А так как я запустил несколько таких скриптов параллельно (для разных серверов), нагрузка на SSH-сервис начала расти экспоненциально.
Первые признаки: Как я обнаружил проблему и ее масштаб
Первые тревожные сигналы появились через несколько часов после запуска скриптов. Сначала я заметил небольшую задержку при подключении к серверам через SSH. Потом задержки стали значительными — от нескольких секунд до минут.
Когда я попытался зайти на один из серверов вручную, соединение просто не устанавливалось. Я решил проверить системные логи и увидел что-то неестественное:
sshd[12345]: Failed password for invalid user admin from 192.168.1.100 port 22 ssh2
sshd[12346]: Did not receive identification string from 192.168.1.100 port 22
sshd[12347]: Maximum authentication attempts exceeded for invalid user admin from 192.168.1.100 port 22 ssh2
Похоже, что мой скрипт пытался подключиться с неверными учетными данными. Проверив сетевой трафик, я ужаснулся: мой компьютер отправлял на сервер более 100 запросов в секунду! А так как я запустил 5 таких скриптов, общая нагрузка на SSH-сервис достигала 500+ запросов в секунду.
Реакция и последствия: Что произошло с сетью университета и мои действия
Через несколько часов после начала инцидента в моей лаборатории появился системный администратор университета. Он спросил, не замечаю ли я чего-то странного в работе сети, так как SSH-сервис практически перестал отвечать.
Поняв, что именно я стал причиной проблемы, я сразу же:
- Остановил все запущенные скрипты
- Удалил бесконечные циклы из кода
- Добавил ограничение на количество попыток reconnect'а
Но к тому моменту ситуация уже вышла из-под контроля. Сеть университета начала работать со сбоями — SSH-сервис был практически недоступен, а некоторые внутренние ресурсы тоже пострадали от перегрузки.
Администраторы университета были вынуждены:
- Временно заблокировать мой доступ к сети
- Перезапустить SSH-серверы
- Изучить логи для понимания масштаба атаки
- Уведомить руководство университета о инциденте
В результате мне пришлось написать объяснительную и пройти небольшое "воспитательное мероприятие" от декана факультета информационных технологий.
Разбор инцидента: Технический анализа того, что именно произошло
Давайте разберемся, что именно произошло с технической точки зрения.
Что такое DDoS? DDoS (Distributed Denial of Service) — это распределенная атака на отказ в обслуживании, цель которой — сделать недоступным тот или иной сетевой ресурс. В моем случае атака была не распределенной в классическом смысле (не использовались компрометированные устройства), но ее эффект был схожий.
Как SSH уязвим к таким атакам? SSH-протокол спроектирован для обработки аутентификации каждого подключения. Когда приходит запрос, сервер выделяет ресурсы для проверки учетных данных. Если приходит огромное количество запросов, сервер может исчерпать ресурсы и перестать отвечать на легитимные запросы.
Особенно уязвимы серверы без ограничений на количество подключений с одного IP или без rate limiting. В нашем университете таких защит не было.
Что произошло именно в моем случае? Мой скрипт создавал множество одновременных подключений к SSH-серверу. Каждый раз, когда возникала ошибка (а она возникала часто из-за сетевых задержек), скрипт немедленно пытался установить новое подключение. Это привело к лавинообразному росту количества аутентификационных запросов, которые сервер не мог обработать вовремя.
Кроме того, мой скрипт иногда пытался подключаться с неверными учетными данными (из-за ошибки в обработке конфигурации), что заставляло сервер тратить дополнительные ресурсы на отклонение таких запросов.
Почему это было так разрушительно? В университете SSH-сервис критически важен — он используется студентами, преподавателями и исследователями для доступа к ресурсам, выполнению заданий, обработке данных. Когда сервис перестает отвечать, это парализует работу всего факультета. К тому же, перегрузка одного сервера создала каскадные сбои в других сетевых сервисах.
Уроки извлеченные: Что я узнал о сетевой безопасности и предотвращении подобных инцидентов
Этот инцидент научил меня нескольким важным вещам:
-
Никогда не использовать бесконечные циклы в сетевых скриптах — всегда добавляйте ограничение на количество попыток и время ожидания.
-
Обрабатывать исключения правильно — ошибки в сетевых операциях случаются постоянно, и код должен быть готов к ним.
-
Тестировать скрипты в изолированной среде — перед запуском на продакшене проверяйте все на тестовых серверах.
-
Всегда мониторить сетевой трафик — полезные инструменты вроде
iftop,nethogsили даже простыеnetstatмогут помочь обнаружить проблемы на ранней стадии. -
Знать ограничения сетевых сервисов — каждый сервис имеет свои пределы нагрузки, и важно понимать эти пределы.
-
Иметь план Б — всегда должен быть способ быстро остановить выполнение скрипта, если что-то пошло не так.
-
Рассматривать сетевые скрипты как потенциальные угрозы — даже беззлобный код может нанести ущерб, если написан бездумно.
Профилактика: Меры, которые теперь использую для предотвращения подобных ситуаций
После этого инцидента я изменил подход к написанию сетевых скриптов:
- Добавляю ограничение на количество попыток:
max_retries = 3
retry_count = 0
while retry_count < max_retries:
try:
# код подключения
break
except Exception as e:
retry_count += 1
if retry_count == max_retries:
raise e
time.sleep(5 * retry_count) # экспоненциальная задержка
- Использую таймауты:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('server.university.edu', username='myuser',
key_filename='~/.ssh/id_rsa', timeout=10)
- Реализую асинхронное выполнение с ограничением параллельных подключений:
from concurrent.futures import ThreadPoolExecutor, as_completed
MAX_PARALLEL = 5 # Максимум 5 одновременных подключений
with ThreadPoolExecutor(max_workers=MAX_PARALLEL) as executor:
futures = [executor.submit(connect_and_execute, server) for server in servers]
for future in as_completed(futures):
try:
future.result()
except Exception as e:
print(f"Ошибка при выполнении задачи: {e}")
- Добавляю логирование и мониторинг:
import logging
import sys
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('network_script.log'),
logging.StreamHandler(sys.stdout)
]
)
# В коде:
logging.info("Начинаю подключение к серверу...")
-
Тестирую в изолированной среде перед запуском на продакшене.
-
Использую системные меры защиты:
- Настроил rate limiting через SSH
- Включил fail2ban для блокировки подозрительных IP
- Ограничил количество одновременных сессий
- Добавил уведомления о пиковых нагрузках
Заключение: Важность осторожности при работе с сетевыми сервисами
История о том, как я случайно устроил DDoS-атаку, может показаться комичной, но за ней стоит серьезный урок. Работа с сетевыми сервисами требует не только технических знаний, но и осторожности, внимательности к деталям и понимания последствий своих действий.
Сегодня, когда я пишу любой скрипт, взаимодействующий с сетью, я вспоминаю тот день, когда мой "безобидный" автоматизатор парализовал работу университета. Эта история учит тому, что даже маленькая ошибка в коде может иметь огромные последствия, особенно в больших сетях.
Помните: с большой сетевой силой приходит большая ответственность. Будьте осторожны, тестируйте код, имейте план Б и всегда думайте о том, как ваши действия могут повлиять на других пользователей сети. Ведь когда вы нажимаете "Enter" для запуска скрипта, вы берете на себя ответственность за тысячи запросов, которые он отправит в сеть.