MAATRIX / Блог / Сайт не менялся, а страницы новые: как искать закладки в CMS

Сайт не менялся, а страницы новые: как искать закладки в CMS

MAATRIX

Если в списке страниц сайта вдруг находится «купить реплику часов недорого» или «best online casino bonus 2026», а вы точно ничего такого не публиковали, — это почти всегда не баг движка, а взлом. Чужие страницы на живом, проиндексированном домене — рабочий инструмент чёрного SEO: поисковик доверяет вашему домену больше, чем свежезарегистрированному, и злоумышленник паразитирует на этом трасте, пока вы не заметите. Разбираем по шагам, как искать такие «закладки» в WordPress и похожих CMS — от сверки списка страниц до поиска изменённых файлов темы.

Как выглядит «закладка» на CMS-сайте

Классическая схема простая: получив доступ к админке или к файлам сайта, злоумышленник не портит главную страницу и не оставляет дефейс — это заметят сразу. Вместо этого он тихо добавляет десятки или сотни страниц под чужие коммерческие запросы (реплики брендов, казино, фарма, «купить диплом») либо создаёт скрытые редиректы, которые ведут не всех посетителей, а только поисковых роботов на сторонний ресурс. Расчёт на то, что владелец сайта эти страницы никогда не откроет — они не в меню, не в навигации, ссылки на них — только с других взломанных сайтов или из спам-рассылок.

Типичные косвенные признаки, которые стоит проверить в первую очередь:

  • в Google Search Console (раздел «Страницы» → «Проиндексировано») внезапно появляется в разы больше страниц, чем вы публиковали;
  • в отчёте по запросам растут показы по темам, никак не связанным с вашим бизнесом;
  • антивирус браузера или партнёрская сеть (Adsense, платёжный шлюз) присылает предупреждение о «вредоносном или обманном контенте»;
  • скорость сайта или нагрузка на БД выросла без видимой причины — сотни лишних страниц тоже требуют ресурсов на генерацию и индексацию;
  • при заходе с User-Agent поискового бота сайт отдаёт не то, что видит обычный посетитель (клоакинг).

Последний пункт — самый коварный: администратор открывает страницу в браузере и видит нормальный сайт, потому что вредоносный код показывает спам только Googlebot или определённым IP. Поэтому визуального осмотра недостаточно — нужно смотреть в базу данных и в файлы напрямую, а не только глазами через фронтенд. Если сайт уже показывает явные симптомы взлома «на глаз», начните с общего разбора в статье что делать при взломе сайта — здесь же сфокусируемся именно на поиске скрытых закладок.

Сверяем список страниц с ожидаемым

Первый и самый быстрый шаг — сравнить три независимых источника списка страниц: то, что реально в базе, то, что отдаёт sitemap, и то, что видит поисковик. Если они расходятся — есть о чём беспокоиться.

Выгрузите список страниц напрямую через wp-cli (это надёжнее, чем список в админке — некоторые вредоносы прячут свои записи из экрана «Страницы», подменяя запрос через хук pre_get_posts):

wp post list --post_type=page --fields=ID,post_title,post_status,post_date --format=csv > pages-actual.csv
wp post list --post_type=post --fields=ID,post_title,post_status,post_date --format=csv > posts-actual.csv

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

curl -s https://ваш-домен.ru/sitemap.xml | grep -oE '<loc>[^<]+</loc>'
curl -s https://ваш-домен.ru/wp-sitemap.xml | grep -oE '<loc>[^<]+</loc>'

И третий источник — Google Search Console: раздел «Индекс» → «Страницы», там же можно скачать полный список проиндексированных и не проиндексированных URL. Если в консоли есть URL, которых нет ни в выгрузке wp-cli, ни в sitemap, — это почти наверняка страница, которая генерируется «на лету» динамическим кодом (например, через .htaccess редирект или инъекцию в шаблон), а не хранится как обычный пост. Это отдельный класс закладок, к нему вернёмся в разделе про .htaccess.

Держите под рукой «эталонный» список страниц, которые вы создавали сами — выгружайте его раз в месяц и храните отдельно от сайта (в отдельном репозитории или облаке). Тогда сверка через diff займёт секунды:

diff pages-baseline.csv pages-actual.csv

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

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

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

Ищем чужие страницы и посты по дате создания в базе данных

Если malware прячет записи из админки, самый надёжный способ их найти — обратиться к базе напрямую через mysql-клиент, в обход WordPress и его хуков:

mysql -u wp_user -p wordpress_db -e "
SELECT ID, post_title, post_status, post_type, post_date, post_author
FROM wp_posts
WHERE post_type IN ('page','post')
ORDER BY post_date DESC
LIMIT 100;"

