
Комментарии 10
Есть ли догадки каким образом взломали?
Точно не скажу, первый вход был ещё до меня.
Второй заход разобрал по логам, там сошлись две вещи.
Первая: июльская чистка пропустила закладку. Лежала она в плагинах, с виду обычный «инструмент доступности», а внутри обработчик, который принимает команду прямо в параметре запроса. В uploads, куда лезут первым делом, было чисто. Правда, стрелять эта штука уже не могла: хостинг за три дня до её появления прикрыл своим .htaccess прямой доступ к php внутри wp-content/plugins. Я в сентябре специально в неё постучался, получил 403.
Вторая, главная: пароль утёк повторно, уже после смены. Видно по неудачным входам: весь день одна подсеть долбилась в wp-login, и в поле пароля у неё лежал адрес самого сайта, log=admin&pwd=https://… Кто-то разобрал строку url:login:password со сдвигом на поле. Это формат стилерских выгрузок, то есть паролей из браузера. Доказать нечем, но только это и объясняет, почему после смены пароля вход снова оказался открыт и почему дальше шли с разных адресов, каждый со своим набором плагинов.
С тех пор сверяю плагины и темы со списком того, что там должно стоять. Ну и DISALLOW_FILE_MODS, Basic Auth на wp-login, отдельная учётка владельцу.
Тоже взломали несколько сайтов клиента. Первые следы от 18 июля, а обнаружил только 03 августа, когда пришло письмо о смене пароля.
Там была обнаружена критическая уязвимость в WP, подробнее тут и тут.
Перед wp-login.php добавили Basic Auth с отдельным паролем.
К сожалению это не помогло бы избежать взлома, так как уязвимость была в api. На одном из сайтов у клиента Basic Auth на всю wp-admin, и он так-же был взломан.
А вот своевременное обновление возможно помогло бы)
Тоже вычищал всё руками
Первым делом накатил обновление, снёс админов, поменял и соли, и ключи.
Весь исходный код (движка, плагинов, свой) сравнивал с локальной копией, ничего не изменялось, боты только загружали свои шеллы.
В плагинах было подобное:
/wp-content/plugins/site-health-c459570f9cce/site-health-c459570f9cce.php
В загрузках по типу такого было много файлов:
/wp-content/uploads/w2sx3c9f1e.php.jpg
/wp-content/uploads/wp-cache**.php.jpg и phtml

В базе данных тоже, на медиа файлы:

