gdb disas main
Follow sylv1_secu on Twitter

Flux RSS


Derniers billets blog


























Version 3.0beta4-tuxfamily

Mais en fait, non ! - Le blog de Sylvain Sarméjeanne

News [linux] Même pas CAP

Je n'avais encore jamais joué avec les capabilities sous Linux. Il n'est jamais trop tard pour bien faire :)

Sur une distro GNU/Linux de base, un plus ou moins grand nombre de binaires setuid root sont présents. Par exemple, sur mon système, j'avais (cherchez l'intrus :)

# find / -type f -perm -004000
/usr/libexec/lockspool
/usr/libexec/dbus-daemon-launch-helper
/usr/lib/misc/ssh-keysign
/usr/lib/misc/xscreensaver/sonar
/usr/lib/misc/glibc/pt_chown
/usr/bin/smbumount
/usr/bin/Xorg
/usr/bin/rsh
/usr/bin/chage
/usr/bin/rcp
/usr/bin/tshark
/usr/bin/chsh
/usr/bin/newgrp
/usr/bin/expiry
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/sudo
/usr/bin/smbmnt
/usr/bin/lppasswd
/usr/bin/rlogin
/usr/bin/sudoedit
/usr/bin/dumpcap
/usr/kde/3.5/bin/kgrantpty
/usr/kde/3.5/bin/fileshareset
/usr/kde/3.5/bin/start_kdeinit
/sbin/unix_chkpwd
/bin/mount
/bin/passwd
/bin/su
/bin/umount
/bin/ping
/bin/.backdoor

La plupart des gens considèrent le bit suid sur un binaire appartant à root comme le Mal. Ce n'est pas toujours le cas. Par exemple, depuis la version 0.99.7, Wireshark vous permet de sniffer sur eth0 en tant que simple utilisateur, à partir du moment où vous faites partie du groupe wireshark. En fait, tout le code demandant des privilèges élevés est simplement passé dans dumpcap qui est setuid root, groupe wireshark seulement :

$ l `which dumpcap`
-r-sr-s--- 1 root wireshark 55K avril 13 23:47 /usr/bin/dumpcap

Cela vous évite donc de lancer la GUI Wireshark et ses 1.5 millions de lignes de code en root (NDS : tout cela est indiqué sous Gentoo lorsque vous installez le paquet). Le bit suid a donc augmenté la sécurité de votre système.

Mais il est aussi vrai qu'il suffit d'une vuln dans un binaire setuid et c'est game over ; on cherche donc à avoir le plus petit nombre possible de tels binaires. On peut même n'en avoir aucun, et c'est là où les capabilities entrent en jeu.

