MAATRIX / Блог / Майнер под именем kworker: как отличить системный процесс от подделки

Майнер под именем kworker: как отличить системный процесс от подделки

MAATRIX

Открываете top на сервере и видите десяток процессов kworker/0:1, kworker/u8:3, один из них ест 90% CPU — и первая мысль «это же ядро, наверное так надо». Именно на этой мысли и строится расчёт вредоносного ПО: имя kworker настолько привычно в выводе ps и top, что администратор пролистывает его взглядом, даже не открывая /proc. Ниже — как за пару минут отличить настоящий поток ядра от процесса, который просто назвался так же.

Что такое kworker на самом деле

kworker — это worker-поток механизма workqueue в ядре Linux. Ядру постоянно нужно выполнять отложенную работу вне контекста прерывания: обработку событий диска, сети, USB, отложенные таймеры и так далее. Вместо того чтобы создавать под каждую задачу отдельный поток, ядро держит пул worker-потоков и раздаёт им задания из очередей.

Отсюда и имена вида kworker/0:1 или kworker/u8:3H:

  • число до двоеточия — номер CPU, к которому привязан поток (или u + номер unbound-пула, если поток не привязан к конкретному ядру);
  • число после двоеточия — порядковый номер worker-а в пуле на этом CPU;
  • суффикс H означает high-priority очередь.

Все kworker-потоки — это дети kthreadd, процесса с PID 2, который в Linux отвечает за порождение всех потоков ядра (kswapd, ksoftirqd, migration, rcu_* — все оттуда же). Это ключевая деталь, к которой мы вернёмся при проверке.

Важно: то, что у вас одновременно 15-30 процессов kworker — это абсолютно нормально, их количество растёт с числом ядер CPU и нагрузкой на подсистемы ввода-вывода. Само по себе наличие kworker в списке процессов — не повод для тревоги.

Почему майнеры маскируются именно под kworker

Вредоносный процесс, который майнит криптовалюту или выполняет любую другую фоновую нагрузку, стремится к двум вещам: не выделяться в top по имени и не привлекать внимание при беглом просмотре ps aux. Имя kworker для этого подходит почти идеально:

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

Технически подмена делается тривиально: обычный userspace-процесс может через prctl(PR_SET_NAME, ...) переименовать себя в kworker/0:2, либо просто переписать argv[0] при запуске. Дальше в ps aux или top в столбце COMMAND будет красоваться то же имя, что и у настоящих потоков ядра — но это будет обычный процесс, запущенный из обычного бинарника, с обычным родителем.

Persistence такой процесс получает стандартными способами — через cron, systemd unit с обманчивым именем, запись в .bashrc/.profile, или через легитимный сервис с уязвимостью. Сам факт «нашли процесс с именем kworker, который грузит CPU» — это симптом, а не диагноз: сначала нужно убедиться, что это вообще ядро, а не подделка.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Пять признаков, по которым отличается подделка

ПризнакНастоящий kworkerПодделка (userspace)
/proc/PID/exeсимволическая ссылка не резолвится: «No such file or directory»указывает на реальный бинарник (иногда (deleted), если файл уже удалён после запуска)
Родитель (PPID)всегда 2 (kthreadd)обычно 1 (systemd) или PID произвольного родительского процесса
Владелец (UID)root, но это не «залогиненный root» — поток исполняется в контексте ядраможет быть любой пользователь, в том числе неожиданный (www-data, mysql, произвольный созданный аккаунт)
/proc/PID/cmdlineпустой (0 байт)содержит путь к бинарнику и аргументы, если только процесс намеренно не обнулил себе argv
/proc/PID/mapsпустой — у потока ядра нет отображённой в userspace памятисписок сегментов памяти, как у любого обычного процесса

Ни один признак по отдельности не железобетонен — продвинутый вредонос теоретически может подчистить cmdline, — но связка «PPID ≠ 2» + «/proc/PID/exe резолвится» практически всегда выдаёт подделку. Настоящему потоку ядра неоткуда взять исполняемый файл: у него нет отдельного адресного пространства userspace, он выполняется внутри ядра.

