Логотип ШифрыШифры
ГлавнаяЖурналЛотыУзел ИИВойти
ГлавнаяЖурналЛотыУзел ИИ
Войти
Логотип ШифрыШифры2К26Версияъ1
EmailMAX
Назад к логу

Малварь для WordPress пересобирает себя из файлов, базы и оперативной памяти

10 октября 2026 г.•Репа
#атаки#код

Аналитик Sucuri вычистил заражённый WordPress. Удалил вредоносные файлы, прошёлся по базе, перепроверил тему – и через несколько секунд бэкдор был на месте целиком. Не повторная атака – тот же payload с теми же маркерами SC_ в инжектированном коде. Малварь вернулась, потому что она не была файлом. Она была системой.

Восемь компонентов, замкнутых в петлю взаимной регенерации: удалишь плагин – drop-in перезапишет его, удалишь drop-in – тема перезапишет его, вычистишь все файлы с диска – следующий HTTP-запрос восстановит весь набор из строки в wp_options или из сегмента System V shared memory, живущего в RAM и переживающего любую зачистку. Ни читаемых имён функций, ни eval – подстановочный шифр, которого сигнатурные сканеры не ищут. А команды payload получает не с C2-сервера, который можно конфисковать, а из смарт-контракта Ethereum через двадцать публичных RPC-гатевеев. Как оператор получил первичный доступ – неизвестно: Sucuri фиксирует, что вектор входа не установлен.

Подстановочный шифр вместо eval

Каждый из восьми файлов SC несёт одну и ту же схему обфускации. Ни eval(), ни assert(), ни create_function() – стандартных маркеров, на которые заточены сигнатуры WAF и антивирусов. Вместо этого каждый файл хранит таблицу скрамблированных строк и компактный декодер: числовой индекс проходит через позиционный подстановочный шифр и превращается в имя реальной PHP-функции.

Визуально такой файл не похож на вредонос. Ни одного читаемого имени функции, ни одного строкового литерала, выдающего назначение. Статический анализ не зацепится – индексы ведут к file_put_contents, gzdecode, base64_decode только после прогона декодера, а тот работает исключительно в рантайме. Ни один из свежих файлов не несёт маркера, по которому его можно было бы отличить от обфускации коммерческого плагина.

Выбор инженерно точен. Полиморфизм – перепаковка тела при каждом развёртывании – стоит дороже в поддержке и хрупок при обновлениях: ошибка в мутации ломает payload. Шифрование тела требует ключа, а ключ – ещё одного слоя доставки. Подстановочный шифр не делает ни того, ни другого. Таблица строк обновляется отдельно от логики, декодер стабилен между версиями, а вся низковисящая сигнатурная детекция – по eval, assert, preg_replace с модификатором e – снимается целиком, потому что ни одного из этих вызовов в коде просто нет.

auto_prepend_file и PHP-кэш на 300 секунд

Первая точка исполнения стоит раньше WordPress. Файл .user.ini в корне сайта или в wp-content несёт одну строку:

auto_prepend_file = c1b12371.php

PHP-FPM подключает указанный файл до любого другого кода – на каждом запросе в этом дереве каталогов. WordPress ещё не начал грузиться, а малварь уже работает. Директива срабатывает даже на запросах, которые никогда не дойдут до WordPress – прямой вызов произвольного .php в дереве каталогов тоже проходит через неё.

Файл, на который указывает .user.ini – видимая прокладка (shim). Она выполняет ровно одну операцию: если рядом существует скрытый файл с точкой в начале имени – подключает его:

if (file_exists(__DIR__ . '/.c1b12371.php')) {
    include __DIR__ . '/.c1b12371.php';
}

Зачем два файла вместо одного – прокладка и скрытый загрузчик? Потому что значение auto_prepend_file кэшируется PHP-FPM: кэш держится до 300 секунд. Если защитник удалит файл, на который указывает директива, пока кэш тёплый – каждый PHP-запрос на аккаунте начнёт падать с fatal error. Пять минут полного отказа всех PHP-скриптов. Прокладка – буфер от этого сценария: если скрытый файл удалён, shim тихо ничего не делает, сайт работает как ни в чём не бывало, а защитник считает зачистку успешной. Тем временем другой компонент mesh уже пересоздаёт удалённое.