и куча таких записей, не разобрался как они работают (blockquote+iframe:

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

Страницы в базе тоже все были осмотрены.
В общем, повезло чуть больше - ничего не удалили, ничего не изменили, только намусорили, определенно это были просто автоматические боты.
На всех сайтах так-же установлено define("DISALLOW_FILE_MODS", true);, но это не помогло. С такой уязвимостью и не должно было, так как через шелл они могли сделать всё что угодно. Но возможно некоторым ботам это помешало что-то автоматически напакостить.
Про Basic Auth соглашусь: это защита от перебора и ботов на форме входа, дыру в API она не закрывает. В моём случае и она бы не спасла по другой причине: пароль был настоящий, украденный, с ним прошли бы и через неё.
Что делаю после этой истории, помимо обновлений:
DISALLOW_FILE_MODS. Даже когда админский доступ уже у чужого, залить плагин или тему через админку не получится. У меня атакующий ставил себе инструменты один за другим именно через интерфейс, так что это закрывает самый удобный для него путь.
Раз в неделю два списка: wp user list и wp plugin list. Минута работы, а чужой админ виден сразу. В августе я как раз этого не сделал и подарил им три недели.
wp core verify-checksums плюс поиск свежих файлов по mtime, а не по именам. Имена у шеллов каждый раз новые, дата изменения врёт реже.
За ссылку на разбор wp2shell спасибо, гляну внимательнее 👍 У вас обновления автоматом или руками по расписанию?
Руками обновляю проекты этого клиента, обычно не реже чем раз в 10 дней накатываю, проверяя что ничего не сломалось, но в тот раз занят был сильно другими задачами, и вовремя не заметил аномалий. Хотя там пары дней было достаточно побыть необновленным до первого шелла, практически без шансов...
У других клиентов сайты попроще, и там автоматом обновления стоят - у них ничего не случилось.
Идея с поиском свежих файлов по mtime - интересна, надо сделать скрипт с уведомлением об изменении и в cron поставить раз в сутки, очень бы помогло.
И список пользователей мониторить тоже интересно. Можно хук сделать на user_register и моментально узнавать о добавлении, и в крон тоже добавить сверку для гарантии.
Да и выходы новых версий WP тоже мониторить буду скриптом, чтоб такие вещи не пропускать)
Вы уверены что дело в слитом пароле, а не в пропущенном случайно шелле или в необновлении WP? Не отрицаю версию, просто хочется понять признаки, по которым можно отличить эти сценарии. То что боты круглосуточно долбятся с подбором паролей и всяких инъекций - это всегда было. Нам какой-то из ботов тоже пароли начал менять, на этом и обнаружился, хоть почта была настроена...
Про раз в десять дней вручную понимаю, у меня та же история: я на тот сайт заходил по другой задаче и списки не посмотрел.
По mtime самое простое, что у меня прижилось: find /path/to/site -type f -name '*.php' -newermt '-24 hours' -not -path '*/cache/*'
Два уточнения, без них будет много ложных срабатываний: исключить каталоги кэша и логов, и хранить прошлый список, чтобы в уведомление шли только новые строки. Плюс раз в сутки wp core verify-checksums, он ловит подменённые файлы ядра, у которых дата может выглядеть старой.
По пользователям в том же cron: wp user list --role=administrator --field=user_login. Сравниваю с эталонным списком в файле, при расхождении уведомление. Тем же способом wp plugin list --status=active: чужой плагин видно сразу, даже если он маскируется под что-то безобидное.
И одна мелочь, которая мне помогла больше остального: слать это не на почту, а в телеграм. Почту в запарке проще пролистать, а такие уведомления приходят ровно в тот момент, когда некогда 🙂
Какой кошмар. Годы идут, и ничего не меняется. WordPress как ломали, так и ломают, причем в автоматическом режиме. Зачем пользоваться этим странным дырявым продуктом? Там же достаточно заглянуть в код чтобы понять, что ничего хорошего от такой лапши ждать не приходится.
В этом случае WordPress как раз ни при чём. Зашли по паролю администратора, который утёк с чьего-то компьютера. С таким паролем войдут в любую панель, хоть в самописную.
А в автоматическом режиме его долбят по скучной причине: это самая большая цель на рынке. Боту без разницы, какой там код, он перебирает известные дыры плагинов и ворованные пароли подряд по всему интернету. Была бы на этом месте другая система с такой же долей, доставалось бы ей 🤷
Про лапшу спорить не буду, код местами музейный 😄 Но выбор у парикмахерской или поставщика оборудования выглядит так: движок, который поправит любой подрядчик за пару тысяч рублей в час, либо своя разработка, которую держит один конкретный человек, и когда он пропадает, сайт замирает вместе с ним. Обычно выигрывает первое, и я понимаю почему.
Хотя тут соглашусь: будь этот сайт статикой, вся история свелась бы к «кто-то украл пароль от админки», и на этом бы кончилась.
У wp_delete_user() есть второй необязательный параметр – ID пользователя, на которого переназначить контент, и WP-CLI поддерживает то же самое через `wp user delete <id> --reassign=<id>`. Без него ядро проверяет флаг delete_with_user у каждого зарегистрированного типа записи: у страниц и вложений он по умолчанию true, у кастомных типов вроде WooCommerce-товаров – false, если разработчик не включил его сам. Поэтому в этой истории товары и заказы пережили удаление админа, а страницы и медиатека – нет.
Да, всё так, спасибо. В статье я ограничился общим «набор зависит от их регистрации и фильтров», а вы назвали сам флаг и откуда берётся его значение. Так понятнее, почему одно ушло, а другое осталось.
Про reassign стоит помнить и без всякого взлома. Уходит сотрудник, учётку чистят скриптом, и следом уезжает всё, что он когда-то загружал.
Как я восстанавливал WordPress после взлома и дважды ошибся