CVE-2026-32813: Admidio/admidio SQL Injection Vulnerability

HIGH
Advisory Identifier: GHSA-3x67-4c2c-w45m
CVSS 8.0
CWE-89
Published
Advisory Summary

CVE-2026-32813 is a HIGH (CVSS 8.0) sql injection vulnerability (CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')) affecting Admidio/admidio libraries in the Packagist ecosystem. The issue involves admidio has a second-order sql injection via list configuration (lsc_special_field, lsc_sort, lsc_filter). Remediation is available by upgrading affected packages to version 5.0.7.

CVE-2026-32813 at a Glance

CVE Identifier CVE-2026-32813
GHSA Identifier GHSA-3x67-4c2c-w45m
Severity HIGH
CVSS v3.1 Base Score 8.0
CWE Classification CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Affected Product Admidio/admidio
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 8.0

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-32813

To remediate this vulnerability, upgrade all affected Admidio/admidio dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

admidio/admidio fixed: 5.0.7
Upgrade admidio/admidio to 5.0.7
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for admidio/admidio

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-32813, inspect your project dependencies for Admidio/admidio:

inspect dependencies for admidio/admidio

Analyze the terminal output to locate any version of Admidio/admidio in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.0.7), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'), where the product constructs all or part of an sql command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended sql command when it is sent to a downstream component. without sufficient removal or quoting of sql syntax in user-controllable inputs, the generated sql query can cause those inputs to be interpreted as sql instead of ordinary user data. In Admidio/admidio, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## 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, sort directions, and filter conditions. The only gate for column names is a prefix check (`usr_` or `mem_`), which is trivially satisfied by an attacker (e.g., `usr_id) UNION SELECT ...`). No allowlist of valid column names exists. No server-side validation of sort values exists (should only allow ASC/DESC/empty). The frontend `<select>` element only offers ASC/DESC, but this is trivially bypassed by POSTing arbitrary values. ## PoC **Prerequisites:** Logged-in user with list edit permission (default: all logged-in users). **Step 1: Save a list config with SQL injection in lsc_special_field** ``` curl -X POST "https://TARGET/adm_program/modules/groups-roles/mylist_function.php?mode=save_temporary" \ -H "Cookie: ADMIDIO_SESSION_ID=<session>" \ -d "adm_csrf_token=<csrf_token>" \ -d "column[]=usr_login_name" \ -d "column[]=usr_id FROM adm_users)--" \ -d "sort[]=" \ -d "sort[]=" \ -d "condition[]=" \ -d "condition[]=" \ -d "sel_roles[]=<valid_role_uuid>" ``` The second column value `usr_id FROM adm_users)--` starts with `usr_` so it passes the prefix check. When read back in `getSql()`, it is interpolated directly as a column expression in the SQL SELECT clause. **Step 2: Sort-based injection (simpler, no prefix check needed)** ``` curl -X POST "https://TARGET/adm_program/modules/groups-roles/mylist_function.php?mode=save_temporary" \ -H "Cookie: ADMIDIO_SESSION_ID=<session>" \ -d "adm_csrf_token=<csrf_token>" \ -d "column[]=usr_login_name" \ -d "sort[]=ASC,(SELECT+CASE+WHEN+(1=1)+THEN+1+ELSE+1/0+END)" \ -d "condition[]=" \ -d "sel_roles[]=<valid_role_uuid>" ``` This injects into the ORDER BY clause. The sort value has zero server-side validation. **Step 3:** The `save_temporary` mode automatically redirects to `lists_show.php` which calls `ListConfiguration::getSql()`, executing the injected SQL. ## Impact - **Data Exfiltration:** An attacker can extract any data from the database including password hashes, email addresses, personal data of all members, and application configuration. - **Data Modification:** With stacked queries (supported by MySQL with PDO), the attacker can modify or delete data. - **Privilege Escalation:** Password hashes can be extracted and cracked, or admin accounts can be directly modified. - **Full Database Compromise:** The entire database is accessible through this vulnerability. The attack requires authentication and CSRF token, but: 1. Any logged-in user has this permission by default (when `groups_roles_edit_lists = 1`). 2. The CSRF token is available in the same session. 3. The injected payload persists in the database and triggers every time anyone views the list. ## Recommended Fix ### Fix 1: Allowlist for lsc_special_field Add a strict allowlist of valid special field names before calling `addColumn()` in `mylist_function.php`. The list should match exactly the field names supported in `getSql()` and the JavaScript on `mylist.php`. ### Fix 2: Validate lsc_sort values In `ListConfiguration::addColumn()`, validate that the sort parameter is one of ASC, DESC, or empty string before storing it. ### Fix 3: Defense-in-depth validation in ListConfiguration::getSql() Also validate the `lsc_special_field` value against an allowlist in `getSql()` before interpolating it into the SQL string. This protects against payloads already stored in the database. ### Fix 4: Escape filter values in ConditionParser Use parameterized queries or at minimum escape single quotes in `ConditionParser::makeSqlStatement()`.

