Le setuid
le bit d'autorisation indique à Linux d'exécuter un programme avec l'ID utilisateur effectif du propriétaire au lieu de l'exécuteur :
> cat setuid-test.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv) {
printf("%d", geteuid());
return 0;
}
> gcc -o setuid-test setuid-test.c
> ./setuid-test
1000
> sudo chown nobody ./setuid-test; sudo chmod +s ./setuid-test
> ./setuid-test
65534
Cependant, cela ne s'applique qu'aux exécutables; les scripts shell ignorent le bit setuid :
> cat setuid-test2
#!/bin/bash
id -u
> ./setuid-test2
1000
> sudo chown nobody ./setuid-test2; sudo chmod +s ./setuid-test2
> ./setuid-test2
1000
Wikipédia dit :
En raison de la probabilité accrue de failles de sécurité, de nombreux systèmes d'exploitation ignorent l'attribut setuid lorsqu'il est appliqué à des scripts shell exécutables.
En supposant que je sois prêt à accepter ces risques, existe-t-il un moyen de dire à Linux de traiter le bit setuid de la même manière sur les scripts shell que sur les exécutables ?
Si non, existe-t-il une solution de contournement commune à ce problème ? Ma solution actuelle consiste à ajouter un sudoers
entrée pour autoriser ALL
pour exécuter un script donné en tant qu'utilisateur sous lequel je veux qu'il s'exécute, avec NOPASSWD
pour éviter l'invite de mot de passe. Le principal inconvénient est la nécessité d'un sudoers
entrée chaque fois que je veux faire cela, et la nécessité pour l'appelant de sudo some-script
au lieu de simplement some-script
Réponse acceptée :
Linux ignore le bit setuid¹ sur tous les exécutables interprétés (c'est-à-dire les exécutables commençant par un #!
doubler). La FAQ comp.unix.questions explique les problèmes de sécurité avec les scripts shell setuid. Ces problèmes sont de deux sortes :liés au shebang et liés au shell; J'entre dans plus de détails ci-dessous.
Si vous ne vous souciez pas de la sécurité et que vous souhaitez autoriser les scripts setuid, sous Linux, vous devrez corriger le noyau. À partir des noyaux 3.x, je pense que vous devez ajouter un appel à install_exec_creds
dans le load_script
fonction, avant l'appel à open_exec
, mais je n'ai pas testé.
Shebang sétuide
Il y a une condition de concurrence inhérente à la façon dont le shebang (#!
) est généralement implémenté :
- Le noyau ouvre l'exécutable et constate qu'il commence par
#!
. - Le noyau ferme l'exécutable et ouvre l'interpréteur à la place.
- Le noyau insère le chemin du script dans la liste d'arguments (comme
argv[1]
) et exécute l'interpréteur.
Si les scripts setuid sont autorisés avec cette implémentation, un attaquant peut invoquer un script arbitraire en créant un lien symbolique vers un script setuid existant, en l'exécutant et en s'arrangeant pour modifier le lien après que le noyau a effectué l'étape 1 et avant que l'interpréteur n'arrive à ouvrant son premier argument. Pour cette raison, la plupart des unix ignorent le bit setuid quand ils détectent un shebang.
Une façon de sécuriser cette implémentation serait que le noyau verrouille le fichier de script jusqu'à ce que l'interpréteur l'ait ouvert (notez que cela doit empêcher non seulement de dissocier ou d'écraser le fichier, mais également de renommer tout répertoire dans le chemin). Mais les systèmes Unix ont tendance à éviter les verrous obligatoires, et les liens symboliques rendraient une fonction de verrouillage correcte particulièrement difficile et envahissante. Je ne pense pas que quiconque le fasse de cette façon.
Quelques systèmes Unix (principalement OpenBSD, NetBSD et Mac OS X, qui nécessitent tous l'activation d'un paramètre du noyau) implémentent secure setuid shebang en utilisant une fonctionnalité supplémentaire :le chemin /dev/fd/N
fait référence au fichier déjà ouvert sur le descripteur de fichier N (donc ouvrir /dev/fd/N
est à peu près équivalent à dup(N)
). De nombreux systèmes Unix (y compris Linux) ont /dev/fd
mais pas les scripts setuid.
- Le noyau ouvre l'exécutable et constate qu'il commence par
#!
. Disons que le descripteur de fichier pour l'exécutable est 3. - Le noyau ouvre l'interpréteur.
- Le noyau insère
/dev/fd/3
la liste des arguments (commeargv[1]
) et exécute l'interpréteur.
La page shebang de Sven Mascheck a beaucoup d'informations sur le shebang à travers les unices, y compris le support setuid.
Interprètes Setuid
Supposons que vous ayez réussi à exécuter votre programme en tant que root, soit parce que votre système d'exploitation prend en charge le setuid shebang, soit parce que vous avez utilisé un wrapper binaire natif (tel que sudo
). Avez-vous ouvert une faille de sécurité ? Peut-être . Le problème ici n'est pas sur les programmes interprétés vs compilés. Le problème est de savoir si votre système d'exécution se comporte en toute sécurité s'il est exécuté avec des privilèges.
-
Tout exécutable binaire natif lié dynamiquement est en quelque sorte interprété par le chargeur dynamique (par exemple
/lib/ld.so
), qui charge les bibliothèques dynamiques requises par le programme. Sur de nombreux unix, vous pouvez configurer le chemin de recherche des bibliothèques dynamiques via l'environnement (LD_LIBRARY_PATH
est un nom commun pour la variable d'environnement), et même charger des bibliothèques supplémentaires dans tous les binaires exécutés (LD_PRELOAD
). L'invocateur du programme peut exécuter du code arbitraire dans le contexte de ce programme en plaçant unlibc.so
spécialement conçu dans$LD_LIBRARY_PATH
(entre autres tactiques). Tous les systèmes sains ignorent leLD_*
variables dans les exécutables setuid. -
Dans des coquilles comme sh, csh et les dérivés, les variables d'environnement deviennent automatiquement des paramètres du shell. Via des paramètres tels que
PATH
,IFS
, et bien d'autres encore, l'invocateur du script a de nombreuses opportunités d'exécuter du code arbitraire dans le contexte des scripts shell. Certains shells définissent ces variables sur des valeurs par défaut saines s'ils détectent que le script a été invoqué avec des privilèges, mais je ne sais pas s'il existe une implémentation particulière à laquelle je ferais confiance. -
La plupart des environnements d'exécution (qu'ils soient natifs, bytecode ou interprétés) ont des fonctionnalités similaires. Peu de gens prennent des précautions particulières dans les exécutables setuid, bien que ceux qui exécutent du code natif ne fassent souvent rien de plus sophistiqué que la liaison dynamique (qui prend des précautions).
-
Perl est une exception notable. Il prend explicitement en charge les scripts setuid de manière sécurisée. En fait, votre script peut exécuter setuid même si votre système d'exploitation a ignoré le bit setuid sur les scripts. Cela est dû au fait que perl est livré avec un assistant racine setuid qui effectue les vérifications nécessaires et réinvoque l'interpréteur sur les scripts souhaités avec les privilèges souhaités. Ceci est expliqué dans le manuel perlsec. Auparavant, les scripts perl setuid nécessitaient
#!/usr/bin/suidperl -wT
au lieu de#!/usr/bin/perl -wT
, mais sur la plupart des systèmes modernes,#!/usr/bin/perl -wT
est suffisant.
Notez que l'utilisation d'un wrapper binaire natif ne fait rien en soi pour éviter ces problèmes . En fait, cela peut aggraver la situation , car cela pourrait empêcher votre environnement d'exécution de détecter qu'il est appelé avec des privilèges et de contourner sa configurabilité d'exécution.
Un wrapper binaire natif peut sécuriser un script shell si le wrapper nettoie l'environnement . Le script doit veiller à ne pas faire trop d'hypothèses (par exemple sur le répertoire courant) mais cela va. Vous pouvez utiliser sudo pour cela à condition qu'il soit configuré pour assainir l'environnement. La mise sur liste noire des variables est sujette aux erreurs, donc toujours sur liste blanche. Avec sudo, assurez-vous que le env_reset
l'option est activée, que setenv
est désactivé, et que env_file
et env_keep
ne contiennent que des variables anodines.
TL,DR :
- Setuid shebang n'est pas sûr mais généralement ignoré.
- Si vous exécutez un programme avec des privilèges (via sudo ou setuid), écrivez du code natif ou perl, ou démarrez le programme avec un wrapper qui nettoie l'environnement (comme sudo avec le
env_reset
option).
¹ Cette discussion s'applique également si vous substituez "setgid" à "setuid" ; ils sont tous deux ignorés par le noyau Linux sur les scripts