На что смотреть в результате:

  • кластеры по времени. Если полсотни страниц созданы в один день, а часто — глубокой ночью по вашему времени, это почти всегда автоматическая заливка скриптом, а не ручная работа редактора;
  • post_author, которого вы не узнаёте. Сверьте ID автора с таблицей wp_users — иногда закладки публикуются от имени существующего, но давно неактивного пользователя;
  • post_status = 'publish', но нет ни в одном меню. Легитимные страницы вы обычно куда-то добавляете (в меню, в перелинковку). Страницы-сироты, которые существуют только сами по себе и находятся исключительно через прямую ссылку или sitemap, — подозрительны по умолчанию;
  • несвойственный post_type. Некоторые вредоносы создают записи через кастомный тип (например, attachment с описанием, nav_menu_item c произвольным URL) — такие записи вообще не всегда видны в стандартном списке «Все страницы».

Полезно также поднять историю правок по wp_posts.post_modified — если легитимные страницы не менялись месяцами, а рядом есть записи с недавним post_modified при старом post_date, значит кто-то регулярно возвращается и обновляет уже внедрённый контент (типично для partyware-схем, где спам-страницы периодически «освежают», чтобы не терять позиции в выдаче).

Если у вас включено логирование запросов к БД или есть доступ к бинарным логам MySQL за интересующий период, по ним можно восстановить точное время INSERT-запроса и, если повезёт, IP или сессию, с которой он пришёл — это уже переходит в разбор инцидента, а не просто поиск закладок.

Проверяем список пользователей и админ-аккаунтов

Самый частый способ закрепиться после взлома — не постоянно эксплуатировать уязвимость заново, а один раз создать себе учётную запись администратора. Дальше злоумышленник заходит через обычный /wp-login.php как легитимный пользователь, и ни один сканер уязвимостей плагинов это уже не поймает.

Проверьте список администраторов через wp-cli:

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

И обязательно продублируйте запросом напрямую к базе — по той же причине, что и со страницами: некоторые вредоносы скрывают «служебного» пользователя из списка в админке, подменяя pre_get_users, но в таблице он всё равно есть:

mysql -u wp_user -p wordpress_db -e "
SELECT u.ID, u.user_login, u.user_email, u.user_registered, um.meta_value AS capabilities
FROM wp_users u
JOIN wp_usermeta um ON u.ID = um.user_id
WHERE um.meta_key = 'wp_capabilities'
ORDER BY u.user_registered DESC;"

В колонке capabilities у обычного администратора будет строка вида a:1:{s:13:"administrator";b:1;}. Если видите такую строку у пользователя с подозрительным логином или почтой на одноразовом домене — это и есть закладка-аккаунт. Отдельно стоит проверить дату user_registered: если она не совпадает ни с одним известным вам моментом «мы добавляли нового сотрудника», а стоит где-то посреди инцидента — источник найден.

Дальнейшие шаги стандартные: удалить неизвестный аккаунт (или как минимум понизить его роль и сменить пароль, если нужно сохранить для расследования), принудительно сбросить пароли у всех настоящих администраторов, отозвать все Application Passwords и активные сессии (wp user session destroy --all для каждого пользователя), проверить включённую двухфакторную аутентификацию. Если ситуация похожа не на разовую закладку, а на полноценную компрометацию сервера, дальше действуйте по чек-листу из статьи что делать при взломе сервера — там расписан порядок действий целиком, включая изоляцию и сохранение улик до чистки.

Ищем изменения в файлах темы и плагинов

Ядро WordPress можно сверить с эталоном одной командой — она сравнивает контрольные суммы файлов с официальным репозиторием:

wp core verify-checksums

С плагинами и темами сложнее: официального механизма проверки контрольных сумм для всех плагинов нет (только для тех, что есть в каталоге wordpress.org, и то не всегда). Практичный обходной путь — скачать чистую копию плагина/темы той же версии и сравнить рекурсивно:

diff -rq /path/to/wp-content/plugins/имя-плагина /path/to/clean-copy/имя-плагина

Ещё один способ — искать файлы, изменённые позже, чем «должны были»: возьмите дату последнего планового обновления как реперную точку и найдите всё, что менялось после неё:

find wp-content/plugins wp-content/themes -type f -newermt "2026-08-01" -name "*.php"

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

find wp-content/uploads -iname "*.php"

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

grep -rlE "eval\s*\(\s*(base64_decode|gzinflate|str_rot13)" wp-content/ 2>/dev/null
grep -rl "FilesMan\|c99shell\|WSO Shell" wp-content/ 2>/dev/null

Отдельно проверьте каталог wp-content/mu-plugins — «must-use» плагины подключаются автоматически при каждой загрузке страницы и не отображаются в обычном списке «Плагины» в админке, из-за чего это одно из любимых мест злоумышленников для размещения бэкдора. Если в этом каталоге лежит хоть один файл, а вы туда ничего не клали, — открывайте и читайте его целиком. Похожая история — с «забытыми» плагинами, которыми давно никто не пользуется: там подробный разбор реального случая описан в статье взломали через плагин, которым не пользовались два года — неактивные, но не удалённые плагины остаются рабочей точкой входа даже без единого клика администратора.

