GitHub Security Advisory: GHSA-25cw-98hg-g3cg
Package: admidio/admidio (COMPOSER)
Severity: HIGH (CVSS 8.2)
CWE: CWE-347: Improper Verification of Cryptographic Signature
Vulnerable versions: <= 5.0.8
Patched in: 5.0.9

Summary: Admidio Ignores SAML Signature Validation Result, Processes Forged AuthnRequests and LogoutRequests

Details:
## 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:

```php
// 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:

```php
// 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):
1. `modules/sso/index.php:92` routes to `handleSSORequest()`
2. Line 403: `receiveMessage()` parses SAML binding directly from HTTP GET/POST — no authentication required
3. Line 408-409: Entity ID extracted from the forged request's Issuer element, client config loaded
4. Line 417-419: Signature validation called but return value discarded — flow