1. Tuberías y flujos estándar

En los sistemas derivados de UNIX, las tareas complejas de análisis no se resuelven con programas monolíticos, sino componiendo utilidades especializadas que se comunican mediante tuberías o pipelines. Cada herramienta realiza una operación elemental, como filtrar, proyectar columnas, ordenar o contar, y la combinación de todas ellas permite resumir en segundos registros de millones de líneas sin depender de software adicional, algo especialmente valioso en la respuesta ante incidentes sobre un servidor comprometido, donde no conviene instalar nada.

1.1. Descriptores y redirecciones

Todo proceso dispone de tres flujos estándar:

Descriptor Nombre Uso

0

Entrada estándar, stdin

Datos que recibe el proceso

1

Salida estándar, stdout

Resultados; es lo que viaja por la tubería

2

Salida de error, stderr

Mensajes de error; no atraviesa la tubería

La tubería a | b conecta la salida estándar de a con la entrada estándar de b. Las redirecciones permiten, además, dirigir cada flujo a un fichero:

Sintaxis Efecto

> fichero / >> fichero

Redirige la salida estándar sobrescribiendo o añadiendo

2> /dev/null

Descarta los mensajes de error, como los avisos de permiso denegado de find

2>&1

Envía los errores al mismo destino que la salida estándar

< fichero

Toma la entrada estándar de un fichero

1.2. Privilegios y redirecciones

En sudo grep patrón /var/log/auth.log > informe.txt, solo grep se ejecuta con privilegios; la redirección la realiza la shell del usuario sin privilegios. Por el mismo motivo, sudo echo "valor" > /etc/fichero falla con «Permiso denegado». Para escribir en un fichero protegido se utiliza tee, que sí puede ejecutarse con sudo:

echo 'PermitRootLogin no' | sudo tee /etc/ssh/sshd_config.d/00-bastionado.conf > /dev/null

Aplique también el principio de mínimo privilegio dentro de la tubería: solo el comando que lee el fichero protegido necesita sudo; el resto del procesamiento debe ejecutarse como usuario sin privilegios.

1.3. Tuberías robustas en scripts

El código de salida de una tubería es, por defecto, el del último comando. En grep patrón fichero_inexistente | wc -l, grep falla pero wc devuelve 0, y el script continúa como si todo fuera correcto. En los scripts de auditoría debe activarse:

set -o pipefail

Con esta opción, la tubería devuelve el código del último comando que haya fallado. El array PIPESTATUS de Bash contiene además el código de cada etapa.

2. Utilidades elementales

2.1. Proyección de columnas con cut

# Campos 1 y 7 de /etc/passwd
cut -d: -f1,7 /etc/passwd

# Caracteres 1 a 15, que contienen la marca temporal en el formato clásico de syslog
sudo head -n 5 /var/log/auth.log | cut -c1-15

cut solo admite un delimitador de un carácter y no agrupa separadores consecutivos. Para la salida de ps, ss o df, alineada con un número variable de espacios, debe usarse awk o normalizar antes los espacios con tr -s ' '.

2.2. Ordenación con sort

Opción Efecto

-n

Orden numérico

-r

Orden inverso

-t SEP

Separador de campos

-k N,N

Clave de ordenación limitada al campo N; sin ,N la clave llega hasta el final de la línea

-u

Elimina duplicados tras ordenar

-V

Orden de versiones, útil también para direcciones IPv4

-h

Orden de tamaños con sufijos, como 4K o 1G

# Cuentas ordenadas por UID
sort -t: -k3,3n /etc/passwd

# Direcciones IPv4 en orden numérico real, no lexicográfico
printf '10.0.0.10\n10.0.0.9\n192.168.1.1\n' | sort -V

2.3. Recuento de frecuencias con uniq

uniq solo detecta líneas duplicadas consecutivas, por lo que la entrada debe ordenarse antes con sort.

  • -c antepone el número de repeticiones.

  • -d muestra solo las líneas repetidas.

  • -u muestra solo las líneas que no se repiten.

# Número de cuentas por intérprete de órdenes
cut -d: -f7 /etc/passwd | sort | uniq -c | sort -rn

Use LC_ALL=C sort cuando la salida vaya a uniq, comm o join. Así se garantiza que todas las herramientas comparan con el mismo criterio de ordenación byte a byte, con independencia del idioma configurado.

2.4. Transformación de caracteres con tr

# Conversión a mayúsculas
echo "alerta de seguridad" | tr '[:lower:]' '[:upper:]'

# Eliminación de los retornos de carro de un script creado en Windows
tr -d '\r' < script_windows.sh > script_linux.sh

# Compresión de espacios repetidos para poder usar cut
ps aux | tr -s ' ' | cut -d' ' -f1,2,11

