Skip to main content

Chainlit CVE-2026-45019

| EUVDEUVD-2026-65738 HIGH
Server-Side Request Forgery (SSRF) (CWE-918)
2026-08-25 https://github.com/Chainlit/chainlit GHSA-hvfh-5mj3-5f3j
7.2
CVSS 3.1 · Vendor: https://github.com/Chainlit/chainlit
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Open Socket.IO polling session
  2. Delivery
    Submit clientType/sessionId handshake to /mcp
  3. Exploit
    POST /mcp with attacker-controlled url and headers dict
  4. Execution
    Chainlit server issues outbound HTTP request
  5. Persist
    Reach internal network service or IMDSv1 metadata endpoint
  6. Impact
    Deliver forged Authorization header to trigger state-changing internal API call

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V4 - API and Web Service URL allowlisting, SSRF prevention
MITRE ATT&CK

2 techniques across 2 tactics: Initial Access, Credential Access

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

Exploitation requires that features.mcp.enabled = true is set in .chainlit/config.toml - a non-default configuration; MCP has been disabled by default since v2.7.0, meaning the majority of Chainlit deployments are not affected. When MCP is enabled and no authentication callback has been registered, /mcp is reachable by any client able to open a Socket.IO session (no credentials required), consistent with CVSS PR:N. Exploitation is further limited by: (1) the non-default opt-in configuration requirement - operators must have explicitly enabled MCP; (2) the blind SSRF constraint - response bodies are not returned to the attacker, precluding direct data exfiltration via read-back; (3) IMDSv2 metadata endpoints are not reachable via this primitive because they require a PUT token handshake the attacker cannot perform through the SSRF vector. If an authentication callback is registered, the unauthenticated attack path is closed, though authenticated sessions can still trigger SSRF. The header-forwarding amplification (Authorization, Cookie) is present only in versions >=2.6.4.

Impact

The CVSS 3.1 vector (AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N, score 7.2) accurately reflects the unauthenticated, network-reachable nature of the flaw; the scope change (S:C) captures that the Chainlit server becomes a proxy reaching systems beyond its own trust boundary. A critical real-world limiting factor is that MCP has been disabled by default since v2.7.0 - only deployments that explicitly set features.mcp.enabled = true in .chainlit/config.toml are exposed, substantially narrowing the affected population relative to what raw CVSS implies. EPSS data was not provided in the input. The combination of a published, working POC and full attacker control over both destination URL and request headers makes this a high priority for any deployment that has opted into MCP; the blind nature of the SSRF (response not returned to attacker) limits passive data exfiltration but does not prevent state-changing operations - a forged POST with an Authorization header to an internal API has effect regardless of whether the attacker reads the response. IMDSv1 metadata endpoint probing is feasible; IMDSv2 is not exploitable via this primitive as it requires a PUT handshake the attacker cannot perform. No CISA KEV listing was present in the provided data.

Exploit ScenarioDerived

An unauthenticated attacker targeting a Chainlit instance with features.mcp.enabled = true first establishes a Socket.IO session via the polling transport, then submits a crafted POST to /mcp with clientType set to streamable-http, a url pointing to an internal service such as http://169.254.169.254/latest/meta-data/ or an internal admin API, and a headers object carrying a forged Authorization bearer token. The Chainlit server issues the outbound POST with all attacker-supplied headers to the target; although the response is never returned to the attacker (blind SSRF), a forged POST to an internal API with a valid-looking Authorization header can trigger write operations without requiring a read-back. A working POC was confirmed against Chainlit 2.11.0 by SPL Security researchers and is published in the GHSA advisory.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
Patch available 0.3% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Aug 25, 2026

Blind SSRF in Chainlit's MCP endpoint allows unauthenticated remote attackers to force the server to issue outbound HTTP requests to arbitrary internal or external URLs - including cloud metadata endpoints (e.g., AWS IMDSv1 at 169.254.169.254) - with fully attacker-controlled HTTP headers including Authorization and Cookie. Affected versions span 2.4.0rc0 through 2.11.x; the amplified form with attacker-controlled header forwarding affects 2.6.4 through 2.11.x, enabling state-changing authenticated requests against internal APIs without requiring any response read-back. Publicly available exploit code exists, confirmed against Chainlit 2.11.0 by SPL Security researchers; no CISA KEV listing is present at time of analysis.

