1. Introducción y propósito en la seguridad de sistemas
Las bases de datos locales de identidades, como /etc/passwd, /etc/shadow y /etc/group, así como buena parte de los registros del sistema y de la salida de órdenes como ss, ps o last, se organizan en registros divididos en campos. awk, diseñado en los Laboratorios Bell por Alfred Aho, Peter Weinberger y Brian Kernighan, es un lenguaje de programación orientado precisamente a recorrer ese tipo de datos, seleccionar registros mediante condiciones sobre campos concretos y generar informes.
En auditoría, awk aporta tres capacidades que grep y sed no ofrecen de forma natural:
-
Condiciones sobre campos concretos, en lugar de sobre la línea completa, lo que elimina los falsos positivos de búsquedas como
grep ':0:'. -
Comparaciones numéricas, como
UID >= 1000odías desde el último cambio > 365. -
Agregación mediante arrays asociativos, que permite contar, sumar o cruzar datos de varios ficheros en una sola pasada.
1.1. Implementaciones disponibles
La orden awk es en realidad un enlace a una de varias implementaciones:
| Implementación | Dónde aparece | Observaciones |
|---|---|---|
|
Instalación por defecto en Debian y Ubuntu |
Muy rápida. Las versiones antiguas, como la de Debian 12, no admiten intervalos |
|
Instalación por defecto en RHEL, Fedora y Arch; paquete |
Implementación de GNU con extensiones como |
|
Contenedores Alpine y sistemas embebidos |
Subconjunto reducido |
Puede comprobar cuál utiliza su sistema con readlink -f "$(command -v awk)". Los ejemplos de esta guía se limitan a la sintaxis POSIX, de modo que funcionan con cualquiera de ellas.
2. Estructura y ciclo de ejecución
Un programa awk es una secuencia de reglas patrón { acción }:
awk [opciones] 'patrón { acción } patrón { acción } ...' [fichero...]
awk [opciones] -f programa.awk [fichero...]
-
Patrón: Expresión que decide si la regla se aplica al registro actual. Puede ser una comparación, una expresión regular entre barras, una combinación lógica o un rango
patrón1, patrón2. Si se omite, la acción se aplica a todos los registros. -
Acción: Instrucciones entre llaves. Si se omite, la acción por defecto es
{ print }, que imprime el registro completo. -
Bloques especiales:
-
BEGIN { … }se ejecuta una vez antes de leer la entrada. Se usa para fijar separadores, inicializar variables o imprimir cabeceras. -
END { … }se ejecuta una vez después del último registro. Se usa para imprimir totales y resúmenes.
-
awk -F: 'BEGIN { print "Cuentas con intérprete interactivo" }
$7 ~ /(ba|z|da)?sh$/ { n++; print " " $1 }
END { print "Total:", n+0 }' /etc/passwd
|
El programa |
3. Campos, separadores y variables predefinidas
Por defecto, awk separa los campos por secuencias de espacios y tabuladores e ignora los espacios iniciales, lo que resulta adecuado para la salida de ps, ss o df. Para ficheros con otro delimitador se usa -F o se asigna FS en BEGIN. FS puede ser también una expresión regular, como -F'[:,]'.
-
$0contiene el registro completo. -
$1,$2, …,$NFcontienen cada campo;$NFes siempre el último y$(NF-1), el penúltimo.
| Variable | Descripción | Ejemplo | Explicación técnica |
|---|---|---|---|
|
Field Separator |
|
Separador de campos de entrada, equivalente a |
|
Output Field Separator |
|
Separador que |
|
Number of Fields |
|
Número de campos del registro actual. Permite detectar líneas truncadas o manipuladas. |
|
Number of Records |
|
Número de registros leídos en total, acumulado entre ficheros. Útil para saltar cabeceras. |
|
File Number of Records |
|
Número de registro dentro del fichero actual. La condición |
|
Fichero actual |
|
Nombre del fichero que se está procesando. |
|
Record Separator |
|
Separador de registros de entrada. Con |
# Convertir /etc/passwd en una tabla separada por tabuladores
awk 'BEGIN { FS=":"; OFS="\t" } { $1=$1; print }' /etc/passwd
# Pasar un valor de la shell al programa
awk -F: -v minimo=1000 '$3 >= minimo { print $1 }' /etc/passwd
|
Con los conceptos explicados hasta aquí, ya puede realizar el Ejercicio 1. Preparación del laboratorio y extracción de campos. |
4. Filtrado mediante condiciones y expresiones regulares
4.1. Comparaciones y operadores lógicos
Los patrones admiten los operadores relacionales ==, !=, <, <=, > y >=, y los lógicos &&, || y !:
# Cuentas con UID 0
awk -F: '$3 == 0 { print $1 }' /etc/passwd
# Cuentas de usuarios humanos según los límites definidos en login.defs
awk -F: -v min="$(awk '$1 == "UID_MIN" { print $2 }' /etc/login.defs)" \
-v max="$(awk '$1 == "UID_MAX" { print $2 }' /etc/login.defs)" \
'$3 >= min && $3 <= max { print $1, $3 }' /etc/passwd
Leer UID_MIN y UID_MAX del propio sistema en lugar de escribir 1000 y 60000 hace que la auditoría sea correcta también en distribuciones con otros rangos.
|
|
4.2. Expresiones regulares sobre campos
El operador ~ comprueba si un campo coincide con una expresión regular y !~ comprueba lo contrario. awk utiliza el dialecto ERE:
# Cuentas de sistema con intérprete interactivo
awk -F: '$3 > 0 && $3 < 1000 && $7 !~ /(nologin|false|sync|shutdown|halt)$/ { print $1, $7 }' /etc/passwd
Una expresión regular sin operador, como /Failed password/, se aplica sobre $0.
4.3. Estructuras de control
awk dispone de if/else, for, while, next, que pasa al siguiente registro, y exit:
awk -F: '{
if ($3 == 0) tipo = "SUPERUSUARIO"
else if ($3 < 1000) tipo = "servicio"
else tipo = "usuario"
printf "%-20s %-12s %s\n", $1, tipo, $7
}' /etc/passwd
|
Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 2. Clasificación y filtrado de cuentas. |
5. Arrays asociativos, funciones e informes
5.1. Recuento y agregación
Los arrays de awk se indexan con cadenas, lo que permite contar ocurrencias sin ordenar previamente la entrada:
# Intentos fallidos de autenticación SSH por dirección IP de origen
sudo awk '/Failed password/ {
for (i = 1; i <= NF; i++) if ($i == "from") intentos[$(i+1)]++
}
END {
for (ip in intentos) printf "%6d %s\n", intentos[ip], ip
}' /var/log/auth.log | sort -rn
Buscar la palabra from y tomar el campo siguiente hace que el programa funcione tanto si el mensaje incluye invalid user como si no, ya que en ese caso el número de campos cambia. El orden de recorrido de for (clave in array) no está definido, de ahí que el resultado se ordene con sort.
5.2. Cruce de ficheros con NR == FNR
Cuando se pasan dos ficheros, la condición NR == FNR solo es cierta durante la lectura del primero. Este modismo permite cargar un fichero en memoria y consultarlo al procesar el segundo:
# Cuentas presentes en /etc/passwd sin entrada en /etc/shadow
sudo awk -F: 'NR == FNR { sombra[$1] = 1; next }
!($1 in sombra) { print "Sin entrada en shadow:", $1 }' /etc/shadow /etc/passwd
5.3. Funciones de cadena e informes formateados
| Función | Efecto |
|---|---|
|
Longitud de la cadena |
|
Subcadena de |
|
Posición de |
|
Divide |
|
Sustituye la primera o todas las coincidencias en |
|
Posición de la coincidencia; fija |
|
Conversión de mayúsculas y minúsculas |
|
Salida con formato, igual que en C |
# Identificador del algoritmo de hash de cada cuenta, extraído con split
sudo awk -F: '$2 ~ /^\$/ { split($2, partes, "$"); printf "%-20s %s\n", $1, partes[2] }' /etc/shadow
|
Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 3. Agregación de eventos de autenticación. |
6. Recetario de awk para la auditoría de cuentas
-
Cuentas con UID 0, que en un sistema bastionado solo debe ser
root: Control del CIS Benchmark Ensure root is the only UID 0 account.awk -F: '$3 == 0 && $1 != "root" { print "UID 0 no autorizado:", $1 }' /etc/passwd -
Integridad estructural de
/etc/passwdy/etc/shadow: Cada línea debe tener exactamente 7 y 9 campos respectivamente.awk -F: 'NF != 7 { print "passwd, línea " NR ": " NF " campos" }' /etc/passwd sudo awk -F: 'NF != 9 { print "shadow, línea " NR ": " NF " campos" }' /etc/shadow -
Cuentas sin contraseña: Un campo vacío permite el acceso sin autenticación en los servicios que lo admitan. El CIS Benchmark exige que no exista ninguna.
sudo awk -F: '$2 == "" { print "Cuenta sin contraseña:", $1 }' /etc/shadow -
Clasificación del algoritmo de hash de cada cuenta: Las cuentas con MD5-crypt o DES deben forzar un cambio de contraseña para que se regeneren con el algoritmo actual del sistema.
sudo awk -F: '{ h = $2; estado = "" if (h == "") { print $1, "SIN CONTRASEÑA"; next } if (h ~ /^[!*]+$/) { print $1, "sin contraseña utilizable"; next } if (h ~ /^!/) { estado = " [bloqueada]"; sub(/^!+/, "", h) } if (h ~ /^\$y\$/) alg = "yescrypt" else if (h ~ /^\$gy\$/) alg = "gost-yescrypt" else if (h ~ /^\$6\$/) alg = "SHA-512-crypt" else if (h ~ /^\$5\$/) alg = "SHA-256-crypt" else if (h ~ /^\$2[abxy]\$/) alg = "bcrypt" else if (h ~ /^\$1\$/) alg = "MD5-crypt OBSOLETO" else if (length(h) == 13) alg = "DES OBSOLETO" else alg = "desconocido" print $1, alg estado }' /etc/shadowyescrypt es el algoritmo por defecto en Debian 11 y posteriores y en Ubuntu 22.04 y posteriores, mientras que SHA-512-crypt sigue siendo aceptable según el CIS Benchmark.
-
Cuentas bloqueadas: Recuerde que el prefijo
!impide la autenticación por contraseña, pero no el acceso mediante claves SSH ni otros mecanismos que no consulten/etc/shadow.sudo awk -F: '$2 ~ /^!/ { print "Contraseña bloqueada:", $1 }' /etc/shadow -
Fecha del último cambio de contraseña en el futuro: Control del CIS Benchmark Ensure all users last password change date is in the past. El tercer campo de
/etc/shadowse expresa en días desde el 1 de enero de 1970.sudo awk -F: -v hoy="$(( $(date +%s) / 86400 ))" '$3 > hoy { print "Cambio en el futuro:", $1 }' /etc/shadow -
Contraseñas que no se cambian desde hace más de un año:
sudo awk -F: -v hoy="$(( $(date +%s) / 86400 ))" \ '$2 ~ /^\$/ && $3 != "" && hoy - $3 > 365 { print $1, hoy - $3, "días" }' /etc/shadow -
Miembros de los grupos de administración: El cuarto campo de
/etc/groupsolo contiene los miembros secundarios; los usuarios cuyo grupo principal essudoowheeldeben buscarse además por GID en/etc/passwd.awk -F: '$1 == "sudo" || $1 == "wheel" { print $1 " (GID " $3 "): " $4 }' /etc/group -
Puertos en escucha y proceso asociado:
sudo ss -tulpnH | awk '{ print $1, $5, $7 }' | sort -u
|
Con el recetario anterior, ya puede realizar el Ejercicio 4. Auditoría de credenciales y el Ejercicio 5. Programa de auditoría reutilizable. |
7. Ejercicios prácticos
Los ejercicios deben realizarse en orden sobre una máquina virtual Debian o Ubuntu de pruebas. Trabajan sobre ficheros de muestra creados en ~/lab_filtrado/awk, incluida una copia ficticia de /etc/shadow, de modo que no es necesario leer ni modificar las credenciales reales del sistema. El último ejercicio elimina el laboratorio.
| Nº | Ejercicio | Competencia que desarrolla |
|---|---|---|
1 |
Usar separadores, campos y variables predefinidas |
|
2 |
Combinar condiciones numéricas, lógicas y expresiones regulares |
|
3 |
Contar eventos con arrays asociativos y generar informes |
|
4 |
Detectar debilidades en |
|
5 |
Encapsular controles en un programa |
|
6 |
Revertir el laboratorio |
7.1. Ejercicio 1. Preparación del laboratorio y extracción de campos
Realice los siguientes pasos:
-
Cree el laboratorio con una copia de
/etc/passwda la que se añaden cinco cuentas, una de ellas con un registro dañado, y un ficheroshadowficticio. Las cuentas originales del sistema reciben en él una entrada sin contraseña utilizable, de modo que no se copia ningún hash real:mkdir -p ~/lab_filtrado/awk && cd ~/lab_filtrado/awk cp /etc/passwd passwd cat >> passwd << 'EOF' operador:x:1001:1001:Operador de sistemas:/home/operador:/bin/bash soporte:x:0:0:Soporte tecnico:/root:/bin/bash webapp:x:998:998:Aplicacion web:/srv/webapp:/bin/sh antiguo:x:1002:1002:/home/antiguo:/bin/bash tecnico:x:1003:1003:Tecnico de guardia:/home/tecnico:/bin/bash EOF awk -F: '$1 != "root" && $1 != "daemon" { print $1 ":*:20700:0:99999:7:::" }' /etc/passwd > shadow cat >> shadow << 'EOF' root:$y$j9T$Fq0lU2xJb3m1kQyH9yGz7/$9d3eH1RrN3vV1c0yQyE0sQm4QHkQe6lW2xTqZkqf8A7:20700:0:99999:7::: daemon:*:20700:0:99999:7::: operador:$6$Xb2k9sLq$8sJf3kQ0lYqv5m3o2pP1aR7uB6nC4dE9fG0hI1jK2lM3nO4pQ5rS6tU7vW8xY9zA0bC1dE2fG3hI4jK5lM6n:19800:0:99999:7::: soporte:$1$Ab3dE5gH$wQ9eR8tY7uI6oP5aS4dF3.:20700:0:99999:7::: webapp::20700:0:99999:7::: antiguo:!$y$j9T$Lk8mN7bV6cX5zA4sD3fG2/$h1J2k3L4z5X6c7V8b9N0mQ1wE2rT3yU4iO5pA6sD7fG:19000:0:99999:7::: invitado:aBcDeFgHiJkLm:22000:0:99999:7::: EOF -
Muestre el nombre, el UID y el intérprete de cada cuenta de
passwd, separados por tabuladores. -
Muestre el número de línea y el nombre de las cinco últimas cuentas, usando
NR. Muestre después el último campo de cada línea con$NF. -
Indique qué implementación de
awkutiliza su sistema.
7.2. Ejercicio 2. Clasificación y filtrado de cuentas
Realice los siguientes pasos:
-
Muestre las cuentas con UID 0 distintas de
rooty las cuentas de usuario humano segúnUID_MINyUID_MAXde/etc/login.defs. -
Detecte las cuentas de servicio con intérprete interactivo. Justifique el riesgo que supone
webapp. -
Localice los registros con un número de campos distinto de 7 e indique qué campo falta en la cuenta
antiguo. -
Genere un informe con
printfque clasifique todas las cuentas comoSUPERUSUARIO,servicioousuario, alineado en columnas.
7.3. Ejercicio 3. Agregación de eventos de autenticación
Realice los siguientes pasos:
-
Cree el registro de muestra
auth.log: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: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: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 EOF -
Muestre el campo
$9de las líneasFailed passwordy explique por qué no siempre contiene el nombre de usuario. -
Cuente los intentos fallidos por dirección IP con un array asociativo y ordene el resultado de mayor a menor.
-
Amplíe el programa para que, en el bloque
END, muestre las direcciones que acumulan al menos tres fallos y un acceso aceptado posterior. Interprete el resultado.
7.4. Ejercicio 4. Auditoría de credenciales
Realice los siguientes pasos:
-
Aplique el programa de clasificación de algoritmos del recetario al fichero
shadowdel laboratorio. Indique qué cuentas requieren actuación y qué medida aplicaría a cada una. -
Detecte las cuentas cuya fecha de último cambio está en el futuro y las que no cambian la contraseña desde hace más de 365 días.
-
Cruce
passwdyshadowconNR == FNRpara obtener las cuentas depasswdsin entrada enshadowy las deshadowsin entrada enpasswd.
7.5. Ejercicio 5. Programa de auditoría reutilizable
Escriba el programa auditoria.awk, que se invoca con awk -F: -f auditoria.awk passwd shadow y que, en una sola ejecución, informa de:
-
Cuentas con UID 0 distintas de
root. -
Registros de
passwdcon un número de campos distinto de 7. -
Cuentas de
shadowsin contraseña o con algoritmos obsoletos. -
Un resumen final con el número total de hallazgos.
7.6. Ejercicio 6. Limpieza del entorno
-
Elimine el directorio del laboratorio, que contiene el fichero
shadowficticio.
8. Recursos y referencias
-
GAWK: Effective AWK Programming, de Arnold Robbins: Manual oficial de GNU Awk, que distingue en cada apartado entre la sintaxis POSIX y las extensiones de GNU.
https://www.gnu.org/software/gawk/manual/ -
The AWK Programming Language, segunda edición: Libro de referencia escrito por los autores del lenguaje.
https://awk.dev/ -
POSIX.1-2024, especificación de
awk:
https://pubs.opengroup.org/onlinepubs/9799919799/utilities/awk.html -
The Grymoire, AWK Tutorial, de Bruce Barnett:
https://www.grymoire.com/Unix/Awk.html -
Páginas de manual del sistema:
man awk,man 5 passwd,man 5 shadowyman 5 crypt, esta última con la descripción de los prefijos de cada algoritmo de hash.
9. 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.