GNU/Linux >> Tutoriels Linux >  >> Linux

Utiliser correctement le bit Setuid ?

J'ai un processus qui nécessite des privilèges root lorsqu'il est exécuté par un utilisateur normal. Apparemment, je peux utiliser le "bit setuid" pour accomplir cela. Quelle est la bonne façon de procéder sur un système POSIX ?

De plus, comment puis-je faire cela avec un script qui utilise un interpréteur (bash, perl, python, php, etc.) ?

Réponse acceptée :

Le bit setuid peut être défini sur un fichier exécutable afin que, lors de son exécution, le programme ait les privilèges du propriétaire du fichier au lieu de l'utilisateur réel, s'ils sont différents. C'est la différence entre efficace uid (identifiant utilisateur) et réel uid.

Certains utilitaires courants, tels que passwd , appartiennent à root et sont configurés de cette manière par nécessité (passwd doit accéder à /etc/shadow qui ne peut être lu que par root).

La meilleure stratégie consiste à faire immédiatement tout ce que vous devez faire en tant que superutilisateur, puis à réduire les privilèges afin que les bogues ou les abus soient moins susceptibles de se produire lors de l'exécution de root. Pour ce faire, vous définissez l'efficacité du processus uid à son véritable uid. En 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: %dn",
        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 efficace uid.
  • seteuid()  :Définissez le paramètre effectif uid.

Vous ne pouvez rien faire avec le dernier inapproprié pour le vrai 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 les privilèges :

chown root testuid
chmod u+s testuid

Le dernier définit le bit setuid. Si vous exécutez maintenant ./testuid en tant qu'utilisateur normal, vous verrez que le processus s'exécute par défaut avec l'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 un uid efficace ($> ) et l'uid réel ($< ). Mais si vous essayez le même chown et chmod utilisé 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.

Connexe :Comment mettre correctement à niveau bash vers v4.0+ ?

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 ce gcc --std=c99 whatever.c -o perlsuid , puis chown root perlsuid && chmod u+s perlsuid . Vous pouvez maintenant exécuter tout 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 S'IL VOUS PLAÎT NE LAISSEZ PAS traîner ce genre de chose . Très probablement, vous voulez en fait compiler le nom du script en tant que chemin absolu , c'est-à-dire remplacer tout le code dans main() avec :

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

Ils s'assurent que /opt/suid_scripts et tout ce qu'il contient est 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 à l'appelant d'exécuter du code arbitraire en tant que root. Ainsi, à moins que vous ne sachiez que l'interpréteur et le script lui-même sont robustes contre toutes les variables d'environnement possibles, NE FAITES PAS CELA .

Que dois-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 à faire exécuter à l'utilisateur sudo /path/to/script . Sudo supprime la plupart des variables d'environnement et permet également à l'administrateur de sélectionner finement 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 ? pour un exemple.


Linux
  1. Comment fonctionne le Sticky Bit ?

  2. Utiliser –exclude avec la commande Du ?

  3. Pourquoi utiliser Setuid() dans les programmes Suid ?

  4. Exécuter une application Qt sur le Web

  5. kdevtmpfsi utilisant tout le CPU

Conseils pour utiliser la commande top sous Linux

Utilisation de la commande gratuite Linux

Utilisation du fichier de configuration SSH

Tutoriel sur l'utilisation de la commande Timeout sous Linux

Tutoriel sur l'utilisation de la dernière commande dans le terminal Linux

SELinux dans le monde réel