web-dev-qa-db-fra.com

Autoriser setuid sur les scripts Shell

Le bit d'autorisation setuid 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

Wikipedia 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 suis 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?

Sinon, existe-t-il une solution de contournement commune à ce problème? Ma solution actuelle consiste à ajouter une entrée sudoers pour permettre à ALL d'exécuter un script donné en tant qu'utilisateur sous lequel je souhaite l'exécuter, avec NOPASSWD pour éviter l'invite de mot de passe. Le principal inconvénient de cela est la nécessité d'une entrée sudoers chaque fois que je veux le faire, et la nécessité pour l'appelant de Sudo some-script au lieu de simplement some-script

195
Michael Mrozek

Linux ignore le bit setuid¹ sur tous les exécutables interprétés (c'est-à-dire les exécutables commençant par une ligne #!). La comp.unix.questions FAQ explique les problèmes de sécurité avec les scripts setuid Shell. Ces problèmes sont de deux types: liés à Shebang et liés à Shell; J'entre dans plus de détails ci-dessous.

Si vous ne vous souciez pas de la sécurité et souhaitez autoriser les scripts setuid, sous Linux, vous devrez patcher le noyau. Depuis les noyaux 3.x, je pense que vous devez ajouter un appel à install_exec_creds dans la fonction load_script , avant l'appel à open_exec, Mais je n'ai pas testé.


Setuid Shebang

Il existe une condition de concurrence inhérente à la façon dont Shebang (#!) Est généralement implémenté:

  1. Le noyau ouvre l'exécutable et constate qu'il commence par #!.
  2. Le noyau ferme l'exécutable et ouvre l'interpréteur à la place.
  3. Le noyau insère le chemin du script dans la liste des 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 prenant des dispositions pour modifier le lien après que le noyau a effectué l'étape 1 et avant que l'interprète ne se déplace vers ouvrant son premier argument. Pour cette raison, la plupart des unités ignorent le bit setuid lorsqu'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ète l'ouvre (notez que cela doit empêcher non seulement de dissocier ou d'écraser le fichier, mais aussi de renommer n'importe quel répertoire du 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 invasive. Je pense que personne ne le fait de cette façon.

Quelques systèmes Unix (principalement OpenBSD, NetBSD et Mac OS X, qui nécessitent tous un paramètre du noyau pour être activé) implémentent setuid sécurisé Shebang en utilisant un fonctionnalité: 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 des scripts /dev/fd Mais pas setuid.

  1. Le noyau ouvre l'exécutable et constate qu'il commence par #!. Disons que le descripteur de fichier pour l'exécutable est 3.
  2. Le noyau ouvre l'interpréteur.
  3. Le noyau insère /dev/fd/3 La liste d'arguments (comme argv[1]) Et exécute l'interpréteur.

La page Shebang de Sven Mascheck contient beaucoup d'informations sur Shebang à travers les unités, y compris support setuid .


Interprètes setuides

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 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 est pas à propos des 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 appareils, 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 un libc.so Spécialement conçu dans $LD_LIBRARY_PATH (Entre autres tactiques). Tous les systèmes sensés ignorent les variables LD_* Dans les exécutables setuid.

  • Dans les shells tels que sh, csh et dérivés, les variables d'environnement deviennent automatiquement des paramètres Shell. Grâce à des paramètres tels que PATH, IFS, et bien d'autres, l'invocateur du script a de nombreuses possibilité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 (natifs, bytecode ou interprétés) ont des fonctionnalités similaires. Peu prennent des précautions particulières dans les exécutables setuid, bien que ceux qui exécutent du code natif ne font 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. En effet, 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 avaient besoin de #!/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 rendre la situation pire, car cela pourrait empêcher votre environnement d'exécution de détecter qu'il est invoqué 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 désinfecte l'environnement . Le script doit prendre soin de 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 la liste blanche. Avec Sudo, assurez-vous que l'option env_reset Est activée, que setenv est désactivée et que env_file Et env_keep Ne contiennent que des variables inoffensives.


TL, DR:

  • Setuid Shebang n'est pas sûr mais est 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 désinfecte l'environnement (tel que Sudo avec l'option env_reset).

¹ Cette discussion s'applique également si vous remplacez "setgid" par "setuid"; ils sont tous deux ignorés par le noyau Linux sur les scripts

Une façon de résoudre ce problème consiste à appeler le script Shell à partir d'un programme qui peut utiliser le bit setuid.
c'est quelque chose comme Sudo. Par exemple, voici comment vous pouvez accomplir cela dans un programme C:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    setuid( 0 );   // you can set it at run time also
    system( "/home/pubuntu/setuid-test2.sh" );
    return 0;
 }

Enregistrez-le sous setuid-test2.c.
compiler
Faites maintenant le setuid sur ce programme binaire:

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

Maintenant, vous devriez pouvoir l'exécuter et vous verrez que votre script sera exécuté sans autorisation de personne.
Mais ici aussi, vous devez coder en dur le chemin du script ou le passer comme argument de ligne de commande à l'exe ci-dessus.

57
Hemant

Je préfixe ainsi quelques scripts qui sont dans ce bateau:

#!/bin/sh
[ "root" != "$USER" ] && exec Sudo $0 "$@"

Notez que cela n'utilise pas setuid mais exécute simplement le fichier courant avec Sudo.

24
rcrowley

Si vous voulez éviter d'appeler Sudo some_script vous pouvez simplement faire:

  #!/ust/bin/env sh

  Sudo /usr/local/scripts/your_script

Les programmes SETUID doivent être conçus avec une extrême prudence car ils s'exécutent avec les privilèges root et les utilisateurs ont un contrôle important sur eux. Ils doivent tout vérifier. Vous ne pouvez pas le faire avec des scripts car:

  • Les shells sont de gros logiciels qui interagissent fortement avec l'utilisateur. Il est presque impossible de tout vérifier - surtout parce que la plupart du code n'est pas destiné à fonctionner dans un tel mode.
  • Les scripts sont une solution principalement rapide et ne sont généralement pas préparés avec autant de soin qu'ils autoriseraient setuid. Ils ont de nombreuses fonctionnalités potentiellement dangereuses.
  • Ils dépendent fortement d'autres programmes. Il ne suffit pas que le Shell ait été vérifié. sed, awk, etc. devraient également être vérifiés

Veuillez noter que Sudo fournit un certain contrôle d'intégrité mais ce n'est pas suffisant - vérifiez chaque ligne dans votre propre code.

Enfin, pensez à utiliser les capacités. Ils vous permettent d'accorder à un processus exécuté en tant qu'utilisateur des privilèges spéciaux qui nécessiteraient normalement des privilèges root. Cependant, par exemple, bien que ping doive manipuler le réseau, il n'a pas besoin d'avoir accès aux fichiers. Je ne sais pas cependant s'ils sont hérités.

12
Maciej Piechotka

super [-r reqpath], commande [args]

Super permet aux utilisateurs spécifiés d'exécuter des scripts (ou d'autres commandes) comme s'ils étaient root; ou il peut définir les groupes uid, gid et/ou supplémentaires sur une base par commande avant d'exécuter la commande. Il est destiné à être une alternative sécurisée à la création de scripts setuid root. Super permet également aux utilisateurs ordinaires de fournir des commandes pour exécution par d'autres; ceux-ci s'exécutent avec l'uid, le gid et les groupes de l'utilisateur offrant la commande.

Super consulte un fichier `` super.tab '' pour voir si l'utilisateur est autorisé à exécuter la commande demandée. Si l'autorisation est accordée, super exécutera pgm [args], où pgm est le programme associé à cette commande. (L'exécution de root est autorisée par défaut, mais peut toujours être refusée si une règle exclut root. Les utilisateurs ordinaires ne sont pas autorisés à exécuter par défaut.)

Si la commande est un lien symbolique (ou un lien dur aussi) vers le super programme, alors taper% command args équivaut à taper% super command args (La commande ne doit pas être super, ou super ne reconnaîtra pas qu'elle est invoquée via un lien.)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html

5
Nizam Mohamed

Vous pouvez créer un alias pour Sudo + le nom du script. Bien sûr, c'est encore plus de travail à configurer, car vous devez également configurer un alias, mais cela vous évite d'avoir à taper Sudo.

Mais si cela ne vous dérange pas d'horribles risques de sécurité, utilisez un shell setuid comme interpréteur du script Shell. Je ne sais pas si cela fonctionnera pour vous, mais je suppose que oui.

Permettez-moi de dire que je déconseille de le faire, cependant. Je ne fais que le mentionner à des fins éducatives ;-)

4
wzzrd

Si pour une raison quelconque Sudo n'est pas disponible, vous pouvez écrire un script de wrapper fin en C:

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

Et une fois compilé, définissez-le comme setuid avec chmod 4511 wrapper_script.

Ceci est similaire à une autre réponse publiée, mais exécute le script avec un environnement propre et utilise explicitement /bin/bash Au lieu du shell appelé par system(), et ferme ainsi certains trous de sécurité potentiels.

Notez que cela supprime complètement l'environnement. Si vous souhaitez utiliser certaines variables d'environnement sans ouvrir de vulnérabilités, il vous suffit vraiment d'utiliser Sudo.

Évidemment, vous voulez vous assurer que le script lui-même n'est accessible en écriture que par root.

2
Chris