2.5. Recuento, muestreo y seguimiento

  • wc -l cuenta líneas; wc -c, bytes.

  • head -n N y tail -n N muestran las primeras o las últimas N líneas.

  • tail -f sigue un fichero en tiempo real. tail -F además lo reabre cuando el sistema de rotación lo sustituye, lo que lo hace preferible para supervisar registros.

2.6. Bifurcación, ejecución por lotes y comparación

  • tee copia la entrada a un fichero y a la salida estándar, lo que permite guardar un resultado intermedio como evidencia sin interrumpir la tubería.

  • xargs convierte líneas de entrada en argumentos de otro comando. Con nombres de fichero debe usarse siempre find …​ -print0 | xargs -0, ya que un nombre con espacios o saltos de línea, que un atacante puede crear deliberadamente, rompería la tubería o haría que el comando actuara sobre otro fichero.

  • comm compara dos ficheros ordenados y muestra en tres columnas las líneas exclusivas del primero, las exclusivas del segundo y las comunes. Con -13 muestra solo lo nuevo en el segundo fichero, lo que resulta idóneo para comparar contra una línea base.

  • diff muestra las diferencias línea a línea entre dos ficheros.

3. Flujos de detección y análisis forense

3.1. Ataques de fuerza bruta contra SSH

Un ataque de fuerza bruta o de diccionario contra SSH, recogido en MITRE ATT&CK como Brute Force (T1110), genera una secuencia de eventos Failed password. La siguiente tubería identifica las diez direcciones con más intentos:

sudo grep 'Failed password' /var/log/auth.log \
    | grep -oE 'from ([0-9]{1,3}\.){3}[0-9]{1,3}' \
    | cut -d' ' -f2 \
    | sort | uniq -c | sort -rn \
    | head -n 10
Tubería de detección de fuerza bruta: transformación de los datos en cada etapa
Figura 1. Tubería de detección de fuerza bruta: transformación de los datos en cada etapa
  1. grep selecciona los eventos de autenticación fallida.

  2. grep -oE extrae la dirección precedida de from, lo que evita capturar otras secuencias numéricas del mensaje.

  3. cut elimina la palabra from.

  4. sort agrupa las direcciones idénticas.

  5. uniq -c cuenta los intentos de cada dirección.

  6. sort -rn ordena de mayor a menor número de intentos.

  7. head limita el informe a las diez primeras.

En sistemas sin rsyslog, la fuente equivalente es el diario de systemd:

sudo journalctl -u ssh --since '-24h' -o cat \
    | grep 'Failed password' \
    | grep -oE 'from ([0-9]{1,3}\.){3}[0-9]{1,3}' \
    | cut -d' ' -f2 | sort | uniq -c | sort -rn | head -n 10

La opción -o cat elimina la marca temporal y el nombre del proceso, de modo que el resto de la tubería no depende del formato del registro.

Este análisis es retrospectivo. La respuesta automática ante estos patrones, mediante el bloqueo temporal de la dirección en el cortafuegos, se estudia en la guía de Fail2ban, cuyos filtros son precisamente expresiones regulares aplicadas a estos mismos registros.

3.2. Cuentas objetivo del ataque

sudo grep 'Failed password for' /var/log/auth.log \
    | sed -E 's/.*Failed password for (invalid user )?([^ ]+) from.*/\2/' \
    | sort | uniq -c | sort -rn | head -n 10

El resultado permite distinguir un ataque dirigido contra cuentas administrativas conocidas, como root o admin, de una enumeración masiva de nombres, y comprobar si alguna de las cuentas atacadas existe realmente en el sistema.

3.3. Identificadores de usuario duplicados

Cada cuenta debe tener un UID único, como exige el CIS Benchmark en el control Ensure no duplicate UIDs exist. Dos cuentas con el mismo UID son, a efectos del núcleo, la misma identidad, y comparten ficheros y privilegios:

# UID repetidos
cut -d: -f3 /etc/passwd | sort -n | uniq -d

# Cuentas implicadas en cada UID repetido
awk -F: '{ c[$3]++; n[$3] = n[$3] " " $1 } END { for (u in c) if (c[u] > 1) print "UID " u ":" n[u] }' /etc/passwd

3.4. Comparación con una línea base de ficheros SetUID

Un fichero con el bit SetUID se ejecuta con la identidad de su propietario. La aparición de un nuevo binario SetUID de root es una técnica clásica de persistencia y escalada de privilegios, catalogada como Abuse Elevation Control Mechanism: Setuid and Setgid (T1548.001). La detección consiste en guardar una línea base del sistema recién bastionado y compararla periódicamente:

# Línea base, generada sobre el sistema recién instalado y bastionado
sudo find / -xdev -type f -perm -4000 -print 2>/dev/null | LC_ALL=C sort > suid_base.txt

