1. El modelo de permisos en Linux

Linux implementa un modelo de seguridad basado en el control de acceso discrecional o DAC, del inglés Discretionary Access Control. En este esquema, cada objeto del sistema de archivos, ya sea un archivo regular, un directorio, un socket, una tubería con nombre o un dispositivo, tiene un propietario, y es este quien decide a su discreción qué operaciones se permiten al resto de usuarios.

En un servidor, el modelo DAC es la barrera que separa a unos usuarios de otros y a cada servicio del resto del sistema. Una configuración deficiente de permisos expone información confidencial, permite manipular binarios y configuraciones, y es uno de los vectores más frecuentes de escalada local de privilegios, catalogada en MITRE ATT&CK dentro de la táctica Privilege Escalation y, en particular, en las técnicas T1548.001 (Setuid and Setgid) y T1222.002 (Linux and Mac Permissions, dentro de File and Directory Permissions Modification).

Principio de mínimo privilegio

Todo usuario, servicio o proceso debe disponer únicamente de los permisos imprescindibles para desempeñar su función. Si un proceso solo necesita leer un archivo de configuración, no debe tener permiso de escritura sobre él ni sobre el directorio que lo contiene.

El DAC no es la única capa de control. Sobre un mismo archivo actúan, de fuera hacia dentro, las opciones de montaje del sistema de archivos, los atributos del inodo, los permisos DAC con sus extensiones ACL y, si están activos, los módulos de control de acceso obligatorio como SELinux o AppArmor. Para que una operación se permita, todas las capas deben autorizarla. Esta guía recorre las capas en orden de complejidad creciente, empezando por los permisos DAC y terminando por las opciones de montaje. Los módulos de control de acceso obligatorio quedan fuera de su alcance.

Capas de control de acceso que intervienen en una operación sobre un archivo
Figura 1. Capas de control de acceso que intervienen en una operación sobre un archivo

1.1. Clases de acceso: propietario, grupo y otros

El inodo

El inodo es la estructura en la que el sistema de archivos guarda los metadatos de cada archivo: su tipo, propietario, grupo, permisos, marcas temporales, número de enlaces y atributos extendidos, donde se almacenan las ACL y las capacidades. Cada inodo se identifica por un número, que muestra ls -i. El inodo no contiene el nombre del archivo: el nombre es una entrada del directorio que apunta al número de inodo. Por eso los permisos acompañan al archivo y no a su nombre, y renombrar o borrar un archivo modifica el directorio y no el archivo.

Cada inodo almacena un UID propietario, un GID propietario y nueve bits de permisos agrupados en tres clases:

  1. Propietario (user, u): el usuario al que pertenece el archivo, normalmente quien lo creó.

  2. Grupo propietario (group, g): se aplica a los miembros del grupo asignado al archivo, tanto si es su grupo principal como si es uno de sus grupos secundarios.

  3. Otros (others, o): cualquier usuario que no sea el propietario ni pertenezca al grupo. Es la clase con mayor alcance, ya que incluye a todas las cuentas locales y a todos los servicios del sistema, y por tanto la que más debe restringirse.

Orden de evaluación de los permisos en el núcleo

El núcleo no suma los permisos de las tres clases, sino que selecciona una sola clase y aplica únicamente sus bits.

  1. Si el UID efectivo del proceso coincide con el propietario, se aplican los bits de u y se ignora el resto.

  2. En caso contrario, si alguno de los grupos del proceso coincide con el grupo propietario, se aplican los bits de g.

  3. Solo si no se cumple ninguna de las condiciones anteriores, se aplican los bits de o.

Por ello, un propietario al que se le retira la lectura (---rwxrwx) no puede leer su propio archivo, aunque el grupo y el resto del sistema sí puedan. Por ejemplo, para el siguiente archivo:

----rwxrwx 1 juan ventas 120 oct 26 10:00 datos.txt
  • Si juan intenta leerlo, el núcleo comprueba que es el propietario, aplica los bits --- y deniega el acceso. No llega a evaluar los bits del grupo, aunque juan sea miembro de ventas.

  • Si maria, que no es la propietaria ni pertenece a ventas, intenta leerlo, se le aplican los bits de o, que son rwx, y puede hacerlo.

juan puede recuperar el acceso con chmod u+r datos.txt, ya que cambiar los permisos exige ser el propietario, no tener permiso de lectura. Cuando el archivo tiene ACL, la evaluación incorpora más pasos, como se explica en la sección 5.

1.1.1. El superusuario y las capacidades DAC

Las comprobaciones anteriores no se aplican igual a root. Un proceso con UID efectivo 0 recibe por defecto todas las capabilities del núcleo, entre ellas:

  • CAP_DAC_OVERRIDE, que le permite leer, escribir y atravesar cualquier objeto con independencia de sus permisos. La única excepción es la ejecución de archivos regulares. root solo puede ejecutar un archivo si al menos una de las tres clases tiene el bit x.

  • CAP_DAC_READ_SEARCH, que permite leer cualquier archivo y recorrer cualquier directorio.

  • CAP_FOWNER, que permite cambiar permisos y atributos de archivos ajenos.

Por eso los permisos protegen a los usuarios entre sí, pero no protegen frente a un proceso que ya se ejecuta como root. Frente a ese escenario solo actúan las capas de control de acceso obligatorio, las opciones de montaje y las medidas de detección.


1.2. Semántica de los permisos en archivos y directorios

Los tres permisos básicos, lectura (r), escritura (w) y ejecución (x), tienen un significado distinto según se apliquen a un archivo regular o a un directorio. Confundirlos es el origen de buena parte de las configuraciones inseguras.

Permiso Valor octal En archivos regulares En directorios

r

4

Leer el contenido del archivo

Listar los nombres de las entradas con ls

w

2

Modificar, sobrescribir o truncar el contenido

Crear, eliminar y renombrar entradas, si además se tiene x

x

1

Ejecutar el archivo como programa

Atravesar el directorio: entrar con cd y acceder a las entradas cuyo nombre se conoce

Eliminar un archivo depende del directorio, no del archivo

Borrar o renombrar un archivo modifica el directorio que lo contiene, no el archivo. Un usuario sin ningún permiso sobre un archivo confidencial (----------) puede eliminarlo si tiene permisos w y x sobre su directorio. Por ello, el bastionado de un archivo sensible empieza por el directorio en el que reside. Dentro del modelo de permisos, la única excepción es el Sticky Bit, que se estudia en la sección 4.3. Fuera de él, los atributos inmutable y de solo adición, que se estudian en la sección 6, también impiden el borrado.

1.2.1. Combinaciones de permisos en directorios

  • Solo lectura (r--): se obtienen los nombres de las entradas, pero ls -l muestra signos de interrogación en lugar de sus metadatos y no es posible acceder a ningún archivo ni entrar con cd.

  • Solo ejecución (--x): permite atravesar el directorio sin listarlo. Quien conozca el nombre exacto de un archivo, como /srv/privado/informe.pdf, puede acceder a él si los permisos del archivo lo permiten. Es la base de los directorios de paso, como un directorio personal 711 que da acceso a ~/public_html.

  • Escritura sin ejecución (-w-): no tiene utilidad práctica. Para crear o eliminar entradas se necesitan w y x simultáneamente.

Para ejecutar un script interpretado no basta con x. El intérprete debe poder leerlo, por lo que se necesitan r y x. Un binario compilado, en cambio, puede ejecutarse solo con x, ya que es el núcleo quien lo carga en memoria.


1.3. Representación simbólica y octal

1.3.1. Representación simbólica

-rwxr-xr--     (1) (2) (3) (4)
1 Tipo de archivo, en este caso -, archivo regular.
2 Permisos del propietario: rwx, lectura, escritura y ejecución.
3 Permisos del grupo propietario: r-x, lectura y ejecución.
4 Permisos del resto de usuarios: r--, solo lectura.

Los tipos de archivo posibles son los siguientes:

Carácter Tipo Ejemplo

-

Archivo regular

/etc/passwd

d

Directorio

/etc

l

Enlace simbólico

/bin en sistemas con /usr fusionado

c

Dispositivo de caracteres

/dev/tty, /dev/null

b

Dispositivo de bloques

/dev/sda

s

Socket de dominio UNIX

/run/systemd/notify

p

Tubería con nombre o FIFO

Creada con mkfifo

1.3.2. Representación octal

Cada clase se codifica con un dígito octal que es la suma de r = 4, w = 2 y x = 1:

Octal Binario Simbólico Significado

0

000

---

Ningún acceso

1

001

--x

Solo ejecución o travesía

2

010

-w-

Solo escritura

3

011

-wx

Escritura y ejecución

4

100

r--

Solo lectura

5

101

r-x

Lectura y ejecución

6

110

rw-

Lectura y escritura

7

111

rwx

Acceso completo

Un cuarto dígito, situado a la izquierda, codifica los permisos especiales que se estudian en la sección 4.

1.3.3. Configuraciones de referencia

Modo Simbólico Ámbito Contexto de seguridad

600

rw-------

Archivos confidenciales

Claves privadas SSH, ~/.netrc, tokens, /etc/crontab según el CIS Benchmark

640

rw-r-----

Configuraciones y registros con lectores autorizados

/etc/shadow con grupo shadow, o archivos que lee un servicio a través de su grupo

644

rw-r—​r--

Archivos públicos de solo lectura

/etc/passwd, /etc/group, claves públicas

700

rwx------

Directorios privados

/root, ~/.ssh, /etc/cron.d según el CIS Benchmark

750

rwxr-x---

Directorios de grupo y directorios personales

/home/usuario según el CIS Benchmark

755

rwxr-xr-x

Directorios y binarios públicos

/usr/bin, /etc. Solo root puede modificarlos

777

rwxrwxrwx

Inaceptable en producción

Cualquier cuenta local o servicio comprometido puede alterar o sustituir el contenido


1.4. Inspección de permisos y metadatos

1.4.1. Salida de ls -l

ls -l /etc/shadow
-rw-r----- 1 root shadow 1842 oct 14 09:30 /etc/shadow       (1) (2) (3) (4) (5) (6) (7)
1 Tipo de archivo y bits de permisos.
2 Número de enlaces duros que apuntan al inodo.
3 Usuario propietario.
4 Grupo propietario.
5 Tamaño en bytes.
6 Fecha de última modificación del contenido, o mtime.
7 Nombre del archivo.

Un + o un . tras los permisos cambia su interpretación

Si después de los permisos aparece un +, el archivo tiene una ACL y si aparece un ., tiene un contexto de seguridad SELinux:

-rw-rw----+ 1 juan finanzas 10240 oct 15 10:30 balance.xlsx
-rw-r--r--. 1 root root       158 oct 14 09:30 /etc/hosts

El . solo aparece en sistemas con SELinux activo, como Red Hat Enterprise Linux o Fedora, y no en una instalación estándar de Ubuntu o Debian. Si un archivo tiene a la vez una ACL y un contexto SELinux, ls -l muestra solo el +. El contexto SELinux se consulta con ls -Z. Ambos indicadores obligan a consultar información adicional antes de concluir quién puede acceder al archivo.

Con una ACL, la tripleta del grupo muestra la máscara y no los permisos del grupo, como se explica en la sección 5.1, y getfacl es la orden que muestra los permisos reales.

1.4.2. Metadatos completos con stat

stat muestra todos los metadatos del inodo, incluido el modo en octal:

stat /etc/shadow
  File: /etc/shadow
  Size: 1842       Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d Inode: 262145      Links: 1
