В юрфирме в Химках, 44 рабочих места, меня позвали к «тормозящей» почте. Веб-клиент открывался долго, письма подвисали — обычная заявка на обслуживание. Через десять минут грепа по логам я смотрел на чужой процесс с аптаймом в три недели. Это был май 2025-го. С тех пор я собрал в один список двенадцать признаков, по которым компрометация Zimbra видна в стандартных логах — и рядом честно отметил, какие из них дают ложные срабатывания, чтобы вы не подняли тревогу на пустом месте.
Двенадцать следов взлома Zimbra, которые видно в логах
Как одна заявка на «тормоза» превратилась в форензику
Юрист жаловался на скорость: письмо открывается пять-семь секунд, поиск по ящику думает минуту. В такой ситуации я обычно смотрю IOwait и буфер InnoDB — и в девяти случаях это они. Здесь IOwait действительно был высокий. Но источник оказался не там, где я ждал.
Первое, что я всегда делаю на незнакомом сервере, — смотрю, кто ест CPU, и сверяю процессы со списком служб Zimbra. Тут и вылез чужой:
ps -eo pid,etime,pcpu,user,comm --sort=-pcpu | head
# PID ELAPSED %CPU USER COMMAND
# 21883 21-04:12 690 zimbra kdevtmpfsi <- аптайм 21 день, 690% CPUАптайм 21 день. Процесс, которого нет в Zimbra. Дальше я уже не чинил тормоза — я разбирал взлом, а тормоза оказались его побочкой: майнер забивал CPU, оттого и IOwait, оттого и «медленная почта». Ложная версия про InnoDB отпала за минуту.
Ниже — двенадцать признаков, которые я с тех пор проверяю на каждом сервере, взятом на обслуживание. Все ищутся грепом по стандартным логам. Ни один не требует спецсофта.
Признаки 1–4: нагрузка, файлы, соединения, автозапуск
1. Аномальная загрузка CPU в нерабочие часы. Не «странный трафик», как принято думать, а именно ровное плато CPU ночью и в выходные. Это самый частый первый симптом. Майнеру не нужен трафик — ему нужны ваши ядра.
2. Свежие файлы в /opt/zimbra и /tmp позже даты последнего апгрейда. Логика простая: после апгрейда легитимные файлы Zimbra не меняются сами. Всё, что новее, — подозрительно.
# файлы, изменённые позже установки последнего патча
find /opt/zimbra /tmp /dev/shm -type f -newermt '2025-04-01' \
-not -path '*/log/*' -printf '%TY-%Tm-%Td %p\n' 2>/dev/null | sort3. Исходящие соединения от процессов не из списка служб Zimbra. ss -tnp покажет установленные сессии; если сокет держит процесс, которого нет среди служб Zimbra, — это разговор с C2 или пулом майнинга.
4. Свежие systemd-юниты и cron-задачи от root, не совпадающие с датой установки системы. Персистентность живёт здесь. Юнит с общим именем вроде «System Logger» и бинарём в /lib/udev/ — классическая маскировка.
ss -tnp | grep ESTAB | grep -v -E 'zimbra|mysqld|slapd'
systemctl list-unit-files --state=enabled | grep -vi zimbra
crontab -l -u root; ls -la /etc/cron.dПризнаки 5–8: почта как оружие — релей, base64, чужие адреса
5. Исходящая очередь с адресами, которых нет в вашем домене. Если mailq полон писем от отправителей, которых у вас не существует, ваш сервер используют как открытый релей для спама. Это бьёт по репутации домена сильнее простоя.
mailq | head -40
qshape deferred | head # для каких доменов копится
grep 'reject' /var/log/maillog | tail6. base64-строки в полях заголовков писем в maillog. Это след эксплуатации postjournal: атакующие прятали команды в закодированных полях письма. Длинные base64-блоки там, где должен быть обычный заголовок, — красный флаг. Ищутся так:
# длинные base64-куски в адресных полях транспортного лога
grep -oE 'to=<[^>]*[A-Za-z0-9+/]{40,}={0,2}[^>]*>' /var/log/maillog | head
# что вообще ходило через сам postjournal
grep -E 'postjournal|:10027' /var/log/maillog | tail -20Оговорюсь сразу, потому что на этом обжигаются: base64 в письмах встречается и совершенно законно. Тема письма на кириллице приезжает как =?UTF-8?B?...?=, и это тоже base64. Смотреть надо не на кодировку саму по себе, а на её место: закодированный кусок в адресном поле, в to= или from=, — вот это ненормально. В теме — обычный вторник.
7. Подставные Gmail-адреса в подозрительных письмах. В известных кампаниях против ZCS команды доставлялись письмами с фальшивых Gmail-адресов. Само по себе письмо с gmail — норма; в связке с base64-полями и следующим пунктом — уже нет.
8. Всплеск REJECT/relay-попыток в maillog. Резкий рост отказов на relay или попыток отправки на внешние домены с самого сервера — сигнал, что кто-то тестирует ваш MTA как релей или уже гонит через него. Важна не абсолютная цифра, а форма графика: пара сотен reject в сутки — это фон, который есть у всех, а вот скачок в десять раз за один день по сравнению с прошлой неделей значит, что вами кто-то предметно занялся. Поэтому считать надо по дням, а не одним числом за весь файл:
grep 'reject' /var/log/maillog | awk '{print $1" "$2}' | uniq -c | tail -14Я эту команду держу в заметках именно ради формы: четырнадцать строк, четырнадцать дней, и провал или всплеск видно глазом за секунду, без всякой графаны.
Признаки 9–12: вебшелл, DOS-фильтр, странные логины, аптайм
9. Обращения к незнакомым .jsp в access-логах. Вебшелл в Zimbra — это .jsp-файл с параметром-командой. Запросы вида w.jsp?k=...&c= к файлам, которых нет в штатной поставке, — прямое попадание.
grep -E '\.jsp\?.*(cmd|c=|exec|k=)' /opt/zimbra/log/access_log* | tail
grep -rE 'Runtime|ProcessBuilder' /opt/zimbra/jetty*/webapps 2>/dev/null | head10. Срабатывания DOS-фильтра по IP в mailbox.log. Строка Access to IP suspended, for repeated failed login — встроенная защита Zimbra заблокировала IP после серии неудачных входов. Одна-две — норма, шквал с одного адреса — брутфорс.
11. Успешные логины в неурочное время и из чужой географии. Вход администратора в 03:40 с адреса, которого раньше не было, — повод проверить. Ищется по mailbox.log грепом на oip= и статус аутентификации.
12. Процесс с аптаймом больше, чем время с последнего рестарта Zimbra. Тот самый признак из Химок. Если чужой процесс живёт дольше, чем сами службы почты, — он не часть Zimbra, он поверх неё.
grep -E 'AuthRequest|oip=' /opt/zimbra/log/mailbox.log | grep -i success | tail
grep 'suspended' /opt/zimbra/log/mailbox.log | awk '{print $NF}' | sort | uniq -cПроверьте у себя за 2 минуты
Не надо гонять все двенадцать проверок сразу. Вот минимальный набор, который за пару минут отделит «чисто» от «надо копать глубже». От root, только чтение.
# кто ест CPU и не из Zimbra ли это
ps -eo pid,etime,pcpu,user,comm --sort=-pcpu | head
# исходящие соединения от нештатных процессов
ss -tnp | grep ESTAB | grep -v -E 'zimbra|mysqld|slapd|nginx'
# чужие письма в очереди (сервер как релей)
mailq | grep -c '@' ; qshape deferred | head| Что увидели | Трактовка | Ложное срабатывание? |
|---|---|---|
| Процесс не из Zimbra, аптайм больше, чем у mailboxd | Почти наверняка компрометация | Крайне редко |
| Исходящие соединения от нештатного процесса | Разговор с C2 или пулом | Иногда — агент мониторинга; сверьте, что за процесс |
| Очередь полна писем от несуществующих отправителей | Открытый релей | Проверьте бэкскаттер: bounce-письма выглядят похоже |
| Шквал DOS-suspended с одного IP | Брутфорс | Часто ложь: забытый клиент со старым паролем даёт то же |
Обратите внимание на последний столбец. Половина работы — не найти совпадение, а отсеять ложное. Об этом отдельный раздел, потому что именно здесь новичок либо паникует зря, либо, наоборот, списывает реальный след на «наверное, ерунда».
Где эти признаки врут — и как не поднять тревогу зря
Каждый второй признак из списка умеет срабатывать вхолостую. Разберу самые коварные, чтобы вы не звонили мне в панике из-за штатного поведения.
DOS-фильтр по IP (признак 10). Самый частый ложняк. Забытый на телефоне почтовый клиент со сменённым паролем долбится каждые пять минут и исправно ловит suspended. Выглядит как брутфорс, а это ваш же уволившийся сотрудник. Отличие: у брутфорса адреса меняются и меняются логины-цели; у забытого клиента — один IP и один логин.
Чужие адреса в очереди (признак 5). Бэкскаттер — обратные отбойники на подделанного вас — заполняет очередь письмами, которые выглядят как чужие. Это не всегда релей. Смотреть надо, кто отправитель по факту (ваш сервер или внешний) и куда идёт письмо.
Письма с Gmail (признак 7). Сам по себе ноль информации: вам пишут с gmail постоянно. Признак работает только в связке — gmail-адрес плюс base64 в заголовках плюс временная близость к появлению чужого процесса.
Свежие файлы в /opt/zimbra (признак 2). Логи и индексы меняются постоянно — их надо исключать из поиска, иначе утонете в шуме. Именно поэтому в команде выше стоит -not -path '*/log/*'.
Ночное плато CPU (признак 1). Тот самый признак, с которого я начал список, — и он же умеет врать чаще прочих. У Zimbra ночью своя жизнь: обучение антиспама zmtrainsa, обновление баз ClamAV, ротация и сжатие логов, а если у клиента настроен бэкап или переиндексация — тем более. Всё это честно грузит процессор в три часа ночи. Отличать просто: штатные ночные задачи отрабатывают и заканчиваются, а майнер держит ровное плато и в три ночи, и в шесть утра, и в воскресенье. Посмотрите sar -u за неделю, если установлен sysstat, — форма кривой скажет больше, чем любая единичная цифра.
Моё правило: один признак — повод посмотреть внимательнее, не повод для выводов. Тревога обоснована, когда сходятся два-три независимых. В Химках сошлись сразу четыре: аптайм, исходящее соединение, свежий бинарь и cron от root.
Чем закончилось в Химках и сколько это стоило
Сервер оказался скомпрометирован больше трёх недель — по аптайму процесса. Всё это время он майнил на чужой кошелёк и параллельно тормозил юристам работу: те самые пять-семь секунд на письмо. 44 человека ежедневно теряли на подвисаниях по несколько минут — в сумме это часы рабочего времени юрфирмы в неделю, оплаченные впустую.
Но денежный ущерб от тормозов — мелочь на фоне главного риска. Три недели чужого доступа к почтовому серверу юрфирмы означают, что атакующий мог читать переписку по делам клиентов. Доказать, что не читал, невозможно. Именно эта неопределённость и есть настоящая цена — её не выразить в часах простоя.
Часы я всё же посчитал, потому что «несколько минут» звучит несерьёзно, пока не умножишь. Замеряли на трёх машинах: в среднем 12 минут в день на человека уходило на ожидание — открытие письма, поиск, вложения. Сорок четыре человека — это 8,8 часа в день, за пятнадцать рабочих дней компрометации 132 часа. Час сотрудника юрфирмы со всеми налогами и взносами обходится работодателю примерно в 1 000 рублей: 132 000 рублей просто выброшены, и это ещё без единого сорванного срока. Разбор, чистка и вывод почты на новый контур встали клиенту в 94 000 — то есть дешевле, чем один месяц жизни с майнером.
Разбор занял день, чистка и изоляция — ещё несколько, дальше сервер выводили на новый контур со сменой всех паролей. Если бы эти двенадцать проверок прогнали превентивно, при плановом обслуживании, взлом поймали бы в первые же сутки, а не на третьей неделе.
Что из этого следует вам
Взлом Zimbra редко приходит с табличкой «вас взломали». Он приходит как «почта тормозит», «письма подвисают», «сервер греется». За бытовой жалобой пользователя может стоять чужой процесс с трёхнедельным аптаймом, и увидеть его — вопрос десяти минут грепа, а не дорогого аудита.
Прогоните у себя минимальный набор из блока «проверьте за 2 минуты». Если хоть один признак сработал и вы не уверены, ложный он или нет, — пришлите мне вывод трёх команд: ps с аптаймом процессов, ss -tnp по установленным соединениям и mailq с началом очереди. За день отвечу, что из этого шум, а что — реальный след, и если след — с чего начинать, чтобы не спугнуть и не затереть улики.
Частые вопросы
Мой сервер просто тормозит. Это точно взлом?
Не точно — тормоза бывают и от честных причин: высокий IOwait, маленький буфер InnoDB, забитый индексный том, нехватка памяти под Java heap. Но проверить на взлом стоит первым делом, потому что это быстро и потому что майнер даёт ровно такую же картину: забивает CPU, поднимает IOwait, и почта еле шевелится. В Химках заявка была именно «тормозит», а под ней сидел майнер с аптаймом 21 день. Начните с ps на аптайм процессов и сверки со списком служб Zimbra — если чужого процесса нет, тогда уже копайте InnoDB и память.
Что важнее искать — необычный трафик или что-то ещё?
Вопреки расхожему мнению, не трафик. Самый частый первый симптом компрометации — аномальная загрузка CPU в нерабочие часы, а не всплеск трафика. Майнеру трафик почти не нужен, ему нужны ваши ядра, поэтому он и виден как ровное плато CPU ночью и в выходные, когда в офисе никого. Трафик вырастает позже — когда сервер начинают использовать как релей для спама. Так что смотрите в первую очередь на CPU по времени суток и на список процессов, а сетевые аномалии — вторым шагом.
Я нашёл в очереди кучу писем от адресов, которых у нас нет. Это релей?
Возможно, но не обязательно. Есть похожий по виду, но безобидный случай — бэкскаттер: когда спамеры подделывают ваш домен как отправителя, к вам возвращаются отбойники (bounce), и очередь заполняется письмами, которые выглядят чужими. Настоящий открытый релей отличается тем, что письма реально отправляются с вашего сервера на внешние домены, а не приходят как отказы. Посмотрите qshape deferred и логи maillog: если ваш сервер сам инициирует отправку на чужие домены и в reject нет отбоя — это релей, надо срочно закрывать mynetworks.
Насколько можно доверять срабатыванию DOS-фильтра по IP?
С осторожностью — это признак с самым высоким процентом ложных тревог. Строка про suspended в mailbox.log означает, что Zimbra заблокировала IP после серии неудачных логинов, и чаще всего виноват не хакер, а забытый почтовый клиент со старым паролем: телефон уволившегося сотрудника, неотключённый скрипт, устройство с просроченными кредами. Отличить брутфорс просто: у атаки адреса и логины-цели меняются, у забытого клиента — один IP долбится в один и тот же логин. Считать это взломом можно только в связке с другими признаками.
Сколько признаков должно совпасть, чтобы бить тревогу?
Моё рабочее правило — два-три независимых. Один признак почти всегда имеет невинное объяснение, и делать по нему выводы — путь к ложной тревоге либо к панике на ровном месте. А вот когда сходятся, например, чужой процесс с большим аптаймом, его исходящее соединение наружу и свежий cron от root — это уже не совпадение, это картина. В Химках сошлись сразу четыре признака, и сомнений не осталось. Если у вас совпало два и больше и вы не уверены — не гадайте, пришлите вывод команд, разберём вместе.
Оставить комментарий