# Comprobación posterior: ficheros SetUID que no estaban en la línea base
sudo find / -xdev -type f -perm -4000 -print 2>/dev/null | LC_ALL=C sort | LC_ALL=C comm -13 suid_base.txt -

-xdev evita descender a otros sistemas de ficheros, como /proc o los montajes de red. La línea base debe almacenarse fuera del sistema auditado o protegida contra escritura, ya que un atacante con privilegios podría modificarla.

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 3. Investigación de un ataque de fuerza bruta y el Ejercicio 4. Línea base de ficheros SetUID.

4. Recetario de tuberías para la inspección del sistema

  1. Número de cuentas de usuario humano frente a cuentas de sistema:

    awk -F: '{ if ($3 >= 1000 && $3 != 65534) h++; else s++ } END { print "Humanas:", h+0, "Sistema:", s+0 }' /etc/passwd

    El UID 65534 corresponde a nobody y no debe contarse como usuario humano.

  2. Puertos en escucha y proceso asociado, sin cabecera:

    sudo ss -tulpnH | awk '{ print $1, $5, $7 }' | sort -u
  3. Ficheros SetUID agrupados por propietario:

    sudo find / -xdev -type f -perm -4000 -printf '%u\n' 2>/dev/null | sort | uniq -c | sort -rn

    -printf evita encadenar find con ls mediante xargs, lo que fallaría con nombres que contengan espacios.

  4. Comandos más ejecutados mediante sudo:

    sudo grep -h 'COMMAND=' /var/log/auth.log | sed -E 's/.*COMMAND=//' | sort | uniq -c | sort -rn | head -n 10
  5. Registros de /etc/shadow con un número de campos distinto de 9:

    sudo awk -F: 'NF != 9 { print "Línea " NR }' /etc/shadow
  6. Inicios de sesión correctos por usuario y dirección de origen:

    sudo grep -h 'Accepted' /var/log/auth.log | awk '{ for (i=1;i<=NF;i++) if ($i=="for") u=$(i+1); else if ($i=="from") ip=$(i+1); print u, ip }' | sort | uniq -c | sort -rn
  7. Ficheros modificados en /etc en las últimas 24 horas:

    sudo find /etc -xdev -type f -mtime -1 -printf '%TY-%Tm-%Td %TH:%TM  %p\n' | sort

5. Ejercicios prácticos

Los ejercicios deben realizarse en orden sobre una máquina virtual Debian o Ubuntu de pruebas. Trabajan sobre ficheros de muestra situados en ~/lab_filtrado/tuberias. El último ejercicio elimina el laboratorio.

Nº Ejercicio Competencia que desarrolla

1

Preparación del laboratorio y utilidades básicas

Usar cut, tr, wc, head y tail

2

Ordenación, recuento y tuberías robustas

Combinar sort y uniq, y controlar errores con pipefail

3

Investigación de un ataque de fuerza bruta

Reconstruir un incidente a partir de registros

4

Línea base de ficheros SetUID

Detectar persistencia comparando con una línea base

5

Limpieza del entorno

Revertir el laboratorio

5.1. Ejercicio 1. Preparación del laboratorio y utilidades básicas