Access: (0640/-rw-r-----)  Uid: (    0/    root)   Gid: (   42/  shadow)
Access: 2026-10-15 08:00:12.124500120 +0200
Modify: 2026-10-14 09:30:45.981023450 +0200
Change: 2026-10-14 09:30:45.981023450 +0200
 Birth: 2026-09-01 10:15:00.000000000 +0200

Para auditorías automatizadas resulta más práctico su modo de formato, que devuelve solo los campos solicitados:

stat -c '%a %U:%G %n' /etc/passwd /etc/shadow /etc/group /etc/gshadow
Parámetro Función

-c formato

Define el formato de salida mediante secuencias que empiezan por %

%a

Modo en octal

%A

Modo en notación simbólica, como en ls -l

%U, %G

Nombre del usuario y del grupo propietarios

%n

Nombre del archivo

Marca temporal Descripción Se actualiza al Explicación técnica

atime

Último acceso

Leer el contenido

Con la opción de montaje relatime, habitual por defecto, solo se actualiza si es anterior a mtime o a ctime, o si tiene más de 24 horas.

mtime

Última modificación

Cambiar el contenido

Puede fijarse arbitrariamente con touch -d, que asigna la fecha que se le indique, por lo que no es fiable en un análisis forense.

ctime

Último cambio del inodo

Cambiar el contenido o los metadatos: permisos, propietario, enlaces

El núcleo la fija siempre con la hora actual y no existe ninguna llamada al sistema para asignarle un valor arbitrario.

Birth

Creación

Crear el inodo

Disponible en ext4, XFS y Btrfs con núcleos y versiones de stat recientes.

ctime es un indicador forense valioso. Un chmod o un chown malicioso la actualiza aunque el atacante restaure después mtime con touch. No es infalible, ya que un atacante con privilegios de root puede alterar el reloj del sistema o editar el sistema de archivos desmontado, pero exige un esfuerzo considerablemente mayor.

2. Gestión de permisos y propiedad

Comando Función Sintaxis representativa

chmod

Modifica los bits de permisos estándar y especiales

chmod [opciones] modo archivo…​

chown

Modifica el usuario propietario y, opcionalmente, el grupo

chown [opciones] usuario[:grupo] archivo…​

chgrp

Modifica solo el grupo propietario

chgrp [opciones] grupo archivo…​


2.1. El comando chmod

chmod, del inglés change mode, modifica el modo de un archivo, es decir, los doce bits de permisos que se almacenan en su inodo: tres bits especiales, que se estudian en la sección 4, y nueve bits de permisos básicos, que corresponden a la lectura, la escritura y la ejecución de cada una de las tres clases de la sección 1.1. Cada dígito octal agrupa tres de esos bits, como muestra la descomposición del modo 4755 de /usr/bin/passwd:

Descomposición del modo 4755 en sus doce bits de permisos y su representación en ls -l
Figura 2. Descomposición del modo 4755 en sus doce bits de permisos y su representación en ls -l

ls -l muestra este modo como -rwsr-xr-x. Los bits especiales no tienen columna propia y ocupan la posición de la x de cada clase.

Admite dos formas de indicar los permisos: el modo simbólico, que añade, retira o asigna permisos concretos a cada clase, y el modo octal, que fija el modo completo con un número.

Solo el propietario de un archivo, o un proceso con CAP_FOWNER, puede cambiar sus permisos.

2.1.1. Modo simbólico

La sintaxis es [clases][operador][permisos], y pueden encadenarse varias expresiones separadas por comas:

  • Clases: u propietario, g grupo, o otros, a todas.

  • Operadores: + añade, - retira y = asigna exactamente los permisos indicados, eliminando el resto para esa clase.

  • Permisos: r, w, x, la ejecución condicional X y los bits especiales s y t.

chmod u+x despliegue.sh          # Ejecución solo para el propietario
chmod go-rwx id_ed25519          # Retira cualquier acceso al grupo y a otros
chmod a=r manual.txt             # r--r--r--
chmod u=rw,go=r config.yml       # rw-r--r--

El modo simbólico es preferible en correcciones porque solo modifica los bits indicados. chmod o-w archivo retira la escritura a otros sin alterar ningún otro permiso, mientras que un modo octal sobrescribe todos los bits.

2.1.2. Ejecución condicional con X

Al cambiar los permisos de un árbol completo con la opción -R, que aplica el cambio de forma recursiva, el permiso x plantea un problema, porque su significado depende del tipo de objeto:

  • Los directorios necesitan x para poder atravesarse, como se vio en la sección 1.2. Sin él, nadie puede acceder a su contenido.

  • La mayoría de los archivos, como documentos, imágenes o configuraciones, no deben tener x, ya que no son programas.

Por eso ninguna de las dos opciones evidentes funciona: chmod -R a+rx convierte en ejecutables todos los archivos, y chmod -R a+r no concede x a los directorios que no lo tengan, que seguirán sin poder atravesarse.

La X mayúscula resuelve el problema. Concede la ejecución solo donde tiene sentido.

Objeto ¿Recibe x con X?

Directorio

Sí, siempre

Archivo que ya tiene x para alguna clase, como un script

Sí

Archivo sin x para ninguna clase

No

Por ejemplo, para dejar el contenido de una web accesible al propietario y a su grupo, y cerrado para los demás:

sudo chmod -R u=rwX,g=rX,o= /srv/web
Expresión Efecto

u=rwX

El propietario recibe lectura y escritura, y ejecución solo en directorios y archivos ejecutables

g=rX

El grupo recibe lectura, y ejecución solo en directorios y archivos ejecutables

o=

Otros no reciben ningún permiso

2.1.3. Modo octal

El modo octal asigna el modo completo de una sola vez con los valores de la sección 1.3.2. Cada dígito corresponde a una clase y es la suma de r = 4, w = 2 y x = 1. A diferencia del modo simbólico, no parte de los permisos actuales, sino que los sustituye todos por los indicados.

sudo chmod 640 /etc/rsyslog.conf
sudo chmod 750 /opt/scripts
Orden Resultado Motivo

chmod 640 /etc/rsyslog.conf

rw-r-----

El propietario puede modificar la configuración, el grupo solo puede leerla y el resto de usuarios no tiene ningún acceso

chmod 750 /opt/scripts

rwxr-x---

El propietario gestiona el directorio, el grupo puede listarlo y atravesarlo para ejecutar sus scripts, y el resto de usuarios no tiene ningún acceso

El número de dígitos determina qué bits se fijan:

Dígitos Ejemplo Interpretación

Tres

640

Fijan los permisos del propietario, del grupo y de otros. El dígito de los bits especiales se considera 0

Cuatro

2770

El primer dígito fija los bits especiales de la sección 4: 4 para SetUID, 2 para SetGID y 1 para el Sticky Bit. Pueden sumarse, como en 6755, que activa SetUID y SetGID a la vez

Menos de tres

7

Se completan con ceros por la izquierda: chmod 7 archivo equivale a chmod 007 archivo y concede todos los permisos a otros y ninguno al propietario, un error frecuente

El modo octal es adecuado cuando se conoce el estado final completo, como al desplegar un archivo nuevo o al ajustar un archivo a los valores de referencia de la sección 1.3.3 o del CIS Benchmark. Para corregir un permiso concreto sin alterar el resto es preferible el modo simbólico, como se explicó en la sección 2.1.1.

El modo octal y los bits SetUID y SetGID en directorios

GNU chmod conserva los bits SetUID y SetGID de un directorio cuando se le aplica un modo octal de tres o cuatro dígitos. chmod 0770 directorio no elimina un SetGID existente. Para retirarlo hay que indicarlo de forma explícita con chmod g-s directorio o con un quinto dígito, como en chmod 00770 directorio. El Sticky Bit, en cambio, no se conserva. chmod 777 sobre un directorio con modo 1777 lo elimina. Compruebe siempre el resultado con stat tras una modificación.

2.1.4. Copia de permisos con --reference

Cuando un archivo se crea con cp sin opciones, con un editor que lo reescribe o al extraerlo de una copia de seguridad, no recibe los metadatos del original. Su modo depende de la umask, que se estudia en la sección 3, y su propietario es quien lo crea. Para dejarlo igual que otro archivo, la opción --reference copia sus metadatos en lugar de escribirlos a mano:

sudo chown --reference=/etc/shadow /etc/shadow.bak
sudo chmod --reference=/etc/shadow /etc/shadow.bak
Orden Qué copia del archivo de referencia

chown --reference=referencia archivo

El propietario y el grupo

chmod --reference=referencia archivo

Los doce bits del modo, incluidos los especiales

El orden importa. chown se ejecuta primero porque, al cambiar el propietario de un ejecutable, el núcleo elimina sus bits SetUID y SetGID, como se explica en la sección 2.2.

Con los conceptos explicados hasta aquí, ya puede realizar el Ejercicio 3. Gestión de permisos con chmod.


2.2. El comando chown

chown, del inglés change owner, cambia el usuario propietario de un archivo y, opcionalmente, su grupo propietario. El inodo no guarda nombres, sino el UID y el GID numéricos, que ls -l y stat traducen a nombres consultando las bases de datos de cuentas. Por eso, cuando se elimina una cuenta, sus archivos muestran un número en lugar de un nombre y pasan a ser archivos huérfanos, que se auditan en la sección 8.2.

sudo chown usuario archivo                 # Cambia el propietario
sudo chown usuario:grupo archivo           # Cambia propietario y grupo
sudo chown :grupo archivo                  # Cambia solo el grupo
sudo chown -R usuario:grupo directorio     # Aplica el cambio de forma recursiva

El primer argumento admite varias formas:

Forma Efecto

usuario

Cambia solo el propietario

usuario:grupo

Cambia el propietario y el grupo

usuario:

Cambia el propietario y asigna como grupo el grupo principal de ese usuario

:grupo

Cambia solo el grupo, igual que chgrp

1001:1001

Usa directamente un UID y un GID numéricos, lo que resulta útil con archivos huérfanos o con identificadores que no existen en el sistema local

Las opciones más relevantes son las siguientes:

Parámetro Función

-R

Aplica el cambio de forma recursiva a todo el árbol

-h

Cambia el propio enlace simbólico indicado en la línea de órdenes. Sin esta opción ni -R, chown cambia el destino del enlace

--from=usuario:grupo

Solo cambia los archivos cuyo propietario y grupo actuales coinciden con los indicados. Permite reasignar de forma segura los archivos de una cuenta concreta sin tocar los demás

-P

No sigue ningún enlace simbólico y cambia el propio enlace. Es el comportamiento por defecto con -R

-H

Recorre los enlaces simbólicos a directorios indicados en la línea de órdenes y cambia el destino de los enlaces en lugar del propio enlace

-L

Recorre todos los enlaces simbólicos a directorios que encuentra en el árbol y cambia el destino de los enlaces en lugar del propio enlace

--reference=archivo

Copia el propietario y el grupo de otro archivo, como se vio en la sección 2.1.4

Restricciones del núcleo sobre chown

En Linux, solo un proceso con CAP_CHOWN, normalmente root, puede cambiar el propietario de un archivo. Muchos UNIX clásicos permitían que un usuario «regalase» sus archivos a otro, lo que facilitaba eludir las cuotas de disco e imputar falsamente la autoría de un archivo a otra cuenta.

Además, al cambiar el propietario o el grupo de un archivo ejecutable, el núcleo elimina sus bits SetUID y SetGID y sus capacidades de archivo, incluso cuando el cambio lo hace root. Así se evita que un binario privilegiado conserve sus privilegios con un nuevo propietario. Por ello, en una instalación manual debe ejecutarse primero chown y después chmod.


