Linux y seguridad de sistemas · Lección 06

Usuarios, grupos, permisos y sudo

Casi toda la seguridad de un sistema Unix se reduce a una pregunta repetida millones de veces al día: este proceso, ¿puede tocar este archivo? La respuesta sale de tres números y un puñado de bits especiales. Entenderlos con precisión es la diferencia entre un servidor donde un fallo en la aplicación se queda en la aplicación, y uno donde ese mismo fallo entrega la máquina entera. Esta lección fija el modelo de permisos, el bit SUID que tanta gente usa sin entender, y el mínimo privilegio aplicado al sistema de ficheros. Al final vas a levantar un laboratorio con un permiso de más y a corregirlo.

Al terminar sabrás

  • Leer una línea de ls -l y decir exactamente quién puede leer, escribir y ejecutar.
  • Distinguir permisos de usuario, grupo y otros, y traducir entre rwx y octal.
  • Explicar qué hace el bit SUID y por qué un binario SUID mal elegido es una vía de escalada.
  • Entender sudo como delegación acotada, no como "modo dios".
  • Auditar un sistema en busca de binarios SUID y directorios de escritura peligrosos.
  • Aplicar mínimo privilegio a permisos: conceder lo justo, no 777 "por si acaso".
Antes de empezar · qué sabes ya

Tres preguntas rápidas. No puntúan para completar la lección: ajustan la profundidad de lo que viene.

¿Qué significa el bit SUID en un ejecutable?

El SUID cambia la identidad efectiva del proceso a la del dueño del binario. Por eso passwd, propiedad de root, puede editar /etc/shadow aunque lo lance un usuario normal.

chmod 777 sobre un archivo, ¿qué concede?

Cada 7 es rwx. Tres sietes = todos los permisos para dueño, grupo y otros. Es la forma más rápida de convertir un archivo en un problema.

¿Quién puede leer un archivo con permisos 600 propiedad de root?

600 es rw-------: lectura y escritura para el dueño, nada para grupo ni otros. Es el permiso correcto para un secreto.

0 / 3

1Por qué importa

Cuando una aplicación web sufre una inyección de comandos, lo que el atacante consigue no es "acceso al servidor": consigue ejecutar como el usuario que corre la aplicación. Si ese usuario es www-data y está bien confinado, el daño se limita a lo que www-data puede tocar. Si es root, el daño es total. La diferencia entre esos dos mundos es enteramente una cuestión de usuarios y permisos, y se decide antes de que exista ningún atacante, al desplegar.

Este es el primer eslabón de la defensa en profundidad aplicada a un sistema real. Una aplicación siempre acabará teniendo un fallo; la pregunta de diseño es qué alcance tiene ese fallo cuando ocurra. Los permisos de archivo, el usuario de ejecución y la configuración de sudo son las tres palancas que fijan ese alcance. Entenderlas mal no da un error visible: da un servidor que funciona perfectamente hasta el día en que un fallo pequeño se convierte en un incidente grande.

El objetivo real

No estás memorizando comandos de chmod. Estás aprendiendo a responder, para cualquier proceso de tu sistema: si esto se compromete, ¿hasta dónde llega? Esa respuesta es la que convierte un permiso en una decisión de seguridad.

2Conceptos

Vocabulario mínimo
  • Usuario / grupo / otros: los tres ámbitos sobre los que se definen los permisos.
  • rwx: leer, escribir, ejecutar. Sobre un directorio, x es "poder entrar".
  • Octal: r=4, w=2, x=1; se suman por ámbito.
  • Dueño: usuario y grupo a los que pertenece el archivo (chown).
  • SUID: el proceso adopta la identidad del dueño del binario al ejecutarse.
  • SGID: lo mismo con el grupo; sobre un directorio, fija el grupo de lo que se cree dentro.
  • sticky bit: en un directorio compartido, cada quien solo borra lo suyo (así es /tmp).
  • sudo: ejecutar un comando concreto como otro usuario, según una política.

3Explicación profunda

