Tuto 3 done

This commit is contained in:
nemunaire 2021-10-05 17:23:09 +02:00
commit 8c402e6d65
15 changed files with 604 additions and 310 deletions

View file

@ -1,16 +1,14 @@
\newpage
Les *capabilities*
==================
## Présentation
------------------
Historiquement, dans la tradition UNIX, on distinguait deux catégories de
processus :
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
* 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
@ -21,24 +19,25 @@ 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 :
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 39 en tout (ça dépend de la
version du noyau) !
* `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'existe pas d'interface
exposée par le noyau aux utilisateurs pour envoyer des paquets ICMP. Pour le
faire, il est nécessaire de pouvoir écrire directement sur l'interface ; ça,
seul le super-utilisateur peut le faire.
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*. Cela permet à n'importe quel
@ -48,35 +47,88 @@ 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 à
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
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`.\
## Les attributs de fichier étendus
Si vous vous rendez compte que votre binaire `ping` est dans ce cas, testez
depuis un conteneur, par exemple :
Une grosse majorité des systèmes de fichiers (ext[234], XFS, btrfs, ...)
<div lang="en-US">
```
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)$ _
```
</div>
Dans le conteneur le binaire `ping` est *setuid root*, vous pouvez faire des
tests en retirant le *setuid* :
<div lang="en-US">
```
(ctnr)# chmod u-s /bin/ping
(ctnr)$ ping epita.fr
ping: socket: Operation not permitted
```
</div>
Puis en ajoutant la *capability* :
<div lang="en-US">
```
(ctnr)# setcap cap_net_raw+p /bin/ping
(ctnr)$ ping epita.fr
PING epita.fr (172.67.156.141) 56(84) bytes of data.
```
</div>
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 :
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 ;
* *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
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 :
Par exemple, on peut définir un attribut sur un fichier comme cela :
<div lang="en-US">
```bash
@ -88,14 +140,16 @@ user.foo="bar"
```
</div>
Encore plus fort, vous pouvez utiliser les ACL POSIX :
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 :
<div lang="en-US">
```bash
42sh$ sudo chown root:root toto && sudo chmod o-r toto
42sh$ cat toto
cat: toto: Permission denied
42sh$ sudo setfattr -m u:$USER:r toto
42sh$ sudo setfacl -m u:$USER:r toto
42sh$ cat toto
Hello World!
```
@ -104,7 +158,7 @@ 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 avec la commande :
Vous pouvez voir ces attributs bruts avec la commande :
<div lang="en-US">
```bash
@ -114,17 +168,19 @@ system.posix_acl_access=0sgAAEAD/////AgAEOgDAEAA/////xAABAD////8=
```
</div>
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`
#### `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 alors voir le Setuid root comme
l'utilisation de cet attribut auquel on accroîtrait l'ensemble des
*capabilities*.
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 :
Si votre distribution profite de ces attributs étendus, vous devriez obtenir :
<div lang="en-US">
```bash
@ -134,7 +190,8 @@ security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA=
```
</div>
Ou, dans sa version plus lisible :
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 :
<div lang="en-US">
```bash
@ -144,10 +201,10 @@ Ou, dans sa version plus lisible :
</div>
## Exercice : visualisateur de capabilities d'un processus {-}
### Exercice : visualisateur de capabilities d'un processus {-}
Écrivons maintenant un programme permettant de voir les *capabilities*
d'un processus :
d'un processus :
<div lang="en-US">
```
@ -179,24 +236,27 @@ inheritable: 0x0
```
</div>
Astuces : `capget(2)`, X-macros, ...
Appelé sans argument, `view_caps` affichera les capabilities du processus
courant.
Astuces : `capget(2)`, X-macros, ...
## Pour aller plus loin {-}
### Pour aller plus loin {-}
Je vous recommande la lecture des *man* suivants :
Je vous recommande la lecture des *man* suivants :
* `capabilities(7)` : énumérant tous les capabilities, leur utilisation, etc. ;
* `xattrs(7)` : à propos des attributs étendus.
* `capabilities(7)` : énumérant tous les capabilities, leur utilisation, etc. ;
* `xattrs(7)` : à propos des attributs étendus.
Et de ces quelques articles :
Et de ces quelques articles :
* [Secure Your Containers with this One Weird Trick](https://www.redhat.com/en/blog/secure-your-containers-one-weird-trick)
* [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)
Pour revenir à Docker, par défaut, un certain nombre de *capabilities* sont
sactivées par défaut ; vous pouvez en ajouter et en retirer via les arguments
`--cap-add` et `--cap-drop` du `docker container run`.
Pour revenir à Docker, un certain nombre de *capabilities* sont désactivées par
faut ; vous pouvez en ajouter et en retirer via les arguments `--cap-add` et
`--cap-drop` du `docker container run`.