2.3. El comando chgrp

chgrp, del inglés change group, cambia solo el grupo propietario de un archivo. Equivale a chown :grupo, pero expresa con más claridad la intención y es la forma habitual de conceder acceso a un equipo. Se asigna el archivo al grupo y, a continuación, se fijan los permisos de la clase g. Por ejemplo, para que los miembros de auditores puedan leer un registro sin poder modificarlo:

sudo chgrp auditores /var/log/auditoria.log
sudo chmod 640 /var/log/auditoria.log
ls -l /var/log/auditoria.log
-rw-r----- 1 root auditores 20480 oct 26 10:00 /var/log/auditoria.log

Como ocurre con chown, cambiar el grupo de un ejecutable elimina sus bits SetUID y SetGID, por lo que chgrp debe ejecutarse antes que chmod. En los directorios, en cambio, el bit SetGID se conserva.

Parámetro Función

-R

Aplica el cambio de forma recursiva a todo el árbol, con las mismas precauciones frente a los enlaces simbólicos que chown -R

-h

Cambia el propio enlace simbólico en lugar de su destino

--reference=archivo

Copia el grupo de otro archivo

2.3.1. Uso por usuarios sin privilegios

A diferencia del cambio de propietario, un usuario sin privilegios puede usar chgrp, o chown :grupo, sobre sus propios archivos, pero solo para asignarles un grupo del que sea miembro, ya sea su grupo principal o uno secundario. Esto le permite compartir un archivo con su equipo sin necesidad de un administrador, sin poder asignarlo a un grupo ajeno:

id -Gn                          # Grupos del proceso actual
chgrp desarrollo informe.txt    # Funciona si el usuario pertenece a desarrollo
chgrp root informe.txt          # Operation not permitted

El núcleo comprueba los grupos del proceso, que se fijan al iniciar la sesión. Si se acaba de añadir al usuario a un grupo con usermod -aG, chgrp no reconocerá la nueva pertenencia hasta que inicie una sesión nueva o ejecute newgrp. id -Gn muestra los grupos del proceso actual, mientras que id -Gn usuario muestra los que figuran en /etc/group.

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 4. Propiedad con chown y chgrp.

3. La máscara de creación de archivos umask

Cada archivo nuevo necesita unos permisos iniciales, pero nadie ejecuta chmod tras cada redirección >, cada archivo que guarda un editor o cada registro que escribe un servicio. Los permisos de casi todos los archivos nuevos los decide la máscara de creación, o umask, que actúa como la política de permisos por defecto de cada usuario y de cada servicio.

Su importancia en el bastionado se aprecia con un ejemplo. Un administrador cuya sesión tiene la umask tradicional 022 exporta una base de datos:

mysqldump --all-databases > /srv/backups/copia.sql
ls -l /srv/backups/copia.sql
-rw-r--r-- 1 admin admin 52428800 oct 26 10:00 /srv/backups/copia.sql

La copia nace con permisos 644, por lo que cualquier cuenta local o servicio comprometido puede leer la base de datos completa si puede atravesar el directorio. Nadie ha cometido un error con chmod. Simplemente nadie lo ejecutó. Con umask 027, el mismo archivo nacería con 640 y el resto del sistema no tendría acceso.

La umask permite así invertir el enfoque de la protección:

  • Con 022, todo archivo nuevo nace legible por todos y hay que acordarse de cerrar cada archivo sensible.

  • Con 027, todo archivo nuevo nace cerrado para los demás y solo se abre de forma explícita, con chmod, chgrp o una ACL, lo que deba compartirse.

Es el principio de mínimo privilegio de la sección 1 aplicado a todo lo que se cree en el futuro, y por eso el CIS Benchmark exige 027 como mínimo.

El mecanismo es el siguiente. Cuando un programa crea un archivo o un directorio, solicita al núcleo unos permisos iniciales en la llamada al sistema open() o mkdir(). El núcleo no los aplica tal cual, sino que antes elimina los bits indicados en la umask del proceso. La umask se hereda de proceso padre a proceso hijo, de modo que la que se fija al iniciar la sesión se aplica a todo lo que el usuario ejecute, y la de cada servicio se fija en su unidad de systemd, como se explica en la sección 3.3.

La umask solo actúa al crear un archivo

No modifica los permisos de los archivos que ya existen ni impide que un programa los cambie después con chmod. Tampoco puede conceder permisos que el programa no solicite. ssh-keygen crea las claves privadas con 600 sea cual sea la máscara. Por eso se complementa con las auditorías periódicas de la sección 8.


3.1. Cálculo de los permisos resultantes

3.1.1. Modo solicitado por los programas

La umask solo puede retirar permisos, nunca añadirlos. El punto de partida es el modo que solicita cada programa:

  • Las utilidades habituales, como touch, los editores o la redirección > de la shell, solicitan 666 para los archivos. Por eso un archivo nuevo nunca es ejecutable, con independencia de la máscara.

  • mkdir solicita 777 para los directorios, ya que sin x no podrían atravesarse.

  • Algunos programas solicitan otros valores. El enlazador de un compilador pide 777 para el binario que genera, y ssh-keygen crea las claves privadas con 600 sea cual sea la máscara.

3.1.2. Operación a nivel de bits

El núcleo calcula el modo final con una operación lógica, no con una resta:

modo final = modo solicitado AND (NOT umask)

La resta octal da el resultado correcto en los casos habituales, como 022, 027 o 077, pero falla cuando la máscara retira bits que el modo solicitado no tenía:

umask Archivo, cálculo bit a bit Resta octal 666 - umask Resultado real

027

110 110 110 AND NOT 000 010 111

640

640

033

110 110 110 AND NOT 000 011 011

633, que es incorrecto

644

Para comprobar una máscara sin hacer cálculos, ejecute umask -S, que muestra los permisos que se conservan en notación simbólica, por ejemplo u=rwx,g=rx,o= para 027.


3.2. Comparativa de máscaras

umask Archivos Directorios Perfil de seguridad

022

644

755

Tradicional: cualquier usuario local puede leer todos los archivos nuevos. No es adecuada en sistemas multiusuario.

002

664

775

Colaborativa con UPG: el grupo puede escribir. Es segura solo si cada usuario tiene su propio grupo privado, como se explicó en el capítulo de gestión de usuarios y grupos, pero sigue concediendo lectura a otros.

027

640

750

Bastionado corporativo: el grupo puede leer y el resto de usuarios no tiene ningún acceso. Valor exigido como mínimo por el CIS Benchmark.

077

600

700

Máxima privacidad: solo el propietario accede. Adecuada para root y para cuentas de servicio que manejan secretos.


3.3. Configuración persistente