Pour que ça marche, il vous faut :

  • côte noyau : CONFIG_SECURITY_FILE_CAPABILITIES et CONFIG_EXT{2,3}_FS_XATTR (je crois que c'est tout)
  • côte userland : la libcap (je n'ai pas dit libpcap :)

On va prendre l'exemple hyper bateau du binaire ping. L'envoi d'un paquet ICMP nécessite l'ouverture d'une socket en mode RAW, opération privilégiée sous Linux. Pour que tout le monde puisse quand même utiliser ping, le binaire est donc setuid root :

$ l `which ping`
-rws--x--x 1 root root 36K août 31  2008 /bin/ping

On supprime le bit suid, le ping n'est plus possible :

# chmod -s `which ping`

$ ping www.free.fr
ping: icmp open socket: Operation not permitted

Un strace nous indique là où ça coince :

$ strace ping www.free.fr 2>&1 | grep EPERM
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = -1 EPERM (Operation not permitted)

Ne reste plus qu'à regarder à quelle capability ça correspond :

$ egrep -i "socket|raw" /usr/include/linux/capability.h
[...]
/* Allow use of RAW sockets */
#define CAP_NET_RAW          13
[...]

On modifie maintenant les attributs du fichier pour lui rajouter la capability CAP_NET_RAW. Le ping remarche bien :

# setcap CAP_NET_RAW=ep `which ping`

$ ping www.free.fr
PING www.free.fr (212.27.48.10) 56(84) bytes of data.
64 bytes from www.free.fr (212.27.48.10): icmp_seq=1 ttl=120 time=33.2 ms
[...]

Les flags ep correspondent à "Effective" et "Permitted" (cf la doc pour les détails).

A ce point, on a juste remplacé le bit suid par la capability. On aimerait aller plus loin, en attribuant par exemple la capability en fonction de l'utilisateur. C'est possible en appelant setcap avec les flags ei ("Effective" et "Inheritable") au lieu de ep :

# setcap CAP_NET_RAW=ei `which ping`

Il faut ensuite ajouter la ligne suivante dans /etc/pam.d/login :

auth       required     pam_cap.so

Enfin, il faut modifier le fichier /etc/security/capability.conf pour indiquer quel user se verra attribuer la capability :

$ grep sylv1 /etc/security/capability.conf 
cap_net_raw sylv1

Le ping marchera ainsi pour le user sylv1, mais pas pour les autres. En répétant la manip pour les autres binaires, vous obtiendrez ainsi un système sans binaire setuid root. A noter que l'utilisation des capabilities est un domaine d'actualité, par exemple Gentoo devrait par exemple travailler dessus lors du prochain Google Summer of Code.

On vient de voir que les capabilities peuvent être utilisées afin de restreindre les privilèges. Ils peuvent aussi être utilisés pour les augmenter ; si cela est fait intelligemment, cela peut aussi contribuer à améliorer la sécurité du système. Exemple : pour modifier votre conf réseau, vous ouvrez un shell root et lancez ifconfig ou route. Si vous laissez ce shell ouvert sans verrouiller votre session, c'est fini. Alors que vous auriez pu mettre la bonne capability, en l'occurence CAP_NET_ADMIN, et rester dans votre shell standard (évidemment, cela n'est applicable que si vous êtes seul sur le système). A méditer...

Mais ce n'est pas tout. Vous vous souvenez peut-être du MISC 13 (mai-juin 2004, et oui ça remonte :) où la page 50 parlait des capabilities. C'était pour introduire la notion de capability bounding set, un masque où chaque bit représente une capability : si un bit est à 0, la capability associée n'est disponible pour aucun processus. Vous pouvez donc utiliser /proc/sys/kernel/cap-bound (liée à la variable cap_bset du noyau) pour diminuer les capabilities de façon non-réversible (jusqu'au prochain reboot quoi). Problème : si vous oubliez aussi de désactiver CAP_SYS_RAWIO en plus de CAP_SYS_MODULE (chargement de module noyau), un attaquant peut accéder en dur à /dev/mem et écrire la valeur qu'il souhaite dans cap_bset. L'attaque ne date vraiment pas d'hier puisque l'article de MISC semble lui-même tiré d'un article datant de 2000.

De nos jours, c'est-à-dire depuis Linux 2.6.25, /proc/sys/kernel/cap-bound n'existe plus ; ce n'est plus un attribut global mais par thread, qui se transmet par fork/exec. Autrement dit, si vous voulez supprimer une capability pour tout le système, il faudra restreindre dès init. Regardons dans /usr/src/linux/include/linux/init_task.h :

#define INIT_TASK(tsk)	\
{
[...]
    .cap_bset = CAP_INIT_BSET,
[...]

Un peu plus haut :

#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
# define CAP_INIT_BSET  CAP_FULL_SET
#else
# define CAP_INIT_BSET  CAP_INIT_EFF_SET
#endif

Donc il faut modifier CAP_FULL_SET, défini dans /usr/src/linux/include/linux/capability.h :

# define CAP_FULL_SET ((kernel_cap_t){{ ~0, ~0 }})

Par défaut, le cap_bset vaut donc 0xffffffff (toutes les capabilities activées). Si on veut par exemple désactiver le chargement des modules pour tous, même root, on peut donc y rajouter CAP_SYS_MODULE (oui, je sais, c'est moins idiot de désactiver les modules dans la conf du noyau, c'est juste pour l'exemple :) :

# define CAP_FULL_SET ((kernel_cap_t){{ ~CAP_TO_MASK(CAP_SYS_MODULE), ~0 }})

N'oubliez pas aussi de désactiver CAP_SYS_RAWIO :)

Références :

Posté par sylv1 le 25/05/2009 à 23:45:00
2 commentaires

Ce billet est publié sous la licence CC-BY-SA.

[ Site créé par Sylvain Sarméjeanne ]
Cette page a été générée par mes scripts en 0.039 secondes :)
[Valid XHTML 1.1!] [Valid CSS!] [[Valid RSS]]