Скрытый dot-prefixed файл .c1b12371.php – первый настоящий загрузчик в цепочке. Его задача – найти основной payload (фейковый плагин) и восстановить его в каталоге mu-plugins. Поиск идёт по трём источникам в порядке приоритета: существующая копия в каталоге plugins, закодированный stub в каталоге кэша и ZIP-бандл восстановления со случайным hex-именем. Запись – через временный файл: атомарная операция, исключающая race condition при параллельных запросах. Права – 0644, стандартные для файлов, читаемых веб-сервером. Сразу после записи – opcache_invalidate(), чтобы свежий файл исполнился немедленно, не дожидаясь сброса OPcache по таймеру.

Загрузчик определяет константу SC_AUTO_PREPEND, привязывающую его к auto_prepend-ветви семейства. SC знает, какой из восьми входов его запустил, и ведёт себя соответственно: не пытается заново создать то, что уже исполняется.

Имена c1b12371.php и .c1b12371.php не являются стабильными индикаторами: на каждом заражённом сайте они свои. Sucuri приводит эти значения как конкретный пример, не как универсальный IOC.

db.php – полный бэкдор в одной строке base64

При загрузке WordPress подключает wp-content/db.php на раннем этапе bootstrap – это штатный drop-in для пользовательского слоя работы с базой данных. Файл необязателен; если он есть – WordPress включит его без проверки содержимого.

SC использует эту позицию как автономный контейнер. Внутри db.php лежит полный payload – весь бэкдор – упакованный в gzip и закодированный в base64. Один монолитный blob. На каждом запросе drop-in проверяет: существует ли фейковый плагин, и достаточен ли размер его файла? Если плагин отсутствует или усечён – db.php разворачивает blob и перезаписывает плагин на диск:

file_put_contents($target, gzdecode(base64_decode($blob)));

Один файл, одна проверка, мгновенное восстановление. Никакой сетевой активности, никаких внешних зависимостей – payload не скачивается, а извлекается из тела самого drop-in'а.

Пять источников advanced-cache.php – ни один не лишний

Если db.php – контейнер с payload внутри, то advanced-cache.php – поисковик, способный найти payload где угодно. Это самая ранняя и самая опасная позиция из всех восьми: когда в wp-config.php определена константа WP_CACHE (что типично для любого сайта с кешированием), WordPress загружает wp-content/advanced-cache.php до обычных плагинов.

В отличие от db.php, этот drop-in не хранит payload в себе. Он ищет – по пяти независимым источникам, перебирая в порядке убывания скорости доступа.

Первый – существующая копия mu-plugin'а в wp-content/mu-plugins. Самый быстрый путь: файл уже на диске и готов к подключению. Второй – копия в каталоге обычных плагинов wp-content/plugins. Третий – сегмент System V shared memory, содержащий готовый PHP-код: payload, записанный прямо в оперативную память ядра. Четвёртый – ZIP-бандл восстановления, который advanced-cache.php ищет в нескольких каталогах: wp-content, wp-content/uploads, папки тем. Пятый – строка в таблице wp_options базы данных, содержащая payload в формате gzip+base64.

Для пятого источника advanced-cache.php открывает прямое соединение с MySQL, используя учётные данные из wp-config.php – константы DB_HOST, DB_USER, DB_PASSWORD, DB_NAME. WordPress API для этого не нужен и не используется: drop-in работает до инициализации фреймворка.

Как только payload найден хотя бы в одном из пяти мест – drop-in вешается на хук plugins_loaded и подключает его. Система продолжает работать.

Пять источников расположены в трёх физически разных доменах хранения: файловая система (источники 1, 2, 4), оперативная память (3), база данных (5). Чтобы advanced-cache.php не нашёл ни одного – защитнику нужно вычистить все три домена, прежде чем придёт следующий HTTP-запрос. Один пропущенный источник – и при следующем page load весь mesh пересоздаётся заново.

Инъекция в тему khorshidi и поддельный кеширующий плагин в двух экземплярах

Шестой компонент – инъекция в functions.php активной темы. На сайте, который разбирала Sucuri, активной темой была khorshidi. Оператор дописал блок кода в конец легитимного файла, обрамив его маркерами начала и конца. Блок – функциональный близнец db.php: несёт тот же payload в формате gzip+base64 и на каждом запросе перезаписывает фейковый плагин, если тот отсутствует.