RemediationAI

Upgrade to Chainlit 2.12.0 (vendor-released patch, 2026-08-25; fix commit 0565fd0eccb915fce159929598b053ed79f6e0c9). This is a breaking change: legacy MCP config sections ([features.mcp.sse], [features.mcp.stdio], [features.mcp.streamable-http], allowed_executables) now abort startup when MCP is enabled and must be migrated to the unified [[features.mcp.servers]] array and optional [features.mcp.user_servers] section before upgrading - see the CHANGELOG migration guide at https://github.com/Chainlit/chainlit/releases/tag/2.12.0. User-provided SSE/HTTP connections additionally require features.mcp.user_servers.enabled = true and a non-empty allowed_urls allowlist; the default post-upgrade is deny-all. If immediate upgrade is not possible, set features.mcp.enabled = false in .chainlit/config.toml - this fully eliminates the attack surface for both CVE-2026-45019 and the companion command-injection issue CVE-2026-45018, with the trade-off of disabling all MCP functionality. As a supplementary control, restrict outbound network egress from the Chainlit host via firewall rules blocking RFC 1918 ranges and 169.254.169.254; this limits post-exploitation reach but does not close the SSRF endpoint. Registering an authentication callback removes the unauthenticated attack path but does not protect authenticated users from triggering SSRF. Advisory: https://github.com/Chainlit/chainlit/security/advisories/GHSA-hvfh-5mj3-5f3j.

Affected VersionsENISA

Chainlit 2.4.0rc0, < 2.12.0

Affected Packages

pypi chainlit fix: 2.12.0
euvd chainlit_2_4_0rc0 fix: 2.12.0

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 2 packages in pypi, euvd

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

Chainlit is a Python framework for building LLM-backed chat applications. Its MCP (Model Context Protocol) feature, introduced in v2.4.0rc0 via PR #1977, exposes a POST /mcp HTTP endpoint supporting SSE and streamable-http transports. The vulnerability (CWE-918: Server-Side Request Forgery) arises because the Pydantic request models in backend/chainlit/types.py define the url field as a bare str with no scheme validation, no private-IP filtering, and no allowlist. The connect_mcp handler in backend/chainlit/server.py passes the caller-supplied URL and headers dictionary directly to the MCP SDK's sse_client() or streamablehttp_client(), which execute outbound HTTP requests from the server process. PR #2292 (merged 2025-07-30, shipped in v2.6.4) amplified the primitive by adding attacker-controlled header forwarding for both transports, elevating the attack from URL-only SSRF to authenticated SSRF capable of delivering forged Authorization and Cookie headers to internal targets. The affected package is pkg:pip/chainlit.

Know when the next one hits your stack

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/Chainlit/chainlit) PRIMARY 7.2 HIGH
AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
vuln.today AI 7.2 HIGH

Network-reachable unauthenticated endpoint; scope changed as server proxies requests to other systems; C:L/I:L from blind SSRF with full header control enabling internal API writes.

3.1 AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N