Типичные места внедрения: functions.php, .htaccess и инъекции редиректов

Если поиск по файлам с недавней датой изменения ничего не дал, а закладки всё равно есть, — вероятно, вредоносный код спрятан не отдельным файлом, а вставкой в существующий, который и так должен часто редактироваться (поэтому дата изменения не выглядит подозрительно) или в котором вообще никто не проверяет контрольные суммы.

functions.php активной темы. Самое частое место: файл выполняется на каждой загрузке сайта, у него нет официального эталона для сверки (тема часто кастомная или сильно изменённая), и правки в нём выглядят обыденно. Типичная находка — хук на init или wp, который либо динамически генерирует и отдаёт спам-контент по определённым URL (без единой записи в базе — отсюда несовпадение со списком постов из раздела выше), либо проверяет User-Agent и для ботов подменяет содержимое страницы. Открывайте файл и ищите обращения к $_SERVER['HTTP_USER_AGENT'], curl_exec, file_get_contents с внешними URL, add_rewrite_rule с незнакомыми паттернами.

.htaccess. Второе по частоте место для Apache/LiteSpeed-хостинга — блок RewriteCond, который направляет только поисковых роботов на сторонний домен, а обычным посетителям показывает штатный сайт:

RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (googlebot|bingbot) [NC]
RewriteRule ^promo-page-xyz$ https://сторонний-домен.tld/ [R=302,L]

Такие блоки часто дописывают в самый конец файла, после стандартных правил WordPress permalink — бегло просматривая файл сверху, их легко пропустить. Смотрите файл целиком, особенно всё, что идёт после стандартного блока # BEGIN WordPress ... # END WordPress.

Таблица wp_options. Реже, но встречается: подмена значений siteurl/home (сайт продолжает открываться нормально, но часть внутренних ссылок или редиректов уходит на другой домен), либо запись в wp_options с сериализованным списком «партнёрских» ссылок, которые читает модифицированный шаблон.

Запланированные задачи WP-Cron. Отдельная головная боль — если удалить найденную закладку, но не проверить cron, вредоносный код может вернуться сам:

wp cron event list

Ищите события с незнакомым именем хука или с колбэком, ссылающимся на файл, которого не должно быть в теме/плагине.

Место внедренияКак обнаружитьПочему туда
functions.php темыручной просмотр, поиск по curl_exec, HTTP_USER_AGENTвыполняется всегда, нет контрольных сумм
.htaccessпросмотр файла целиком, особенно конецработает до PHP, клоакинг для ботов «из коробки»
wp-content/uploads/*.phpfind … -iname "*.php"папка обычно доступна на запись, часто без защиты выполнения
wp-content/mu-pluginsлистинг каталога вручнуюавтозагрузка, не виден в списке плагинов
wp_options (siteurl, произвольные записи)сравнение с ожидаемыми значениямитихая подмена без изменения файлов
WP-Cron событияwp cron event listсамовосстановление закладки после чистки

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

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

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

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

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

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

Почему антивирус на сервере не находит эти закладки?

Обычный антивирус ищет сигнатуры известных вредоносных файлов, а описанные здесь схемы — это чаще легитимный PHP-код с вредоносной логикой (записи в БД, условные редиректы, подмена контента по User-Agent) или обфусцированные вставки под конкретный сайт. Формально «вирус» тут один на миллион заражений, сигнатуры под него никто не пишет.

Как понять, что дело не в закладках, а сайт просто криво обновился?

Ключевой признак закладки — новый контент, который явно продвигает чужую тему (казино, реплики, фарма) и которого не было раньше, а не сломанная вёрстка или ошибки после обновления. Если сомневаетесь между взломом и обычным багом после апдейта, порядок диагностики расписан в статье взлом или кривое обновление.

Нужно ли сразу переустанавливать WordPress с нуля?

Не обязательно, если вы нашли и удалили все закладки: чужие страницы/посты из базы, посторонних администраторов, изменённые functions.php и .htaccess, файлы в uploads. Но ядро и все плагины стоит переустановить поверх официальных копий той же версии — это надёжнее построчного вычитывания каждого файла.

Сколько времени обычно живёт незамеченная закладка?

Точных цифр по вашему случаю никто не даст — это сильно зависит от того, как часто вы смотрите Search Console и логи. Ориентировочно: схемы с клоакингом для ботов рассчитаны на месяцы незаметной жизни именно потому, что не видны при обычном просмотре сайта человеком — отсюда и важность регулярной сверки списков страниц, а не разовой проверки.

Что делать с бэкапами — они тоже заражены?

Если точная дата внедрения закладки неизвестна, проверяйте бэкапы тем же методом (выгрузка списка страниц, поиск неизвестных админов) прежде чем восстанавливать из них — иначе рискуете вернуть ту же дыру.

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

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

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