functions.php темы исполняется при каждом запросе к сайту – фронтенд, админка, AJAX, REST API. Инжектированный блок тонет в легитимном коде темы, особенно если она объёмная. Для защитника здесь ловушка другого рода: functions.php нельзя удалить целиком, как вредоносный drop-in – это уронит оформление сайта. Нужно аккуратно вырезать ровно тот фрагмент между маркерами начала и конца, оставив весь легитимный код темы нетронутым. Маркер начала при этом может быть продублирован – ещё один штрих, затрудняющий автоматическую очистку.

Седьмой и восьмой компоненты – сам бэкдор. Крупный PHP-файл, написанный как полноценный WordPress-плагин и установленный одновременно в двух местах: как must-use plugin (wp-content/mu-plugins/hyper-engine-kit.php) и как обычный плагин (wp-content/plugins/hyper-engine-kit/hyper-engine-kit.php). Две идентичные копии одного и того же кода.

Must-use плагин загружается WordPress автоматически и безусловно – его нельзя деактивировать через админку, он не отображается в стандартном интерфейсе деактивации. Обычный плагин – резервная копия. SC конкурентно синхронизирует обе: если версия одной копии новее – она перезаписывает другую. Обе всегда на одной и той же версии.

Камуфляж обычного плагина проработан на уровне, рассчитанном на беглую проверку. Каталог hyper-engine-kit содержит убедительную фальшивую страницу настроек, зарегистрированный шорткод и стандартный activation hook. При быстром просмотре в админке или в файловом менеджере хостинга hyper-engine-kit выглядит как настоящий плагин кеширования – с интерфейсом, функциональностью и хуками, которые ожидаешь увидеть. Без чтения обфусцированного кода отличить его от легитимного невозможно.

Mesh-архитектура SC: восемь компонентов файловой системы, две off-disk копии в БД и RAM, канал C2 через смарт-контракт Ethereum
Mesh-архитектура SC: восемь компонентов, три яруса хранения, blockchain-канал C2

Плагин, который стирает себя из собственного списка

Когда payload запущен, первое, что он делает – прячет себя от администратора. Маскировка работает на двух уровнях.

На уровне PHP: payload фильтрует хуки, через которые WordPress формирует данные для панели управления. all_plugins – массив установленных плагинов: SC удаляет свою запись до того, как WordPress отрисует список в админке. site_transient_update_plugins – транзиент доступных обновлений: запись SC исчезает и оттуда. На мультисайте фильтруются дополнительно сетевые вью плагинов. Результат – hyper-engine-kit не появляется ни в списке установленных, ни в списке доступных обновлений, ни в сетевом интерфейсе.

На уровне JavaScript – фоллбэк. Payload инжектирует JS-код в административную панель WordPress. Этот код находит строку hyper-engine-kit в HTML-таблице плагинов и удаляет её из DOM. Дублирование фильтрации в JS при работающих PHP-хуках – не перестраховка, а ответ на конкретную проблему: некоторые security-плагины перехватывают хуки all_plugins и восстанавливают оригинальный массив. JS-фоллбэк работает после полного рендера страницы – и вычищает из DOM то, что PHP не сумел скрыть.

Двадцать Ethereum RPC-гатевеев и смарт-контракт вместо C2-домена

Канал управления SC не проходит через захардкоженный домен. Payload несёт список примерно из двадцати публичных Ethereum RPC-гатевеев – тех, через которые работают легитимные блокчейн-приложения и криптокошельки, – и набор селекторов методов смарт-контракта. При старте payload отправляет к гатевеям eth_call – стандартный read-only вызов к смарт-контракту:

POST https://<rpc-gateway>/v3/...
{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x...","data":"0x..."},"latest"],"id":1}

В ответе – адрес актуального C2-эндпоинта, закодированный в возвращаемых данных контракта. Payload резолвит его и дальше работает с этим адресом.

Блокировка одного гатевея бесполезна: payload пробует следующий из списка. Двадцать гатевеев – это не два-три резервных домена, а инфраструктура, которую защитник не контролирует. Каждый из этих сервисов – легитимный публичный эндпоинт Ethereum, через который работают dApp'ы, кошельки и криптобиржи. Заблокировать все двадцать на файрволе – значит сломать все блокчейн-приложения и крипто-интеграции на сервере.

Приём не новый: мы разбирали блокчейн как C2-канал в контексте npm-пакетов с вшитой малварью. Но в WordPress-экосистеме это пока передний край, и масштаб здесь другой.

Классический C2 – хрупкая конструкция. Домен конфискуется через регистратора по судебному постановлению. IP блокируется на файрволе. DGA-домены – домены, генерируемые алгоритмически – вычисляются по тому же алгоритму и синкхолятся заранее. Fast-flux DNS переживёт takedown одного сервера, но не скоординированный удар по всему пулу. У каждого из этих вариантов есть точка разрыва, на которую можно надавить.

У смарт-контракта Ethereum такой точки нет. Данные в контракте иммутабельны без приватного ключа владельца. Чтение – публичное и бесплатное, через JSON-RPC любого полного узла Ethereum. Отозвать произвольный смарт-контракт не может никакая юрисдикция – блокчейн не имеет оператора, которому можно вручить постановление. Единственный способ заблокировать чтение из контракта – заблокировать все RPC-гатевеи, через которые payload обращается к сети, а их двадцать, и при обновлении payload список расширяется.

Что этот выбор стоил оператору? Деплой смарт-контракта – разовый расход на gas, десятки долларов. Обновление C2-адреса – ещё одна транзакция. Ограничение – публичность: вызов eth_call виден любому, кто мониторит RPC-трафик сервера, и адрес контракта, попав в отчёт, позволяет прочитать текущий C2-эндпоинт напрямую из блокчейна. Но это ограничение – не уязвимость: знание адреса C2 не равно возможности его выключить. DGA-подход с тысячами доменов стоил бы дороже (регистрация, инфраструктура), требовал бы ротации серверов и при этом оставался бы уязвимым к preregistration синкхолов – при достаточных ресурсах защитника все сгенерированные домены перехватываются до того, как payload успеет к ним обратиться.

Слепок сайта, скиммер в чекауте и зачистка security-плагинов

С установленным каналом управления payload собирает полный слепок заражённого сайта. В пакет входят: URL и hostname сайта, версии WordPress и каждого установленного плагина, хеши путей файловой системы, список активных тем, перечень must-use плагинов и текущие session token'ы всех администраторов. Пакет шифруется и отправляется на C2-эндпоинт, адрес которого получен из смарт-контракта.

Ответ C2 может нести три типа нагрузки. Первый – JavaScript для инжекта во фронтенд сайта. На интернет-магазине – WooCommerce или любом другом плагине корзины – этот JS становится чекаут-скиммером: перехватывает данные банковских карт прямо из формы оплаты и отправляет на внешний эндпоинт. Второй тип – PHP-код к установке на сервере. Третий – список конкретных security-плагинов для деактивации и удаления.

При удалении security-плагина SC не ограничивается вызовом deactivate_plugins(). Payload способен стереть каталог плагина целиком, а перед этим – повысить привилегии другого аккаунта или переназначить роль, чтобы у оператора остался административный доступ даже после удаления инструмента, через который этот доступ поддерживался. Защита убрана – доступ сохранён.

Администратор с прямым SQL, поддельными cookies и тремя слоями маскировки

Для постоянного интерактивного доступа к сайту payload создаёт скрытый аккаунт администратора. Первый шаг – проверка: если на сайте уже есть скрытый аккаунт от предыдущего заражения, payload принимает его и продолжает работу. Если нет – создаёт новый, и не через WordPress API.

Прямая запись в таблицы MySQL:

INSERT INTO wp_users (user_login, user_pass, user_email, ...)
VALUES ('...', '$P$B...', '...', ...);

INSERT INTO wp_usermeta (user_id, meta_key, meta_value)
VALUES (<id>, 'wp_capabilities',
        'a:1:{s:13:"administrator";b:1;}');

Ключ wp_capabilities – стандартный: именно его WordPress проверяет при авторизации. Аккаунт получает полные права администратора, минуя все хуки, которые могли бы зафиксировать создание пользователя через штатный wp_insert_user().

Маскировка – три слоя фильтров. Payload перехватывает SQL-запросы к таблице пользователей через pre_user_query: скрытый аккаунт исключается из выборки. Отдельно фильтруются счётчики ролей – число в «Все (N)» и в «Администраторы (N)» в интерфейсе управления пользователями. Отдельно – представления списка: аккаунт не появляется в HTML-таблице. Администратор сайта не видит скрытого пользователя ни в списке, ни в подсчёте, ни в фильтре по роли.

Для входа без пароля payload подделывает auth cookies. WordPress аутентифицирует сессию по куке, содержащей HMAC от логина, срока действия и фрагмента хеша пароля. Ключ HMAC – AUTH_KEY и AUTH_SALT из wp-config.php. SC генерирует эту куку для скрытого аккаунта, используя те же ключи, что и сам WordPress. Результат – валидная сессия, неотличимая от настоящей, без единого обращения к форме логина, без записи в access-лог авторизации.

Ещё одна точка восстановления – орфанная запись. Если скрытый аккаунт удалён, но в wp_options осталась запись с его ID, payload находит её и пересоздаёт аккаунт при следующем запуске. Одна строка в таблице опций, которую легко пропустить при ручной зачистке, – и доступ восстановлен.

Зонд, который отвечает раньше WordPress

Payload обрабатывает magic-параметр в HTTP-запросе – короткий префикс и фиксированное значение. Проверка срабатывает до того, как WordPress завершит загрузку: если mesh на месте, запрос с magic-параметром возвращает нормальный HTTP-ответ с телом и завершает исполнение через exit.

Для оператора это зонд доступности. Один GET с правильным параметром – и характер ответа подтверждает: payload жив, mesh на месте, сайт контролируется. Для защитника beacon практически невидим: запрос выглядит как обычный визит, ответ – как обычная страница сайта. Единственное отличие – ответ сформирован не WordPress, а payload'ом, и в логах нет ни обращения к базе, ни инициализации темы. Но чтобы заметить это, нужно знать, что искать.

Хуки, которые переписывают весь mesh при каждом запросе

Все восемь компонентов описаны – но что замыкает петлю? Payload вешается на большой набор WordPress actions и filters, чьё единственное назначение – восстанавливать каждый компонент mesh при каждом запросе.

При каждом исполнении payload проверяет: на месте ли db.php? На месте ли advanced-cache.php? Цела ли инъекция в functions.php темы? Живы ли .user.ini, .htaccess и prepend-загрузчик с его прокладкой? Существует ли ZIP-бандл восстановления? Записан ли payload в SysV shared memory? Есть ли актуальная строка в wp_options?

Что отсутствует – перезаписывается. Что устарело – обновляется. Drop-in'ы db.php и advanced-cache.php восстанавливаются из payload в памяти. Инъекция в тему дописывается заново между маркерами. .user.ini и .htaccess получают директиву auto_prepend_file. Скрытый dot-prefixed загрузчик и его прокладка пересоздаются. ZIP-бандл упаковывается и помещается в каталог с hex-именем.

Для записи в wp_options payload открывает прямое соединение с MySQL, минуя WordPress – тот же подход, что и при создании скрытого администратора. Прямой INSERT обходит все хуки, которые могли бы перехватить запись через update_option(), и не оставляет следов в object cache WordPress.

Результат – замкнутая система без единой точки разрыва. Удалить один компонент – значит заставить следующий запрос восстановить его. Удалить все файлы – значит заставить advanced-cache.php (если он ещё не удалён) или следующий page load (если drop-in уже восстановлен из off-disk копии) пересобрать всё. Mesh SC – это не восемь независимых бэкдоров, а один бэкдор в восьми позициях, каждая из которых является и хранилищем, и восстановителем.

Три хранилища за пределами файловой системы

Круговая регенерация держится на файлах – но SC пережил бы и полную файловую зачистку. Payload хранит себя в трёх местах, куда стандартные сканеры и процедуры восстановления не заглядывают.

Строка в wp_options. Полный payload лежит в таблице опций WordPress: одна строка со случайным именем, значение – gzip+base64. Имя строки генерируется при заражении и не содержит маркеров SC_, по которым можно было бы его найти поиском по prefix'у. advanced-cache.php находит эту строку через прямое подключение к MySQL и декодирует payload оттуда. Именно это делает идеально чистую файловую зачистку бесполезной: следующий page load восстанавливает весь набор файлов из одной строки в базе.

System V shared memory. На серверах с поддержкой SysV IPC – а это подавляющее большинство Linux-хостингов – payload записывает себя в сегмент разделяемой памяти с фиксированным числовым ключом:

$shm = shmop_open($key, 'c', 0644, strlen($payload));
shmop_write($shm, $payload, 0);

Сегмент живёт в RAM – в адресном пространстве ядра ОС, вне какого-либо процесса. Он не является частью файловой системы. Он не является частью базы данных. Он переживает удаление всех файлов. Он переживает TRUNCATE wp_options. Он переживает перезапуск Apache, nginx и PHP-FPM – потому что принадлежит ядру, а не веб-серверу. Единственное, что его убивает – ipcrm, перезагрузка ОС или исчерпание TTL (если он установлен, а обычно нет).

На shared hosting ситуация приобретает отдельное измерение. SysV-сегмент идентифицируется числовым ключом и принадлежит UID процесса, который его создал. На shared-хостинге разные аккаунты работают под разными UID, но в общем пространстве IPC. Если малварь создала сегмент из-под процесса PHP, принадлежащего другому аккаунту на том же сервере – владелец заражённого сайта физически не может его удалить. У него нет на это прав. Удалить может только владелец UID сегмента или root. При этом drop-in'ы, читающие этот сегмент, продолжают работать: shmop_open с флагом 'a' (read-only) не требует владения.

Это приём на текущем переднем крае persistence для WordPress-малвари. Классический incident response работает с файлами (сканеры: ClamAV, Sucuri SiteCheck, Wordfence CLI) и с базой данных (аудит wp_options, wp_usermeta, cron table). SysV shared memory не попадает ни туда, ни туда – для подавляющего большинства IR-инструментов этого хранилища не существует. Альтернативой для оператора был бы /tmp или другой путь файловой системы, но это снова файл, который найдёт любой сканер. Запись в БД под случайным именем – уже сделана (первое хранилище), но БД чистят вторым шагом в любом грамотном IR. SysV IPC – off-process, off-disk, off-DB хранилище, обходящее весь привычный инструментарий защитника. Цена для оператора невелика: ограничение на серверы с поддержкой SysV IPC (на VDS и dedicated – практически всегда; на некоторых managed-хостингах PHP собран без --enable-shmop) и фиксированный числовой ключ, обнаружимый через ipcs -m. Но ipcs – это команда, которую большинство WordPress-специалистов при зачистке не запускают.

Cron-хуки. Payload регистрирует WordPress cron-задачи – часть с рандомизированными именами, часть с известным fetch-хуком. На серверах, где system cron вызывает wp-cron.php по расписанию (а не по визиту пользователя), переразвёртывание запустится автоматически, даже если на сайт никто не заходит.

Отдельный класс – database trigger, зафиксированный в родственных вариантах SC. Триггер на INSERT в таблицу пользователей автоматически пересоздаёт удалённого администратора. Триггер живёт внутри MySQL, переживает полный file restore и даже удаление того самого аккаунта, которого он воссоздаёт – потому что удаление есть DELETE, а триггер стоит на INSERT нового. Пока триггер на месте, чистить таблицу пользователей бесполезно: каждый легитимный INSERT (регистрация, импорт) воссоздаст скрытого админа.

Шесть шагов разборки, где нарушение порядка восстанавливает всё

Mesh SC – замкнутая система, и порядок её разборки жёстко задан архитектурой самой малвари, а не предпочтениями аналитика. Удаление файлов первым – ошибка: любой компонент, оставшийся в живых в БД, в RAM или в cron, пересоздаёт все файлы при следующем HTTP-запросе. Единственный работающий подход – вырубить исполнение, зачистить вне-файловые хранилища, и только потом снести файлы, целиком за один проход.

Первый шаг – обезвредить auto_prepend_file, не удаляя его. PHP-FPM кэширует значение директивы до 300 секунд. Удалить файл, на который указывает auto_prepend_file, пока кэш тёплый – каждый PHP-запрос на аккаунте начнёт падать с fatal error на пять минут. Правильный ход – записать в файл-прокладку пустышку (один <?php без кода), затем убрать директиву из .user.ini, php.ini и .htaccess. Сайт продолжает работать, prepend-ветвь малвари больше не исполняется, и при естественном сбросе кэша PHP ничего не ломается.

Второй шаг – зачистить все вне-файловые копии payload. Удалить строку из wp_options (payload row со случайным именем). Purge-ить SysV-сегмент через ipcrm -M <key> или shmop_delete(). Удалить контрольные options и transient'ы с sc_-стайл-префиксом. Если хоть одна из этих копий останется – advanced-cache.php на следующем запросе восстановит всё на диск. На shared hosting SysV-сегмент может принадлежать чужому аккаунту – тогда удалить его может только этот аккаунт или хостер, а drop-in'ы, читающие этот сегмент, становятся безвредными после удаления самих drop-in'ов (шаг пятый).

Третий шаг – снять cron-хуки и database triggers. Удалить вредоносные WordPress cron-задачи: рандомизированные имена плюс known fetch hook. Проверить information_schema.TRIGGERS на наличие триггера, пересоздающего администратора по INSERT. Выживший триггер восстановит доступ оператора даже после полного file restore и очистки таблицы пользователей.

Четвёртый шаг – удалить скрытого администратора. Удалить аккаунт и вычистить орфанную запись в wp_options, содержащую его ID. Пока запись на месте – payload найдёт её и пересоздаст аккаунт.

Пятый шаг – удалить все вредоносные файлы за один проход. Загрузчики и прокладку. Обе копии фейкового плагина – из mu-plugins и из plugins. ZIP-бандл восстановления – где бы он ни лежал. Вредоносные drop-in'ы db.php и advanced-cache.php. Из functions.php темы – вырезать блок между маркерами начала и конца, оставив легитимный код темы нетронутым.

Шестой шаг – полный скан и мониторинг. Прогнать полный скан файловой системы и базы данных. Если какой-либо файл вернулся – значит, где-то осталась живая копия payload. Не удалять файл повторно – возвращаться к вне-файловым источникам. Повторное появление файла – индикатор незавершённой зачистки, а не нового заражения.

Шесть шагов разборки mesh SC: обезвреживание auto_prepend_file, зачистка off-disk копий, снятие cron и триггеров, удаление скрытого админа, удаление файлов, скан и ротация
Порядок разборки mesh SC: нарушение последовательности возвращает систему в исходное состояние при первом же HTTP-запросе

Восемь позиций, три яруса хранения и ноль доменов, которые можно конфисковать

SC – не PHP-шелл в uploads и не инжект в index.php. Это распределённая система, спроектированная по принципам, знакомым каждому, кто строил отказоустойчивые сервисы: circular dependency graph без single point of failure, ordered fallback с пятью источниками через три домена хранения, конкурентная синхронизация реплик и канал управления через инфраструктуру, которую защитник не контролирует.

Обфускация выбрана точно: подстановочный шифр стоит дешевле полиморфизма и снимает всю сигнатурную детекцию по eval-семейству – ни один из стандартных маркеров, по которым ищут вредонос, в коде SC не существует. SysV shared memory – приём, которого большинство WordPress IR-тулзов не видит: файловый сканер и аудит wp_options бессильны против payload в RAM ядра, а на shared hosting сегмент может быть физически недоступен владельцу заражённого сайта. Blockchain C2 через двадцать легитимных Ethereum RPC-гатевеев убирает последнюю хрупкую точку классического канала управления – домен, который можно конфисковать, синкхолить или заблокировать на DNS.

Единственный известный пробел – начальный вектор доступа: Sucuri фиксирует, что он не установлен. Всё, что идёт после доступа, спроектировано на уровне, при котором классическая зачистка «удалить вредоносные файлы и заменить на чистые» не работает. Mesh – это не набор бэкдоров в разных местах, а одна архитектура с одной целью: пережить любую зачистку, не охватившую все три домена хранения одновременно и в правильном порядке. Нарушение порядка – мгновенная рекурсия, и система на месте целиком.

Защитнику здесь нужен тот же подход, которым система построена – системный. Не файлы, а домены хранения. Не удаление, а обезвреживание в правильной последовательности. Перед стартом – ipcs -m на сервере, потому что в RAM может лежать то, чего нет ни в одном файле и ни в одной таблице. После финала – ротация всего, что payload мог видеть: session token'ы администраторов (уехавшие на C2 в fingerprinting-пакете), пароль БД из wp-config.php, ключи AUTH_KEY и AUTH_SALT, API-ключи сторонних плагинов. Валидный token админа, не аннулированный после зачистки, работает до своего естественного expiry независимо от того, что происходит с mesh. Разбор проведён исследовательской лабораторией Sucuri.

Автор: hacker@shifry.local