virli/tutorial/3/capabilities.md

202 lines
6.3 KiB
Markdown
Raw Normal View History

2016-10-05 09:13:56 +00:00
\newpage
2017-10-23 20:25:51 +00:00
Les *capabilities*
==================
2016-10-05 09:13:56 +00:00
## Présentation
2017-10-23 20:25:51 +00:00
Historiquement, dans la tradition UNIX, on distinguait deux catégories de
2016-10-05 20:49:08 +00:00
processus :
2016-10-05 09:13:56 +00:00
2016-10-05 20:49:08 +00:00
* les processus *privilégiés* : dont l'identifiant de son utilisateur est 0 ;
* les processus *non-privilégiés* : dont l'identifiant 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 ;
2016-10-06 01:58:52 +00:00
* et beaucoup d'autres, il y en a environ 39 en tout (ça dépend de la
version du noyau) !
2016-10-05 20:49:08 +00:00
### `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.
Pour permettre à tous les utilisateurs de pouvoir envoyer des ping, le
2017-10-22 22:14:32 +00:00
programme est donc généralement *Setuid root*. Cela permet à n'importe quel
2016-10-05 20:49:08 +00:00
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
2017-10-22 22:14:32 +00:00
programmes *Setuid root*. En effet, s'il devient possible pour un utilisateur
2016-10-05 20:49:08 +00:00
d'exécuter du code arbitraire, ce code sera exécuté avec les privilèges de
2017-10-22 22:14:32 +00:00
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.
2016-10-05 20:49:08 +00:00
C'est donc à ce moment que les *capabilities* entrent en jeu : un processus (ou
2016-10-06 01:58:52 +00:00
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`.
2016-10-05 20:49:08 +00:00
## Les attributs de fichier étendus
Une grosse majorité des systèmes de fichiers (ext[234], XFS, btrfs, ...)
2017-10-23 20:25:51 +00:00
permet d'enregistrer, pour chaque fichier, des attributs (dits attributs
2016-10-05 20:49:08 +00:00
*é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 :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```bash
42sh$ echo 'Hello World!' > toto
42sh$ setfattr -n user.foo -v bar toto
42sh$ getfattr -d toto
# file: toto
user.foo="bar"
2016-10-05 20:49:08 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-05 20:49:08 +00:00
Encore plus fort, vous pouvez utiliser les ACL POSIX :
2017-10-17 06:29:07 +00:00
<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$ cat toto
Hello World!
2016-10-05 20:49:08 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-05 20:49:08 +00:00
2016-10-06 01:58:52 +00:00
Bien que les droits UNIX traditionnels ne vous donnent pas accès au fichier,
les ACL POSIX vous autorisent à le lire.
2016-10-05 20:49:08 +00:00
Vous pouvez voir ces attributs avec la commande :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```bash
42sh$ getfattr -d -m "^system" toto
# file: toto
system.posix_acl_access=0sgAAEAD/////AgAEOgDAEAA/////xAABAD////8=
2016-10-05 20:49:08 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-05 20:49:08 +00:00
### `ping`
2017-10-23 20:25:51 +00:00
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
2016-10-05 20:49:08 +00:00
l'utilisation de cet attribut auquel on accroîtrait l'ensemble des
*capabilities*.
Si votre distribution profite de ces attributs étendus, vous devriez obtenir :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```bash
42sh$ getfattr -d -m "^security" $(which ping)
# file: bin/ping
security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA=
2016-10-05 20:49:08 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-05 20:49:08 +00:00
Ou, dans sa version plus lisible :
2017-10-17 06:29:07 +00:00
<div lang="en-US">
```bash
42sh$ getcap $(which ping)
/bin/ping = cap_net_raw+ep
2016-10-05 20:49:08 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-05 09:13:56 +00:00
## Exercice : visualisateur de capabilities d'un processus
2016-10-06 01:58:52 +00:00
Écrivons maintenant un programme permettant de voir les *capabilities*
d'un processus :
2016-10-05 20:49:08 +00:00
2017-10-17 06:29:07 +00:00
<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
2016-10-05 09:13:56 +00:00
```
2017-10-17 06:29:07 +00:00
</div>
2016-10-05 20:49:08 +00:00
2016-10-06 01:58:52 +00:00
Astuces : `capget(2)`, X-macros, ...
2016-10-05 20:49:08 +00:00
## Pour aller plus loin {-}
2016-10-05 20:49:08 +00:00
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 :
2017-10-15 22:10:10 +00:00
* [Secure Your Containers with this One Weird Trick](https://rhelblog.redhat.com/2016/10/17/secure-your-containers-with-this-one-weird-trick/)
2016-10-06 01:58:52 +00:00
* [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/)
2017-10-25 20:07:57 +00:00
* [False Boundaries and Arbitrary Code Execution](https://forums.grsecurity.net/viewtopic.php?f=7&t=2522&sid=c6fbcf62fd5d3472562540a7e608ce4e#p10271)
2016-10-06 01:58:52 +00:00
Pour revenir à Docker, par défaut, un certain nombre de *capabilities* sont
désactivées par défaut ; vous pouvez en ajouter et en retirer via les arguments
2017-10-22 22:14:32 +00:00
`--cap-add` et `--cap-drop` du `docker container run`.