CVE-2026-54695
Unauthenticated dev runner endpoint allows remote call termination.
- CVSS 6.5
- CWE-862
- Authentication, Authorization, and Session Management
- Remote
Pipecat is an open-source Python framework for building real-time voice and multimodal conversational agents. Prior to 1.4.0, the pipecat development runner registers a /ws WebSocket endpoint for telephony testing that accepts connections without authentication, reads an attacker-supplied callSid from a Twilio stream-start handshake in src/pipecat/runner/utils.py, and passes it to TwilioFrameSerializer so the server can issue an authenticated Twilio REST API hang-up request with the server operator's credentials; equivalent unauthenticated call-control sinks exist for Telnyx and Plivo. This issue is fixed in version 1.4.0.
- CWE
- CWE-862
- CVSS base score
- 6.5
- Published
- 2026-07-09
- OWASP
- A10 Server-Side Request Forgery
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Server-Side Request Forgery (SSRF)
- Accessibility scope
- Remote
- Impact
- Privilege Escalation
- Affected component
- Pipecat
- Fixed by upgrading
- Yes
Solution
Upgrade Pipecat to version 1.4.0 or later.
Vulnerable code sample
import json
from fastapi import FastAPI, WebSocket
# This code is a simplified, functional representation of the vulnerability
# described in CVE-2026-54695, as it might have existed in
# `pipecat/runner/utils.py` before version 1.4.0. It is not the actual
# source code but is designed to demonstrate the flaw for educational purposes.
# --- Mock Objects to simulate the application's environment ---
class MockAuthenticatedApiClient:
"""
Simulates a client (for Twilio, Plivo, or Telnyx) that is authenticated
with the server operator's credentials.
"""
def __init__(self, api_key, api_secret):
# In a real app, these are the server operator's credentials.
self._api_key = api_key
self._api_secret = api_secret
print(f"Authenticated client initialized for API Key: {self._api_key}")
def hangup_call(self, call_sid: str):
"""
Simulates making an authenticated API request to terminate a call.
This is the sensitive action an attacker can trigger.
"""
print(
f"[VULNERABLE ACTION] Issuing an authenticated hang-up request "
f"for call SID: {call_sid}"
)
print(f"Call {call_sid} would be terminated now.")
# --- Vulnerable Application Code ---
# The server's globally available, authenticated API client.
# It is initialized with the server operator's private credentials.
authenticated_client = MockAuthenticatedApiClient(
api_key="SERVER_OPERATOR_API_KEY",
api_secret="SERVER_OPERATOR_API_SECRET"
)
app = FastAPI()
@app.websocket("/ws")
async def vulnerable_telephony_websocket(websocket: WebSocket):
"""
This is the vulnerable WebSocket endpoint. It is exposed by the development
runner and crucially, it does not perform any authentication or authorization
checks on incoming connections.
"""
await websocket.accept()
try:
# The endpoint expects a handshake message, like one from Twilio Streams.
data = await websocket.receive_text()
message = json.loads(data)
# The vulnerability lies in trusting the initial handshake message.
if message.get("event") == "start":
start_data = message.get("start", {})
# An attacker can supply any `callSid` in this message.
call_sid = start_data.get("callSid")
if call_sid:
# The unvalidated `callSid` from the attacker is passed directly
# to a function that performs a privileged action (hanging up a call)
# using the server's pre-configured, authenticated client.
authenticated_client.hangup_call(call_sid)
except Exception:
# Ignore exceptions for this demonstration.
pass
finally:
await websocket.close()Patched code sample
import secrets
import logging
from typing import Dict
# Assume these types are defined within the Pipecat framework
class WebSocketDisconnect(Exception):
def __init__(self, code: int, reason: str):
self.code = code
self.reason = reason
super().__init__(reason)
class TelephonyTransport:
def __init__(self, auth_token: str):
self.auth_token = auth_token
logger = logging.getLogger("pipecat")
async def authenticate_telephony_connection(
query_params: Dict[str, str], transport: TelephonyTransport
):
"""
Represents the logic added to fix the vulnerability.
This function is called at the beginning of the WebSocket connection handler.
It authenticates the connection using a token provided in the URL query
parameters, preventing unauthenticated access to call control functions.
"""
client_token = query_params.get("token")
# The vulnerability was the absence of this authentication check. The fix is
# to add this block, which rejects any request that does not provide the
# correct, server-generated authentication token.
if not client_token or not secrets.compare_digest(
client_token, transport.auth_token
):
logger.error("Authentication token is missing or invalid for telephony WebSocket.")
# This exception causes the WebSocket handler to immediately terminate
# the connection, before any vulnerable logic (like reading a `callSid`
# from the client) is executed.
raise WebSocketDisconnect(code=4001, reason="Auth token missing or invalid")
# If authentication is successful, the connection proceeds.
# The formerly vulnerable code that processes call information is now protected.Payload
{
"event": "start",
"sequenceNumber": "1",
"start": {
"accountSid": "ACaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"streamSid": "MZbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"callSid": "CAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"tracks": [
"inbound"
],
"mediaFormat": {
"encoding": "audio/x-mulaw",
"sampleRate": 8000,
"channels": 1
}
},
"streamSid": "MZbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
Cite this entry
@misc{vaitp:cve202654695,
title = {{Unauthenticated dev runner endpoint allows remote call termination.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-54695},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-54695/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
