
Technischer Advisory und Proof of Concept für CVE-2026-97286, eine authentifizierte gespeicherte XSS-Schwachstelle im WordPress-Plugin Strong Testimonials, mit Angriffskette, PoC und Patch-Analyse.
Ein authentifizierter Contributor kann vom Angreifer kontrollierte Plattform-Foto-Metadaten im WordPress-Plugin Strong Testimonials speichern. Vor Version 3.3.12 rendert das Plugin den gespeicherten Wert innerhalb eines src-Attributs eines Bildes ohne kontextbezogene Escaping. Nachdem ein Administrator das präparierte Testimonial und einen Beitrag, der es anzeigt, veröffentlicht hat, wird die Payload für Besucher ausgeführt, die diesen Beitrag öffnen, einschließlich nicht authentifizierter Besucher.
Diese Untersuchung wurde in einem autorisierten lokalen Labor durchgeführt und vor der öffentlichen Veröffentlichung über Patchstack offengelegt.
| Feld | Wert |
|---|---|
| CVE | CVE-2026-97286 |
| Produkt | Strong Testimonials |
| Anbieter | WP Chill |
| Plugin-Slug | strong-testimonials |
| Schwachstelle | Authentifizierte gespeicherte Cross-Site-Scripting |
| Betroffene Versionen | <= 3.3.11 |
| Gepatchte Version | 3.3.12 |
| Erforderliche Berechtigung | Contributor oder höher |
| CWE | CWE-79: Unsachgemäße Neutralisierung von Eingaben während der Webseitengenerierung |
| CVSS v3.1 | 6.5 Mittel — CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L |
| Forscher | Rully Miftahur Rozaq |
| Gemeldet | 16. September 2026 |
| Öffentliche Offenlegung | 30. September 2026 |
Patchstack stuft das Advisory als niedrige Priorität ein, obwohl die zugewiesene CVSS-v3.1-Basisschwere Mittel ist.
Der Testimonial-Editor akzeptiert ein custom-Request-Array und speichert jeden übermittelten Schlüssel als Post-Metadaten. Schlüssel, die nicht in der konfigurierten Definition benutzerdefinierter Felder vorhanden sind, fallen auf sanitize_text_field() zurück. Dies ermöglicht es einem Contributor, plugin-reservierte Metadaten wie platform und platform_user_photo zu übermitteln, während er ein Testimonial bearbeitet, das ihm gehört.
Für unterstützte soziale Plattformen ruft das Plugin platform_user_photo ab, überprüft, ob die URL eine HTTP-200-Antwort zurückgibt, und interpoliert anschließend die ursprünglich gespeicherte Zeichenkette in ein <img src="https://raw.githubusercontent.com/rully2212/cve-2026-97286/main/...">-Attribut. Die Validierung des HTTP-Status macht eine Zeichenkette nicht sicher für ein HTML-Attribut. Ein Anführungszeichen im URL-Fragment kann src beenden und einen Event-Handler einführen.
Die verifizierte Angriffskette lautet:
Die Injection ist authentifiziert. Der Besucher, der die bereits veröffentlichte Payload auslöst, muss nicht authentifiziert sein.
Eine erfolgreiche Ausnutzung ermöglicht eine persistente JavaScript-Ausführung im betroffenen WordPress-Ursprung auf Seiten, die das präparierte Testimonial über den verwundbaren Thumbnail-Pfad rendern. Der Code wird mit den Browser-Privilegien und der WordPress-Sitzung des Besuchers ausgeführt, sofern vorhanden.
Der reproduzierte Pfad erfordert eine Veröffentlichung durch einen Administrator, was den CVSS-Metrikwert UI:R erklärt. Diese Untersuchung hat keine nicht authentifizierte Injection, keine automatische Ausführung auf jeder Seite der Website, keine Privilegienerweiterung, keinen Cookie-Diebstahl und keine vollständige Kompromittierung der Website nachgewiesen.
Der Proof of Concept wurde mit der folgenden Konfiguration reproduziert:
| Komponente | Konfiguration |
|---|---|
| WordPress | 7.1 |
| Strong Testimonials | 3.3.7, kostenlose Edition |
| Theme | Twenty Twenty-Five |
| Anzeigevorlage | default-theme |
| Thumbnail | Aktiviert |
| PHP / Apache | PHP 8.3.33 / Apache 2.4.68 |
| Datenbank | MariaDB 11.4 |
| Angreiferrolle | Contributor |
| Angreiferfähigkeiten | edit_posts=true, publish_posts=false, unfiltered_html=false |
Version 3.3.7 war die exakte Version, die im Labor verwendet wurde. Der öffentliche CVE-Eintrag identifiziert alle Versionen bis 3.3.11 als betroffen und Version 3.3.12 als behoben.
In admin/class-strong-testimonials-post-editor.php iteriert save_details() über jeden Eintrag im übermittelten custom-Array. Ein nicht erkannter Schlüssel erreicht den generischen Bereinigungszweig und wird anschließend mit update_post_meta() gespeichert:
$custom = $_POST['custom'];
foreach ( $custom as $key => $value ) {
// ...
$sanitized_value = sanitize_text_field( $value );
update_post_meta( $post_id, $key, $sanitized_value );
}
Der echte Handler führt Nonce-Prüfungen und feldspezifische Bereinigungen durch. Der Fund beruht nicht auf der Umgehung dieser Prüfungen oder der Bearbeitung des Testimonials eines anderen Benutzers. Der Contributor bearbeitet legitim sein eigenes Testimonial, liefert jedoch die reservierten Schlüssel platform und platform_user_photo.
In verwundbaren Versionen liest includes/functions-image.php platform_user_photo, führt eine HTTP-Statusprüfung über wpmtst_is_valid_image_url() durch und fügt die ursprüngliche Zeichenkette in ein HTML-Attribut ein:
$platform_user_photo = get_post_meta( $post_id, 'platform_user_photo', true );
if ( ! $platform_user_photo || ! wpmtst_is_valid_image_url( $platform_user_photo ) ) {
return $img;
}
return sprintf(
'<img src="https://raw.githubusercontent.com/rully2212/cve-2026-97286/main/%25s" %s %s/>',
$platform_user_photo,
$width ? "width='{$width}'" : '',
$height ? "height='{$height}'" : ''
);
Der Fragment-Teil einer URL wird nicht an den HTTP-Server gesendet, bleibt aber in der Zeichenkette erhalten, die später von PHP gerendert wird. Der Server kann daher eine harmlose URL sehen, die HTTP 200 zurückgibt, während der Browser einen Wert erhält, der eine Attribut-Ausbruchssequenz enthält.
Das default-theme-Template ruft wpmtst_get_thumbnail() auf und gibt das zurückgegebene Bild-Markup direkt aus:
<figure class="wp-block-image wpmtst-testimonial-avatar">
<?php echo $wpmtst_avatar_html; ?>
</figure>
Die Vorlage geht davon aus, dass der Helper bereits sicheres HTML erzeugt hat. In den betroffenen Versionen hat der Plattform-Foto-Helper die URL für seinen src-Kontext nicht escaped.
Reproduzieren Sie dieses Problem nur auf einem System, das Ihnen gehört oder für dessen Test Sie ausdrücklich autorisiert sind.
<= 3.3.11 auf einer autorisierten WordPress-Testseite.default-theme und aktivierten Thumbnails.Melden Sie sich als Contributor an und erstellen Sie einen neuen Testimonial-Entwurf.
Erfassen Sie die legitime POST /wp-admin/post.php-Speicheranfrage mit einem abfangenden Proxy. Behalten Sie den vollständigen Formular-Body, das Session-Cookie, den frischen WordPress-Nonce, den Plugin-Nonce und die ID des Testimonials im Besitz des Contributors bei.
Hängen Sie diese Parameter an den Formular-Body an:
custom[platform]=facebook
custom[platform_user_photo]=http://wordpress/index.php?rest_route=/#" onerror="alert(1)" x="
URL-kodiertes Formularfragment: