GitHub Security Advisory: GHSA-3gw8-3mg3-jmpc
Package: devcode-it/openstamanager (COMPOSER)
Severity: HIGH (CVSS 8.8)
CWE: CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Vulnerable versions: <= 2.10.1
Patched in: 2.10.2

Summary: OpenSTAManager has a Time-Based Blind SQL Injection via `options[stato]` Parameter

Details:
## Description

Multiple AJAX select handlers in OpenSTAManager <= 2.10.1 are vulnerable to Time-Based Blind SQL Injection through the `options[stato]` GET parameter. The user-supplied value is read from `$superselect['stato']` and concatenated directly into SQL WHERE clauses as a bare expression, without any sanitization, parameterization, or allowlist validation.

An authenticated attacker can inject arbitrary SQL statements to extract sensitive data from the database, including usernames, password hashes, financial records, and any other information stored in the MySQL database.

## Affected Endpoints

Three modules share the same vulnerability pattern:

### 1. Preventivi (Quotes) - Primary

- **Endpoint:** `GET /ajax_select.php?op=preventivi`
- **File:** `modules/preventivi/ajax/select.php`, line 60
- **Required parameters:** `options[idanagrafica]` (any valid ID)

**Vulnerable code:**

```php
// modules/preventivi/ajax/select.php, lines 59-60
$stato = !empty($superselect['stato']) ? $superselect['stato'] : 'is_pianificabile';
$where[] = '('.$stato.' = 1)';
```

The `$stato` variable is inserted as a bare expression inside parentheses. The resulting SQL fragment becomes `({user_input} = 1)`, allowing an attacker to break out of the expression and inject arbitrary SQL.

### 2. Ordini (Orders)

- **Endpoint:** `GET /ajax_select.php?op=ordini-cliente`
- **File:** `modules/ordini/ajax/select.php`, line 52
- **Required parameters:** `options[idanagrafica]` (any valid ID)

**Vulnerable code:**

```php
// modules/ordini/ajax/select.php, lines 51-52
$stato = !empty($superselect['stato']) ? $superselect['stato'] : 'is_fatturabile';
$where[] = '`or_statiordine`.'.$stato.' = 1';
```

The `$stato` variable is inserted as a column name reference. The resulting SQL fragment becomes `` `or_statiordine`.{user_input} = 1 ``, allowing injection after the table-column reference.

### 3. Contratti (Contracts)

- **Endpoint:** `GET /ajax_select.php?op=contratti`
- **File:** `modules/contratti/ajax/select.php`, line 57
- **Required parameters:** `options[idanagrafica]` (any valid ID)

**Vulnerable code:**

```php
// modules/contratti/ajax/select.php, lines 56-57
$stato = !empty($superselect['stato']) ? $superselect['stato'] : 'is_pianificabile';
$where[] = '`idstato` IN (SELECT `id` FROM `co_staticontratti` WHERE '.$stato.' = 1)';
```

The `$stato` variable is inserted inside a subquery. The resulting SQL fragment becomes `WHERE {user_input} = 1)`, allowing an attacker to close the subquery and inject into the outer query.

## Root Cause Analysis

### Data Flow

1. The attacker sends a GET request with `options[stato]=<payload>` 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