Primary rating from Vendor (https://github.com/Chainlit/chainlit).

CVSS VectorVendor: https://github.com/Chainlit/chainlit

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
Low
Integrity
Low
Availability
None

DescriptionCVE.org

Am I affected?

Only if your deployment sets features.mcp.enabled = true in .chainlit/config.toml. MCP has been disabled by default since v2.7.0, so most Chainlit deployments are not affected. No authentication is required: /mcp is reachable by any client that can open a session.

Summary

When MCP is enabled (features.mcp.enabled = true), the POST /mcp endpoint for sse and streamable-http transports accepts a user-controlled url and optional headers dictionary without any validation. An unauthenticated attacker can force the Chainlit server to make outbound HTTP requests to arbitrary URLs - including internal network services and cloud metadata endpoints - with attacker-controlled HTTP headers such as Authorization and Cookie.

Affected / patched versions

CVECVE-2026-45019
Affected - URL-based SSRF>=2.4.0rc0, <2.12.0 (sink present since MCP support was introduced, PR #1977)
Affected - attacker-controlled header forwarding (amplifies the above)>=2.6.4, <2.12.0 (added in PR #2292)
Patched2.12.0 (releasing 2026-08-25)

Details

The Pydantic request models in backend/chainlit/types.py define url as a bare str with no scheme check, no private IP filtering, and no allowlist. When clientType is "sse" or "streamable-http", the handler in backend/chainlit/server.py passes the URL and headers directly to the MCP SDK's sse_client() or streamablehttp_client(), which make outbound HTTP requests from the server.

The SSE URL sink has existed since MCP support was first introduced in v2.4.0rc0 (PR #1977). PR #2292 (merged 2025-07-30, released in v2.6.4) added streamable-http support and introduced attacker-controlled headers forwarding for both transports. This amplified the SSRF from a simple URL-based request to one where the attacker can set arbitrary HTTP headers like Authorization and Cookie.

This is a blind SSRF: the server makes the outbound request, but the response is consumed internally by the MCP client and never returned to the attacker. In cloud environments, an attacker could probe metadata endpoints (e.g., 169.254.169.254).

Vulnerable code: backend/chainlit/server.py - connect_mcp handler Sink: backend/chainlit/server.py - sse_client / streamablehttp_client

PoC

Tested against Chainlit 2.11.0 with features.mcp.enabled = true and a local TCP listener.

  1. Start a listener to capture the server-side request:
bash
nc -l 4445
  1. Establish a Socket.IO session and trigger the SSRF:
bash
EIO_SID=$(curl -s 'http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling' \
  | python3 -c "import sys,json; print(json.loads(sys.stdin.read()[1:])['sid'])")

curl -s -X POST \
  "http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling&sid=$EIO_SID" \
  -d '40{"sessionId":"ssrf","userEnv":"{}","clientType":"webapp"}'

curl -s -X POST 'http://TARGET:8000/mcp' \
  -H 'Content-Type: application/json' \
  -d '{
    "sessionId": "ssrf",
    "clientType": "streamable-http",
    "name": "probe",
    "url": "http://127.0.0.1:4445/internal-admin",
    "headers": {
      "Authorization": "Bearer attacker-controlled-token",
      "X-Internal-Secret": "exfiltrated",
      "Cookie": "session=hijacked"
    }
  }'
  1. The listener captures the server-side request with all attacker-controlled headers:
POST /internal-admin HTTP/1.1
Host: 127.0.0.1:4445
Authorization: Bearer attacker-controlled-token
X-Internal-Secret: exfiltrated
Cookie: session=hijacked

Impact

High. An unauthenticated attacker can force the Chainlit server to make HTTP requests to arbitrary internal or external services, with fully attacker-controlled headers.

Although this is a blind SSRF - the response body is never returned to the attacker - the vulnerable versions apply no allowlist to either the destination URL or the headers. Full control over both is enough to issue state-changing, authenticated requests to internal APIs: the PoC above is itself a POST carrying a forged Authorization header. Write operations against internal services do not require reading the response to have effect, so this goes beyond passive reconnaissance. The same primitive also enables internal service discovery, port scanning, and probing cloud metadata endpoints (e.g., AWS IMDSv1 at 169.254.169.254). Any Chainlit deployment with MCP enabled is affected.

Fix

Chainlit 2.12.0 introduces an opt-in, allowlist-based model for user-provided SSE / streamable-http connections:

  • User-provided MCP connections now require explicit opt-in via features.mcp.user_servers.enabled = true, plus a non-empty allowed_urls allowlist. The default is deny-all - no outbound URL is permitted unless explicitly listed.
  • URLs are validated: http/https only, with scheme/host/port/path-prefix matching against the allowlist. Requests with ./.. path segments, encoded separators (%2e, %2f, %5c), double-encoded sequences (%25), backslashes, or non-ASCII characters in the path are rejected.
  • Restricted headers are stripped from user-supplied headers before the request is sent: Cookie, Host, Forwarded, X-Forwarded-*, X-Real-IP, Via, Proxy-Authorization, X-HTTP-Method-Override, X-Original-URL, X-Rewrite-URL, and hop-by-hop headers. Authorization is deliberately still forwarded - for user-provided servers, passing a caller-supplied credential to the allowlisted target is the point of the feature, and the destination is now constrained by allowed_urls.
  • Named (developer-configured) server URLs and headers are no longer returned to the browser, on either the success or the error path, and GET /project/settings no longer discloses allowed_urls.

During remediation the maintainers also identified and closed two ways an allowlist could otherwise be bypassed once introduced. Neither adds to the pre-fix impact described above, since the vulnerable versions had no allowlist to bypass in the first place - they are hardening measures for the new allowlist:

  • HTTP redirects are no longer followed on MCP transports. The underlying SDK hardcoded follow_redirects=True, so only the first hop of a request would ever have been checked against an allowlist.
  • Every outgoing transport request is now re-checked against the connection's grant, not just the initial URL. The MCP SSE protocol takes its POST target from the server's endpoint event, and the SDK validates only scheme and host on that event, so an allowlisted server could otherwise redirect subsequent writes elsewhere on the same host.

A companion advisory (CVE-2026-45018) covers the corresponding fix for command injection via the stdio transport.

Workarounds

If you cannot upgrade immediately:

  • Set features.mcp.enabled = false in .chainlit/config.toml. This fully prevents exploitation of this issue (and of the companion stdio command-injection issue, CVE-2026-45018).
  • Restrict outbound network egress from the host running Chainlit (e.g., firewall rules blocking access to internal address ranges and the cloud metadata endpoint).
  • Configure authentication (register an auth callback) so that /mcp requires an authenticated session. This does not eliminate the SSRF for authenticated users, but removes the unauthenticated attack path.

Upgrading to 2.12.0

> Breaking change. 2.12.0 changes how MCP servers are configured. If .chainlit/config.toml still uses the legacy [features.mcp.sse], [features.mcp.stdio], or [features.mcp.streamable-http] sections, or the allowed_executables setting, the application will fail to start once MCP is enabled, until you migrate to the new [[features.mcp.servers]] / allowed_urls configuration. See the migration guide in CHANGELOG.md before upgrading. Deployments with features.mcp.enabled = false are not affected by this startup check.

Residual risk after upgrading

  • On deployments with no authentication configured, /mcp remains reachable anonymously after upgrading, because get_current_user returns None when no auth callback is registered. Where features.mcp.user_servers.enabled = true, an anonymous client can therefore still drive outbound requests to any URL on the allowed_urls allowlist, with an Authorization header of its own choosing.
  • There is no private-IP or IP-literal blocking. The allowlist is hostname-based, so a DNS name that resolves to a loopback, link-local, or cloud metadata address is not rejected. Closing this without introducing a TOCTOU window requires resolve-then-pin validation, which is deliberately deferred rather than shipped as a partial mitigation.
  • Header filtering in 2.12.0 is a denylist, not an allowlist. An allowlist model is the intended future direction.

Credits

Vipin <vipin@spl.team> SPL <security@spl.team>

Executive ViewAI

Why It Matters

Chainlit versions 2.4.0rc0 through 2.11.x contain a blind SSRF vulnerability in the MCP endpoint that allows unauthenticated attackers to force outbound requests to arbitrary URLs, including AWS metadata endpoints, risking exposure of cloud credentials and enabling unauthorized access to internal APIs. Organizations running Chainlit as a production service face potential credential theft and lateral movement into backend systems without requiring authentication.

Business Impact: High
Compensating Controls

Apply vendor patch.

Threat Intelligence Overview

EPSS Score
0.3%
Top 76% exploitable
Prevalence
Low
niche Python LLM chat framework, limited enterprise footprint
Threat Intel
1
1 source
Exploit
POC
Public exploit code exists
Data Quality
5 sources
Confidence: Medium

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity HIGH
CISA KEV No
Public POC Yes
EPSS 0.3%
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Out-of-Cycle
Exploitation· derived
Poc
System Exposure· derived
Open
Automatable· derived
Yes
Human Impact
High

Priority Score

56
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +36
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Server-Side Request Forgery exploits applications that fetch remote resources based on user-supplied URLs.

OS command injection occurs when an application passes unsanitized user input directly into system shell commands.

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy