Pourquoi le bit setuid fonctionne-t-il de manière incohérente?

8

J'ai écrit le code:

// a.c
#include <stdlib.h>
int main () {
  system("/bin/sh");
  return 0;
}

compilé avec la commande:

gcc a.c -o a.out

ajouté un bit setuid dessus:

sudo chown root.root a.out
sudo chmod 4755 a.out

Sur Ubuntu 14.04, lorsque je cours en tant qu'utilisateur général, j'ai obtenu le privilège root.

mais sur Ubuntu 16.04, j'ai toujours le shell de l'utilisateur actuel.

Pourquoi est-ce différent?

user10883182
la source

Réponses:

9

Ce qui a changé, c'est que /bin/shsoit est devenu, soit est bashresté, dashce qui a obtenu un drapeau supplémentaire -pimitant le comportement de bash.

Bash nécessite que le -pdrapeau ne supprime pas le privilège setuid comme expliqué dans sa page de manuel :

Si le shell est démarré avec l'ID utilisateur (groupe) effectif différent de l'ID utilisateur (groupe) réel et que l'option -p n'est pas fournie, aucun fichier de démarrage n'est lu, les fonctions shell ne sont pas héritées de l'environnement, le SHELLOPTS , Les variables BASHOPTS, CDPATH et GLOBIGNORE, si elles apparaissent dans l'environnement, sont ignorées et l'ID utilisateur effectif est défini sur l'ID utilisateur réel . Si l'option -p est fournie lors de l'appel, le comportement de démarrage est le même, mais l'ID utilisateur effectif n'est pas réinitialisé.

Avant, dashne se souciait pas de cela et permettait l'exécution de setuid (en ne faisant rien pour l'empêcher). Mais la dashpage de manuel d'Ubuntu 16.04 a une option supplémentaire décrite, similaire à bash:

-p priv
N'essayez pas de réinitialiser l'uid effectif s'il ne correspond pas à l'uid. Ce paramètre n'est pas défini par défaut pour éviter une utilisation incorrecte par les programmes racine setuid via le système (3) ou popen (3).

Cette option n'existait pas en amont (qui pourrait ne pas avoir été réactif à un correctif proposé * ) ni Debian 9 mais est présente dans Debian buster qui a obtenu le correctif depuis 2018.

NOTE: comme l' explique Stéphane Chazelas, il est trop tard pour invoquer "/bin/sh -p"en system()raison system()runs quoi que ce soit donné par /bin/shet donc le setuid est déjà tombé. La réponse de Derobert explique comment gérer cela, dans le code précédent system().

* plus de détails sur l'histoire ici et .

UN B
la source
system("bash -p")s'exécute de sh -c "bash -p"sorte que les privilèges ont déjà été supprimés lors de bashson exécution.
Stéphane Chazelas
eh bien ok. Je supprimerai de toute façon la partie "solution" mieux gérée par la réponse de derobert.
AB
Voir aussi bugs.debian.org/cgi-bin/bugreport.cgi?bug=734869
Stéphane Chazelas
Ressemble à un tiret fixe d'Ubuntu dans wily (15.10). bugs.launchpad.net/ubuntu/+source/dash/+bug/1215660
Mark Plotnick
1
Notez que Debian faisait l'inverse. Il corrigeait bash afin de ne pas supprimer les privilèges. Que ce soit une amélioration est discutable. Maintenant, les gens qui veulent faire un system()avec des privilèges élevés (ce qui, d'accord, ils ne devraient pas en premier lieu) finissent par faire un setresuid()avant, ce qui est bien pire car le shell ne sait pas que c'est suid, activer le mode où il ne fait pas confiance à son environnement.
Stéphane Chazelas
8

Le shell est probablement en train de remplacer son ID utilisateur effectif par l'ID utilisateur réel dans le cadre de son démarrage pour une raison ou une autre. Vous pouvez le vérifier en ajoutant:

/* needs _GNU_SOURCE; non-Linux users see setregid/setreuid instead */
uid_t euid = geteuid(), egid = getegid();
setresgid(egid, egid, egid);
setresuid(euid, euid, euid);

avant votre system(). (En fait, même sous Linux, vous n'avez probablement besoin que de définir les vrais; ceux enregistrés devraient être bien de laisser seuls. C'est juste une force brute pour déboguer. Selon la raison pour laquelle vous êtes set-id, vous devrez bien sûr avoir besoin pour enregistrer également les vrais identifiants.)

[De plus, si ce n'est pas seulement un exercice pour apprendre comment fonctionne setid, alors il y a beaucoup de problèmes de sécurité à se soucier, en particulier lors de l'appel d'un shell. Il existe de nombreuses variables d'environnement, par exemple, qui affectent le comportement du shell. Préférez une approche déjà existante comme sudosi possible.]

derobert
la source
Je me suis concentré sur les raisons pour lesquelles cela a changé, tandis que vous avez expliqué comment éviter le problème. +1
AB
Bien sûr, si vous avez vraiment besoin d'un endroit pour enregistrer les vrais ID, l'ID enregistré est un endroit extrêmement pratique pour le faire. Surtout si les ID effectifs ne sont pas root: root.
Kevin