04 septiembre 2008

Linux POSIX capabilities

El mundo GNU y específicamente su proyecto estrella Linux, están de enhorabuena, acaban de cumplir 25 años

Para sumarnos a esa celebración, voy a escribir un poco sobre un aspecto de seguridad en Linux que no parece demasiado difundido y que puede resultar interesante de cara a securizar sistemas Linux.

Me refiero a las POSIX capabilities. Antes de entrar en materia, contextualizare un poco sobre que son y porque nos pueden ser útiles.

En un sistema Unix, hay una serie de restricciones (lógicas) a la hora de hacer cierto tipo de cosas que requieren privilegios especiales (ser root) esto supone que, enviar RAW SOCKETS, o abrir un puerto por debajo de 1024 son tareas absolutamente imposibles de hacer para un usuario normal. Esta restricción es evidentemente necesaria, pero supone otro problema, tal vez yo necesite que un usuario normal haga SOLO una tarea que requiera privilegios de root y no deseo darle 'todo el poder'.

Rápidamente muchos habrán caído en que, si es solo una tarea, un comando, puedo poner ese comando en modo set-uid para que se ejecute con privilegios de root. Pero esto no soluciona el problema, por ejemplo, si yo necesito abrir el puerto 200, hacer que un comando se ejecute como root supone, colateralmente, darle acceso a /etc/shadow, ¿Yo quiero hacer esto? No, obviamente.

Para eso están las POSIX capabilities que permiten asociar funcionalidades de forma granular sin tener que abrir una brecha de seguridad de proporciones acromegalicas.

El listado completo de las capabilities soportadas en Linux (y su correspondiente descripción) se encuentra en el fichero capability.h

Las capabilities se encuentran soportadas dentro de la estructura ELF desde hace unos años, con lo que además de los consabidos datos xwr también se pueden asociar capabilities a un fichero.

Juguemos un poco con algún ejemplo, véase el archifamoso nmap. Supongamos que yo pretendo usar nmap para realizar escaneos desde un usuario no privilegiado. Nmap hace uso en algunos modos de scaneo de RAW SOCKETS, y eso solo lo puede hacer root:

$ nmap -sS www.google.com
You requested a scan type which requires root privileges.
QUITTING!

No podemos.
En este punto yo podría, dentro de mi inconsciencia, poner nmap como set-uid para que se ejecute como root, y efectivamente ya podría realizar el scaneo

(como root)
#chmod +s /usr/local/bin/nmap

Y luego

$ nmap -sS www.google.com

Starting Nmap 4.53 ( http://insecure.org ) at 2008-09-04 02:04 CEST
Warning: Hostname www.google.com resolves to 4 IPs. Using 216.239.59.147.

Todo correcto salvo porque, además, ese usuario no privilegiado podría hacer esto:

$ nmap -iL /etc/shadow

Starting Nmap 4.53 ( http://insecure.org ) at 2008-09-04 02:05 CEST
Invalid host expression: root:$6$LHKAdZSmlnxl2Vwo$839gbMniA0cQvLaWjurT2P0fSttT9Fve.:14037:0:99999:7::: -- colons only allowed in IPv6 addresses, and then you need the -6 switch
QUITTING!

WOW, definitivamente ha sido una pésima idea poner nmap como set-uid, como se puede ver, al haber dado esos privilegios a nmap, ahora puedo leer la primera linea del fichero /etc/shadow donde se encuentra la password de root. Nmap, además, tiene flags que permiten sobre-escribir ficheros, así que, el daño podría haber sido mucho peor.

¿Solucion? Usar las POSIX capabilities de forma granular. ¿Que necesita nmap para funcionar? Acceso a los RAW SOCKETS, vale, entonces, concedamosle únicamente esa capacidad. Para ello usaremos la herramienta setcap que pertenece al paquete libcap

(como root)
# setcap cap_net_raw=ep /usr/bin/nmap

De esta forma, con un usuario no privilegiado podemos ejecutar nmap así:
$ nmap -sS www.google.com --privileged

Y podremos realizar escaneos que, en principio, deberían requerir privilegios de root pero que al haber otorgado esa capacidad al comando, podemos ejecutar sin dar los máximos privilegios.

Setcap, no aparece demasiado bien documentada, para fijar un privilegio has de usarla de la siguiente forma:
setcap nombre_capability=ep /ruta/del/comando
Y para ver que capabilities tiene activadas un comando, usaremos la herramienta getcap
getcap /ruta/del/comando