Cada archivo lleva nueve bits de permiso agrupados en tres tríadas: dueño, grupo y otros. ls -l los muestra como -rwxr-x---. El primer carácter es el tipo (- archivo, d directorio, l enlace). Los nueve siguientes se leen de tres en tres: aquí el dueño tiene rwx, el grupo r-x y los demás nada. En octal eso es 750.

La regla que la gente olvida es que el kernel comprueba una sola tríada, la más específica que aplique. Si eres el dueño, se usan tus bits y ya está, aunque el grupo tuviera más permisos. No hay suma: ser dueño de un archivo 400 y estar además en un grupo con rw no te da escritura, porque como dueño solo te miran tus cuatro. Esto sorprende y es fuente de bugs de permisos reales.

Sobre directorios, los bits cambian de sentido y esto importa para la seguridad. r es listar el contenido, w es crear y borrar entradas dentro, y x es atravesarlo para llegar a lo que hay debajo. Un directorio con w para otros es peligroso: aunque un archivo dentro sea de solo lectura, cualquiera puede borrarlo y poner otro en su lugar, porque borrar es un permiso del directorio, no del archivo.

El bit SUID es donde los permisos se cruzan con la identidad. Normalmente un proceso corre con la identidad de quien lo lanza. Un binario con SUID corre con la del dueño del binario. Es un mecanismo legítimo e imprescindible: passwd necesita escribir en /etc/shadow, que es de root, así que passwd es SUID root. El problema no es el SUID: es ponérselo a un binario que hace algo demasiado general. Un binario SUID root que lee archivos arbitrarios lee cualquier archivo del sistema. Uno que ejecuta una orden de tu elección ejecuta esa orden como root. Por eso el inventario de binarios SUID de un sistema es una de las primeras cosas que mira alguien que ya entró: es el mapa de las escaleras hacia arriba.

sudo resuelve el mismo problema —dar un poco de poder sin darlo todo— pero por política en vez de por bit. /etc/sudoers declara qué usuario puede ejecutar qué comando como quién. Bien usado es mínimo privilegio en estado puro: "este operador puede reiniciar solo este servicio". Mal usado —ALL=(ALL) NOPASSWD: ALL, o permitir un comando que a su vez lanza una shell— es una puerta a root con otro nombre.

4Ejemplo sencillo

# Un edificio de oficinas

DUENO    tu despacho: tu llave abre, entras y trabajas          rwx
GRUPO    tu equipo: pueden pasar y leer, no reorganizar          r-x
OTROS    el resto del edificio: ni entran                        ---

# El SUID es una tarjeta de mantenimiento

Un becario NO puede abrir el cuarto de servidores.
Pero hay una "linterna de mantenimiento" (binario SUID de
mantenimiento) que, mientras la usas, te da los permisos de
mantenimiento. Si esa linterna solo alumbra, bien. Si ademas
abre puertas, cualquiera que la coja abre TODAS las puertas.
El SUID no es una llave maestra que se reparte: es una herramienta que presta, mientras se usa, la identidad de su dueño. El riesgo está en qué deja hacer esa herramienta.

5Ejemplo técnico

Mira una tríada de archivos reales de un sistema y lo que cada permiso significa para la seguridad:

ArchivoPermisosPor qué así
/etc/shadowrw------- root (600)Hashes de contraseña. Solo root lee o escribe; nadie más lo mira.
/usr/bin/passwdrwsr-xr-x root (4755)SUID root: cualquiera lo ejecuta, pero corre como root para tocar shadow. La s es el SUID.
/tmprwxrwxrwt root (1777)Todos escriben, pero el sticky bit (t) impide borrar archivos ajenos.

El comando que convierte esto en una auditoría es find. Buscar todos los binarios SUID de un sistema es una línea, y leerla con criterio es la mitad del trabajo de endurecer una máquina:

# todos los binarios SUID del sistema
find / -perm -4000 -type f 2>/dev/null

# directorios donde cualquiera puede escribir (world-writable)
find / -perm -0002 -type d 2>/dev/null

6Diagrama

