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 ...