Ce qui a changé, c'est que /bin/sh
soit est devenu bash
ou est resté dash
qui a un drapeau supplémentaire -p
imitant le comportement de bash.
Bash nécessite le -p
drapeau pour ne pas supprimer 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 du shell ne sont pas héritées de l'environnement, les 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 à l'appel, le comportement au démarrage est le même, mais l'ID utilisateur effectif n'est pas réinitialisé.
Avant, dash
ne s'en souciait pas et autorisait l'exécution setuid (en ne faisant rien pour l'empêcher). Mais le dash
d'Ubuntu 16.04 La page de manuel de contient 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. Ceci n'est pas défini par défaut pour éviter une utilisation incorrecte avec les programmes root setuid via system(3) ou popen(3).
Cette option n'existait pas dans l'amont (qui n'aurait peut-être pas été réactif à un correctif proposé) ni dans Debian 9 mais est présente dans Debian Buster qui a reçu le correctif depuis 2018.
NOTE :comme l'explique Stéphane Chazelas, il est trop tard pour invoquer "/bin/sh -p"
en system()
car system()
exécute tout ce qui passe par /bin/sh
et donc le setuid est déjà supprimé. La réponse de derobert explique comment gérer cela, dans le code avant system()
.
plus de détails sur l'histoire ici et là.
Le shell est probablement en train de remplacer son ID utilisateur effectif par le véritable ID utilisateur 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 qui sont sauvegardés devraient pouvoir être laissés seuls. C'est juste de la force brute pour déboguer. Selon la raison pour laquelle vous êtes set-id, vous aurez peut-être bien sûr besoin pour enregistrer également les identifiants réels quelque part.)
[De plus, s'il ne s'agit pas simplement d'un exercice d'apprentissage du fonctionnement de setid, il y a de nombreux problèmes de sécurité à craindre, 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 sudo
si possible.]