Команды для быстрой проверки

Смотрим, у кого какой родитель — все настоящие kworker должны быть детьми PID 2:

ps -eo pid,ppid,user,comm | grep kworker

Выводит все потоки, реально порождённые kthreadd — сверяем со списком выше, лишних быть не должно:

pstree -p 2 | grep -o 'kworker[^,()]*'

Главная проверка — пытаемся прочитать путь к исполняемому файлу. Для настоящего kworker это всегда ошибка:

for pid in $(pgrep -x kworker 2>/dev/null; pgrep -f 'kworker/'); do
  echo "PID $pid: PPID=$(awk '/PPid/{print $2}' /proc/$pid/status 2>/dev/null)"
  ls -l /proc/$pid/exe 2>&1
done

Если видите что-то вроде /proc/31337/exe -> /tmp/.hidden/xmr или /proc/31337/exe -> /var/tmp/.x/kworker (deleted) вместо «No such file or directory» — это стопроцентный userspace-процесс, а не поток ядра.

Проверяем cmdline — у настоящего он пустой:

xxd /proc/PID/cmdline | head -1

На части современных дистрибутивов в /proc/PID/status есть явное поле, которое проще всего смотреть первым:

grep -E 'Name|PPid|Uid|Kthread' /proc/PID/status

Если строка Kthread: присутствует и равна 1 — перед вами настоящий поток ядра, дальше можно не проверять. Если поле отсутствует (старое ядро) или равно 0 — переходите к проверке /proc/PID/exe и PPID.

И контрольный выстрел — сетевые соединения. У настоящего kworker их не бывает в принципе, у майнера почти всегда есть исходящее соединение к пулу:

ss -tnp | grep PID

Разбор на живом примере

Допустим, top показывает подозрительный процесс с PID 4821, имя kworker/1:2, постоянно 95-100% CPU одного ядра. Порядок действий:

# 1. Кто родитель?
awk '/PPid/{print $2}' /proc/4821/status
# PPid: 1   <- уже подозрительно, у ядра родитель всегда 2

# 2. Есть ли исполняемый файл?
ls -l /proc/4821/exe
# lrwxrwxrwx 1 root root 0 ... /proc/4821/exe -> /var/tmp/.cache/kworker (deleted)
# <- есть реальный бинарник, файл уже удалён (типичный приём, чтобы усложнить анализ)

# 3. Чей процесс?
awk '/Uid/{print $2}' /proc/4821/status
# 33  <- www-data, а не root

# 4. Кто его слушает по сети?
ss -tnp | grep 4821
# ESTAB ... 185.xx.xx.xx:443  users:(("kworker",pid=4821,...))

Три из трёх проверок против ядра: PPID не 2, /proc/PID/exe резолвится в удалённый бинарник, UID — не root, а www-data (типичная точка входа — уязвимое веб-приложение). Дальше это уже не вопрос «а точно ли это майнер», а вопрос зачистки и поиска точки входа — подробный разбор именно этого шага есть в статье майнер на сервере: как попал и как выкорчевать.

Что делать, если подделку нашли

Порядок действий стандартный для любого скрытого процесса, не только замаскированного под kworker:

  1. Не убивайте процесс сразу — сначала зафиксируйте PID, PPID, путь к бинарнику (даже если файл удалён, /proc/PID/exe можно скопировать: cp /proc/PID/exe /root/evidence_binary), список открытых сетевых соединений и файлов (ls -l /proc/PID/fd).
  2. Найдите точку персистентности — иначе через минуту после kill процесс перезапустится. Проверьте crontab -l для всех пользователей, systemctl list-units --type=service на незнакомые unit-файлы, /etc/rc.local, ~/.bashrc и ~/.profile для скомпрометированного пользователя, а также LD_PRELOAD в /etc/ld.so.preload.
  3. Проверьте, кто ещё в системе с этим UID — раз точка входа www-data, стоит посмотреть логи веб-сервера на предмет эксплуатации (RCE-уязвимость в приложении, забытый веб-шелл, устаревшая CMS).
  4. Смените все секреты, до которых потенциально мог дотянуться скомпрометированный процесс — SSH-ключи, токены API, пароли БД.
  5. При сомнении в целостности системы — не чистите вручную, а поднимайте сервер заново из чистого образа и переносите только данные. Если в систему успели вкрутить руткит, локальная чистка ничего не гарантирует. Как быстро поднять новый сервер и перенести только нужное — в статье перенос сайта на новый VPS без простоя.

