--[ SSH et la redirection X11 ]-- 1. Introduction Cette brève commente quelques problèmes liés à la redirection X11 avec SSH. Ces « problèmes » ne sont pas nouveaux, généralement largement documentés et même explicitement mentionnés dans la page de manuel du client OpenSSH : « X11 forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the user's X authorization database) can access the local X11 display through the forwarded connection. An attacker may then be able to perform activities such as keystroke monitoring. » L'idée de cette brève est donc de : - Faire un point rapide sur ces risques, de les illustrer et surtout d'introduire l'option '-Y' d'OpenSSH, qui met en oeuvre le mode de redirection X11 (X11Forwarding) dit « trusted ». - Plus généralement, rappeler que l'utilisation de SSH s'accompagne de quelques précautions (par exemple, pour lors de la mise en place de SFTP, qui dépasse le cadre de cette brève), même s'il s'agit d'une solution de référence pour le remplacement de telnet -- à défaut de telnet/ssl. 2. Redirection X11 Avant d'aborder la redirection X11, un premier type d'erreur fréquemment rencontré : négliger l'existence même du X11Forwarding, autrement dit, lancer son terminal « à la telnet » depuis la machine distante : host-client $ xhost +host-server ; ssh host-server host-server $ DISPLAY=host-client:10.0 xterm& En toute logique, si le serveur X de l'hôte host-client est effectivement en écoute sur la socket TCP spécifiée (correspondant au port 6010/TCP dans l'exemple), une fenêtre devrait apparaître au niveau de host-client. Le problème est double dans cette situation : - utilisation du mécanisme xhost, qui prend la main avant xauth, donnant accès, depuis host-server, au serveur X de host-client. => La sécurité s'appuie donc sur une identification du client X (host-server) par son adresse IP, et non une authentification de l'utilisateur connecté à host-server sur la base d'un secret partagé (cookie xauth). - trafic X11 en clair entre host-client et host-server. Flux X11 ------------------------------------- | 6010/TCP | \|/ | ---x--- ------- | | | | | | ==========================> | | | | Session SSH | | ------- ------- host-client host-server Remarque : « client » est à prendre au sens de « client SSH », mais aussi de « serveur X11 ». Afin d'éviter un flux X11 « en clair », l'option X11 Forwarding de SSH est donc à activer -- au niveau client comme au niveau serveur -- mais, nous allons le voir, avec précaution. Au niveau du client, il s'agit de l'option en ligne de commande '-X'. Au niveau du serveur, il s'agit de la directive « X11Forwarding yes » du fichier de configuration sshd_config. Pour illustrer le fonctionnement et les problèmes potentiels, considérons l'exemple suivant -- cas d'un poste d'administration se connectant (avec déport d'affichage via SSH) sur un serveur distant infogéré : - L'utilisateur admin souhaite se connecter via SSH à l'hôte compromised-server, depuis l'hôte network-admin ; au niveau de network-admin, la variable d'environnement DISPLAY est :0.0 : admin@network-admin > echo $DISPLAY :0.0 => Le serveur X correspondant est en écoute sur la socket unix suivante : srwxrwxrwx 1 root root 0 2004-09-28 11:52 /tmp/.X11-unix/X0 L'application locale doit donc se connecter à ce serveur X pour affichage. Le magic-cookie, secret à partager entre serveur X et applications souhaitant se connecter à ce dernier, est obtenu par la commande xauth(1) : admin@network-admin > xauth list $DISPLAY network-admin/unix:0 MIT-MAGIC-COOKIE-1 << 32 character hexkey >> La valeur du magic-cookie est la chaîne de 32 caractères obtenue. Cette dernière est enregistrée dans le fichier ~/.Xauthority de l'utilisateur -- les permissions sur ce dernier doivent donc être particulièrement strictes. Elle est transmise en clair du client au serveur X, d'où l'intérêt d'une encapsulation dans un canal SSH. - L'utilisateur admin se connecte sur l'hôte compromised-server, en activant la redirection X11 -- autorisée au niveau du serveur SSH (X11Forwarding positionné à yes) : admin@network-admin > ssh -X compromised-server admin@compromised-server > Le DISPLAY sur le serveur compromised-server pointe sur une socket TCP locale en 6010/TCP : admin@compromised-server > echo $DISPLAY localhost:10.0 => Effectivement, au niveau du serveur, le port 6010/TCP est ouvert localement par le démon sshd, qui assure la redirection X11 : root@compromised-server # lsof -ni TCP:6010 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME sshd 15663 root 9u IPv4 437835 TCP 127.0.0.1:6010 (LISTEN) => Ainsi, les connexions sur compromised-server en 127.0.0.1:6010/TCP seront redirigées sur network-admin vers la socket unix /tmp/.X11-unix/.X0 par SSH. Reste le problème de l'authentification X11 : en mode debug2 (-vv), on constate que la commande suivante est exécutée par SSH au niveau client, afin de récupérer le magic-cookie xauth -- qui permettra de se connecter à X via la socket unix : debug2: x11_get_proto: /usr/bin/X11/xauth list :0.0 . 2>/dev/null Où :0.0 correspond au DISPLAY local (network-admin). Le danger survient, classiquement, lorsque l'hôte compromised-server est.. compromis (dans le pire des cas -- des droits d'accès permissifs sur ~/.Xauthority conduiraient au même résultat) : un utilisateur possédant les privilèges suffisants pour accéder au fichier ~/.Xauthority -- manipulé par la commande xauth(1) -- de l'utilisateur admin sera en mesure de se connecter au serveur X local en 6010/TCP (compromised-server) redirigé au niveau de l'hôte du serveur X du client SSH (network-admin), sorte de « back channel ». Figure : redirection de compromised-server:127.0.0.1:6010/TCP vers network-admin:/tmp/.X11-unix/X0 via SSH avec authentification par xauth(1) ----- --------------------- \|/ | | | ---x--- | | ------- | | .X0 | --------------------------- | x 6010/TCP - | | ==========================> | | | | Session SSH | | ------- ------- network-admin compromised-server Remarque : l'utilisation d'une socket TCP est rendue nécessaire par la séparation des privilèges dans OpenSSH, dans la mesure où le processus possède alors l'identité de l'utilisateur authentifié, et ne peut donc écrire dans le répertoire /tmp/.X11-unix dans la mesure où il n'est pas SUID root. Dès lors, les « attaques » ultra classiques relatives à X11 peuvent être mises en oeuvre au niveau du serveur compromis, afin de capturer les évènements X via xev(1) par exemple, ou encore afin d'exécuter des commandes au niveau du poste client, par l'intermédiaire des XSendEvent -- dont le résultat sera variable suivant le terminal utilisé (dans l'exemple, aterm(1) est utilisé et accepte de tels évènements). On imagine sans difficulté les conséquences de la compromission d'un serveur Web (par exemple) administré depuis un réseau d'admin dédié via une solution de type SSH + X11 Forwarding. Voire la situation d'un prestataire infogérant n plateformes de différents clients, présentant potentiellement des niveaux de sécurité hétérogènes. Plus efficace qu'un long discours (plugin Flash nécessaire) : http://www.hsc.fr/~davy/breves/ssh.html Remarque : dans la démonstration, l'utilisateur root dispose d'un accès en lecture au fichier ~/.Xauthority de l'utilisateur admin. 3. Redirection X11 en mode « trusted » Dans tout l'exposé précédent, l'option '-X' est utilisée. En fait, le comportement de cette option a été modifié - ou plutôt « enrichi » - dans les versions postérieures à OpenSSH 3.8p1, via l'introduction de l'option '-Y'. Cette dernière est décrite (très succinctement) de la façon suivante : « -Y Enables trusted X11 forwarding. » En pratique, l'option '-X' active le forwarding X11. Son comportement dépend ensuite de la présence ou non de l'option '-Y' sur la ligne de commande, ou de la configuration par défaut dans le fichier ssh_config. Par défaut, la configuration adoptée est « untrusted » (ForwardX11Trusted no), qui correspond à ne pas employer l'option '-Y'. La notion de client X « trusted » et « untrusted » est définie dans les extensions de sécurité X11 qui mettent en oeuvre un contrôle d'accès par client, fondé sur la politique de sécurité décrite dans le fichier /etc/X11/xserver/SecurityPolicy. Dans ce mode, le client SSH génère un cookie xauth à la volée, qui sera donc considéré comme « untrusted », et donnera un accès restreint par la politique de sécurité aux programmes l'employant. Le principe est, une fois la session SSH authentifiée, que le client SSH exécute la commande suivante : debug2: x11_get_proto: /usr/bin/X11/xauth -f /tmp/ssh-TPOAQ11862/xauthfile generate :0.0 MIT-MAGIC-COOKIE-1 untrusted timeout 1200 2>/dev/null debug2: x11_get_proto: /usr/bin/X11/xauth list :0.0 . 2>/dev/null => Le mot-clef « untrusted » est employé dans la commande xauth(1). Ainsi, dans un premier temps, le client SSH génère un cookie « untrusted » qu'il associe au serveur local (:0.0). Ce cookie est alors récupéré par la commande « list ». Il est donc associé au serveur X local, tout en étant différent du cookie statique et trusted utilisé par les applications locales. Un *autre* cookie est alors généré par le client SSH, et transmis au serveur SSH cette fois -- ce dernier alors met à jour le fichier ~/.Xauthority de l'utilisateur au niveau du serveur SSH en lançant les scripts ~/.ssh/rc, /etc/ssh/sshrc ou, en dernier lieu, la commande xauth (avec l'option add). À titre d'illustration, la page de manuel de sshd(8) précise que de tels scripts, s'ils sont présents, doivent gérer la redirection X11 (détectée par une variable d'environnement $DISPLAY initialisée), de la façon suivante : if read proto cookie && [ -n "$DISPLAY" ]; then if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then # X11UseLocalhost=yes echo add unix:`echo $DISPLAY | cut -c11-` $proto $cookie else # X11UseLocalhost=no echo add $DISPLAY $proto $cookie fi | xauth -q - => script qui a donc pour effet de mettre à jour le fichier ~/.Xauthority au niveau du serveur SSH. Ensuite, pour toute redirection X11 à travers le tunnel, le client SSH réalisera l'association entre le cookie untrusted présenté et le cookie untrusted réellement utilisé au niveau du serveur X (localement, récupéré via xauth list). => D'où le terme de « X11 forwarding with authentication spoofing. » mentionné en mode verbeux : le cookie employé par les applications déportées est remplacé (par le client SSH) par le cookie réel employé au niveau du serveur X. Par rapport à la démonstration, en mode « untrusted » la capture ou l'envoi d'évènements n'est donc plus possible. En revanche, l'énumération via xlsclients est toujours autorisée, mais elle peut être désactivée dans le fichier /etc/X11/xserver/SecurityPolicy pour les clients « untrusted ». Remarque : au contraire, avec l'option '-Y' (mode trusted), la commande suivante serait exécutée : debug2: x11_get_proto: /usr/bin/X11/xauth list :0.0 . 2>/dev/null Le cookie ainsi généré permet un accès sans limitation au serveur X, les risques précédemment évoqués s'appliquent donc. Encore une fois, le cookie niveau serveur SSH est différent du cookie local au client SSH. Suivant les distributions, le comportement par défaut peut changer. Typiquement, sous debian, la page de manuel de ssh_config(5) explique : ForwardX11Trusted [...] The default is ``yes'' (Debian-specific). See the X11 SECURITY extension specification for full details on the restrictions imposed on untrusted clients. Ainsi, les commandes « ssh -X host » et « ssh -Y host » sont équivalentes dans ce cas précis, et correspondent à un mode « trusted ». Les raisons justifiant une telle configuration sont aussi bien d'ordre pratique que technique, par exemple, celles notamment avancées dans ces messages suivants -- cas de la distribution debian-knoppix, suivi d'une entrée de la FAQ du serveur Cygwin/X, relatif à un dysfonctionnement en mode « untrusted » : http://mailman.linuxtag.org/pipermail/debian-knoppix/2004-May/005235.html http://x.cygwin.com/docs/faq/cygwin-x-faq.txt 4. Conclusion On retiendra que l'application SSH est bien plus qu'un simple « telnet chiffré/authentifié », et comporte quelques effets de bord pour la sécurité qui, sans constituer une « vulnérabilité », sont à considérer. Aux précautions à prendre concernant les redirections X11, on ajoutera celles liées au remplacement du protocole FTP par SCP on SFTP dans les environnements de production (très courant), visant à offrir un FTP « chiffré/authentifié », mais offrant, en fait, un accès distant avec shell et redirection de connexions TCP. Problème auquel la réponse généralement apportée consistera en la mise en place d'un serveur SSH sécurisé (désactivation du TCP Forwarding, mise en place de wrapper/shells restreints pour les utilisateurs), voire d'une solution alternative de FTP/SSL http://www.hsc.fr/ressources/breves/ftp-ssl.html.fr 5. Références Quelques papiers/présentations très intéressants faisant un point sur les mécanismes d'authentification et la sécurité dans X11, et les attaques traditionnelles qui s'y rapportent : - « Enhancing XFree86 Security » par Matthieu Herrb -- auquel vont nos remerciements pour ses remarques et son avis éclairé sur le sujet : http://www.openbsd.org/papers/xf86-sec.pdf - « SSH X11 forwarding considered harmful » par Holger van Lengerich : http://www.giac.org/practical/GCIH/Holger_Van_Lengerich_GCIH.pdf Enfin, quelques pointeurs permettant, de façon similaire, d'offrir un canal de retour vers le poste client connecté (sans X11Forwarding dans cette situation), via des failles au niveau des émulateurs de terminaux : - « Vulnérabilité dans les émulateurs de terminaux » http://www.certa.ssi.gouv.fr/site/CERTA-2003-AVI-067/index.html - « Terminal Emulator Security Issues » http://www.securityfocus.com/archive/1/313007 par Franck Davy (27/09/2004)