Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
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
6il y a 1 moisPas 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

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)

🔎 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

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 :

$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)

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.

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 :

Télécharger l’outil