Un usuario sin privilegios ejecuta un binario SUID cuyo dueño es root; el proceso adopta la identidad de root y accede a un archivo que el usuario no podía tocar becario sin privilegios binario SUID dueño: root · bit s identidad: root durante la ejecución /etc/shadow 600, solo root ejecuta adopta accede
El becario nunca gana privilegios: los toma prestados del binario mientras lo ejecuta. Si ese binario puede leer archivos arbitrarios, el préstamo abre /etc/shadow.
Ver el mismo diagrama como código Mermaid (editable)
flowchart LR
    U[becario · sin privilegios] -->|ejecuta| B[binario SUID · dueño root]
    B -->|adopta| R{{identidad: root}}
    R -->|accede| S[/etc/shadow · 600/]

7Código

Los permisos se vuelven auditables cuando se escriben como una comprobación repetible. Este script lista lo que hay que revisar en cualquier máquina y marca lo sospechoso:

#!/usr/bin/env bash
# auditar-permisos.sh — inventario minimo para endurecer un host
set -euo pipefail

echo "== binarios SUID (revisa que cada uno DEBA serlo) =="
find / -perm -4000 -type f 2>/dev/null

echo "== directorios world-writable (¿alguno en el PATH?) =="
find / -perm -0002 -type d 2>/dev/null | grep -v '/proc'

echo "== reglas sudo con NOPASSWD o ALL (peligrosas) =="
grep -RniE 'nopasswd|\(all\)\s*all' /etc/sudoers /etc/sudoers.d 2>/dev/null || echo "ninguna"

La lista de SUID no se borra entera: se compara con la esperada. Un passwd o un sudo SUID root son normales. Un find, un cp o un binario propio con SUID casi nunca lo son, y son exactamente el tipo de cosa que convierte a un usuario cualquiera en root.

8Qué puede salir mal

Los errores clásicos

chmod 777 como solución. Aparece cuando "algo no funciona por permisos" y alguien abre todo para salir del paso. Casi siempre queda para siempre, y convierte el archivo o el directorio en escribible por cualquiera.

SUID en el binario equivocado. Poner SUID root a una herramienta que lee o escribe archivos arbitrarios, o que puede lanzar otra orden, es entregar root con un rodeo. El clásico es un script propio "para que el equipo pueda hacer X sin sudo".

Ejecutar la aplicación como root. "Es más fácil, así no hay problemas de permisos." También es más fácil para el atacante: cualquier fallo de la app se ejecuta con todo el poder de la máquina.

sudo a un comando que escapa. Permitir sudo less, sudo vi o sudo find parece inocuo, pero todos ellos pueden lanzar una shell desde dentro. Un permiso de "solo leer un log" acaba siendo una shell root.

9Cómo lo abusaría un atacante

Vista desde el otro lado

Alguien que acaba de conseguir ejecución como un usuario cualquiera —por una inyección, por una credencial filtrada— tiene un solo objetivo inmediato: subir. Y lo primero que hace no es nada sofisticado, es enumerar. id para saber quién es y en qué grupos está. sudo -l para ver qué le dejan ejecutar. Y el find / -perm -4000 de siempre para listar los binarios SUID.

Esa lista es un menú. Cada binario SUID que haga algo demasiado general es un camino. Un SUID que lea archivos arbitrarios le entrega /etc/shadow y las claves SSH de otros usuarios. Un SUID que escriba le deja modificar /etc/passwd o plantar su propia tarea programada. Y si hay un directorio world-writable dentro del PATH, planta ahí un binario con el nombre de algo que un proceso privilegiado va a llamar, y espera.

Lo relevante para ti como defensor es que nada de esto requiere un exploit. Son permisos de más que llevaban meses ahí, funcionando, sin dar ningún síntoma. El atacante no rompe nada: usa lo que ya estaba mal configurado.

10Cómo defenderlo

Qué hacer con esto mañana
  • Ejecuta cada servicio con un usuario propio sin privilegios. Nunca root "por comodidad".
  • Inventaría los binarios SUID y justifica cada uno. Lo que no tenga justificación, quítale el bit: chmod u-s.
  • Da a los secretos permisos 600 y dueño el usuario que los necesita. Nada de 644 en una clave privada.
  • En sudoers, concede comandos concretos, no ALL, y evita comandos que puedan lanzar una shell.
  • Vigila los directorios del PATH: ninguno debe ser escribible por usuarios sin privilegios.
  • Convierte la auditoría en un check repetible (el script de arriba) y córrelo tras cada despliegue.

11Laboratorio

Estado: laboratorio local lab-linux-privesc
Targetcontenedor privesc-caja, sin puertos publicados
Autorizaciónsistema local creado por ti para entrenamiento
Este laboratorio es tuyo, está aislado y no tiene salida a internet. Todo lo que se practica aquí se practica aquí dentro. Ninguna de estas técnicas debe usarse contra un sistema que no sea tuyo o para el que no tengas autorización escrita.

Levanta el escenario y entra como el usuario sin privilegios:

cd cursos/seguridad/labs/lab-linux-privesc docker compose up -d docker compose exec caja su - becario

Recorre el ciclo del curso sobre un permiso de más real:

construir → atacar → observar → defender → verificar
cat /srv/datos/secreto.txt cat: /srv/datos/secreto.txt: Permission denied # correcto: como becario no puedes leer el secreto ls -l /usr/local/bin/leer -rwsr-xr-x 1 operador operador ... /usr/local/bin/leer # la 's' es el SUID: 'leer' corre como 'operador' /usr/local/bin/leer /srv/datos/secreto.txt clave-de-produccion-que-no-deberias-ver # el SUID te presto la identidad que abre el archivo

Ahora defiende: desde otra terminal, como root (docker compose exec caja bash), quita el bit con chmod u-s /usr/local/bin/leer y vuelve a intentar el mismo comando como becario. Debe fallar. Cuando termines:

docker compose down -v

12Ejercicios

Traduce cinco permisos

Di en octal o en rwx, según toque, y explica quién puede hacer qué: (1) rw-r--r--; (2) 750; (3) rwsr-xr-x; (4) 777; (5) una clave SSH privada, ¿qué permiso debería tener?

Suma por ámbito: r=4, w=2, x=1.
La s en la posición del dueño es el bit SUID, y se escribe como un 4 delante del octal.
Una clave privada que otro pueda leer se considera comprometida; ssh se niega a usarla si es demasiado abierta.
Solución

(1) 644: dueño lee/escribe, grupo y otros solo leen. (2) rwxr-x---: dueño todo, grupo entra y ejecuta, otros nada. (3) 4755 con SUID: todos ejecutan, corre como el dueño. (4) rwxrwxrwx: todos todo, casi siempre un error. (5) 600: solo el dueño lee y escribe.

Clave: 644 · rwxr-x--- · 4755 · rwxrwxrwx · 600. El caso (3) es el interesante: es el permiso legítimo de passwd y el sospechoso de un binario cualquiera.

Audita el laboratorio

Con el lab-linux-privesc levantado, ejecuta dentro el inventario de la lección: lista los binarios SUID y los directorios world-writable. Identifica cuál de los SUID es el peligroso y por qué, y cuál directorio del PATH no debería ser escribible.

Usa los dos find de la sección de código.
Compara la lista de SUID con lo que esperarías en un Debian limpio; sobra uno.
El escenario deja /opt/util con permisos 0777 a propósito.
Solución

El SUID peligroso es /usr/local/bin/leer: es una copia de cat con el bit puesto y dueño operador, así que lee cualquier archivo con los privilegios de operador. El directorio problemático es /opt/util, con 0777: al estar en un PATH de servicio, permitiría plantar un binario que otro proceso ejecutaría.

Clave: el SUID sobrante es leer; el world-writable a corregir es /opt/util. La corrección es chmod u-s en el binario y chmod 755 (o quitarlo del PATH) en el directorio.

Por qué falla la suma de permisos