Realice los siguientes pasos:

  1. Cree el laboratorio con una copia de /etc/passwd que incluye una cuenta con UID duplicado y un registro de autenticación de muestra:

    mkdir -p ~/lab_filtrado/tuberias && cd ~/lab_filtrado/tuberias
    cp /etc/passwd passwd
    echo 'operador:x:1001:1001:Operador:/home/operador:/bin/bash' >> passwd
    echo 'mantenimiento:x:1001:1001:Mantenimiento:/home/mant:/bin/bash' >> passwd
    cat > auth.log << 'EOF'
    Oct 18 08:01:12 srv01 sshd[1201]: Failed password for root from 203.0.113.45 port 51234 ssh2
    Oct 18 08:01:15 srv01 sshd[1201]: Failed password for root from 203.0.113.45 port 51234 ssh2
    Oct 18 08:01:19 srv01 sshd[1201]: Failed password for root from 203.0.113.45 port 51234 ssh2
    Oct 18 08:01:20 srv01 sshd[1203]: Failed password for invalid user admin from 198.51.100.23 port 40022 ssh2
    Oct 18 08:01:24 srv01 sshd[1203]: Failed password for invalid user admin from 198.51.100.23 port 40022 ssh2
    Oct 18 08:01:31 srv01 sshd[1207]: Failed password for invalid user oracle from 198.51.100.23 port 40030 ssh2
    Oct 18 08:01:33 srv01 sshd[1208]: Failed password for invalid user test from 198.51.100.24 port 40031 ssh2
    Oct 18 08:01:40 srv01 sshd[1209]: Failed password for operador from 192.0.2.10 port 50098 ssh2
    Oct 18 08:02:01 srv01 sshd[1210]: Accepted password for operador from 192.0.2.10 port 50100 ssh2
    Oct 18 08:03:44 srv01 sudo: operador : TTY=pts/0 ; PWD=/home/operador ; USER=root ; COMMAND=/usr/bin/apt update
    Oct 18 08:04:10 srv01 sudo: operador : TTY=pts/0 ; PWD=/home/operador ; USER=root ; COMMAND=/bin/bash
    Oct 18 08:05:02 srv01 sshd[1250]: Failed password for root from 203.0.113.45 port 51302 ssh2
    Oct 18 08:05:09 srv01 sshd[1250]: Failed password for root from 203.0.113.45 port 51302 ssh2
    Oct 18 08:07:12 srv01 sshd[1290]: Accepted password for root from 203.0.113.45 port 51400 ssh2
    Oct 18 08:07:40 srv01 sudo: operador : TTY=pts/0 ; PWD=/home/operador ; USER=root ; COMMAND=/usr/bin/apt update
    Oct 18 08:08:02 srv01 useradd[1302]: new user: name=sysupdate, UID=0, GID=0, home=/root, shell=/bin/bash, from=/dev/pts/1
    EOF
  2. Muestre el nombre y el intérprete de cada cuenta de passwd con cut, y cuente cuántas cuentas hay en total.

  3. Extraiga con cut la marca temporal de las tres primeras líneas de auth.log.

  4. Muestre el PID, el usuario y la orden de los procesos del sistema combinando ps aux, tr -s y cut. Compare el resultado con ps -eo pid,user,comm.


5.2. Ejercicio 2. Ordenación, recuento y tuberías robustas

Realice los siguientes pasos:

  1. Cuente cuántas cuentas de passwd usan cada intérprete, de mayor a menor.

  2. Detecte los UID duplicados de passwd y muestre los nombres de las cuentas implicadas.

  3. Ordene estas direcciones primero con sort y después con sort -V, y explique la diferencia: 10.0.0.10, 10.0.0.9, 10.0.0.100.

  4. Ejecute grep Failed noexiste.log | wc -l; echo $? y repita la orden tras set -o pipefail. Muestre también el contenido de PIPESTATUS. Desactive la opción al terminar con set +o pipefail.


5.3. Ejercicio 3. Investigación de un ataque de fuerza bruta

Realice los siguientes pasos:

  1. Obtenga el número de intentos fallidos por dirección IP, de mayor a menor.

  2. Obtenga las cuentas atacadas y su número de intentos. Indique cuáles no existen en passwd.

  3. Determine si alguna dirección con intentos fallidos ha conseguido después un acceso correcto. Guarde la lista de direcciones sospechosas en ips_sospechosas.txt con tee sin dejar de verla en pantalla.

  4. Construya la cronología de todos los eventos de la dirección más sospechosa y muestre todo lo registrado desde su acceso correcto. Identifique la técnica de persistencia empleada por el atacante.


5.4. Ejercicio 4. Línea base de ficheros SetUID

Realice los siguientes pasos:

  1. Cree un directorio que simula un árbol de binarios, con dos ficheros SetUID legítimos, y genere la línea base:

    mkdir -p bin_simulado
    for f in passwd sudo ls "mi script"; do printf '#!/bin/sh\n' > "bin_simulado/$f"; done
    chmod u+s bin_simulado/passwd bin_simulado/sudo
    find bin_simulado -type f -perm -4000 -print | LC_ALL=C sort > suid_base.txt
    cat suid_base.txt
  2. Simule la acción de un atacante: añada un nuevo fichero SetUID y active el bit en mi script. Detecte los cambios comparando con la línea base mediante comm.

  3. Muestre los permisos de los ficheros SetUID detectados con ls -l, primero con find | xargs ls -l y después con find -print0 | xargs -0 ls -l. Explique qué ocurre con mi script en el primer caso.

  4. En su sistema real, genere el inventario de ficheros SetUID agrupados por propietario.


5.5. Ejercicio 5. Limpieza del entorno

  1. Elimine el directorio del laboratorio, que incluye los ficheros SetUID simulados.

6. Recursos y referencias

7. Licencia

Licencia CC BY-NC-ND 4.0

Esta página forma parte del curso Bastionado de Redes y Sistemas © 2026 de José Juan Sánchez Hernández y su contenido se distribuye bajo una licencia Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International.

Esta licencia exige que quienes reutilicen el material otorguen el debido crédito al autor. Permite copiar y redistribuir el material en cualquier medio o formato, únicamente en su forma original, y solo para fines no comerciales.