GitHub Security Advisory: GHSA-3x67-4c2c-w45m
Package: admidio/admidio (COMPOSER)
Severity: HIGH (CVSS 8.0)
CWE: CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Vulnerable versions: <= 5.0.6
Patched in: 5.0.7

Summary: Admidio has a Second-Order SQL Injection via List Configuration (lsc_special_field, lsc_sort, lsc_filter)

Details:
## Summary

The MyList configuration feature in Admidio allows authenticated users to define custom list column layouts. User-supplied column names, sort directions, and filter conditions are stored in the `adm_list_columns` table via prepared statements (safe storage), but are later read back and interpolated directly into dynamically constructed SQL queries without sanitization or parameterization. This is a classic second-order SQL injection: safe write, unsafe read.

An attacker can inject arbitrary SQL through these stored values to read, modify, or delete any data in the database, potentially achieving full database compromise.

## Details

### Step 1: Storing the Payload (Safe Write)

In `modules/groups-roles/mylist_function.php` (lines 89-115), user-supplied POST array values for column names, sort directions, and filter conditions are accepted. The only validation on column values is a prefix check (must start with `usr_` or `mem_`). Sort and condition values have no validation at all. These values are stored in the database via `ListConfiguration::addColumn()` which calls `Entity::save()` using prepared statements -- so the INSERT/UPDATE is safe.

Key source file references:
- `D:\bugcrowd\admidio\repo\modules\groups-roles\mylist_function.php` lines 89-115
- `D:\bugcrowd\admidio\repo\src\Roles\Entity\ListConfiguration.php` lines 106-116

### Step 2: Triggering the Payload (Unsafe Read)

When the list is viewed (via `lists_show.php`), `ListConfiguration::getSql()` reads the stored values and interpolates them directly into SQL in four locations:

**Injection Point 1 -- lsc_special_field in SELECT clause:**
File `D:\bugcrowd\admidio\repo\src\Roles\Entity\ListConfiguration.php` lines 739-770.
The `lsc_special_field` value is read from the database and used as a column name in the SELECT clause. Only three values (`mem_duration`, `mem_begin`, `mem_end`) get special handling; all others fall through to the `default` case where the raw value is used directly as both `$dbColumnName` and `$sqlColumnName`, then interpolated into the SQL as `$dbColumnName AS $sqlColumnName`.

**Injection Point 2 -- lsc_sort in ORDER BY clause:**
File `D:\bugcrowd\admidio\repo\src\Roles\Entity\ListConfiguration.php` lines 790-792.
The `lsc_sort` value is appended directly after the column name in the ORDER BY clause.

**Injection Point 3 -- lsc_special_field in search conditions:**
File `D:\bugcrowd\admidio\repo\src\Roles\Entity\ListConfiguration.php` lines 611-621.
The `lsc_special_field` value is interpolated into COALESCE() expressions used in search WHERE conditions.

**Injection Point 4 -- lsc_filter via ConditionParser:**
File `D:\bugcrowd\admidio\repo\src\Roles\ValueObject\ConditionParser.php` line 347.
The ConditionParser appends raw characters from the stored filter value to the SQL string. A single quote can break out of the SQL string context.

### Root Cause

The `addColumn()` method and `mylist_function.php` accept arbitrary strings for column names,