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 faits 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... Dans les années 90, ce système s'est rélévé être un peu trop basique et conduisait régulièrement à des abus, au moyen de vulnérabilités trouvées dans les programmes *setuid root*. Avant de regarder plus en détail les solutions qui ont été apportées à ce problème, commençons par mettre le doigt sur les difficultés. ### `setuid` : être `root` le temps de l'exécution De manière générale, un processus s'exécute dans le contexte de privilège de l'utilisateur qui l'a démarré. Ainsi, lorsque l'on souhaite supprimer un fichier ou un répertoire, en tant que simple utilisateur on ne pourra supprimer que ses propres fichiers, et en aucun cas on ne pourra supprimer ... la racine par exemple. Il y a cependant des tâches nécessitant des privilèges, que l'on souhaite pouvoir réaliser en tant que simple utilisateur. C'est le cas notamment de la modification de son mot de passe : il serait inconcevable de devoir demander à son administrateur à chaque fois que l'on souhaite modifier son mot de passe. Pourtant bien que l'on puisse lire le fichier `/etc/passwd`, seul `root` peut y apporter des modifications (il en est de même pour `/etc/shadow` qui contient les hashs des mots de passe). C'est ainsi qu'est apparu le `suid-bit` parmi les modes de fichiers. Lorsque ce bit est défini sur un binaire exécutable, au moment de l'exécution, le contexte passe à celui du propriétaire du fichier (`root` si le propriétaire est `root`, mais cela fonctionne quelque soit le propriétaire du fichier : on ne devient pas `root`, mais bien l'utilisateur propriétaire).\ Un autre exemple : 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 socket 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*, pour permettre à 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. ### Et ainsi les privilèges furent séparés Depuis Linux 2.2 (en 1998), les différentes actions réclamant des privilèges sont regroupées en catégories que l'on appelle *capabilities*. Chacune donne accès à un certain nombre d'actions, on trouve notamment : * `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) ! ::::: {.more} Petit point historique, Linux n'est pas à l'origine du concept de *capabilities*, il s'agit au départ de la norme POSIX 1003.1e, mais celle-ci s'est éteinte après le 17\textsuperscript{ème} *draft*. Il devait y être standardisé de nombreux composants améliorant la sécurité des systèmes POSIX, notamment les *capabilities* POSIX, les *ACL POSIX*, ... ::::: Ainsi, `ping` pourrait se contenter de `CAP_NET_RAW`, une *capability* qui permet notamment d'envoyer des données brutes sur n'importe quelle *socket*, sans passer par les types de *socket* plus restreints, mais accessibles aux utilisateurs. C'est peut-être encore beaucoup, mais au moins, une vulnérabilité dans `ping` ne permettrait plus d'accéder à tous les fichiers ou à toute la mémoire. ::::: {.question} #### Peut-on faire mieux pour `ping` ? {-} Un paramètre existe bien [depuis 2011 dans le noyau](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=c319b4d76b9e583a5d88d6bf190e079c4e43213d) : `net.ipv4.ping_group_range`. Mais 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`. Cette option prend un intervalle d'identifiant numérique de groupes autorisés à créer de `ECHO_REQUEST`. Par défaut la valeur invalide de `1 0` est définie, ce qui signifie qu'aucun groupe n'est autorisé à créer des `ECHO_REQUEST` à moins d'être privilégié. ::::: ::::: {.warning} Nous allons par la suite travailler avec le binaire `ping` pour appréhender les *capabilities*. Si vous vous rendez compte que votre binaire `ping` est dans le cas figure décrit juste avant (avec une distribution ayant mis en œuvre l'option `net.ipv4.ping_group_range`), vous pouvez retrouver le comportement historique en désactivant l'option :
``` 42sh# sysctl net.ipv4.ping_group_range="1 0" ```
Pas d'inquiétudes, le paramètre est changé de manière temporaire seulement, au prochain redémarrage il reprendra sa valeur définie par la distribution. ::::: ### Les ensembles de *capabilities* Tout d'abord, il faut noter que chaque *thread* dispose de 5 ensembles de *capabilities*. Au cours de son exécution, il peut changer ses ensembles de *capabilities*. Ceux-ci sont utilisés de la façon suivante : - ***bounding*** (B) : c'est l'ensemble limitant des *capabilities* qui pourront faire l'objet d'un calcul lors des prochaines exécutions. Quelques soient les *capabilities* que peut nous faire gagner une exécution, si la *capability* ne se trouve pas dans le *bounding set*, elle ne sera pas considérée et il sera impossible de l'obtenir. L'option `--cap-drop` de Docker modifie cet ensemble pour restreindre les *capabilities* disponibles dans un conteneur ; - ***effective*** (E) : il s'agit des *capabilities* actuellement actives, qui seront vérifiées lors des tests de privilèges ; - ***permitted*** (P) : ce sont les *capabilities* que la tâche peut placer dans l'ensemble *effective* via `capset(2)`. L'ensemble *effective* ne peut pas avoir de *capabilities* qui ne sont pas dans l'ensemble *permitted* ; - ***inheritable*** (I) : est utilisé au moment de la résolution des *capabilities* lors de l'exécution d'un nouveau processus. Il s'agit des *capabilities* qui seront transmises au processus fil. À moins d'avoir la *capability* `CAP_SETPCAP`, cet ensemble ne peut pas avoir plus de *capability* que celles présentent dans l'ensemble *permitted* ; - ***ambient*** (A) : existe depuis Linux 4.3 pour permettre aux utilisateurs non-`root` de conserver des *capabilities* d'une exécution à l'autre (sans que l'ensemble des binaires soient marqués avec toutes les *capabilities* dans leur ensemble **inheritable**)[^AMBIENT_PB]. L'ensemble évolue automatiquement à la baisse si une *capability* n'est plus *permitted*. [^AMBIENT_PB]: Le problème ayant donné naissance aux *ambient capabilities* est décrit dans cet échange : ### 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, tels que le propriétaire, le groupe, les modes du fichier, ...). 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" ```
Lorsque l'on est propriétaire du fichier, on peut modifier les attributs des espaces *security*, *system* et *user*. Évidemment, *root* peut aussi intervenir et par exemple les ACL POSIX[^ACLPOSIX] (espace *system*) : [^ACLPOSIX]: Les ACL POSIX sont des permissions supplémentaires qui viennent s'ajouter aux modes standards du fichier (propriétaire, groupe, reste du monde). Avec les ACL POSIX, on peut doonner des droits à un ou plusieurs utilisateurs ou groupe, de manière spécifique.
```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! ```
Dans cet exemple, 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 structure du noyau encodée en base64 (la valeur débute par `0s`, de la même manière que l'on a l'habitude de reconnaître l'hexadécimal avec `0x`, voir `getfattr(1)`), pas forcément très lisible en l'état. On utilisera plutôt `getfacl` pour la version lisible. ::::: {.code} Les structures utilisées pour stocker les ACL POSIX dans les attributs étendus sont définies dans `linux/posix_acl_xattr.h` :\ D'abord un en-tête, composé de la version avec laquelle la suite des octets a été enregistrée (actuellement 2) :
```c struct posix_acl_xattr_header { uint32_t a_version; }; ```
Puis on trouve directement la liste d'entrée(s) :
```c struct posix_acl_xattr_entry { uint16_t e_tag; uint16_t e_perm; uint32_t e_id; }; ```
Le *tag* identifie de quel type d'entrée il s'agit (propriétaire, utilisateur, groupe, masque, reste du monde, ...). Les *perm*issions sont un champ de bits pour la lecture, écriture et exécution. Enfin l'*id*entifiant renseigne sur le numéro d'utilisateur ou de groupe à qui s'applique l'entrée.\ Les constantes utilisées sont définies dans `linux/posix_acl.h`. ::::: ### *Capabilities* et attributs étendus pour `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= ```
Suivant votre distribution, d'autres programmes sont susceptibles de profiter des attributs étendus pour se passer du *setuid root*, par exemple : `chvt`, `beep`, `iftop`, `mii-tool`, `mtr`, `nethogs`, ... 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 avec `getcap` :
```bash 42sh$ getcap $(which ping) /bin/ping = cap_net_raw+ep ```
::::: {.code} La structure utilisée pour stocker les *capabilities* dans les attributs étendus est définie dans `linux/capability.h` :
```c struct vfs_cap_data { uint32_t magic_etc; struct { uint32_t permitted; uint32_t inheritable; } data[VFS_CAP_U32]; }; ```
La valeur `magic` contient la version sur 1 octet, puis 3 octets sont réservés pour des *flags*. Actuellement un seul *flag* existe, il s'agit de `VFS_CAP_FLAGS_EFFECTIVE` qui détermine si la liste effective de *capabilities* du programme doit être remplie avec les *capabilities* *permitted* si elle doit rester vide (auquel cas ce sera au programme de s'ajouter les *capabilities* au cours de l'exécution).\ Deux entiers de 32 bits représentent ensuite les *capabilities* de 0 à 31 sous forme de champ de bits, un entier pour la liste des *capabilities* permises, un autre pour les *capabilities* héritables. Comme il y a une quarantaine de *capabilities*, celles de 32 à 40 se retrouvent à la suite, dans deux nouveaux entiers de 32 bits. C'est pour cela que `data` est un tableau, avec `VFS_CAP_U32` valant 2, car on a deux fois nos 2 entiers de 32 bits à la suite.\ Il s'agit de la version 2 de la structure. La version 1 était utilisée lorsqu'il n'y avait encore que moins de 33 *capabilities* : il n'y avait alors pas de tableau, seulement les deux entiers de 32 bits. On remarque que les deux versions sont facilement compatibles entre-elles, la seconde version étendant simplement la première.\ Une version 3 existe, la structure obtient un champ supplémentaire `rootid` :
```c struct vfs_ns_cap_data { uint32_t magic_etc; struct { uint32_t permitted; uint32_t inheritable; } data[VFS_CAP_U32]; uint32_t rootid; }; ```
Ce nouveau champ `rootid` est utilisé pour stocker un identifiant d'utilisateur `root` au sein d'un *namespace* User. C'est utile pour pouvoir jouer avec les *capabilities* au sein d'un conteneur non-privilégié. S'il vaut autre chose que 0 et que l'on ne se trouve pas dans un *namespace* User, les *capabilities* ne seront pas appliquées. ::::: ### Gagner des *capabilities* C'est à l'exécution (`execve(2)`) que sont calculés les nouveaux ensembles de *capabilities* : c'est donc uniquement à ce moment que l'on peut en gagner de nouvelles. Voici comment les différents ensembles du nouveau processus sont calculés : $$p′_A = (\textsf{file caps or setuid or setgid}\: ?\: ∅\: :\: p_A)$$ $$p′_P = (p_I\: \&\: f_I)\ |\ (f_P\: \&\: p_B)\ |\ p′_A$$ $$p′_E = f_E\: ?\: p_P′\: :\: p′_A$$ $$p′_I = p_I$$ $$p′_B = p_B$$ Avec $p$ le processus avant l'exécution, $f$ le fichier exécutable donnant naissance au nouveau processus et $p′$ le nouveau processus.\ On remarque que sans les *ambients capabilities*, on perd systématiquement les *capabilities* dont on disposait avant l'`execve(2)`, car $f_I$ n'est que très rarement défini sur un exécutable. Dans le cas général, on récupère donc soit $f_P$, soit $p_A$ (les deux étant exclusifs : si $f_P$ est défini, $p′_A$ est vide). Bien entendu, lorsque l'on se trouve dans le contexte d'exécution de `root` (ou que l'on exécute un binaire *setuid root*), ces calculs sont biaisés, car le super-utilisateur a normalement toutes les *capabilities* (mais toujours limitées par l'ensemble *bounding*) : $f_P$ et $f_I$ contiennent alors toutes les *capabilities*, indifféremment du fichier considéré. Les calculs peuvent alors être simplifiés en : $$p′_P = (p_I\: \&\: p_B)$$ $$p′_E = p′_P$$ Il y a cependant une exception, lorsque l'utilisateur réel n'est pas `root` (comme par exemple face à un binaire *setuid root*, seul l'utilisateur effectif change) : dans ce cas, si le fichier expose des *capabilities*, seulement celles-ci sont gagnées. ::::: {.question} #### Peut-on placer des *capabilities* sur un script ? {-} De la même manière qu'il n'est pas possible d'avoir de script *setuid root* sous Linux[^NO_SETUID_SCRIPT], ajouter des *capabilities* à un script ne permettra pas d'en gagner. Le calcul s'effectue en effet sur les *capabilities* de l'exécutable de l'interpréteur et non sur celles du script. [^NO_SETUID_SCRIPT]: ::::: ### Gérer ses *capabilities* Un *thread* peut agir comme il le souhaite sur les ensembles *effective*, *permitted* et *inheritable*. À condition bien sûr de ne jamais dépasser les *capabilities* déjà contenues dans l'ensemble *permitted*, sauf si l'on dispose de la *capability* `CAP_SETPCAP` : dans ce cas, on se retrouve limité seulement par l'ensemble *bounding*. ::::: {.code} On utilise les appels système `capget(2)` et `capset(2)` pour respectivement connaître les *capabilities* actuelles de notre fil d'exécution et pour les écraser. Ces appels système utilisent une structure d'en-tête et une structure de données, qui sont définies dans `linux/capability.h` :
```c #define _LINUX_CAPABILITY_VERSION_1 0x19980330 #define _LINUX_CAPABILITY_U32S_1 1 /* V2 added in Linux 2.6.25; deprecated */ #define _LINUX_CAPABILITY_VERSION_2 0x20071026 #define _LINUX_CAPABILITY_U32S_2 2 /* V3 added in Linux 2.6.26 */ #define _LINUX_CAPABILITY_VERSION_3 0x20080522 #define _LINUX_CAPABILITY_U32S_3 2 typedef struct __user_cap_header_struct { uint32_t version; int pid; } *cap_user_header_t; typedef struct __user_cap_data_struct { uint32_t effective; uint32_t permitted; uint32_t inheritable; } *cap_user_data_t; ```
La structure d'en-tête permet de renseigner sur la version de la structure de données que l'on souhaite utiliser ou recevoir. Comme pour les *capabilities* dans les attributs étendus, la première version était utilisée lorsqu'il y avait moins de 33 *capabilities*, ce qui permettait de tout stocker sur un seul entier de 32 bits non signé. Les versions 2 et 3 sont identiques et permettent de récupérer les *capabilities* au moyen d'un tableau de 2 structures.\ ::::: {.warning} Les versions 2 et 3 ici ne sont pas comparables aux versions 2 et 3 de nos structures `vfs_cap_data` et `vfs_ns_cap_data` : il n'y a pas de notion de *namespace* ici. Il y a eu un couac dans les en-têtes distribués avec Linux 2.6.25, causant des *buffers overflow* dans les applications qui n'avaient pas prévu de gérer les versions. Cela a été corrigé dans la version 2.6.26 : un avertissement est consigné dans les journaux système en cas d'utilisation malheureuse de la version 2. ::::: Dans la pratique, on préférera utiliser `cap_get_proc(3)` et `cap_set_proc(3)` fournis par la `libcap`. ::::: Pour agir sur l'ensemble *bounding*, il faut disposer de la *capability* `CAP_SETPCAP`[^WHY_BOUNDING_SETPCAP]. Lorsque l'on retire une *capability* de cet ensemble, elle n'est pas retirée des autres ensembles et on peut donc continuer de bénéficier des privilèges qu'elle accorde. Il faut bien penser, lorsque l'on retire une *capability* de l'ensemble *bounding*, à également la retirer de l'ensemble *inheritable*, sans quoi si le programme exécuté a la *capability* en question dans ses attributs, celle-ci sera préservée (revoir la formule pour $p′_P$, l'ensemble *bounding* n'est pas considéré avec l'ensemble *inheritable*). [^WHY_BOUNDING_SETPCAP]: En effet, avant Linux 2.6.25, cet ensemble était utilisé par tout le système, pas seulement pas le *thread* courant et ses fils. ::::: {.code} La consultation et la modification de l'ensemble *bounding* se fait au moyen de `prctl(2)`, en utilisant les paramètres `PR_CAPBSET_READ` ou `PR_CAPBSET_DROP`. Le second paramètre attendu est l'une des constantes représentant une *capability*.\ Avec `PR_CAPBSET_READ`, `prctl(2)` retournera 0 si la *capability* ne fait pas partie de l'ensemble *bounding*, ou 1 si elle est bien présente. Avec `PR_CAPBSET_DROP`, `prctl(2)` retirera la *capability* passée en argument de l'ensemble *bounding*. Une fois cette action effectuée, il est impossible de revenir en arrière.\ Dans la pratique, on préférera utiliser `cap_get_bound(3)` et `cap_drop_bound(3)` fournis par la `libcap`. ::::: Enfin, le *thread* peut aussi modifier à sa guise l'ensemble *ambient*, à condition que les *capabilities* ajoutées soient dans les ensembles *permitted* et *inheritable*. ::::: {.code} On consulte et modifie l'ensemble *ambient* avec `prctl(2)` à qui l'on passe comme premier paramètre `PR_CAP_AMBIENT`. Le second paramètre permet de préciser l'action que l'on veut réaliser :\ - `PR_CAP_AMBIENT_RAISE` : ajoute la *capability* précisée comme troisième paramètre ; - `PR_CAP_AMBIENT_LOWER` : retire la *capability* précisée comme troisième paramètre ; - `PR_CAP_AMBIENT_IS_SET` : retourne 1 si la *capability* précisée comme troisième paramètre fait partie de l'ensemble *ambient*, sinon retourne 0 ; - `PR_CAP_AMBIENT_CLEAR_ALL` : vide l'ensemble *ambient*. ::::: ### Explorer les *capabilities* avec un shell La `libcap`, au travers des paquets `libcap2-bin` (Debian et ses dérivées) ou `libcap` (Alpine, Archlinux, Gentoo et leurs dérivées), apporte le programme `capsh(1)`, très utile pour appréhender les *capabilities*.
``` 42sh# capsh --drop=cap_net_raw -- -c "/bin/ping nemunai.re" /bin/bash: line 1: /bin/ping: Operation not permitted ```
Une autre commande intéressante est `pscap(1)`, de la `libcap-ng`. Parmi tous les programmes en cours d'exécution, cet utilitaire va afficher tous les programmes s'exécutant actuellement avec des *capabilities*, en indiquant pour chacun lesquelles sont actives et disponibles. ::::: {.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, avec en plus les informations sur son ensemble *ambient* et *bounding* :
``` 42sh# ./view_caps cap_user_header_t ----------------- Version: 20080522 PID: 42 cap_user_data_t --------------- effective: 0x3fffffffff CAP_AUDIT_CONTROL CAP_AUDIT_READ [...] CAP_SYSLOG CAP_WAKE_ALARM permitted: 0x3fffffffff CAP_AUDIT_CONTROL CAP_AUDIT_READ [...] CAP_SYSLOG CAP_WAKE_ALARM inheritable: 0x0 ambient: CAP_AUDIT_CONTROL CAP_AUDIT_READ [...] CAP_SYSLOG CAP_WAKE_ALARM bounding: CAP_AUDIT_CONTROL CAP_AUDIT_READ [...] CAP_SYSLOG CAP_WAKE_ALARM ```
``` 42sh$ ./view_caps cap_user_header_t ----------------- Version: 20080522 PID: 42 cap_user_data_t --------------- effective: 0x0 permitted: 0x0 inheritable: 0x0 ambient: bounding: CAP_AUDIT_CONTROL CAP_AUDIT_READ [...] CAP_SYSLOG CAP_WAKE_ALARM ```
Il peut être intéressant de lancer `view_caps` au sein d'un conteneur Docker pour voir évoluer l'ensemble *bounding*, ou bien d'utiliser `capsh(1)` en amont. ::::: ### 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) :\ - [POSIX Access Control Lists on Linux](https://www.usenix.org/legacy/publications/library/proceedings/usenix03/tech/freenix03/full_papers/gruenbacher/gruenbacher_html/main.html) :\