Полный чек-лист именно по майнерам, включая типичные пути заражения (уязвимые Docker-контейнеры, слабые SSH-пароли, устаревшие CMS-плагины), — в статье как проверить сервер на майнер и вирусы.

Профилактика: как не пропустить подделку в следующий раз

Разовая проверка руками полезна, но реальная защита — это не проверять вручную каждый раз, когда top показывает что-то незнакомое, а сделать так, чтобы аномалия сама попадала на глаза:

  • Мониторинг числа реальных kworker vs общего числа процессов с этим именем — если у вас 8 ядер CPU, разумного диапазона kworker-потоков достаточно легко определить эмпирически на конкретной машине и алертить на выход за него (плюс всегда сверять, что PPID у всех них — 2).
  • auditd с правилом на execve — логирует запуск каждого процесса вместе с реальным путём к бинарнику, вне зависимости от того, как процесс сам себя переименовал в comm. Поддельный kworker в аудит-логе будет виден по своему настоящему пути запуска.
  • Регулярный обзор задач cron и systemd-таймеров — большинство persistence-механизмов заметны просто при чтении списка запланированных задач, если делать это раз в неделю, а не никогда.
  • Ограничение прав сервисных аккаунтов — если www-data физически не может писать в /tmp с правом на исполнение или создавать cron-задачи, у эксплуатации веб-приложения меньше шансов перерасти в персистентный майнер.
  • Базовый разбор скрытых процессов — если сомневаетесь не только в конкретном PID, а в системе целиком, есть более широкий чек-лист поиска того, что не видно в обычном ps aux: скрытые процессы на сервере: как найти.

Всю эту гигиену проще выстроить сразу на новом сервере, чем разгребать постфактум — базовый чек-лист первичной настройки есть в статье чек-лист безопасности нового сервера.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Почему в top всегда есть kworker, даже на пустом сервере без нагрузки?

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

Можно ли по загрузке CPU понять, что kworker подделка?

Нет напрямую, но косвенно да — настоящие kworker редко держат одно ядро на 100% продолжительное время без явной причины (тяжёлый I/O, сборка мусора в файловой системе и т.п.). Стабильная высокая нагрузка от процесса с этим именем — повод проверить PPID и /proc/PID/exe, а не повод для паники самой по себе.

Хватит ли одной проверки /proc/PID/exe, или нужны все признаки сразу?

Для быстрой проверки достаточно /proc/PID/exe и PPID — если оба указывают на подделку, дальше можно разбираться предметно. Но если процесс — часть продуманной атаки, стоит проверить весь список: изощрённый вредонос теоретически может обойти отдельные проверки, а вот все сразу — маловероятно.

Антивирус на сервере поймает такую подмену?

Не обязательно и не быстро — классический antivirus на Linux-сервере ловит сигнатуры известных бинарников, а не аномалии в процессной модели. Ручная или скриптовая проверка PPID/exe для процессов с «системными» именами работает надёжнее и не зависит от базы сигнатур.

А если это не майнер, а легитимный демон просто назвал себя похоже?

Бывает редко, но проверка та же: смотрите /proc/PID/exe — если это известный и ожидаемый вами бинарник (например, часть мониторинга или какого-то агента), проблемы нет. Тревогу должно вызывать именно сочетание «системного» имени с признаками обычного userspace-процесса без объяснимой причины.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →