GitHub Security Advisory: GHSA-c2jg-5cp7-6wc7
Package: pipecat-ai (PIP)
Severity: CRITICAL (CVSS 9.8)
CWE: CWE-502: Deserialization of Untrusted Data
Vulnerable versions: >= 0.0.41, < 0.0.94
Patched in: 0.0.94

Summary: Pipecat: Remote Code Execution by Pickle Deserialization Through LivekitFrameSerializer

Details:
Remote Code Execution via Unsafe Deserialization in Pipecat's LivekitFrameSerializer

### Summary

A critical vulnerability exists in Pipecat's `LivekitFrameSerializer` – an **optional, non-default, undocumented** frame serializer class (now deprecated) intended for LiveKit integration. The class's `deserialize()` method uses Python's `pickle.loads()` on data received from WebSocket clients without any validation or sanitization. This means that a malicious WebSocket client can send a crafted pickle payload to execute arbitrary code on the Pipecat server. The vulnerable code resides in `src/pipecat/serializers/livekit.py` (around line 73), where untrusted WebSocket message data is passed directly into `pickle.loads()` for deserialization. If a Pipecat server is configured to use LivekitFrameSerializer and is listening on an external interface (e.g. 0.0.0.0), an attacker on the network (or the internet, if the service is exposed) could achieve remote code execution (RCE) on the server by sending a malicious pickle payload.

### Details

The `LivekitFrameSerializer` is a serializer meant to convert between Pipecat audio frames and LiveKit's audio frame format. **It is not enabled by default** – developers would have to explicitly use this serializer. In Pipecat version 0.0.90, this class was officially deprecated in favor of a safer `LiveKitTransport` method, but it remains in the code for backward compatibility. The vulnerability arises in how it handles incoming data: the `deserialize()` method blindly unpickles whatever byte stream it receives over the WebSocket. Below is a snippet illustrating the core issue in the `LivekitFrameSerializer.deserialize` implementation:

Vulnerable code in src/pipecat/serializers/livekit.py:

```python
async def deserialize(self, data: str | bytes) -> Frame | None:
    # ... (docstring omitted for brevity)
    audio_frame: AudioFrame = pickle.loads(data)["frame"]   # Unsafely deserializing untrusted data
    return InputAudioRawFrame(
        audio=bytes(audio_frame.data),
        sample_rate=audio_frame.sample_rate,
        num_channels=audio_frame.num_channels,
    )
```

Python's `pickle.loads` will execute arbitrary code if the input data is a maliciously crafted pickle object. In this case, an attacker can exploit the `LivekitFrameSerializer` by sending a pickle payload that, when deserialized, runs attacker-controlled code on the server. The serializer assumes the data contains a LiveKit `AudioFrame` object (possibly in a dictionary with key "frame"), but does not verify the content or source. As a result, any object with a malicious `__reduce__` or other pickle protocol methods will be executed upon deserialization. This is a known risk: pickle should never be used on untrusted data.

In summary, whenever `LivekitFrameSerializer` is active, any client that can connect to the Pipecat WebSocket can send a specially crafted pickle binary. Upon receiving it, Pipecat will call `pickle.loads`, immediately executi