Why I Run Every Engagement Through tmux
Updated July 23, 2026 · Written by PWNMI — see About.
Every target gets the same tmux session layout, every time. Not because it looks organized — because losing an SSH session mid-engagement shouldn't mean losing a reverse shell, and because "let me just set up a listener real quick" shouldn't happen forty times a week.
Why tmux specifically
- Persistence. A dropped VPN connection or a closed laptop lid doesn't kill anything running inside tmux. Reattach and everything — including a live reverse shell in another pane — is exactly where you left it.
- One screen, many contexts. A window for notes, a window for recon, a window with a listener already running, a window with a file server already running. Switching contexts is a keybinding, not alt-tabbing between six terminal windows trying to remember which one had the nmap scan.
- Reattach from anywhere. SSH into the same box from a different machine and pick up the identical session — useful when you switch from a laptop to a desktop mid-engagement, or when a VM reboots and you reconnect.
The layout
I don't build this by hand per target — a wrapper script creates the working directory and the tmux session together, so starting a new target is one command, not five minutes of setup. The session name matches the platform (HTB, THM, CTF, PG), and every target gets the same base windows, plus a couple that depend on the target OS:
| Window | Purpose | When |
|---|---|---|
notes |
Notes file in the left pane, initial nmap scan already running in the right pane | Always |
shell |
General-purpose working shell | Always |
catch |
rlwrap nc -lvnp 443 — a listener that's already up before I need it |
Always |
serve |
python3 -m http.server 80 — already running for pulling tools onto a target |
Always |
web |
Blank — directory brute-forcing, vhost fuzzing, curl | Always |
priv |
Blank — privilege escalation work (see the script privesc checklist) | Linux targets |
smb |
Pre-seeded with an SMB fingerprint command against the target | Windows targets |
tunnel |
Blank — pivoting setup (chisel, ligolo) | Windows targets |
enum |
Blank — general enumeration | OS unknown |
The point of pre-arming catch and serve isn't laziness, it's removing a step from the critical path. When a shell actually lands, you want to be typing into it immediately — not typing the netcat command for the first time while whatever got you the shell might not fire twice.
The notes/nmap split matters more than it looks like it should: recon starts the second the session opens, running in the background while you write down what you already know about the target. By the time you've finished the notes header, the scan is often already producing output.
The config
A few settings layered on top of a normal tmux config, specific to engagement sessions:
# Large scrollback for nmap / linpeas / bloodhound output
set -g history-limit 50000
# Refresh status bar every 5s so target info stays current
set -g status-interval 5
# Highlight window tab when output appears (nmap done, shell caught, etc.)
setw -g monitor-activity on
set -g visual-activity off
history-limit matters more here than in a normal dev session — a full-port nmap scan or a linpeas run produces a wall of output, and scrolling back to find one line in it is routine. monitor-activity with visual-activity off gives a quiet highlight on any window with new output (the catch window flips color the moment a shell connects) without an intrusive flash stealing focus from whatever pane is currently active.
The status bar itself pulls target info live from shell variables set when the session starts, so the current IP and hostname are always visible without running anything — useful when you're several targets deep and windows all look the same at a glance.
Custom keybindings
| Binding | Action | Why |
|---|---|---|
Prefix + C |
Open a fresh catch window with a new listener |
For a second shell, or catching an upgraded connection without killing the first one |
Prefix + V |
Open a window with the target's Python venv already activated | Most impacket/BloodHound tooling lives in a venv — this skips the source .venv/bin/activate every time |
Prefix + L |
Toggle output logging for the current pane to a file | Capture a long tool run or an interesting shell session to a file without remembering to pipe through tee up front |
Prefix + G |
Toggle synchronized panes | Running the same command across multiple split panes at once — useful when pivoting through several hosts that need identical setup |
What this is part of
The tmux piece is one component of a larger wrapper I run at the start of every target: it also builds a consistent working directory, tracks credentials and loot per target, and provides quick-reference command templates so I'm not re-typing the same impacket/ffuf/chisel invocations from memory. That's outside the scope of this page — the tmux layout and config above is the part that's directly reusable on its own.
Lessons worth keeping
- Consistency beats memory. The same window exists in the same place on every target, so there's nothing to remember or rebuild — just muscle memory for which window is which.
- Pre-arm anything you'll need under time pressure. A listener that's already running is a listener you can't forget to start.
- Persistence is not optional for anything long-running. A reverse shell or a long password-cracking job with no session persistence behind it is one dropped connection away from starting over.
Next step
If you're not comfortable with the Linux command line yet, start with Linux fundamentals first — this workflow assumes that's second nature. Otherwise, see the script privesc checklist for what actually happens in the priv window.
Get new write-ups in your inbox
New roadmaps, tool walkthroughs, and lab write-ups. No spam. Unsubscribe anytime.