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.
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
- Usuario / grupo / otros: los tres ámbitos sobre los que se definen los permisos.
- rwx: leer, escribir, ejecutar. Sobre un directorio,
xes "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.
5Ejemplo técnico
Mira una tríada de archivos reales de un sistema y lo que cada permiso significa para la seguridad:
| Archivo | Permisos | Por qué así |
|---|---|---|
/etc/shadow | rw------- root (600) | Hashes de contraseña. Solo root lee o escribe; nadie más lo mira. |
/usr/bin/passwd | rwsr-xr-x root (4755) | SUID root: cualquiera lo ejecuta, pero corre como root para tocar shadow. La s es el SUID. |
/tmp | rwxrwxrwt 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
/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
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
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
- 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
600y dueño el usuario que los necesita. Nada de644en una clave privada. - En
sudoers, concede comandos concretos, noALL, 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
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:
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?
r=4, w=2, x=1.s en la posición del dueño es el bit SUID, y se escribe como un 4 delante del octal.ssh se niega a usarla si es demasiado abierta.
(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.
find de la sección de código./opt/util con permisos 0777 a propósito.
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í.
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.
leer hace lo mismo que cat, pero con otra identidad.
/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.... && echo FALLA || echo OK.
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.
600.sudoers puede permitir un único comando concreto, no ALL.
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
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.
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 sí 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.passwd solo cambia contraseñas; no te da una shell ni lee archivos que elijas.
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
- 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,
wes 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.
sudodebe 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.
440 y estás en su grupo, que tiene w. ¿Puedes escribir?r--) y no suma la del grupo. Ser dueño "gana".find / -perm -4000 -type f 2>/dev/null. Cada resultado hay que justificarlo.sudo less?less puede lanzar una shell desde dentro, y sería una shell root. Un permiso de lectura se vuelve acceso total.16Mis notas
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.