Un archivo es -r--rw---- becario equipo (440 para dueño, 060 para grupo). El usuario becario es el dueño y además pertenece al grupo equipo. ¿Puede escribir en el archivo? Explica exactamente por qué, y qué tendría que cambiar para que sí.

El kernel no suma tríadas: elige una.
¿Cuál tríada aplica cuando eres a la vez dueño y miembro del grupo?
La más específica gana, y "dueño" es más específico que "grupo".
Solución

No puede escribir. Como becario es el dueño, el kernel usa solo la tríada de dueño, que es r--: lectura, sin escritura. El w del grupo no se suma; ser dueño "gana" y descarta las demás tríadas. Para que pudiera escribir habría que dar w en la tríada de dueño (por ejemplo 640), o que becario no fuera el dueño y sí miembro del grupo.

Clave: no escribe, porque se aplica la tríada de dueño (r--) y no la de grupo. La regla es "la más específica que aplique, sin sumar".

Red Team · lee el secreto que no te toca

Dentro del lab-linux-privesc, y solo dentro de él: como becario, consigue leer /srv/datos/secreto.txt aprovechando el binario SUID. Documenta el comando exacto y explica qué identidad usó el proceso para conseguirlo.

Empieza mirando qué binarios SUID hay y de quién son.
El binario leer hace lo mismo que cat, pero con otra identidad.
Pásale la ruta del secreto como argumento.
Solución

/usr/local/bin/leer /srv/datos/secreto.txt. El binario tiene el bit SUID y su dueño es operador, dueño también del secreto (permisos 600). Al ejecutarlo, el proceso adopta la identidad de operador y puede abrir el archivo, aunque becario por sí mismo no pueda. No hiciste nada "ilegal" en el sistema: usaste un permiso mal concedido.

Clave: el proceso corrió como operador (dueño del binario SUID), no como becario. Ese préstamo de identidad es todo el ataque. La defensa es chmod u-s.

Blue Team · corrige y verifica

Aplica las correcciones a los dos fallos del laboratorio (el SUID sobrante y el directorio world-writable). Después demuestra, con comandos, que la lectura del secreto ya no funciona y que el directorio ya no es escribible por becario. Escribe la comprobación como un pequeño check que devuelva éxito o fallo.

chmod u-s quita el SUID; chmod 755 cierra el directorio.
La verificación es re-ejecutar el ataque y comprobar que ahora da "Permission denied".
Un check útil usa el código de salida: ... && echo FALLA || echo OK.
Solución

Como root: chmod u-s /usr/local/bin/leer y chmod 755 /opt/util. Verificación como becario:

# debe fallar ahora: si lee el secreto, la defensa no sirvio
/usr/local/bin/leer /srv/datos/secreto.txt 2>/dev/null \
  && echo "FALLA: aun se lee" || echo "OK: sin acceso"

Clave: la corrección es quitar el bit y cerrar el directorio; la verificación es que el ataque de antes ahora devuelve "Permission denied". Sin esa segunda parte, no sabes si arreglaste algo.

Architecture Challenge · el usuario de una aplicación

Diseña el modelo de usuarios y permisos para desplegar una aplicación web con base de datos en un servidor. Define: qué usuario corre la app, qué usuario la base de datos, quién es dueño de los archivos de configuración con secretos, qué permisos llevan, y cómo das al equipo de operaciones la capacidad de reiniciar el servicio sin darles root.

Cada servicio con su propio usuario sin privilegios; nunca compartir root.
El archivo de config con secretos: dueño el usuario de la app, permisos 600.
sudoers puede permitir un único comando concreto, no ALL.
Solución

Una propuesta razonable: un usuario webapp sin shell de login que corre la aplicación; un usuario postgres propio de la base de datos; el archivo config.env con los secretos propiedad de webapp:webapp y permisos 600, para que ni siquiera otros servicios lo lean. A operaciones se les da, en /etc/sudoers.d/, exactamente ops ALL=(root) NOPASSWD: /bin/systemctl restart webapp y nada más: pueden reiniciar ese servicio y solo ese, sin ser root ni poder lanzar una shell.

