virli/tutorial/3/capabilities.md

25 KiB
Raw Blame History

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 : net.ipv4.ping_group_range. Mais ce n'est que depuis 2020 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)1. L'ensemble évolue automatiquement à la baisse si une capability n'est plus permitted.

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 POSIX2 (espace system) :

```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 permissions sont un champ de bits pour la lecture, écriture et exécution. Enfin l'identifiant 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 Linux3, 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.

:::::

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;

</div>

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*.

<div lang="en-US">

42sh# capsh --drop=cap_net_raw -- -c "/bin/ping nemunai.re" /bin/bash: line 1: /bin/ping: Operation not permitted

</div>

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 :

<div lang="en-US">

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

</div>

Appelé sans argument, `view_caps` affichera les *capabilities* du processus
courant, avec en plus les informations sur son ensemble *ambient* et
*bounding* :

<div lang="en-US">

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

</div>

<div lang="en-US">

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

</div>

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) :\
  <https://blog.container-solutions.com/linux-capabilities-why-they-exist-and-how-they-work>
* [Guidelines for extended attributes](https://www.freedesktop.org/wiki/CommonExtendedAttributes/) :\
  <https://www.freedesktop.org/wiki/CommonExtendedAttributes/>
* [File-based capabilities](https://lwn.net/Articles/211883/) : <https://lwn.net/Articles/211883/>
* [A bid to resurrect Linux capabilities](https://lwn.net/Articles/199004/) : <https://lwn.net/Articles/199004/>
* [False Boundaries and Arbitrary Code Execution](https://forums.grsecurity.net/viewtopic.php?f=7&t=2522#p10271) :\
  <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) :\
  <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) :\
  <https://www.usenix.org/legacy/publications/library/proceedings/usenix03/tech/freenix03/full_papers/gruenbacher/gruenbacher_html/main.html>

  1. Le problème ayant donné naissance aux ambient capabilities est décrit dans cet échange : https://lwn.net/Articles/636533/ ↩︎

  2. 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. ↩︎

  3. https://unix.stackexchange.com/a/2910 ↩︎