Skip to content

Security: TUI mode TCP listener bypasses session key authentication (incomplete fix for #5) #206

@Mike-7777777

Description

@Mike-7777777

Description

The session key authentication introduced in #5 was applied to the server (detached) mode but not to the TUI (interactive) mode. Any local process that can read ~/.psmux/*.port can connect to a TUI-mode session's TCP control port and execute commands without authentication.

Root Cause

In server mode (src/server/mod.rs), the accept loop generates a session key, writes it to a .key file, and passes it to connection::handle_connection(), which enforces AUTH <key> as the first line of every connection:

In TUI mode (src/app.rs), the accept loop reads the first line directly as a command. No session key is generated, no .key file is written, and no authentication check is performed:

Impact

Commands accepted without auth in TUI mode include new-window and split-window, both of which accept an arbitrary command argument that gets passed to create_window()build_command()spawn_command(). This allows any local process to execute arbitrary commands as the psmux user.

source-file is also accepted unauthenticated, which can load config files containing run-shell directives.

PoC (PowerShell)

# 1. Find the port file for any running TUI session
#    (No .key file exists — TUI mode doesn't create one)
$portFile = Get-ChildItem "$env:USERPROFILE\.psmux\*.port" | Select-Object -First 1
$port = Get-Content $portFile

# 2. Send a command without authentication
$tcp = New-Object System.Net.Sockets.TcpClient("127.0.0.1", [int]$port)
$stream = $tcp.GetStream()
$writer = New-Object System.IO.StreamWriter($stream)
$writer.WriteLine('new-window "cmd /c calc.exe"')
$writer.Flush()
$tcp.Close()

Scope

The TCP listener is bound to 127.0.0.1, so this is a local-only attack surface. On single-user desktops the practical risk is limited. The concern is multi-user systems or environments with sandboxed/compromised processes that can make TCP connections and read the port file.

Suggested Fix

Apply the same authentication pattern from server/mod.rs to app.rs:

  1. Generate a session key in app.rs (same logic as server/mod.rs:334-342)
  2. Write the key to ~/.psmux/<session>.key
  3. Route TCP connections through connection::handle_connection() instead of inline command parsing

This would unify both code paths and ensure all TCP connections are authenticated regardless of the mode.

Additional Hardening Suggestion

Session names are used directly in file paths (types.rs:774, session.rs:93) without sanitization. A name containing ../ could write .port/.key files outside ~/.psmux/. Consider rejecting path separators and .. sequences in session names. (Low severity — requires local CLI access.)

Environment

  • psmux v3.3.2 (commit 3bf380d)
  • Windows 11 Pro (Build 26200)
  • Audited on: 2026-04-12

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions