Admidio PKCS#12 private key export action lacks CSRF protection
Description
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=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.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Admidio v5.0.9 SSO key export endpoint lacks CSRF protection, enabling attackers to force an admin's browser to export private keys via a forged cross-site request.
Vulnerability
In Admidio v5.0.9, the mode=export action in modules/sso/keys.php implements a PKCS#12 bundle export of the configured private key and certificate. The CSRF validation call (SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);) is commented out, leaving the endpoint unprotected against cross‑site request forgery. The route accepts a key UUID and an export password from the POST request and directly returns the bundle as a file download [1][2].
Exploitation
An attacker must first obtain a valid SSO key UUID (e.g., through a prior information disclosure or by brute‑forcing a predictable UUID). They then craft a malicious HTML page that, when visited by an authenticated administrator, submits a POST request to /modules/sso/keys.php?mode=export&uuid= with the parameter key_password=attacker-chosen-password. No CSRF token is required, so the browser automatically sends the administrator’s session cookies. The attacker does not need any special network position beyond hosting the malicious page [1][2].
Impact
On success, the attacker forces the administrator’s browser to download a PKCS#12 file containing the private key and its associated certificate. With the private key in hand, the attacker can decrypt SSL/TLS‑protected communications meant for the SSO service, sign forged assertions, or impersonate the identity provider. This effectively compromises the entire single sign‑on trust relationship and can lead to unauthorized access to all applications relying on that SSO key [1][2].
Mitigation
The immediate fix is to re‑enable CSRF token validation by uncommenting the line SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); in modules/sso/keys.php. As of this writing (2026‑05‑29), no patched version of Admidio has been released that addresses this vulnerability. Administrators should apply the code change manually or monitor upstream for a hygiene update. No workaround is available without modifying source code [1][2].
AI Insight generated on May 29, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
475dbaaed6e26CSRF protection on SSO key export missing #2049
1 file changed · +4 −1
modules/sso/keys.php+4 −1 modified@@ -87,7 +87,10 @@ break; case 'export': - // SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); + // check form field input and sanitized it from malicious content + $passwordForm = $gCurrentSession->getFormObject($_POST['adm_csrf_token']); + $passwordForm->validate($_POST); + $keyService = new KeyService($gDb); $password = admFuncVariableIsValid($_POST, 'key_password', 'string'); $keyService->exportToPkcs12($getKeyUUID, $password);
996e287d94f7CSRF token check in enablen SSO missing #2041
2 files changed · +15 −7
modules/sso/clients.php+5 −1 modified@@ -96,7 +96,11 @@ echo json_encode(array('status' => 'success')); break; case 'enable': - $enabled = admFuncVariableIsValid($_GET, 'enabled', 'boolean'); + // check the CSRF token of the form against the session token + SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); + + $enabled = admFuncVariableIsValid($_POST, 'enabled', 'boolean'); + $getClientUUID = admFuncVariableIsValid($_POST, 'uuid', 'uuid'); $client = new SAMLClient($gDb); $client->readDataByUuid($getClientUUID); if ($client->isNewRecord()) {
src/UI/Presenter/SSOClientPresenter.php+10 −6 modified@@ -773,11 +773,14 @@ function generateClientSecret(length = 32) { /** - * Display a toggle to enable/disable a client (via a json call). - * @param $name + * Display a toggle to enable/disable a client (via a JSON call). + * @param SSOClient $client + * @return string + * @throws Exception */ - protected function generateEnableLink(SSOClient $client) { - global $gL10n; + protected function generateEnableLink(SSOClient $client): string + { + global $gL10n, $gCurrentSession; $enabled = $client->isEnabled(); $uuid = $client->getValue($client->getColumnPrefix() . '_uuid'); @@ -960,9 +963,10 @@ public function createList(): void var currentlyEnabled = \$link.data('enabled') === 1 || \$link.data('enabled') === '1'; var newEnabled = currentlyEnabled ? 0 : 1; - $.get('clients.php?mode=enable', { + $.post('clients.php?mode=enable', { uuid: \$link.data('uuid'), - enabled: newEnabled + enabled: newEnabled, + adm_csrf_token: '" . $gCurrentSession->getCSRFToken() . "' }) .done(function (response) { var data = typeof response === 'string' ? JSON.parse(response) : response;
f7dd842c4186Missing CSRF token check when sending new login data #2038
1 file changed · +4 −0
modules/registration.php+4 −0 modified@@ -125,6 +125,10 @@ } } elseif ($getMode === 'send_login') { // User already exists and has a login than sent access data with a new password + + // check the CSRF token of the form against the session token + SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']); + $user = new User($gDb, $gProfileFields); $user->readDataByUuid($getUserUUIDAssigned); $user->sendNewPassword();
8f7a2884e9dcset version to 5.0.10
1 file changed · +1 −1
system/bootstrap/constants.php+1 −1 modified@@ -23,7 +23,7 @@ const ADMIDIO_VERSION_MAIN = 5; const ADMIDIO_VERSION_MINOR = 0; -const ADMIDIO_VERSION_PATCH = 9; +const ADMIDIO_VERSION_PATCH = 10; const ADMIDIO_VERSION_BETA = 0; const ADMIDIO_VERSION = ADMIDIO_VERSION_MAIN . '.' . ADMIDIO_VERSION_MINOR . '.' . ADMIDIO_VERSION_PATCH;
Vulnerability mechanics
Root cause
"The CSRF token validation call is commented out in the export action, allowing a cross-site POST to export private key material without a valid form token."
Attack vector
An attacker crafts a malicious page that, when visited by an authenticated administrator, submits a cross-site POST request to `/modules/sso/keys.php?mode=export&uuid=<key-uuid>` with a `key_password` parameter. Because the CSRF token validation is commented out, the server processes the request and returns a PKCS#12 file containing the configured private key and certificate. Same-origin policy prevents the attacker from reading the response directly, but the export action itself is performed without any anti-forgery check.
Affected code
The vulnerable code is in `modules/sso/keys.php` where the `case 'export':` branch has the CSRF validation call `SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);` commented out. The export logic itself resides in `src/SSO/Service/KeyService.php` in the `exportToPkcs12()` method, which calls `openssl_pkcs12_export()` and streams the PKCS#12 bundle as a download.
What the fix does
The patch restores the previously commented-out call to `SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);` inside the `case 'export':` branch of `modules/sso/keys.php`. This ensures that every export request must include a valid anti-CSRF token in the POST body, preventing cross-site request forgery. Without this check, an attacker could trick an administrator's browser into exporting the private key material.
Preconditions
- authThe attacker must trick an authenticated administrator into visiting a malicious page while the administrator has an active session.
- configThe application must have at least one SSO key pair configured (created or seeded).
- inputThe attacker must know or guess the UUID of an existing SSO key.
- networkThe request is sent via HTTP POST to the export endpoint.
Generated on May 29, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.