El comando umask 027 solo afecta a la shell actual y a sus procesos hijos. Para que la política sea persistente debe configurarse en los mecanismos que inicializan cada tipo de sesión:

  1. /etc/login.defs: la directiva UMASK define el valor por defecto que aplica el módulo pam_umask y que useradd utiliza para calcular los permisos del directorio personal, salvo que se defina HOME_MODE.

    UMASK 027
  2. pam_umask: en Debian y Ubuntu está activado en /etc/pam.d/common-session. Sin argumentos toma el valor de login.defs, y se aplica a todas las sesiones gestionadas por PAM, incluidas las de SSH:

    session optional pam_umask.so
  3. Perfiles de shell: el CIS Benchmark comprueba también que ningún archivo de /etc/profile, /etc/profile.d/*.sh ni /etc/bash.bashrc establezca una máscara más permisiva:

    echo 'umask 027' | sudo tee /etc/profile.d/50-umask.sh > /dev/null
    sudo chmod 644 /etc/profile.d/50-umask.sh

    La redirección > la realiza la shell del usuario sin privilegios, por lo que sudo echo …​ > archivo no podría escribir en /etc/profile.d/. Por eso se recurre a sudo tee, que lee la entrada estándar y la escribe en el archivo con privilegios de root. La redirección > /dev/null descarta la copia que tee envía también a la salida estándar.

  4. Servicios de systemd: los servicios no inician una sesión de usuario y su máscara por defecto es 0022. Se ajusta en cada unidad con la directiva UMask=:

    sudo systemctl edit nginx.service
    # [Service]
    # UMask=0027

    systemctl edit abre un editor y guarda las directivas en un archivo complementario, o drop-in, /etc/systemd/system/nginx.service.d/override.conf, sin modificar la unidad que instala el paquete. Las líneas comentadas del ejemplo muestran el contenido que debe escribirse en el editor. Al guardar, systemd recarga su configuración, pero la nueva máscara solo se aplica tras reiniciar el servicio con sudo systemctl restart nginx.

USERGROUPS_ENAB modifica la máscara de los usuarios con grupo privado

Con USERGROUPS_ENAB yes en login.defs, valor por defecto en Debian y Ubuntu, pam_umask copia los bits del propietario en los del grupo para los usuarios cuyo grupo principal tiene su mismo nombre. Con UMASK 027, la máscara efectiva de estos usuarios es 007. El grupo obtiene escritura, pero como ese grupo solo contiene al propio usuario, el resto del sistema sigue sin acceso. Compruebe siempre la máscara real iniciando una sesión nueva y ejecutando umask.

Referencias

umask es una orden interna de la shell, no un ejecutable independiente, por lo que se documenta en el manual de bash y en el estándar POSIX:

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 5. Cálculo y configuración de la umask.

4. Permisos especiales y capacidades

El modelo básico no permite resolver situaciones habituales: que un usuario cambie su contraseña en un archivo que solo puede modificar root, que los archivos de un directorio de equipo pertenezcan siempre al grupo del equipo, o que en un directorio de escritura pública nadie pueda borrar los archivos ajenos. Para ello existen tres bits especiales:

Bit Octal Simbólico Se aplica a Efecto

SetUID

4000

u+s

Archivos ejecutables

El proceso se ejecuta con el UID del propietario del archivo

SetGID

2000

g+s

Ejecutables y directorios

En ejecutables, el proceso toma el GID del archivo. En directorios, los nuevos archivos heredan su grupo

Sticky Bit

1000

+t

Directorios

Solo el propietario de cada archivo puede borrarlo o renombrarlo

Minúsculas y mayúsculas en ls -l

Los bits especiales ocupan la posición de la x de cada clase. Una minúscula, s o t, indica que el bit especial y el permiso de ejecución están activos. Una mayúscula, S o T, indica que el bit especial está activo sin el permiso de ejecución, lo que en un archivo suele delatar un error de configuración, ya que no tiene ningún efecto.


4.1. SetUID

4.1.1. Funcionamiento

Al ejecutar un archivo con el bit SetUID, el núcleo asigna al nuevo proceso un UID efectivo igual al del propietario del archivo, mientras que el UID real sigue siendo el del usuario que lo lanzó. El UID efectivo es el que se usa para las comprobaciones de permisos, y el UID guardado, o saved set-user-ID, permite al programa alternar entre ambos para usar los privilegios solo cuando los necesita.

ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68248 mar 28  2024 /usr/bin/passwd

Cuando un usuario sin privilegios ejecuta passwd, el proceso se ejecuta con UID efectivo 0, lo que le permite escribir el nuevo hash en /etc/shadow. El programa está diseñado para que el usuario solo pueda cambiar su propia contraseña, de modo que la seguridad del sistema depende por completo de la ausencia de errores en ese código.

SetUID y scripts interpretados

Linux ignora el bit SetUID en los scripts que empiezan por #!. La restricción evita una condición de carrera clásica entre el momento en que el núcleo lee el shebang y el momento en que el intérprete abre el script, durante el cual un atacante podía sustituir el archivo. Por eso los programas SetUID son siempre binarios compilados.

4.1.2. Riesgos y abuso de SetUID

Un binario SetUID de root amplía la superficie de ataque del sistema. Basta con que contenga un error de memoria, que invoque otros programas confiando en variables de entorno como PATH, o que permita leer, escribir o ejecutar archivos arbitrarios, para que cualquier usuario local obtenga privilegios de superusuario.

El proyecto GTFOBins cataloga las utilidades estándar que permiten escapar a una shell o leer y escribir archivos arbitrarios. Si alguna de ellas recibe el bit SetUID, la escalada es inmediata.

Ejemplo de escalada con un binario SetUID mal asignado

Si un administrador asignase SetUID a find para «facilitar» búsquedas, cualquier usuario podría obtener una shell de root.

Primero, se asigna SetUID a find:

sudo chmod u+s /usr/bin/find

Ahora un usuario sin privilegios puede obtener una shell de root:

find . -exec /bin/bash -p \; -quit
Parámetro Función

-exec orden \;

Ejecuta la orden por cada archivo que encuentra find. El ; cierra la orden y se escapa para que no lo interprete la shell. Como find se ejecuta con UID efectivo 0, la shell que lanza lo hereda

-quit

Termina la búsqueda tras el primer resultado, de modo que solo se abre una shell

-p

Impide que la shell descarte el UID efectivo heredado, comportamiento que bash y dash aplican por defecto como medida de protección

¿Cómo se mitiga este riesgo?

Con el parámetro -perm /4000, el comando devuelve los archivos que tienen el bit SetUID activo:

find / -type f -perm /4000 2>/dev/null

Se debe revisar la salida y eliminar el bit de cualquier archivo que no lo necesite.

En nuestro caso, deberíamos quitar el bit SetUID a find:

sudo chmod u-s /usr/bin/find

4.2. SetGID

El bit SetGID tiene dos efectos distintos según se aplique a un archivo ejecutable o a un directorio. Ambos comparten la misma idea, que es hacer que el grupo determine un comportamiento en lugar del usuario. El segundo uso, SetGID en directorios, es una de las herramientas más útiles para organizar el trabajo en equipo sin recurrir a root ni abrir los archivos a todo el sistema.

4.2.1. SetGID en ejecutables

Al ejecutar un archivo con SetGID, el proceso toma como GID efectivo el grupo propietario del archivo, igual que SetUID hace con el UID del propietario. Permite conceder un privilegio muy acotado a través de un grupo en lugar de dar acceso de root.

El ejemplo típico es la lectura de /etc/shadow, que pertenece al grupo shadow con modo 640. En Debian y Ubuntu, chage y expiry son SetGID del grupo shadow, de modo que al ejecutarlos el proceso adquiere ese GID y puede leer el archivo, sin que ningún usuario ni el propio binario necesiten ser root. De forma análoga, crontab es SetGID del grupo crontab para escribir en /var/spool/cron/crontabs.

ls -l /usr/bin/chage
-rwxr-sr-x 1 root shadow 72184 mar 28  2024 /usr/bin/chage

Desde el punto de vista del bastionado, SetGID de un grupo concreto es preferible a SetUID de root: si el binario tiene un fallo, el atacante obtiene como mucho los privilegios de ese grupo, no el control total del sistema. Por eso, al revisar binarios privilegiados, un SetGID de shadow o crontab es menos grave que un SetUID de root, aunque ambos deben figurar en la línea base de la sección 8.4.

4.2.2. SetGID en directorios

Este es el uso más frecuente y el más interesante para el bastionado. Resuelve un problema concreto del trabajo en equipo.

El problema. Varias personas de un mismo equipo necesitan editar los archivos de una carpeta compartida. Sin SetGID, cada archivo nuevo recibe el grupo principal de quien lo crea, que con los grupos privados de usuario es un grupo distinto para cada persona:

drwxrwx--- 2 root  desarrollo  4096 oct 15 10:00 proyecto/
-rw-r--r-- 1 ana   ana          120 oct 15 10:05 proyecto/modulo_ana.py
-rw-r--r-- 1 luis  luis          85  oct 15 10:06 proyecto/modulo_luis.py

Aunque el directorio pertenezca al grupo desarrollo, el archivo de ana pertenece al grupo ana y el de luis al grupo luis, así que no pueden editar los archivos del otro a través del grupo. La alternativa de abrir los archivos a others sería mucho peor, porque los expondría a todo el sistema.

La solución. Con el bit SetGID activado en el directorio, todo archivo o subdirectorio nuevo hereda el grupo del directorio en lugar del grupo de quien lo crea, y los subdirectorios heredan además el propio bit SetGID, de modo que la política se propaga a todo el árbol sin repetir la configuración.

sudo mkdir -p /srv/proyectos/desarrollo
sudo chown root:desarrollo /srv/proyectos/desarrollo
sudo chmod 2770 /srv/proyectos/desarrollo
ls -ld /srv/proyectos/desarrollo
drwxrws--- 2 root desarrollo 4096 oct 15 10:00 /srv/proyectos/desarrollo

La s en la posición de la x del grupo confirma que SetGID está activo. El modo 2770 combina el 2 del bit SetGID con el 770 que da acceso completo al propietario y al grupo desarrollo, y ninguno a others. A partir de aquí, cualquier archivo que cree un miembro del equipo nace con grupo desarrollo y el resto del equipo puede editarlo.

Parámetro Función

mkdir -p

Crea también los directorios intermedios que no existan y no devuelve error si el directorio ya existe

ls -d

Muestra los metadatos del propio directorio en lugar de listar su contenido

Para que la herencia del grupo sea útil, los miembros deben además poder escribir como grupo: la umask de la sección 3 no debe retirar la escritura del grupo, lo que exige 002 o 007 en lugar de 027. Si la máscara es 027, el archivo hereda el grupo desarrollo pero nace con 640, y los compañeros solo podrán leerlo, no modificarlo.

La herencia de grupo solo se produce al crear

mv conserva el grupo original del archivo que se mueve dentro del directorio, de modo que un archivo creado en el directorio personal y movido después a la carpeta compartida mantiene el grupo privado de su autor y sus compañeros no podrán acceder a él. cp, en cambio, crea un archivo nuevo que sí hereda el grupo. Para corregir el grupo de los archivos ya existentes se usa chgrp -R desarrollo /srv/proyectos/desarrollo.

SetGID resuelve la propiedad de los archivos nuevos, pero no garantiza por sí solo que todos los miembros tengan acceso de escritura, que depende de la umask, ni impide que un miembro borre los archivos de otro. Para esto último se combina con el Sticky Bit de la sección 4.3, como se hace en los directorios de colaboración más estrictos.


4.3. Sticky Bit

El Sticky Bit resuelve un problema que los nueve bits básicos no pueden expresar, el de un directorio donde todos puedan crear archivos, pero nadie pueda borrar los de los demás. Es exactamente lo que hace falta en un directorio compartido como /tmp.

El problema. Recuerde, de la sección 1.2, que borrar un archivo no depende de los permisos del archivo, sino de los del directorio que lo contiene. Hace falta w y x sobre el directorio. En un directorio de escritura pública con modo 777, cualquier usuario tiene esa escritura, así que cualquiera puede borrar o renombrar los archivos de cualquier otro, aunque esos archivos sean 600 y no pueda leerlos. En un directorio como /tmp, por el que pasan los archivos temporales, los sockets y los archivos de bloqueo de todos los usuarios y servicios, esto permitiría borrar el archivo de bloqueo de un servicio, provocar una denegación de servicio o sustituir un archivo que otro proceso va a leer.

La solución. El Sticky Bit añade una restricción al borrado. En un directorio que lo tenga activado, un usuario solo puede eliminar o renombrar un archivo si es su propietario, o el propietario del directorio, o tiene CAP_FOWNER. Todos pueden seguir creando archivos y modificando los propios, pero nadie toca los ajenos.

Históricamente, el bit tenía otro significado. Indicaba al sistema que mantuviese un programa en el área de intercambio para acelerar su carga. En Linux ese uso desapareció y solo queda el efecto sobre directorios.

Los directorios temporales del sistema ya lo traen activado, lo que se ve en la t final de los permisos:

ls -ld /tmp /var/tmp
drwxrwxrwt 14 root root 4096 oct 15 10:15 /tmp
drwxrwxrwt  3 root root 4096 oct 15 09:00 /var/tmp

La t ocupa la posición de la x de others. Una t minúscula indica que el directorio conserva el permiso x para others, y una T mayúscula que no lo tiene, lo que en un directorio compartido sería un error, porque nadie podría atravesarlo.

Desde el punto de vista del bastionado, la regla es simple: todo directorio de escritura pública debe tener el Sticky Bit.

Se activa con chmod, en modo simbólico u octal, anteponiendo el 1 a los tres dígitos habituales:

sudo chmod +t /srv/buzon        # Modo simbólico
sudo chmod 1777 /srv/buzon      # Modo octal

Con los conceptos de SetGID y Sticky Bit, ya puede realizar el Ejercicio 6. Directorio colaborativo con SetGID y el Ejercicio 7. Protección contra el borrado con Sticky Bit.


4.4. Capacidades de archivos

Un binario SetUID de root es una herramienta contundente. Concede todos los privilegios del superusuario a un programa que, casi siempre, solo necesita uno. Como se vio en la sección 4.1, si ese programa tiene un fallo, el atacante hereda el control total del sistema. Las capacidades, o capabilities, son la respuesta del núcleo a ese exceso. Dividen los poderes de root en unidades independientes que pueden concederse por separado.

El núcleo define unas cuarenta capacidades, cuya lista completa figura en la página de manual capabilities(7) y crece con cada versión. La tabla siguiente recoge solo cuatro de las más relevantes para el bastionado, dos de red y dos que equivalen casi por completo a root:

Capacidad Privilegio que concede

CAP_NET_BIND_SERVICE

Abrir puertos inferiores a 1024

CAP_NET_RAW

Usar sockets en bruto, como hace ping

CAP_DAC_OVERRIDE

Ignorar las comprobaciones de permisos de archivos, como root

CAP_SETUID

Cambiar el UID del proceso a cualquier valor, incluido el 0

No todas las capacidades representan el mismo riesgo. Algunas, como CAP_NET_BIND_SERVICE, conceden un privilegio muy acotado, mientras que otras, como CAP_SYS_ADMIN, abarcan tantas operaciones que equivalen en la práctica a ser root. Por eso lo importante no es memorizar la lista, sino saber consultar qué capacidad tiene un binario y valorar si el privilegio que otorga es proporcionado a su función.

4.4.1. Las capacidades como alternativa a SetUID

El caso de uso que mejor ilustra su ventaja es un servidor que debe escuchar en un puerto bajo, como el 443. La forma tradicional sería ejecutarlo como root o darle SetUID, con lo que todo el proceso tendría los privilegios del superusuario. En su lugar, se le concede solo la capacidad necesaria:

# Permitir que un servidor escuche en el puerto 443 sin ejecutarse como root
sudo setcap cap_net_bind_service=ep /usr/local/bin/servidor
getcap /usr/local/bin/servidor

Así, aunque el servidor sea vulnerable, un atacante que lo comprometa solo obtiene la capacidad de abrir puertos bajos, no el control del sistema. Esta es la ventaja de bastionado: reduce el privilegio al mínimo imprescindible, en lugar de conceder todo root.

El argumento de setcap sigue el formato capacidad=conjuntos, donde cada letra añade la capacidad a uno de los conjuntos del archivo:

Conjunto Función

p, permitted

Contiene las capacidades que el proceso puede utilizar

e, effective

Activa las capacidades desde el inicio de la ejecución sin que el programa tenga que solicitarlas. Es imprescindible en programas que no gestionan capacidades

i, inheritable

Regula la transmisión de las capacidades a los programas que el proceso ejecute

=ep es la combinación habitual. getcap muestra las capacidades asignadas a cada archivo indicado.

4.4.2. El riesgo: capacidades invisibles

Las capacidades reducen el privilegio, pero introducen un peligro propio para la auditoría. Se almacenan en el atributo extendido security.capability y no aparecen en ls -l: un binario con capacidades se ve igual que uno sin ellas. Por eso una auditoría que solo busque bits SetUID con find -perm las pasará por alto por completo.

El riesgo es grave porque algunas capacidades equivalen en la práctica a un SetUID de root. Si se asignan a un intérprete de uso general como python3 o perl, cualquier usuario puede usarlo para escalar privilegios:

Capacidad en un intérprete Por qué equivale a root

cap_setuid

El intérprete puede cambiar su UID a 0 y ejecutar código como root

cap_dac_override

Puede leer y escribir cualquier archivo, incluido /etc/shadow

cap_sys_admin

Concede un amplio conjunto de operaciones administrativas del núcleo

GTFOBins cataloga estos casos igual que los binarios SetUID abusables. Por eso el inventario de capacidades debe hacerse de forma explícita, con una orden propia, y no confiarse a la búsqueda de bits SetUID:

# Inventario de archivos con capacidades
sudo getcap -r / 2>/dev/null
Parámetro Función

-r

Recorre de forma recursiva el árbol indicado

2>/dev/null

Descarta los errores que producen los pseudosistemas de archivos como /proc

El resultado debe compararse con una línea base, como se hace con los binarios SetUID en la sección 8.4. Cualquier capacidad que no figure en ella, sobre todo en un intérprete, debe investigarse. Para retirar una capacidad asignada se usa sudo setcap -r archivo.

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 8. SetUID, capacidades y escalada de privilegios.


4.5. Resumen de permisos especiales

Mecanismo Octal o comando Efecto principal Riesgo principal Auditoría

SetUID

4xxx

Ejecución con el UID del propietario

Escalada a root si el binario es vulnerable o está catalogado en GTFOBins

find / -xdev -type f -perm -4000

SetGID

2xxx

Ejecución con el GID del archivo o herencia de grupo en directorios

Acceso a recursos del grupo, como shadow

find / -xdev -type f -perm -2000

Sticky Bit

1xxx

Solo el propietario borra sus archivos

Su ausencia permite sabotaje y suplantación en directorios compartidos

find / -xdev -type d -perm -0002 ! -perm -1000

Capacidades

setcap

Privilegios de root parciales

Escalada si se asignan a intérpretes o utilidades genéricas

getcap -r /

Las búsquedas de la columna de auditoría utilizan las siguientes opciones de find:

Parámetro Función

-xdev

No desciende a otros sistemas de archivos montados bajo la ruta inicial, lo que evita recorrer /proc, /sys o recursos de red

-type f, -type d

Limita la búsqueda a archivos regulares o a directorios

-perm -modo

Selecciona los archivos que tienen activos todos los bits del modo: -perm -4000 encuentra los que tienen SetUID

-perm /modo

Selecciona los archivos que tienen activo alguno de los bits del modo: -perm /6000 encuentra a la vez los SetUID y los SetGID

!

Niega la condición siguiente: ! -perm -1000 selecciona los que no tienen Sticky Bit

-xdev excluye también las particiones locales montadas bajo /. Si /home, /var o /tmp son sistemas de archivos independientes, deben añadirse como rutas iniciales, como en find / /home /var -xdev …​, o la auditoría no los revisará.

5. Listas de control de acceso (ACL)

El modelo DAC solo permite definir permisos para un usuario, un grupo y el resto. Si una auditora externa, maria (o el grupo de auditores al que pertenece), necesita leer un archivo de finanzas que pertenece a juan:finanzas, no hay forma de concedérselo sin añadirla al grupo propietario, con acceso a todos los archivos del grupo, o sin abrirlo a others, con acceso para todo el sistema.

Las listas de control de acceso o ACL (Access Control Lists) resuelven esta limitación permitiendo añadir entradas para usuarios y grupos concretos. Linux implementa el borrador POSIX.1e, que nunca llegó a aprobarse como estándar pero es la implementación de referencia en los sistemas UNIX.


5.1. Soporte e identificación

Los sistemas de archivos ext4, XFS y Btrfs admiten ACL sin opciones de montaje adicionales. El soporte reside en el núcleo y en el sistema de archivos, y las ACL se almacenan como atributos extendidos del inodo, de modo que un archivo puede tener ACL aunque las órdenes getfacl y setfacl no estén disponibles.

getfacl y setfacl las proporciona el paquete acl, que puede faltar en una imagen mínima o de nube. Puede instalarlo mediante el comando:

# Actualiza el índice de paquetes
sudo apt update

# Instala el paquete que aporta getfacl y setfacl
sudo apt install -y acl

Un archivo con ACL se identifica por el signo + tras los permisos:

-rw-rw----+ 1 juan finanzas 10240 oct 15 10:30 balance.xlsx

ls -l no muestra los permisos del grupo en un archivo con ACL

Cuando existe una ACL, la tripleta central de ls -l muestra la máscara de la ACL, no los permisos del grupo propietario. Además, chmod g=…​ modifica la máscara. Un administrador que lea rw- en el grupo y ejecute chmod g-w para corregirlo estará limitando también los permisos de todos los usuarios y grupos con nombre. En archivos con +, consulte siempre getfacl.


5.2. Consulta con getfacl

getfacl, del inglés get file access control list, muestra la ACL completa de un archivo o directorio. Es la orden que revela los permisos reales cuando ls -l marca un +, ya que, como se explicó en la sección 5.1, la tripleta del grupo de ls -l muestra entonces la máscara y no los permisos efectivos de cada entrada. No modifica nada, solo consulta.

getfacl balance.xlsx
# file: balance.xlsx
# owner: juan
# group: finanzas
user::rw-                      (1)
user:maria:rw-                 (2)
group::r--                     (3)
group:auditores:r--            (4)
mask::rw-                      (5)
other::---                     (6)
1 Propietario del archivo, equivalente a la clase u.
2 Usuario con nombre: permisos específicos para maria.
3 Grupo propietario, en este caso finanzas.
4 Grupo con nombre: permisos para los miembros de auditores.
5 Máscara: techo de permisos para las entradas con nombre y el grupo propietario (2, 3 y 4). No afecta al propietario ni a other.
6 Resto de usuarios, equivalente a la clase o.

Cada entrada sigue el formato tipo:calificador:permisos de la sección 5.3. Las entradas con el calificador vacío, user::, group:: y other::, son las tres clases del modo tradicional, mientras que lo que añade la ACL son las entradas con nombre, como user:maria: o group:auditores:.

Algunas opciones de uso frecuente son las siguientes:

Parámetro Función

-R

Recorre de forma recursiva los directorios indicados

-s

Omite los archivos que solo tienen los permisos básicos, de modo que la salida contiene únicamente los que tienen una ACL extendida

-p

No elimina la / inicial de las rutas, lo que permite restaurar la copia desde cualquier directorio

-t

Presenta la salida en forma de tabla, más legible

5.2.1. Algoritmo de evaluación

Con ACL, el núcleo amplía el orden de evaluación de la sección 1.1. Se aplica la primera regla que corresponda al proceso, sin acumular permisos:

  1. Si el proceso es el propietario, se aplica user::.

  2. Si existe una entrada user:nombre: para el proceso, se aplica esa entrada limitada por la máscara.

  3. Si el proceso pertenece al grupo propietario o a algún grupo con nombre, se concede el acceso si alguna de esas entradas, limitada por la máscara, otorga el permiso solicitado. Si ninguna lo otorga, el acceso se deniega sin pasar a other::, aunque other:: sí lo concediera.

  4. En otro caso, se aplica other::.

Algoritmo de evaluación de permisos con ACL POSIX
Figura 3. Algoritmo de evaluación de permisos con ACL POSIX

5.3. Modificación con setfacl

setfacl, del inglés set file access control list, es la orden complementaria de getfacl. Añade, modifica o elimina entradas en la ACL de un archivo o directorio. Cuando se crea la primera entrada con nombre, el archivo pasa a mostrar el + de ls -l y el núcleo empieza a evaluarlo según el algoritmo de la sección 5.2.1.

Las operaciones se controlan con tres opciones básicas:

  • -m añade una entrada nueva o modifica una existente.

  • -x elimina una entrada concreta, sin tocar el resto de la ACL.

  • -b borra la ACL completa, de modo que el archivo recupera sus permisos tradicionales y pierde el +.

Las entradas de usuario y de grupo se indican con el formato tipo:calificador:permisos que se detalla más abajo.

setfacl -m u:maria:rw balance.xlsx         # Añade o modifica una entrada de usuario
setfacl -m g:auditores:r balance.xlsx      # Añade o modifica una entrada de grupo
setfacl -x u:maria balance.xlsx            # Elimina una entrada concreta
setfacl -b balance.xlsx                    # Elimina toda la ACL y desaparece el +

Cada entrada sigue el formato tipo:calificador:permisos:

Tipo Entrada Calificador

u

Usuario

Nombre del usuario. Si se omite, como en u::rw, la entrada es la del propietario

g

Grupo

Nombre del grupo. Si se omite, como en g::r, la entrada es la del grupo propietario

m

Máscara

Siempre vacío, como en m::r

o

Resto de usuarios

Siempre vacío, como en o::---

Con -x solo se indican el tipo y el calificador, sin permisos.

A maria se le conceden aquí lectura y escritura (rw) para poder ilustrar después el efecto de la máscara sobre los permisos efectivos (sección 5.3.1). Si, como plantea el ejemplo inicial, solo necesitara consultar el archivo, el mínimo privilegio aconsejaría concederle únicamente lectura con setfacl -m u:maria:r balance.xlsx.

5.3.1. La máscara y los permisos efectivos

La máscara (mask::) es un techo: limita los permisos efectivos de los usuarios con nombre, el grupo propietario y los grupos con nombre, pero no afecta al propietario (user::) ni a other::. El permiso efectivo de cada entrada afectada es la intersección entre lo que la entrada concede y lo que la máscara permite.

1. La máscara se ajusta sola. Cada vez que se añade o modifica una entrada con setfacl -m, la máscara se recalcula automáticamente para abarcar todos los permisos concedidos. Por eso, al dar a maria lectura y escritura sobre un archivo cuyo grupo solo tenía lectura, la máscara sube a rw- y maria conserva su acceso completo:

setfacl -m u:maria:rw balance.xlsx
getfacl balance.xlsx
user::rw-
user:maria:rw-
group::r--
mask::rw-
other::---

2. Bajar la máscara restringe de golpe. Como la máscara acota a la vez a todas las entradas con nombre y al grupo propietario, basta con reducirla para recortar todos esos accesos en una sola orden, sin editar las entradas una a una. Al fijarla en r--, la escritura de maria deja de surtir efecto y getfacl lo señala con el comentario #effective:r--:

setfacl -m m::r balance.xlsx
getfacl balance.xlsx
user::rw-
user:maria:rw-  #effective:r--
group::r--
mask::r--
other::---

La entrada de maria sigue diciendo rw-, pero su permiso real es solo de lectura, porque manda la máscara. El propietario (user::rw-) no se ve afectado, porque la máscara no lo limita.

3. Mantener una máscara restrictiva con -n. Si se fija una máscara baja y luego se añade otra entrada con setfacl -m, el recálculo automático volvería a subirla y anularía la restricción. La opción -n evita ese recálculo, de modo que la máscara conserva el valor fijado y la nueva entrada queda limitada por ella:

setfacl -n -m u:ana:rw balance.xlsx   # añade a ana sin recalcular la máscara

4. La trampa de chmod (clave en bastionado). Como se advirtió en la sección 5.1, cuando un archivo tiene ACL, un chmod g-w o chmod g=r no modifica el grupo propietario, sino la máscara. Un administrador que vea rw- en el grupo con ls -l y lo «corrija» con chmod g-w estará recortando a la vez a maria, al grupo propietario y a cualquier grupo con nombre, casi siempre sin pretenderlo. En archivos con +, ajuste los permisos con setfacl y verifique siempre el resultado con getfacl.


5.4. ACL por defecto en directorios

Una ACL por defecto, definida con la opción -d, no controla el acceso al directorio, sino que actúa como plantilla para los archivos y subdirectorios que se creen en él:

# Acceso al propio directorio
sudo setfacl -m u:maria:rwx /srv/compartido
# Plantilla heredada por los nuevos archivos y subdirectorios
sudo setfacl -d -m u:maria:rwX /srv/compartido
getfacl /srv/compartido

La X mayúscula es la ejecución condicional que se explicó en la sección 2.1.2. En la plantilla concede el permiso de ejecución solo a los subdirectorios y a los archivos que ya sean ejecutables, pero no a los archivos de datos normales, evitando marcarlos como ejecutables de forma indebida.

Cuando un directorio tiene una ACL por defecto, la umask del proceso no se aplica a los archivos creados en él. Los permisos resultantes se obtienen combinando el modo solicitado por el programa con la ACL por defecto. Es un mecanismo útil para imponer permisos de colaboración con independencia de la máscara de cada usuario.


5.5. Copias de seguridad de ACL

No todas las herramientas conservan las ACL. cp -a, rsync -A y tar --acls las preservan, pero un cp sin opciones o una copia a un sistema de archivos sin soporte las pierde:

Parámetro Función

cp -a

Copia en modo archivo, conservando permisos, propietarios, marcas temporales y atributos extendidos, entre ellos las ACL

rsync -A

Conserva las ACL. Suele combinarse con -a, que conserva el resto de metadatos

tar --acls

Guarda las ACL en el paquete y las restaura al extraerlo. Debe indicarse en ambas operaciones

Antes de cambios masivos conviene guardar una copia restaurable:

getfacl -R -p /srv/compartido > acl_compartido.txt
sudo setfacl --restore=acl_compartido.txt
Parámetro Función

getfacl -R

Recorre el árbol de forma recursiva

getfacl -p

Conserva la / inicial de las rutas

setfacl --restore=archivo

Aplica a cada archivo de la copia las ACL que figuran en ella

Sin -p, getfacl guarda rutas relativas y setfacl --restore las interpretaría respecto al directorio actual, por lo que la restauración solo funcionaría desde /.

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 9. Control de acceso granular con ACL.

6. Atributos de inodo: chattr y lsattr

Los sistemas de archivos ext4, XFS y Btrfs permiten marcar cada inodo con atributos que el núcleo aplica antes de evaluar los permisos. No deben confundirse con los atributos extendidos, o xattr, que son pares clave-valor donde se almacenan las ACL, las capacidades o las etiquetas SELinux.


6.1. Atributos relevantes para el bastionado

Atributo Letra Efecto Uso en bastionado

Inmutable

i

No se puede modificar, borrar, renombrar, enlazar ni cambiar sus metadatos, ni siquiera siendo root

Proteger archivos de configuración estáticos frente a errores y frente a atacantes sin privilegios

Solo adición

a

Solo se puede abrir en modo de adición. No se puede sobrescribir, truncar ni borrar

Registros locales que no rota ninguna herramienta

sudo chattr +i archivo        # Activa el atributo inmutable
sudo chattr -i archivo        # Lo desactiva
lsattr archivo                # Muestra los atributos
----i---------e------- archivo

La e indica que el archivo usa extents, el formato de asignación de bloques de ext4, y no tiene relevancia para la seguridad.

Alcance real de la inmutabilidad

Activar o desactivar i y a exige la capacidad CAP_LINUX_IMMUTABLE, que root posee. Un atacante con privilegios de root puede, por tanto, ejecutar chattr -i y modificar el archivo. El atributo protege frente a errores administrativos, procesos de servicio sin privilegios y herramientas automatizadas que no lo tienen en cuenta, pero no es una barrera frente a un compromiso total del sistema.

Efectos secundarios operativos

  • chattr +i /etc/shadow o /etc/passwd impide que passwd, useradd o las actualizaciones de paquetes funcionen, y los errores resultantes no siempre son evidentes.

  • chattr +a sobre un archivo de registro rotado por logrotate hace fallar la rotación, ya que esta necesita renombrar o truncar el archivo, y el disco puede acabar llenándose. La protección eficaz de los registros frente a su borrado es su envío en tiempo real a un servidor centralizado.

  • Las actualizaciones de paquetes fallan al intentar sustituir un archivo inmutable, lo que puede dejar el sistema a medio actualizar.


6.2. Los atributos como técnica de persistencia

El atributo inmutable también lo utilizan los atacantes. Es habitual que el malware de minería o las botnets marquen como inmutables sus tareas de cron, su archivo authorized_keys o sus binarios para dificultar la limpieza, una variante de la técnica T1222.002 de MITRE ATT&CK. Por ello, en la respuesta ante incidentes es necesario buscar archivos con atributos inesperados:

sudo lsattr -R /etc /root /home /var/spool/cron 2>/dev/null | grep -E '^[^ ]*[ia][^ ]* '

La expresión regular de grep selecciona las líneas cuyo primer campo, el de los atributos, contiene una i o una a:

Elemento Función

lsattr -R

Recorre los directorios de forma recursiva

^ y [^ ]*

Recorren desde el inicio de la línea cualquier secuencia de caracteres distintos del espacio, de modo que la búsqueda no sale del primer campo ni coincide con las letras del nombre del archivo

[ia]

Exige una i o una a dentro del campo

[^ ]*

Completa el campo hasta el espacio que lo separa del nombre del archivo

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 10. Atributos inmutable y de solo adición.

7. Opciones de montaje seguras

La defensa en profundidad exige no depender exclusivamente de que los permisos de cada archivo sean correctos. Si un atacante consigue depositar un binario SetUID o un exploit en un directorio de escritura pública, las opciones de montaje del sistema de archivos pueden neutralizarlo.


7.1. Opciones de endurecimiento

Opción Efecto Amenaza que mitiga

nodev

El núcleo no interpreta los archivos de dispositivo de la partición

Creación de nodos de dispositivo con mknod para acceder directamente a discos o memoria

nosuid

Se ignoran los bits SetUID y SetGID y las capacidades de archivos

Escalada mediante binarios SetUID depositados en particiones de usuario o temporales

noexec

El núcleo rechaza ejecutar binarios de la partición

Ejecución directa de exploits y malware descargados en directorios temporales

Límites de noexec

noexec impide que el núcleo ejecute un archivo de la partición, pero no que un intérprete lea un script y lo ejecute. bash /tmp/script.sh o python3 /tmp/exploit.py siguen funcionando. Es una medida que eleva el coste del ataque, no una barrera absoluta. Además, algunos instaladores y scripts de mantenimiento de paquetes ejecutan archivos en /tmp, por lo que debe probarse antes de aplicarla en producción.


7.2. Configuración recomendada por el CIS Benchmark

El CIS Benchmark recomienda ubicar /tmp, /var/tmp, /dev/shm y /home en sistemas de archivos independientes con las siguientes opciones:

Punto de montaje nodev nosuid noexec

/tmp

Sí

Sí

Sí

/var/tmp

Sí

Sí

Sí

/dev/shm

Sí

Sí

Sí

/home

Sí

Sí

No

Ejemplo en /etc/fstab:

/dev/mapper/vg0-tmp    /tmp        ext4    defaults,nodev,nosuid,noexec    0  2
/dev/mapper/vg0-vartmp /var/tmp    ext4    defaults,nodev,nosuid,noexec    0  2
tmpfs                  /dev/shm    tmpfs   defaults,nodev,nosuid,noexec    0  0
/dev/mapper/vg0-home   /home       ext4    defaults,nodev,nosuid           0  2
Campo Descripción Ejemplo Explicación técnica

1

Origen

/dev/mapper/vg0-tmp

Dispositivo, volumen lógico o tipo de sistema virtual, como tmpfs

2

Punto de montaje

/tmp

Directorio en el que se monta el sistema de archivos

3

Tipo

ext4

Tipo de sistema de archivos

4

Opciones

defaults,nodev,nosuid,noexec

defaults aplica las opciones predeterminadas, como rw, a las que se añaden las de endurecimiento

5

Copia con dump

0

Indica si dump debe incluir el sistema de archivos en sus copias. 0 lo excluye

6

Orden de fsck

2

Orden en que fsck comprueba el sistema de archivos al arrancar: 1 para la raíz, 2 para el resto y 0 para no comprobarlo

En Debian 13 y otras distribuciones recientes, /tmp se monta como tmpfs mediante la unidad tmp.mount de systemd, que ya incluye nosuid y nodev. Para añadir noexec se crea una configuración complementaria con sudo systemctl edit tmp.mount que redefina la directiva Options=.

Las opciones se aplican sin reiniciar remontando el sistema de archivos, y se verifican con findmnt:

sudo mount -o remount,nodev,nosuid,noexec /dev/shm
for m in /tmp /var/tmp /dev/shm /home; do
    findmnt -no TARGET,OPTIONS "$m" || echo "$m: no es un punto de montaje independiente"
done
Parámetro Función

mount -o remount,opciones

Aplica las nuevas opciones a un sistema de archivos ya montado sin desmontarlo. El cambio no persiste tras un reinicio si no se refleja también en /etc/fstab

findmnt -n

Suprime la línea de encabezado

findmnt -o columnas

Selecciona las columnas que se muestran, en este caso TARGET, el punto de montaje, y OPTIONS, sus opciones

El operador || ejecuta el echo solo cuando findmnt falla.

findmnt solo admite un punto de montaje por invocación. Con varias rutas interpreta las dos primeras como origen y destino y no muestra nada, por lo que se recorre la lista con un bucle. Si una ruta no es un punto de montaje independiente, como ocurre con /tmp o /home en una instalación con una sola partición, findmnt no devuelve nada y no es posible aplicarle opciones propias.

Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 11. Opciones de montaje y sus límites.

8. Guía consolidada de bastionado y lista de verificación

Como síntesis de los capítulos anteriores, esta sección reúne los riesgos, las medidas de corrección y los comandos de verificación que deben aplicarse de forma sistemática.


8.1. Matriz de riesgos y medidas técnicas

Vector de amenaza Impacto potencial Medida de bastionado Verificación técnica

Permisos 777 como solución rápida

Sustitución de archivos y webshells persistentes

Grupos dedicados y permisos 750/640

find / -xdev -type f -perm -0002

Binarios SetUID innecesarios

Escalada local a root

Retirar el bit y mantener una línea base

find / -xdev -type f -perm /6000

Capacidades en intérpretes

Escalada local a root invisible para ls -l

Revisar y retirar con setcap -r

getcap -r / 2>/dev/null

Directorios compartidos sin Sticky Bit

Borrado y suplantación de archivos ajenos

chmod +t

find / -xdev -type d -perm -0002 ! -perm -1000

Máscara permisiva

Archivos nuevos legibles por todos

UMASK 027 y UMask= en servicios

umask en una sesión nueva

Archivos huérfanos

Apropiación al reutilizar un UID

Reasignar o eliminar antes de crear cuentas

find / -xdev \( -nouser -o -nogroup \)

ACL olvidadas

Accesos extraordinarios no documentados

Revisión periódica con getfacl

getfacl -R -s -p /srv

Particiones temporales ejecutables

Ejecución de exploits descargados

nodev,nosuid,noexec

findmnt -no OPTIONS /tmp

En la verificación de las ACL, getfacl -s omite los archivos que solo tienen los permisos básicos, de modo que la salida contiene únicamente los que tienen una ACL extendida. Las opciones -nouser y -nogroup de find se explican en la lista de verificación siguiente.


8.2. Lista de verificación (CIS Benchmark)

  1. Permisos de los archivos de cuentas: el CIS Benchmark exige los siguientes valores, o más restrictivos, en los archivos de cuentas y sus copias de seguridad automáticas, terminadas en -:

    Archivo Propietario Grupo Modo máximo

    /etc/passwd, /etc/passwd-

    root

    root

    644

    /etc/group, /etc/group-

    root

    root

    644

    /etc/shadow, /etc/shadow-

    root

    shadow o root

    640

    /etc/gshadow, /etc/gshadow-

    root

    shadow o root

    640

    /etc/shells

    root

    root

    644

    /etc/security/opasswd

    root

    root

    600

    sudo stat -c '%a %U:%G %n' /etc/{passwd,group,shadow,gshadow}{,-} /etc/shells /etc/security/opasswd 2>/dev/null

    Las llaves son una expansión de la shell: {passwd,group,shadow,gshadow}{,-} genera las ocho rutas combinando cada nombre con una cadena vacía y con -. 2>/dev/null oculta el error de los archivos que no existan en el sistema.

  2. Permisos de configuraciones de servicios críticos:

    Archivo o directorio Propietario Modo máximo

    /etc/crontab

    root:root

    600

    /etc/cron.hourly, cron.daily, cron.weekly, cron.monthly, cron.d

    root:root

    700

    /etc/ssh/sshd_config y /etc/ssh/sshd_config.d/*.conf

    root:root

    600

    Claves privadas de host /etc/ssh/ssh_host_*_key

    root:root

    600

    /boot/grub/grub.cfg

    root:root

    600

    /etc/sudoers y archivos de /etc/sudoers.d/

    root:root

    440

  3. Ningún archivo regular con escritura para todos:

    sudo find / -xdev -type f -perm -0002 -ls

    La acción -ls muestra cada resultado con sus metadatos, en un formato similar al de ls -dils.

  4. Todos los directorios con escritura para todos tienen Sticky Bit:

    sudo find / -xdev -type d -perm -0002 ! -perm -1000 -ls
  5. Ningún archivo sin propietario ni grupo válido:

    sudo find / -xdev \( -nouser -o -nogroup \) -ls
    Parámetro Función

    -nouser

    Selecciona los archivos cuyo UID no corresponde a ninguna cuenta existente

    -nogroup

    Selecciona los archivos cuyo GID no corresponde a ningún grupo existente

    -o

    Combina las dos condiciones con un O lógico

    \( y \)

    Agrupan la expresión. Se escapan para que no los interprete la shell

  6. Inventario de binarios SetUID, SetGID y con capacidades comparado con una línea base:

    sudo find / -xdev -type f -perm /6000 -printf '%m %u:%g %p\n' 2>/dev/null | sort > suid_actual.txt
    sudo getcap -r / 2>/dev/null | sort > caps_actual.txt
    Parámetro Función

    -printf formato

    Define el formato de salida de cada resultado

    %m

    Modo en octal

    %u, %g

    Usuario y grupo propietarios

    %p

    Ruta del archivo

    \n

    Salto de línea

    sort ordena el inventario para que pueda compararse línea a línea con la línea base. Cualquier entrada que no figure en la línea base generada tras la instalación debe investigarse.

  7. Archivos de inicio de los usuarios sin escritura para el grupo ni para otros:

    sudo find /home -maxdepth 2 -name '.*' -type f -perm /022 -ls
    Parámetro Función

    -maxdepth 2

    Limita la búsqueda a /home y al primer nivel de cada directorio personal

    -name '.*'

    Selecciona los archivos ocultos. El patrón va entre comillas para que no lo expanda la shell

    -perm /022

    Selecciona los que conceden escritura al grupo o a otros

  8. Máscara por defecto 027 o más restrictiva en /etc/login.defs, /etc/profile.d/ y /etc/bash.bashrc, según la sección 3.3.

  9. Opciones de montaje de /tmp, /var/tmp, /dev/shm y /home según la sección 7.2.


8.3. Retirada persistente del bit SetUID

Ejecutar chmod u-s sobre un binario del sistema no es una medida duradera. La siguiente actualización del paquete restaurará el bit. En Debian y Ubuntu, la retirada debe registrarse con dpkg-statoverride, que hace que dpkg respete el modo indicado en futuras actualizaciones:

# Ejemplo: retirar el bit SetUID de un binario que no utilizan los usuarios
sudo dpkg-statoverride --update --add root root 0755 /usr/bin/binario_prescindible
dpkg-statoverride --list
Parámetro Función

--add usuario grupo modo ruta

Registra en la base de datos de dpkg el propietario, el grupo y el modo que debe tener la ruta

--update

Aplica de inmediato el modo registrado al archivo. Sin esta opción, el cambio solo se haría efectivo en la siguiente instalación del paquete

--list

Muestra las sustituciones registradas

--remove ruta

Elimina la sustitución registrada para esa ruta

Antes de retirar el bit, compruebe con dpkg -S a qué paquete pertenece el binario y documente el motivo, ya que algunas funciones, como su, passwd o sudo, dejarán de estar disponibles para los usuarios sin privilegios.


8.4. Auditoría automatizada

Las comprobaciones anteriores deben ejecutarse periódicamente. El siguiente script compara el inventario actual de binarios privilegiados con una línea base y registra cualquier diferencia en el diario del sistema:

sudo install -d -m 700 /var/lib/auditoria-permisos

sudo tee /etc/cron.daily/auditoria-permisos > /dev/null << 'EOF'
#!/bin/bash
# Auditoría diaria de binarios privilegiados y escritura universal (CIS Benchmark)
set -o pipefail
DIR=/var/lib/auditoria-permisos
TAG=auditoria-permisos

inventario() {
    find / -xdev -type f -perm /6000 -printf '%m %u:%g %p\n' 2>/dev/null
    getcap -r / 2>/dev/null
}

inventario | LC_ALL=C sort > "$DIR/actual.txt"
[ -f "$DIR/base.txt" ] || cp "$DIR/actual.txt" "$DIR/base.txt"

NUEVOS=$(LC_ALL=C comm -13 "$DIR/base.txt" "$DIR/actual.txt")
[ -n "$NUEVOS" ] && logger -t "$TAG" -p auth.alert "ALERTA: nuevos binarios privilegiados: $NUEVOS"

ESCRITURA=$(find / -xdev -type f -perm -0002 2>/dev/null)
[ -n "$ESCRITURA" ] && logger -t "$TAG" -p auth.warning "Archivos con escritura universal: $ESCRITURA"

exit 0
EOF
sudo chmod 700 /etc/cron.daily/auditoria-permisos
sudo /etc/cron.daily/auditoria-permisos
journalctl -t auditoria-permisos

Las órdenes y parámetros del script tienen la siguiente función:

Parámetro Función

install -d -m 700

Crea el directorio, por la opción -d, y le asigna con -m el modo indicado en una sola orden

<< 'EOF'

Envía a tee las líneas que siguen hasta EOF. Las comillas impiden que la shell expanda $DIR y el resto de variables al escribir el archivo, de modo que se evalúen cuando se ejecute el script

set -o pipefail

Hace que una tubería devuelva el código de error de la última orden que falle, y no solo el de la última orden de la tubería

LC_ALL=C

Fuerza la ordenación byte a byte, de modo que sort y comm aplican el mismo criterio con independencia del idioma configurado. comm exige que sus dos entradas estén ordenadas igual

[ -f ruta ]

Comprueba si existe el archivo. Si no existe, se ejecuta cp, por lo que la primera ejecución genera la línea base

comm -13

Compara dos archivos ordenados y suprime la columna 1, con las líneas exclusivas de la base, y la 3, con las comunes. Solo quedan las líneas que aparecen en el inventario actual y no en la base

[ -n "$VARIABLE" ]

Es cierto si la variable no está vacía, y && envía el mensaje solo en ese caso

logger -t etiqueta

Etiqueta el mensaje con un identificador

logger -p facilidad.nivel

Fija la prioridad del mensaje: auth.alert para los binarios privilegiados nuevos y auth.warning para los archivos con escritura universal

exit 0

Evita que el script termine con error cuando la última comprobación no encuentra nada, ya que en ese caso [ -n …​ ] devuelve un código distinto de cero

journalctl -t etiqueta

Filtra el diario por el identificador asignado con logger -t

El nombre del archivo no lleva extensión porque run-parts, que ejecuta los scripts de /etc/cron.daily, ignora los nombres que contienen un punto.

La línea base debe regenerarse de forma controlada tras cada actualización que añada binarios privilegiados legítimos, y conviene conservar una copia fuera del sistema auditado, ya que un atacante con privilegios de root podría modificarla.

Una vez estudiadas todas las medidas de esta guía, puede consolidar sus competencias completando el Ejercicio 12. Auditoría CIS de permisos.

9. Ejercicios prácticos

Los ejercicios están diseñados para realizarse en orden sobre una máquina virtual Ubuntu o Debian de pruebas con sistema de archivos ext4, ya que algunos reutilizan usuarios, grupos y archivos creados en ejercicios anteriores. Los atributos de inodo no funcionan en contenedores ni en sistemas de archivos superpuestos. El último ejercicio deja el sistema en su estado original.

No realice estos ejercicios en un servidor en producción. Algunos pasos crean de forma deliberada configuraciones inseguras, como un binario SetUID de root.

Nº Ejercicio Competencia que desarrolla Dificultad

1

Preparación del laboratorio e inspección de metadatos

Interpretar ls -l y stat

Baja

2

Semántica de los permisos en directorios

Comprobar el efecto real de r, w y x en directorios

Baja

3

Gestión de permisos con chmod

Aplicar permisos en modo simbólico, octal y con X

Media

4

Propiedad con chown y chgrp

Comprender las restricciones del núcleo sobre la propiedad

Media

5

Cálculo y configuración de la umask

Predecir y fijar los permisos de los archivos nuevos

Media

6

Directorio colaborativo con SetGID

Implementar la herencia de grupo y conocer sus límites

Media

7

Protección contra el borrado con Sticky Bit

Proteger directorios compartidos

Baja

8

SetUID, capacidades y escalada de privilegios

Reconocer y auditar mecanismos de elevación

Alta

9

Control de acceso granular con ACL

Gestionar entradas con nombre, máscara y ACL por defecto

Alta

10

Atributos inmutable y de solo adición

Usar y detectar atributos de inodo

Media

11

Opciones de montaje y sus límites

Verificar el efecto de nosuid y noexec

Media

12

Auditoría CIS de permisos

Detectar y corregir desviaciones de forma automatizada

Alta

13

Limpieza del entorno

Revertir los cambios realizados durante la práctica

Baja

9.1. Ejercicio 1. Preparación del laboratorio e inspección de metadatos

Todo análisis de permisos empieza por leer correctamente los metadatos de los inodos.

Realice los siguientes pasos:

  1. Cree un usuario de pruebas llamado usuario_lab con directorio personal y shell bash, que se utilizará para comprobar los accesos desde una cuenta distinta de la suya.

  2. Cree el directorio de trabajo /srv/lab_permisos, asígnelo a su usuario y concédale permisos 755, de modo que usuario_lab pueda atravesarlo.

  3. Cree el archivo documento.txt y el directorio departamento. Identifique con ls -l el tipo de cada uno y explique los permisos que han recibido.

  4. Obtenga con stat el modo en octal, el propietario y las tres marcas temporales de documento.txt. Ejecute chmod 600 documento.txt y compruebe qué marca temporal ha cambiado.

  5. Muestre en una sola línea por archivo el modo octal, el propietario y el nombre de /etc/passwd, /etc/shadow y /usr/bin/passwd.


9.2. Ejercicio 2. Semántica de los permisos en directorios

Realice los siguientes pasos:

  1. Cree el directorio privado con un archivo secreto.txt en su interior. Asigne al directorio el modo 711 y compruebe que usuario_lab no puede listarlo pero sí leer el archivo si conoce su nombre y el archivo tiene permisos 644.

  2. Cambie el modo del directorio a 744 y compruebe qué muestra ls -l ejecutado por usuario_lab.

  3. Cree el directorio buzon con modo 777 y, dentro, el archivo contrato.txt con modo 600, ambos de su propiedad. Compruebe que usuario_lab no puede leer el contrato pero sí borrarlo.


9.3. Ejercicio 3. Gestión de permisos con chmod

Realice los siguientes pasos:

  1. Aplique a documento.txt, en modo simbólico, lectura y escritura para el propietario, lectura para el grupo y ningún permiso para otros. Repita la operación en modo octal y verifíquela con stat.

  2. Cree el script despliegue.sh y concédale permiso de ejecución solo al propietario.

  3. Cree el árbol proyecto/ con dos archivos de texto en distintos niveles y copie dentro despliegue.sh. Aplique de forma recursiva u=rwX,g=rX,o= y compruebe que los directorios y el script quedan con 750 y los archivos de texto con 640.

  4. Cree el directorio equipo con modo 2770. Aplíquele chmod 0770 y compruebe si conserva el bit SetGID. Retírelo después de forma explícita.


9.4. Ejercicio 4. Propiedad con chown y chgrp

Realice los siguientes pasos:

  1. Intente asignar documento.txt a usuario_lab sin sudo y analice el error.

  2. Sin sudo, cambie el grupo de documento.txt a uno de sus grupos secundarios, como sudo o adm. Intente después asignarle el grupo usuario_lab, del que no es miembro.

  3. Copie /usr/bin/id como id_prueba en el laboratorio, asígnele el propietario root y el modo 4755. Cambie después su propietario a usuario_lab y compruebe qué ha ocurrido con el bit SetUID.

  4. Devuelva a documento.txt el propietario y el grupo de su directorio personal usando --reference. Tenga en cuenta que la shell no expande ~ tras el signo = de una opción.


9.5. Ejercicio 5. Cálculo y configuración de la umask

Realice los siguientes pasos:

  1. Consulte la máscara de su sesión en formato octal y simbólico, y los valores de UMASK, USERGROUPS_ENAB y HOME_MODE en /etc/login.defs.

  2. Calcule los permisos de un archivo y de un directorio nuevos con las máscaras 027 y 033. Compruebe sus cálculos en una subshell para no alterar la sesión.

  3. Compruebe la máscara efectiva de usuario_lab en una sesión de inicio y explique el valor obtenido a partir de USERGROUPS_ENAB.

  4. Configure de forma persistente umask 027 para las shells interactivas mediante un archivo en /etc/profile.d/ y verifique el resultado en una sesión nueva de usuario_lab.


9.6. Ejercicio 6. Directorio colaborativo con SetGID

Realice los siguientes pasos:

  1. Cree el grupo desarrolladores y añada a él su usuario y usuario_lab.

  2. Cree /srv/compartido_dev con propietario root, grupo desarrolladores y modo 2770.

  3. Cree un archivo en el directorio como usuario_lab y compruebe que su grupo es desarrolladores. Intente listar el directorio con su propio usuario y explique por qué falla en la sesión actual, mientras que sudo -u sí reconoce la nueva pertenencia al grupo.

  4. Como usuario_lab, cree un archivo en su directorio personal y muévalo a la carpeta compartida; cree otro y cópielo. Compare el grupo de ambos.


9.7. Ejercicio 7. Protección contra el borrado con Sticky Bit

Realice los siguientes pasos:

  1. Cree /srv/buzon_publico con modo 777 y, como su usuario, cree en él archivo_usuario.txt. Compruebe que usuario_lab puede borrarlo.

  2. Active el Sticky Bit, cree de nuevo el archivo y repita el intento de borrado como usuario_lab.

  3. Compruebe que /tmp y /var/tmp tienen Sticky Bit.


9.8. Ejercicio 8. SetUID, capacidades y escalada de privilegios

Realice los siguientes pasos:

  1. Obtenga el inventario de binarios SetUID y SetGID del sistema e identifique a qué paquete pertenecen tres de ellos.

  2. Copie /bin/bash como bash_suid en el laboratorio, asígnelo a root con modo 4755 y ejecútelo como usuario_lab, primero sin opciones y después con -p. Compare la salida de id en ambos casos.

  3. Compruebe que el binario aparece en el inventario de SetUID y elimínelo de inmediato.

  4. Obtenga el inventario de archivos con capacidades del sistema. Copie /usr/bin/python3 en el laboratorio, asígnele cap_setuid=ep, compruebe que ls -l no muestra nada anómalo y demuestre la escalada como usuario_lab. Elimine la copia.


9.9. Ejercicio 9. Control de acceso granular con ACL

Este ejercicio consolida el uso de las ACL de la sección 5. Trabaje dentro del directorio /srv/lab_permisos creado en el Ejercicio 1 y utilice la cuenta usuario_lab, que puede atravesar ese directorio pero no es propietaria ni miembro del grupo de los archivos que usted cree en él.

Realice los siguientes pasos:

  1. Instale el paquete acl si no está presente. Dentro de /srv/lab_permisos, cree el archivo informe_financiero.txt con modo 640 y compruebe, desde la cuenta de usuario_lab, que no puede leerlo, ya que no es el propietario ni pertenece a su grupo.

  2. Añada una entrada de ACL que conceda a usuario_lab permiso de lectura y escritura sobre informe_financiero.txt. Compruebe que en ls -l aparece el signo + y verifique, desde la cuenta de usuario_lab, que ahora sí puede leer el archivo.

  3. Ejecute chmod g-w informe_financiero.txt y observe con getfacl cómo ha cambiado la máscara y, con ella, el permiso efectivo de usuario_lab, que pasa a aparecer como #effective:r--. Es la trampa descrita en la sección 5.3.1: chmod no toca el grupo, sino la máscara. Compruebe desde la cuenta de usuario_lab que ya no puede escribir en el archivo.

  4. Cree el directorio compartido_acl. Asígnele una ACL de acceso que conceda a usuario_lab lectura, escritura y ejecución (la ejecución es imprescindible para poder entrar en el directorio) y una ACL por defecto, que actúa como plantilla, con lectura y escritura. Cree después un archivo dentro del directorio y compruebe con getfacl que ha heredado la entrada de usuario_lab.

  5. Guarde de forma recursiva las ACL de /srv/lab_permisos en un archivo de su directorio personal. Elimine a continuación toda la ACL de informe_financiero.txt con setfacl -b, compruebe que desaparece el +, y restaure las ACL a partir de la copia guardada, verificando con getfacl que el informe recupera la entrada de usuario_lab.


9.10. Ejercicio 10. Atributos inmutable y de solo adición

Realice los siguientes pasos:

  1. Cree firewall.conf, actívele el atributo inmutable y compruebe con lsattr que está presente.

  2. Intente, con sudo, modificar, renombrar, cambiar los permisos y borrar el archivo.

  3. Cree auditoria.log con atributo de solo adición y compruebe que admite >> pero no >. Retire después ambos atributos y elimine los archivos.

  4. Busque en /etc, /root, /home y /var/spool/cron archivos con los atributos i o a y razone si su presencia sería legítima.


9.11. Ejercicio 11. Opciones de montaje y sus límites

Realice los siguientes pasos:

  1. Consulte las opciones de montaje actuales de /, /tmp, /var/tmp, /dev/shm y /home, e indique qué controles del CIS Benchmark se cumplen.

  2. Monte un sistema de archivos tmpfs de prueba en /mnt/lab_noexec con las opciones nodev,nosuid,noexec. Copie en él despliegue.sh e intente ejecutarlo directamente y a través de bash.

  3. Remonte el sistema de archivos sin noexec pero con nosuid, copie en él /bin/bash con modo 4755 y propietario root, y compruebe que bash -p no obtiene privilegios.


9.12. Ejercicio 12. Auditoría CIS de permisos

Realice los siguientes pasos:

  1. Verifique los permisos y propietarios de los archivos de cuentas y de configuración de la sección 8.2 e indique si alguno incumple el CIS Benchmark.

  2. Cree en el laboratorio un archivo con modo 666 y un directorio con modo 777 sin Sticky Bit. Localícelos con las búsquedas de la lista de verificación y corríjalos.

  3. Cree el usuario temporal_lab, genere un archivo suyo en el laboratorio y elimine la cuenta sin borrar el archivo. Localice el archivo huérfano y reasígnelo a root.

  4. Instale el script de auditoría de la sección 8.4 y ejecútelo para generar la línea base. Copie después /usr/bin/id en /srv/lab_permisos con SetUID de root, ejecute de nuevo el script y compruebe la alerta en el diario. Elimine el binario.


9.13. Ejercicio 13. Limpieza del entorno

Para terminar, revierta los cambios realizados durante la práctica.

Realice los siguientes pasos:

  1. Desmonte el sistema de archivos de prueba y elimine su punto de montaje.

  2. Retire cualquier atributo de inodo que haya quedado en el laboratorio y elimine los directorios /srv/lab_permisos, /srv/compartido_dev y /srv/buzon_publico, así como la copia ~/acl_lab.txt.

  3. Elimine el usuario usuario_lab con su directorio personal y el grupo desarrolladores.

  4. Decida si conserva la máscara configurada en /etc/profile.d/50-umask.sh y el script de auditoría /etc/cron.daily/auditoria-permisos. Si no los conserva, elimínelos junto con /var/lib/auditoria-permisos.

  5. Compruebe que no queda ningún archivo huérfano generado durante la práctica.

Con la realización de estos ejercicios se consolidan los procedimientos esenciales de configuración, auditoría y corrección de permisos, máscaras, bits especiales, capacidades, listas de control de acceso, atributos de inodo y opciones de montaje en sistemas Linux, conforme a las directrices del CIS Benchmark.

10. 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.