\newpage Les *capabilities* ------------------ Historiquement, dans la tradition UNIX, on distinguait deux catégories de processus : * les processus *privilégiés* : dont l'identifiant numérique de son utilisateur est 0 ; * les processus *non-privilégiés* : dont l'identifiant numérique de son utilisateur n'est pas 0. Lors des différents tests de permission fait par le noyau, les processus privilégiés outrepassaient ces tests, tandis que les autres devaient passer les tests de l'effective UID, effective GID, et autres groupes supplémentaires... Depuis Linux 2.2 (en 1998), les processus privilégiés peuvent activer ou désactiver des *capabilities*, chacune donnant accès à un groupe d'actions privilégiées au sein du noyau. On trouve par exemple : * `CAP_CHOWN` : permet de modifier le propriétaire d'un fichier de manière arbitraire ; * `CAP_KILL` : permet de tuer n'importe quel processus ; * `CAP_SYS_BOOT` : permet d'arrêter ou de redémarrer la machine ; * `CAP_SYS_MODULE` : permet de charger et décharger des modules ; * et beaucoup d'autres, il y en a environ 41 en tout (ça dépend de la version du noyau) ! ### `ping` Pour émettre un ping, il est nécessaire d'envoyer des paquets ICMP. À la différence des datagrammes UDP ou des segments TCP, il n'est pas forcément simple d'envoyer des paquets ICMP lorsque l'on est simple utilisateur, car l'usage du protocole ICMP dans une soket est restreint : il faut soit être super-utilisateur, soit que le noyau ait été configuré pour autoriser certains utilisateurs à envoyer des `ECHO_REQUEST`. Pour permettre à tous les utilisateurs de pouvoir envoyer des ping, le programme est donc généralement *Setuid root*. Cela permet à n'importe quel utilisateur de prendre les droits du super-utilisateur, le temps de l'exécution du programme. Les problèmes surviennent lorsque l'on découvre des vulnérabilités dans les programmes *Setuid root*. En effet, s'il devient possible pour un utilisateur d'exécuter du code arbitraire, ce code sera exécuté avec les privilèges de l'utilisateur *root* ! Dans le cas de `ping`, on se retrouverait alors à pouvoir lire l'intégralité de la mémoire, alors que l'on avait juste besoin d'écrire sur une interface réseau. C'est donc à ce moment que les *capabilities* entrent en jeu : un processus (ou même un thread) privilégié peut décider, généralement à son lancement, de réduire ses *capabilities*, pour ne garder que celles dont il a réellement besoin. Ainsi, `ping` pourrait se contenter de `CAP_NET_RAW`. ::::: {.warning} Bien que ce paramètre existe [depuis 2011](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=c319b4d76b9e583a5d88d6bf190e079c4e43213d), ce n'est que [depuis 2020](https://github.com/systemd/systemd/pull/13141) que les distributions comme Fedora et Ubuntu se mettent à fournir par défaut une configuration qui permette de se passer de *capabilities* pour lancer `ping`.\ Si vous vous rendez compte que votre binaire `ping` est dans ce cas, testez depuis un conteneur, par exemple :
``` 42sh$ docker run -it --rm alpine (ctnr)# apk add --no-cache acl iputils (1/4) Installing libacl (2.2.53-r0) (2/4) Installing acl (2.2.53-r0) (3/4) Installing libcap (2.50-r0) (4/4) Installing iputils (20210202-r0) (ctnr)# su -s/bin/ash daemon (ctnr)$ _ ```
Dans le conteneur le binaire `ping` est *setuid root*, vous pouvez faire des tests en retirant le *setuid* :
``` (ctnr)# chmod u-s /bin/ping (ctnr)$ ping epita.fr ping: socket: Operation not permitted ```
Puis en ajoutant la *capability* :
``` (ctnr)# setcap cap_net_raw+p /bin/ping (ctnr)$ ping epita.fr PING epita.fr (172.67.156.141) 56(84) bytes of data. ```
Vous vous retrouverez dans le scénario attendu, tout en pouvant agir sur le binaire `ping` sans avoir peur de casser votre distribution. ::::: ### Les attributs de fichier étendus Une grosse majorité des systèmes de fichiers (ext[234], XFS, btrfs, ...) permet d'enregistrer, pour chaque fichier, des attributs (dits attributs *étendus*, par opposition aux attributs *réguliers* qui sont réservés à l'usage du système de fichiers). Sous Linux, les attributs sont regroupés dans des espaces de noms : * *security* : espace utilisé par les modules de sécurité du noyau, tel que SELinux, ... ; * *system* : espace utilisé par le noyau pour stocker des objets système, tels que les ACL POSIX ; * *trusted*: espace dont la lecture et l'écriture est limité au super-utilisateur ; * *user* : modifiable sans restriction, à partir du moment où l'on est le propriétaire du fichier. Par exemple, on peut définir un attribut sur un fichier comme cela :
```bash 42sh$ echo 'Hello World!' > toto 42sh$ setfattr -n user.foo -v bar toto 42sh$ getfattr -d toto # file: toto user.foo="bar" ```
En tant que simple utilisateur, vous ne pouvez pas modifier des attributs en dehors de l'espace *user*. Par contre, en *root*, vous pouvez définir et changer les ACL POSIX :
```bash 42sh$ sudo chown root:root toto && sudo chmod o-r toto 42sh$ cat toto cat: toto: Permission denied 42sh$ sudo setfacl -m u:$USER:r toto 42sh$ cat toto Hello World! ```
Bien que les droits UNIX traditionnels ne vous donnent pas accès au fichier, les ACL POSIX vous autorisent à le lire. Vous pouvez voir ces attributs bruts avec la commande :
```bash 42sh$ getfattr -d -m "^system" toto # file: toto system.posix_acl_access=0sgAAEAD/////AgAEOgDAEAA/////xAABAD////8= ```
Il s'agit d'une représentation d'une structure du noyau, pas forcément très lisible en l'état. On utilisera `getfacl` pour la version lisible. #### `ping` De la même manière que l'on peut définir de façon plus fine les droits d'accès par utilisateur, un attribut de l'espace de nom *security* peut être défini pour accroître les *capabilities* d'un processus lorsqu'il est lancé par un utilisateur non-privilégié. On peut voir le *setuid root* comme l'utilisation de cet attribut, qui accroîtrait l'ensemble des *capabilities*. Si votre distribution profite de ces attributs étendus, vous devriez obtenir :
```bash 42sh$ getfattr -d -m "^security" $(which ping) # file: bin/ping security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA= ```
Comme pour les ACL POSIX, une structure du noyau est enregistrée comme attribut du fichier ; et on peut l'afficher dans sa version plus lisible :
```bash 42sh$ getcap $(which ping) /bin/ping = cap_net_raw+ep ```
::::: {.exercice} ### Visualisateur de capabilities d'un processus {-} Écrivons maintenant un programme permettant de voir les *capabilities* d'un processus :
``` 42sh$ ./view_caps 1 cap_user_header_t ----------------- Version: 20080522 PID: 1 cap_user_data_t --------------- effective: 0x3fffffffff CAP_AUDIT_CONTROL CAP_AUDIT_READ [...] CAP_SYS_TIME CAP_SYS_TTY_CONFIG CAP_SYSLOG CAP_WAKE_ALARM permitted: 0x3fffffffff CAP_AUDIT_CONTROL CAP_AUDIT_READ [...] CAP_SYS_TIME CAP_SYS_TTY_CONFIG CAP_SYSLOG CAP_WAKE_ALARM inheritable: 0x0 ```
Appelé sans argument, `view_caps` affichera les capabilities du processus courant. Astuces : `capget(2)`, X-macros, ... ::::: ### Pour aller plus loin {-} Je vous recommande la lecture des *man* suivants : * `capabilities(7)` : énumérant tous les capabilities, leur utilisation, etc. ; * `xattrs(7)` : à propos des attributs étendus. Et de ces quelques articles : * [Linux Capabilities: Why They Exist and How They Work](https://blog.container-solutions.com/linux-capabilities-why-they-exist-and-how-they-work) :\ * [Guidelines for extended attributes](https://www.freedesktop.org/wiki/CommonExtendedAttributes/) :\ * [File-based capabilities](https://lwn.net/Articles/211883/) : * [A bid to resurrect Linux capabilities](https://lwn.net/Articles/199004/) : * [False Boundaries and Arbitrary Code Execution](https://forums.grsecurity.net/viewtopic.php?f=7&t=2522#p10271) :\ * [Linux Capabilities on HackTricks](https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities) :\ Pour revenir à Docker, un certain nombre de *capabilities* sont désactivées par défaut ; vous pouvez en ajouter et en retirer via les arguments `--cap-add` et `--cap-drop` du `docker container run`.