Le bit setuid peut être défini sur un fichier exécutable afin que, lors de son exécution, le programme dispose des privilèges du propriétaire du fichier à la place de l'utilisateur réel, s'ils sont différents. C'est la différence entre uid effectif (user id) et real uid.
Certains utilitaires courants, tels que passwd
, appartiennent à root et sont configurés de cette manière par nécessité ( passwd
accès nécessaire /etc/shadow
qui ne peut être lu que par root).
Pour ce faire, la meilleure stratégie consiste à faire immédiatement ce que vous devez faire en tant que superutilisateur, puis à réduire les privilèges, de sorte que les bogues ou les utilisations abusives risquent moins de se produire lors de l'exécution de root. Pour ce faire, vous définissez l’ outil effectif du processus sur son réel. Dans POSIX C:
#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void report (uid_t real) {
printf (
"Real UID: %d Effective UID: %d\n",
real,
geteuid()
);
}
int main (void) {
uid_t real = getuid();
report(real);
seteuid(real);
report(real);
return 0;
}
Les fonctions pertinentes, qui devraient avoir un équivalent dans la plupart des langages de niveau supérieur si elles sont couramment utilisées sur les systèmes POSIX:
getuid()
: Obtenez le vrai uid.
geteuid()
: Obtenez le uid efficace .
seteuid()
: Définit l’ aide efficace .
Vous ne pouvez rien faire avec le dernier qui soit inapproprié pour le véritable uid sauf dans la mesure où le bit setuid a été défini sur l'exécutable . Donc, pour essayer ceci, compilez gcc test.c -o testuid
. Vous devez ensuite, avec des privilèges:
chown root testuid
chmod u+s testuid
Le dernier définit le bit setuid. Si vous exécutez maintenant en ./testuid
tant qu'utilisateur normal, le processus par défaut s'exécute avec un uid effectif 0, root.
Qu'en est-il des scripts?
Cela varie d'une plate-forme à l'autre , mais sous Linux, les choses qui nécessitent un interpréteur, y compris le bytecode, ne peuvent pas utiliser le bit setuid à moins qu'il ne soit défini sur l'interpréteur (ce qui serait très très stupide). Voici un script Perl simple qui imite le code C ci-dessus:
#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n";
Fidèle à ses racines * nixy, perl a intégré des variables spéciales pour uid ( $>
) et real uid ( $<
). Mais si vous essayez la même chose chown
et que vous l' chmod
utilisez avec l'exécutable compilé (à partir de C, exemple précédent), cela ne fera aucune différence. Le script ne peut pas obtenir de privilèges.
La réponse à cela est d'utiliser un binaire setuid pour exécuter le script:
#include <stdio.h>
#include <unistd.h>
int main (int argc, char *argv[]) {
if (argc < 2) {
puts("Path to perl script required.");
return 1;
}
const char *perl = "perl";
argv[0] = (char*)perl;
return execv("/usr/bin/perl", argv);
}
Compilez ceci gcc --std=c99 whatever.c -o perlsuid
, alors chown root perlsuid && chmod u+s perlsuid
. Vous pouvez maintenant exécuter n'importe quel script Perl avec un uid effectif de 0, quel que soit son propriétaire.
Une stratégie similaire fonctionnera avec php, python, etc. Mais ...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
S'IL VOUS PLAÎT, ne laissez pas ce genre de choses traîner . Très probablement, vous voulez réellement compiler le nom du script sous forme de chemin absolu , c’est-à-dire remplacer tout le code dans main()
:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Assurez /opt/suid_scripts
- vous que tous les éléments sont en lecture seule pour les utilisateurs non root. Sinon, quelqu'un pourrait échanger n'importe quoi contre whatever.pl
.
De plus, sachez que de nombreux langages de script permettent aux variables d'environnement de modifier la façon dont elles exécutent un script . Par exemple, une variable d'environnement peut entraîner le chargement d'une bibliothèque fournie par l'appelant, permettant ainsi à l'appelant d'exécuter du code arbitraire en tant que root. Donc, à moins que vous sachiez que l'interpréteur et le script lui-même sont robustes vis-à-vis de toutes les variables d'environnement possibles, NE LE FAITES PAS .
Alors, que devrais-je faire à la place?
Un moyen plus sûr d'autoriser un utilisateur non root à exécuter un script en tant que root consiste à ajouter une règle sudo et à le faire exécuter sudo /path/to/script
. Sudo supprime la plupart des variables d'environnement et permet également à l'administrateur de sélectionner avec précision qui peut exécuter la commande et avec quels arguments. Voir Comment exécuter un programme spécifique en tant que root sans invite de mot de passe? à titre d'exemple.
-privileged
shell et, s'ils sont appelés à partir d'un script responsable, peuvent également être invoqués en toute sécuritésuid root
. Je crains que la notion de script responsable ne soit un peu un fil conducteur dans le monde réel. Quoi qu'il en soit, il est probablement utile de mentionner combien il est important d'éviter autant que possible les écritures alors que les autorisations sont élevées ...-privileged
shell" et de "script responsable". Voulez-vous faire une autre réponse à propos des coquillages? Je ne peux pas vous promettre le coche, bien sûr;) Il n’ya pas beaucoup de bonnes raisons de le faire -sudo
c’est une bien meilleure option si possible - je l’ai écrite comme une réponse générique issue d’une première question sur l’authentification de mot de passe système en php .sudo
- je considèresudo
comme psychotique en ce sens qu'elle prétend ne pas l'êtreroot
. Vous pouvez même mettre des globes dans votre shellsudoers
.su
est meilleur à mon avis pour des fins de script, maissudo
est bon pour les applications en direct. Mais ni l'un ni l'autre n'est aussi explicite qu'un script avec une ligne de#!/bin/ksh -p
démarcation ou quoi que ce soitsuid ksh
dans$PATH
.Oui, vous pouvez, mais c'est probablement une très mauvaise idée . Généralement, vous ne définissez pas le bit SUID directement sur votre exécutable, mais utilisez un sudo (8) ou un su (1) pour l'exécuter (et limiter le nombre de personnes pouvant l'exécuter)
Notez cependant qu'il existe de nombreux problèmes de sécurité permettant aux utilisateurs réguliers d'exécuter des programmes (et en particulier des scripts!) En tant que root. La plupart d’entre eux doivent faire cela à moins que le programme ne soit spécifiquement et très très soigneusement écrit pour gérer cela, les utilisateurs malveillants pourraient l’utiliser pour obtenir le shell root facilement.
Donc, même si vous deviez le faire, ce serait une très bonne idée de commencer par nettoyer TOUTES les entrées du programme que vous voulez exécuter en tant que root, y compris son environnement, les paramètres de ligne de commande, STDIN et tous les fichiers qu’il traite, les données provenant de connexions réseau. ça s'ouvre, etc. etc. C'est extrêmement difficile à faire et encore plus difficile à faire correctement.
Par exemple, vous pouvez autoriser les utilisateurs qui ne modifient qu'un fichier avec editor. Mais l’éditeur permet l’exécution de commandes par des assistants externes ou la suspension, donnant ainsi aux utilisateurs un shell racine à leur guise. Vous pouvez également exécuter des scripts shell qui vous paraissent très sécuritaires, mais l’utilisateur peut l’exécuter avec $ IFS modifié ou d’autres variables d’environnement qui changent complètement son comportement. Ou bien il peut s'agir d'un script PHP censé exécuter "ls", mais l'utilisateur l'exécute avec $ PATH modifié et lui fait donc exécuter un shell qu'il nomme "ls". Ou des dizaines d'autres problèmes de sécurité.
Si le programme doit vraiment effectuer une partie de son travail en tant que root, il serait probablement préférable de le modifier pour qu'il fonctionne comme un utilisateur normal, mais exécutez simplement (après avoir désinfecté autant que possible) la partie qui a absolument besoin de root via le programme suid helper.
Notez que même si vous faites entièrement confiance à tous vos utilisateurs locaux (par exemple, vous leur donnez un mot de passe root), c’est une mauvaise idée, comme toute surveillance involontaire de la sécurité par TOUS d’entre eux (par exemple, avoir un script PHP en ligne, ou un mot de passe faible, ou que ce soit) permet soudainement à un attaquant distant de compromettre complètement la machine.
la source
man sh
- et même si cela échouecommand -p env -i ...
. C'est assez difficile à faire après tout. Néanmoins, si cela est fait correctement, à des fins explicites, il peut être préférable poursuid
un interprète compétent que d’utilisersu
ousudo
- étant donné que le comportement de l’un ou de l’autre sera toujours en dehors du contrôle du script (bien qu’ilsu
soit moins susceptible de lancer des boules de être un peu moins fou) . Regrouper tout le paquet est le seul pari sûr - il vaut mieux que ce soit tout.