CVE-2026-32813 Affected Admidio/admidio Versions

1 component(s)
Fixed Versions:
5.0.7
Product / Package Ecosystem Affected Versions Fixed Version
admidio/admidio
Packagist
< 5.0.7
5.0.7

References

Frequently Asked Questions

What is CVE-2026-32813?
CVE-2026-32813 is a security vulnerability in Admidio/admidio involving Admidio has a Second-Order SQL Injection via List Configuration (lsc_special_field, lsc_sort, lsc_filter) under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').
Which Admidio/admidio versions are affected?
Affected versions include releases prior to 5.0.7 across affected packages: admidio/admidio.
What version fixes CVE-2026-32813?
Upgrading to 5.0.7 (or the latest patched release for your respective branch) resolves the vulnerability.
Is admidio/admidio affected?
Yes, admidio/admidio is affected in releases prior to 5.0.7. Upgrade your dependency to 5.0.7 or later.
What is the CVSS score of CVE-2026-32813?
CVE-2026-32813 has a CVSS v3.1 base score of 8.0 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2026-32813?
CVE-2026-32813 is classified under CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.).
How can I check Packagist dependencies for CVE-2026-32813?
Run the dependency tree command: 'inspect dependencies for admidio/admidio'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-3x67-4c2c-w45m",
  "modified" : "2026-03-20T21:19:34Z",
  "published" : "2026-03-16T21:19:09Z",
  "aliases" : [ "CVE-2026-32813" ],
  "summary" : "Admidio has a Second-Order SQL Injection via List Configuration (lsc_special_field, lsc_sort, lsc_filter)",
  "details" : "## Summary\n\nThe 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.\n\nAn attacker can inject arbitrary SQL through these stored values to read, modify, or delete any data in the database, potentially achieving full database compromise.\n\n## Details\n\n### Step 1: Storing the Payload (Safe Write)\n\nIn `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.\n\nKey source file references:\n- `D:\\bugcrowd\\admidio\\repo\\modules\\groups-roles\\mylist_function.php` lines 89-115\n- `D:\\bugcrowd\\admidio\\repo\\src\\Roles\\Entity\\ListConfiguration.php` lines 106-116\n\n### Step 2: Triggering the Payload (Unsafe Read)\n\nWhen the list is viewed (via `lists_show.php`), `ListConfiguration::getSql()` reads the stored values and interpolates them directly into SQL in four locations:\n\n**Injection Point 1 -- lsc_special_field in SELECT clause:**\nFile `D:\\bugcrowd\\admidio\\repo\\src\\Roles\\Entity\\ListConfiguration.php` lines 739-770.\nThe `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`.\n\n**Injection Point 2 -- lsc_sort in ORDER BY clause:**\nFile `D:\\bugcrowd\\admidio\\repo\\src\\Roles\\Entity\\ListConfiguration.php` lines 790-792.\nThe `lsc_sort` value is appended directly after the column name in the ORDER BY clause.\n\n**Injection Point 3 -- lsc_special_field in search conditions:**\nFile `D:\\bugcrowd\\admidio\\repo\\src\\Roles\\Entity\\ListConfiguration.php` lines 611-621.\nThe `lsc_special_field` value is interpolated into COALESCE() expressions used in search WHERE conditions.\n\n**Injection Point 4 -- lsc_filter via ConditionParser:**\nFile `D:\\bugcrowd\\admidio\\repo\\src\\Roles\\ValueObject\\ConditionParser.php` line 347.\nThe ConditionParser appends raw characters from the stored filter value to the SQL string. A single quote can break out of the SQL string context.\n\n### Root Cause\n\nThe `addColumn()` method and `mylist_function.php` accept arbitrary strings for column names, sort directions, and filter conditions. The only gate for column names is a prefix check (`usr_` or `mem_`), which is trivially satisfied by an attacker (e.g., `usr_id) UNION SELECT ...`). No allowlist of valid column names exists. No server-side validation of sort values exists (should only allow ASC/DESC/empty). The frontend `<select>` element only offers ASC/DESC, but this is trivially bypassed by POSTing arbitrary values.\n\n## PoC\n\n**Prerequisites:** Logged-in user with list edit permission (default: all logged-in users).\n\n**Step 1: Save a list config with SQL injection in lsc_special_field**\n\n```\ncurl -X POST \"https://TARGET/adm_program/modules/groups-roles/mylist_function.php?mode=save_temporary\" \\\n  -H \"Cookie: ADMIDIO_SESSION_ID=<session>\" \\\n  -d \"adm_csrf_token=<csrf_token>\" \\\n  -d \"column[]=usr_login_name\" \\\n  -d \"column[]=usr_id FROM adm_users)--\" \\\n  -d \"sort[]=\" \\\n  -d \"sort[]=\" \\\n  -d \"condition[]=\" \\\n  -d \"condition[]=\" \\\n  -d \"sel_roles[]=<valid_role_uuid>\"\n```\n\nThe second column value `usr_id FROM adm_users)--` starts with `usr_` so it passes the prefix check. When read back in `getSql()`, it is interpolated directly as a column expression in the SQL SELECT clause.\n\n**Step 2: Sort-based injection (simpler, no prefix check needed)**\n\n```\ncurl -X POST \"https://TARGET/adm_program/modules/groups-roles/mylist_function.php?mode=save_temporary\" \\\n  -H \"Cookie: ADMIDIO_SESSION_ID=<session>\" \\\n  -d \"adm_csrf_token=<csrf_token>\" \\\n  -d \"column[]=usr_login_name\" \\\n  -d \"sort[]=ASC,(SELECT+CASE+WHEN+(1=1)+THEN+1+ELSE+1/0+END)\" \\\n  -d \"condition[]=\" \\\n  -d \"sel_roles[]=<valid_role_uuid>\"\n```\n\nThis injects into the ORDER BY clause. The sort value has zero server-side validation.\n\n**Step 3:** The `save_temporary` mode automatically redirects to `lists_show.php` which calls `ListConfiguration::getSql()`, executing the injected SQL.\n\n## Impact\n\n- **Data Exfiltration:** An attacker can extract any data from the database including password hashes, email addresses, personal data of all members, and application configuration.\n- **Data Modification:** With stacked queries (supported by MySQL with PDO), the attacker can modify or delete data.\n- **Privilege Escalation:** Password hashes can be extracted and cracked, or admin accounts can be directly modified.\n- **Full Database Compromise:** The entire database is accessible through this vulnerability.\n\nThe attack requires authentication and CSRF token, but:\n1. Any logged-in user has this permission by default (when `groups_roles_edit_lists = 1`).\n2. The CSRF token is available in the same session.\n3. The injected payload persists in the database and triggers every time anyone views the list.\n\n## Recommended Fix\n\n### Fix 1: Allowlist for lsc_special_field\n\nAdd a strict allowlist of valid special field names before calling `addColumn()` in `mylist_function.php`. The list should match exactly the field names supported in `getSql()` and the JavaScript on `mylist.php`.\n\n### Fix 2: Validate lsc_sort values\n\nIn `ListConfiguration::addColumn()`, validate that the sort parameter is one of ASC, DESC, or empty string before storing it.\n\n### Fix 3: Defense-in-depth validation in ListConfiguration::getSql()\n\nAlso validate the `lsc_special_field` value against an allowlist in `getSql()` before interpolating it into the SQL string. This protects against payloads already stored in the database.\n\n### Fix 4: Escape filter values in ConditionParser\n\nUse parameterized queries or at minimum escape single quotes in `ConditionParser::makeSqlStatement()`.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "admidio/admidio"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "5.0.7"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 5.0.6"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/Admidio/admidio/security/advisories/GHSA-3x67-4c2c-w45m"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-32813"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/Admidio/admidio/commit/3473bf5a7aa1bfc5043e73979719396276f4189f"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/Admidio/admidio"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-89" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-03-16T21:19:09Z",
    "nvd_published_at" : "2026-03-20T02:16:35Z"
  }
}