Clave: no hay una única respuesta. Se evalúa que cada servicio tenga usuario propio, que los secretos sean 600 del usuario correcto, y que el acceso de operaciones sea un comando concreto, no ALL.

13Preguntas de comprensión

Comprobación

Un binario SUID root con un fallo de inyección de comandos, ¿a qué da acceso al atacante?

El SUID hace que el proceso corra como el dueño; si el dueño es root, la orden inyectada se ejecuta como root.

¿Por qué es peligroso un directorio world-writable dentro del PATH?

Escribir en un directorio del PATH deja poner un ejecutable con el nombre de algo que un proceso privilegiado va a llamar. El permiso de los archivos no protege: borrar y crear es del directorio.

sudo less /var/log/app.log permitido en sudoers, ¿qué riesgo tiene?

less ejecuta órdenes con !. Un permiso de "solo leer un log como root" se convierte en una shell root.

Mínimo privilegio aplicado a permisos de archivo significa…

Mínimo privilegio es dar lo justo: un secreto 600, un binario sin SUID salvo que lo necesite, un directorio no escribible salvo para quien deba.

0 / 4

14Reto adicional

Encuentra un SUID legítimo y explica por qué lo es

En el propio contenedor del laboratorio (o en cualquier Linux tuyo), lista los binarios SUID y elige uno que deba serlo. Explica qué hace, por qué necesita privilegios elevados y por qué ese SUID concreto no es un riesgo, a diferencia del leer del laboratorio.

passwd, su y mount suelen ser SUID root.
La pregunta clave es: ¿este binario deja hacer algo arbitrario, o una sola cosa acotada?
passwd solo cambia contraseñas; no te da una shell ni lee archivos que elijas.
Solución

passwd es el ejemplo canónico. Necesita SUID root porque escribe en /etc/shadow, que es de root. Pero es seguro porque su función está acotada: solo cambia contraseñas siguiendo sus propias reglas, no ejecuta órdenes de tu elección ni lee archivos arbitrarios. Ahí está la diferencia con el leer del laboratorio: passwd hace una cosa concreta y controlada; leer hace algo general (abrir cualquier archivo) con privilegios prestados.

Clave: un SUID es aceptable cuando su función es acotada y no permite operaciones arbitrarias. "Necesita privilegios" no basta; tiene que no dejar hacer nada de más con ellos.

15Resumen

Puntos clave
  • Los permisos se leen en tres tríadas —dueño, grupo, otros— y el kernel aplica solo la más específica, sin sumar.
  • En un directorio, w es crear y borrar entradas; por eso un directorio abierto anula la protección de los archivos que contiene.
  • El SUID presta la identidad del dueño del binario. Es legítimo cuando la función es acotada; peligroso cuando permite algo arbitrario.
  • El usuario con el que corre un servicio fija el alcance de cualquier fallo suyo. Nunca root por comodidad.
  • sudo debe conceder comandos concretos, y nunca comandos que puedan lanzar una shell.
  • Auditar SUID y directorios world-writable es un check repetible, no una tarea de una vez.
¿Qué identidad usa un proceso al ejecutar un binario con el bit SUID?
La del dueño del binario, no la de quien lo lanza. Si el dueño es root, el proceso corre como root.
Eres dueño de un archivo 440 y estás en su grupo, que tiene w. ¿Puedes escribir?
No. El kernel aplica la tríada de dueño (r--) y no suma la del grupo. Ser dueño "gana".
¿Qué comando lista todos los binarios SUID de un sistema?
find / -perm -4000 -type f 2>/dev/null. Cada resultado hay que justificarlo.
¿Por qué es peligroso permitir sudo less?
Porque less puede lanzar una shell desde dentro, y sería una shell root. Un permiso de lectura se vuelve acceso total.

16Mis notas

Cierre de la lección

Estas notas se guardan solo en este navegador. No se envían a ningún servidor. Expórtalas desde el panel de progreso si cambias de máquina.

17Recursos

Los comentarios se cargan solo si los pides, desde GitHub Discussions. No se carga nada de terceros mientras no pulses.