Логи обрываются на середине ночи: признак, что их подчистили
Если при разборе инцидента вы видите, что auth.log живёт ровно до 03:14, а следующая запись — уже в 07:02, и между ними тишина, хотя сервер явно не выключали, — это не совпадение и не баг ротации. Опытный злоумышленник, получив доступ, первым делом чистит следы: удаляет строки, вырезает куски файла, иногда переписывает лог целиком. Разберём, как отличить обрыв логов от нормальной картины и почему единственная надёжная защита — не хранить их единственную копию на том же хосте, который могут скомпрометировать.
Содержание
- Почему в логах вообще остаётся дыра
- Разрыв во времени: первый и самый простой сигнал
- Сверка временной метки файла с последней записью внутри
- Разрывы в последовательности: там, где есть номера событий
- wtmp/utmp/btmp — вторая линия правды для SSH-сессий
- Централизованный удалённый syslog — единственная защита, которая реально работает
- Что делать, если разрыв найден
Почему в логах вообще остаётся дыра
После успешного взлома у атакующего простая логика: чем меньше следов, тем дольше он остаётся незамеченным. Самая грубая чистка — > /var/log/auth.log или rm -f /var/log/*.log, но это заметно сразу: файл пуст или отсутствует, такое палится любым мониторингом за минуту. Опытные вырезают только свои строки: находят интервал своей активности и удаляют записи именно за него, оставляя остальное нетронутым. Здесь и рождается «дыра» — участок времени, за который в логе физически не может не быть записей (система работает, кто-то логинится по cron, systemd что-то делает), а записей нет.
Частые инструменты для точечной правки: sed -i '/паттерн/d', ручное редактирование в vi/nano, готовые log-cleaner скрипты (в духе классических wtmpclean, logclean из арсенала руткитов нулевых — сама идея не изменилась). Реже — подмена файла целиком: атакующий генерирует правдоподобный лог без своих следов и подкладывает его вместо оригинала. Такую подмену сложнее поймать по содержимому, но она почти всегда прокалывается на метаданных файла.
Если вы нашли дыру именно там, где по другим признакам (странный процесс, новый ключ в authorized_keys, исходящий трафик на незнакомый IP) шла атака — считайте компрометацию подтверждённой, а не «возможной». Пустое место в логах в нужный момент — один из самых сильных индикаторов, доступных админу без выделенного SIEM.
Разрыв во времени: первый и самый простой сигнал
Прежде чем лезть в инструменты, стоит просто посмотреть на временную шкалу. У большинства сервисов есть фоновая активность, которая пишет в лог с более-менее регулярным интервалом:
sshd— banner-обмены и попытки подключения от сканеров ботнетов (в интернете почти нет момента тишины дольше 10-15 минут, если порт 22 открыт наружу);cron/systemd— плановые задачи, таймеры;sudo— если кто-то из легитимных пользователей работает;- сам
rsyslogd/journald— периодические mark-сообщения, если включены ($ModLoad immarkв rsyslog).
Практическая проверка — посчитать записи по часам и посмотреть на распределение:
awk '{print $1, $2, $3}' /var/log/auth.log | uniq -c | tail -50
# или, если формат ISO (systemd/rsyslog RFC5424):
grep -oP '^\S+T\d{2}' /var/log/auth.log | uniq -c
Резкий провал до нуля на несколько часов подряд там, где рядом идёт нормальный поток записей — повод присмотреться, особенно если провал совпадает с ночным временем и не совпадает ни с одним плановым простоем.
Отдельно проверьте journald — у него есть встроенный маркер границ загрузок:
journalctl --list-boots
journalctl --since "2026-08-20 02:00" --until "2026-08-20 08:00"
Если за интересующий период journalctl пуст, а перезагрузок в --list-boots не было — сигнал тот же, что и с плоскими файлами, но по другому источнику. Сверяйте оба: если в journalctl записи есть, а в /var/log/auth.log за тот же час пусто — чистили именно плоский файл, а бинарный journald (его сложнее аккуратно поправить руками) остался нетронутым, или наоборот.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверСверка временной метки файла с последней записью внутри
Это недооценённая проверка, и она не требует ничего кроме stat и tail. Идея простая: если файл дописывается штатно (демон открыл его на дозапись и просто добавляет строки), время модификации (mtime) должно практически совпадать с временной меткой последней строки внутри — разница в секунды, максимум минуты при буферизации. Если же кто-то открыл файл в редакторе, вырезал середину и сохранил, mtime станет равен моменту сохранения, а содержимое внутри при этом не изменится синхронно — открытие/пересохранение файла редактором почти всегда оставляет такое несоответствие.
stat --format='%Y %n' /var/log/auth.log
date -d @$(stat -c %Y /var/log/auth.log)
tail -1 /var/log/auth.log
Если mtime файла свежее, чем время последней записи, больше чем на разумный буфер (для rsyslog — секунды) — файл трогали не через штатную дозапись демона. Это же касается ротированных копий (auth.log.1, auth.log.2.gz) — у сжатых архивов mtime не должен меняться после ротации, и если у более старого архива метка новее, чем у более свежего — кто-то в него залезал.
Ещё один косвенный маркер — inode:
ls -li /var/log/auth.log*
При штатной ротации через logrotate (с директивой create, а не copytruncate) создаётся новый inode для активного файла в соответствии с расписанием. Если inode меняется без привязки к плановой ротации (её тоже видно — в /var/lib/logrotate/status или в cron-логе) — типичный побочный эффект скрипта, который сначала копирует лог, вырезает нужное, а потом кладёт обратно уже как новый файл.
Разрывы в последовательности: там, где есть номера событий
Плоские текстовые логи вроде auth.log номеров строк не хранят — там только время, и пропуск виден лишь по здравому смыслу («тут должно было что-то быть, а нет»). Но есть источники со встроенной последовательностью, где дыру находят формально, без интерпретации.
auditd. Каждое событие в /var/log/audit/audit.log содержит msg=audit(...): с уникальным serial-номером. Номера идут по возрастанию без пропусков в рамках сессии аудита (скачок при рестарте демона — нормален):
ausearch -i --start today | grep -oP 'audit\(\d+\.\d+:\K\d+' | sort -n | awk 'NR>1 && $1-prev>1 {print "разрыв:", prev, "->", $1} {prev=$1}'
Скачок на десятки-сотни номеров без соответствующего рестарта auditd в этот момент — почти гарантированный признак того, что часть событий физически не попала в файл (иногда это просто отключение аудита на время атаки правилом auditctl -e 0, что тоже видно).
journald. Бинарный журнал хранит внутренние счётчики целостности (seqnum, hash-цепочку между записями при включённом FSS — Forward Secure Sealing). Проверка встроенным верификатором:
journalctl --verify
Без FSS --verify проверяет только консистентность файла (что записи не битые), но прямое редактирование бинарного файла руками, как правило, всё равно ломает внутреннюю структуру и обнаруживается.
Сервисы с собственной нумерацией (СУБД в режиме WAL, брокеры очередей, кастомные аудит-логи) — самый надёжный источник для проверки на дыры: подделать правдоподобное продолжение последовательности, не зная внутреннего состояния сервиса, сложнее, чем вырезать строки из текстового файла.
wtmp/utmp/btmp — вторая линия правды для SSH-сессий
auth.log (или соответствующий journald-юнит sshd.service) — не единственное место со следом входа по SSH. Отдельно, в бинарном формате, ведутся:
/var/log/wtmp— история успешных входов и выходов (last);/var/log/btmp— история неудачных попыток входа (lastb);/var/run/utmp— текущие активные сессии (who,w).
Вычистить auth.log (текстовый, легко редактируется построчно) и вычистить wtmp (бинарный, фиксированная структура записи) — задачи разной сложности. Продвинутые руткиты умеют бить и wtmp, но на практике до этого доходит не всегда: атакующий часто подчищает только текстовые логи, до которых руки дошли в первую очередь, и не успевает тронуть бинарные.
Практика сверки:
last -F -f /var/log/wtmp | head -30
lastb -F -f /var/log/btmp | head -30
utmpdump /var/log/wtmp | less
Берёте список сессий из wtmp за интересующий период и построчно сверяете с тем, что осталось в auth.log: каждой записи Accepted password/publickey for ... from ... в auth.log должна соответствовать сессия в wtmp, и наоборот. Если в wtmp есть вход в 03:22 с незнакомого IP, а в auth.log за это время — та самая дыра из раздела выше, это уже не гипотеза, а прямое доказательство: сессия была, запись о ней удалили.
Обратная ситуация тоже показательна: если запись в auth.log есть, а в wtmp для неё нет соответствующей строки — стоит проверить, не через su/sudo -i ли был получен доступ в обход полноценного login-сеанса (это не обязательно чистка, но повод разобраться отдельно).
Централизованный удалённый syslog — единственная защита, которая реально работает
Всё, что описано выше — методы обнаружения постфактум, и они работают только если атакующий не подчистил лог идеально (а идеально получается редко, особенно под давлением времени). Но правильный порядок действий — не полагаться на удачу в обнаружении, а лишить локальную чистку смысла заранее. Если root на скомпрометированном хосте не может добраться до единственной копии логов, потому что копия уже уехала на другой сервер в момент записи, — вычистить локальный файл бессмысленно: полная картина останется на приёмнике.
Схема простая: сервер-приёмник логов (отдельная VPS вне зоны компрометации основного проекта — другой аккаунт, другой доступ по SSH, в идеале другой провайдер) принимает поток от rsyslog/syslog-ng/journald с боевых серверов по сети, чаще всего TCP с TLS, и пишет к себе на диск. Даже если атакующий получит root на боевом сервере и вычистит там всё подчистую, на приёмнике останется то, что успело уйти до момента взлома.
Минимальная настройка на стороне клиента (rsyslog, Debian/Ubuntu/AlmaLinux):
# /etc/rsyslog.d/90-remote.conf
*.* action(type="omfwd"
target="log-collector.internal"
port="6514"
protocol="tcp"
action.resumeRetryCount="-1"
queue.type="LinkedList"
queue.filename="fwdrule1"
queue.saveOnShutdown="on"
)
queue.saveOnShutdown и resumeRetryCount="-1" важны отдельно: они гарантируют, что при недоступности сети записи не теряются, а копятся в очереди на диске и досылаются при восстановлении связи — без этого при кратковременном разрыве сети теряется именно тот кусок логов, который мог быть самым важным.
На стороне приёмника — тот же rsyslog в режиме сервера:
# /etc/rsyslog.d/00-server.conf
module(load="imtcp")
input(type="imtcp" port="6514")
$template RemoteLogs,"/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log"
*.* ?RemoteLogs
Для journald аналогичный эффект даёт ForwardToSyslog=yes в /etc/systemd/journald.conf — тогда журнал уходит в локальный rsyslog, а тот уже пересылает удалённо по правилу выше.
Если не хочется собирать rsyslog-связку руками, тот же результат — с интерфейсом поиска, ретеншном и алертами — дают Graylog или Grafana Loki: агент на каждом хосте шлёт логи на центральный инстанс, и локальная чистка снова не имеет смысла, потому что оригинал уже не там. Про то, как это разворачивается на практике, у нас есть отдельный разбор: сбор логов с нескольких серверов.
Две вещи, о которых обычно забывают при внедрении удалённого логирования. Во-первых, доступ на приёмник должен быть строго ограничен и отделён от основного проекта: если у скомпрометированного сервера есть ключ с доступом на запись и удаление на приёмнике, атакующий с root дотянется и туда — приёмник принимает только входящий поток по протоколу логирования, а SSH-доступ к нему держите на отдельных ключах, желательно с 2FA. Во-вторых, chattr +a /var/log/auth.log (append-only) — не защита, а задержка: она не даёт дописывать файл иначе чем в конец даже от root, но root с правами CAP_LINUX_IMMUTABLE снимает флаг командой chattr -a за секунду. Ставьте её как дополнительный барьер, но не как замену удалённой копии — так же как auditd в immutable-режиме (-e 2) защищает только конфигурацию правил, а не уже записанные логи.
Что делать, если разрыв найден
Обнаружив дыру, важно не начинать чистить или перезапускать сервисы раньше, чем зафиксировано текущее состояние — иначе потеряете и то немногое, что осталось. Порядок такой:
- Снимите копию всех логов «как есть» (включая архивы и бинарные wtmp/btmp) на отдельный носитель, желательно через
cp -aс сохранением атрибутов, до перезагрузки сервера — часть улик (открытые сетевые соединения, процессы в памяти) переживает только до первого reboot. - Сверьте найденный интервал с другими источниками: логами файрвола, историей подключений на балансировщике или у хостера, метриками нагрузки за этот период — если инфраструктура успела их снять.
- Проверьте
authorized_keys, crontab всех пользователей и список systemd-таймеров — типичные точки закрепления после первого доступа. - Дальше действуйте по стандартному плану реагирования, а не по наитию — у нас есть пошаговый разбор: взломали сервер: пошаговый план.
- После восстановления обязательно поднимите удалённое логирование, если его не было: в следующий раз обрыв в логах перестанет быть проблемой, потому что копия будет не на том сервере, который снова могут скомпрометировать.
Если систематической защиты аудита нет вовсе, параллельно стоит развернуть auditd: он ловит события уровня ядра (изменение файлов, повышение привилегий), которые обычный auth.log не видит в принципе — настройку разбирали отдельно: аудит логов сервера: настройка auditd.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли восстановить удалённые строки из auth.log, если удалённой копии нет?
Иногда — если строки вырезаны через sed, а не перезаписан весь файл, место на диске может ещё хранить старые данные блока. Это задача для форензик-инструментов (extundelete, анализ журнала ext4, photorec), и гарантии успеха нет: шанс падает с каждой записью поверх. Рассчитывать на это как на план A не стоит — только как на крайнюю меру при уже случившемся отсутствии резервной копии.
Разве атакующий не заметит, что логи уходят на другой сервер, и не отключит пересылку?
Может — ss -tnp или содержимое /etc/rsyslog.d/ это покажет. Но смысл централизованного логирования не в скрытности, а в том, что к моменту обнаружения значительная часть событий уже ушла на приёмник. Даже отключение пересылки в середине сессии оставляет полную картину до этого момента — а она обычно и содержит момент первичного проникновения.
Как часто проверять логи на разрывы, если атаки не было?
Вручную не нужно — это задача для автоматики: cron-скрипт, сравнивающий число строк за час с историческим средним, или правило в SIEM/Graylog на «отсутствие ожидаемого потока событий» справляется лучше человека и не требует, чтобы кто-то помнил зайти и посмотреть.
Сколько нужно хранить централизованные логи, чтобы хватило для расследования?
Зависит от того, как быстро вообще обнаруживается инцидент — а это часто недели, а не дни. Ориентиры по срокам хранения разбирали отдельно: сколько хранить логи.
Если логи шлются и локально, и удалённо — не проще ли читать локальные?
Для повседневной диагностики — да, быстрее и без сетевой задержки. Но при разборе инцидента доверять стоит только удалённой копии: любые данные, до которых физически мог дотянуться злоумышленник с root, считаются потенциально изменёнными, пока не доказано обратное сверкой с независимым источником.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →