
SureForms <= 2.2.0 - Cross-Site Scripting stocké non authentifié
Une vulnérabilité critique de XSS stocké existe dans le plugin SureForms. La vulnérabilité provient d'une implémentation côté client non sécurisée dans entries.js où les entrées utilisateur sont décodées de manière programmatique (inversant la désinfection côté serveur) puis rendues à l'aide de dangerouslySetInnerHTML sans désinfection côté client appropriée. Cela permet à des attaquants non authentifiés d'injecter des charges utiles JavaScript malveillantes via des champs de formulaire standard, contournant les filtres wp_kses par défaut de WordPress.
Pour vérifier si un site cible exécute SureForms, inspectez l'en-tête du fichier de traduction qui contient généralement le numéro de version.
URL cible :
http://target-site.com/wp-content/plugins/sureforms/languages/sureforms.pot
Commande de vérification :
curl -s "http://target-site.com/wp-content/plugins/sureforms/languages/sureforms.pot" | grep "Project-Id-Version"
La vulnérabilité réside dans l'interface d'administration basée sur React responsable de l'affichage des entrées de formulaire (entries.js).
Dans le fichier minifié entries.js, il existe une fonction d'assistance (identifiée comme Xv dans la build analysée) conçue pour gérer les entités HTML.
Logique du code (reconstituée) :
// Fonction Xv : Prétraitement des valeurs de champ
var Xv = function(input) {
var value = input.value;
// CAUSE RACINE DE LA VULNÉRABILITÉ :
// Si la chaîne contient des entités HTML (ex : <), crée une zone de texte,
// injecte le contenu et extrait la valeur.
// Cela DÉCODE effectivement les entités HTML en balises HTML brutes.
// Exemple : "<img ...>" devient ""
if (typeof value === "string" && value.match(/&[a-zA-Z0-9#]+;/)) {
var textarea = document.createElement("textarea");
textarea.innerHTML = value;
// Logique pour annuler la désinfection
if (textarea.value.includes("<") || textarea.value.includes(">")) {
value = textarea.value; // Maintenant 'value' contient du HTML BRUT
}
}
return { ...input, value: value };
}
Analyse : Cette fonction contrecarre la sécurité du backend. Même si WordPress stocke correctement <script> en tant que <script> dans la base de données, cette fonction le reconvertit en <script> immédiatement après l'avoir récupéré depuis l'API.
Après le décodage, les données sont transmises au composant de rendu (identifié comme Jv).
Logique du code (reconstituée) :
// Fonction Jv : Rendu du champ
var Jv = function(props) {
var field = props.field;
var val = field.value; // Cette valeur est désormais du HTML brut (post-décodage)
// LE DÉCLENCHEUR : Vérifie si la chaîne ressemble à du HTML
if (typeof val === "string" && val.match(/<[^>]+>/g)) {
// LE SINK : Rendu via dangerouslySetInnerHTML SANS désinfection côté client (ex : DOMPurify)
return React.createElement("span", {
dangerouslySetInnerHTML: { __html: val }
});
}
// Rendu sûr pour le texte non HTML
return React.createElement("span", null, val);
}
Injection : Un attaquant non authentifié soumet un formulaire sur le frontend. Au lieu d'utiliser des balises HTML brutes (qui seraient supprimées/désinfectées par le backend WordPress), l'attaquant envoie des entités HTML.
Stockage : WordPress considère l'entrée (ex : <img...) comme un texte sûr et la stocke dans la base de données.
Exécution (Le piège) :
Xv détecte les entités et décode <img... en ` Entries.Cliquez sur l'entrée soumise à l'étape 1 pour voir les détails.
Résultat : Le navigateur exécutera la charge utile JavaScript (alert('XSS_SUREFORMS')).