Ceci est un programme set-root-uid
$ls -l
-rwsr-sr-x 1 root root 7406 2011-12-13 22:37 ./x*
int main(void) {
printf(
" UID GID \n"
"Real %d Real %d \n"
"Effective %d Effective %d \n",
getuid (), getgid (),
geteuid(), getegid()
);
seteuid(600);
printf(
" UID GID \n"
"Real %d Real %d \n"
"Effective %d Effective %d \n",
getuid (), getgid (),
geteuid(), getegid()
);
setuid(1000);
printf(
" UID GID \n"
"Real %d Real %d \n"
"Effective %d Effective %d \n",
getuid (), getgid (),
geteuid(), getegid()
);
setuid(0); // HOW DOES THIS SUCCEED IN SETTING THE EUID BACK TO 0
printf(
" UID GID \n"
"Real %d Real %d \n"
"Effective %d Effective %d \n",
getuid (), getgid (),
geteuid(), getegid()
);
return 0 ;
}
UID GID
Real 1000 Real 1000
Effective 0 Effective 0
UID GID
Real 1000 Real 1000
Effective 600 Effective 0
UID GID
Real 1000 Real 1000
Effective 1000 Effective 1000
UID GID
Real 1000 Real 1000
Effective 0 Effective 1000
La page de manuel indique que setuid changera l'uid réel, enregistré et efficace. Ainsi, après l'appel de la fonction setuid(1000)
, les trois deviennent 1000
. Comment est-ce que setuid(0)
permettez-moi de changer euid
en 0
?
Il y a deux cas,
- Vous souhaitez supprimer temporairement le privilège root lors de l'exécution du programme setuid
- Vous souhaitez supprimer définitivement le privilège root lors de l'exécution du programme setuid ...
Cas 1:
Après l'exécution d'un programme setuid
1.seteuid(600);
2.setuid(1000);
3.setuid(0);
Dans ce cas, le privilège root peut être récupéré à nouveau.
+----+------+------------+
| uid|euid |saved-uid |
|----|------|------------|
1.|1000| 0 | 0 |
2.|1000| 600 | 0 |
3.|1000| 1000 | 0 |
4.|1000| 0 | 0 |
| | | |
+------------------------+
Cas 2:
Après l'exécution d'un programme setuid,
1.setuid(1000);
2.setuid(0);
+----+------+------------+
| uid|euid |saved-uid |
|----|------|------------|
1.|1000|0 | 0 |
2.|1000|1000 | 1000 |
| | | |
+------------------------+
Dans ce cas, vous ne pouvez pas récupérer le privilège root. Cela peut être vérifié par la commande suivante,
cat/proc/PROCID/task/PROCID/status | Moins
Uid: 1000 0 0 0
Gid: 1000 0 0 0
Cette commande affichera un Uid et un Gid et elle aura 4 champs (les trois premiers champs sont ceux qui nous intéressent). Quelque chose comme ci-dessus
Les trois champs représentent uid, euid et save-user-id. Vous pouvez introduire une pause (une entrée de l'utilisateur) dans votre programme setuid et vérifier pour chaque étape le cat /proc/PROCID/task/PROCID/status | less
commande. Au cours de chaque étape, vous pouvez vérifier que l'uid enregistré est modifié comme indiqué.
Si vous êtes euid est root et que vous modifiez l'uid, les privilèges sont supprimés définitivement.Si l'ID utilisateur effectif n'est pas root, l'ID utilisateur enregistré n'est jamais touché et vous pouvez retrouver le privilège root à tout moment dans votre programme.
DESCRIPTION setuid () définit l'ID utilisateur effectif du processus appelant. Si l'UID effectif de l'appelant est root, l'UID réel et le set-user-ID enregistré sont également définis.
Sous Linux, setuid () est implémenté comme la version POSIX avec la fonction _POSIX_SAVED_IDS. Cela permet à un programme set-user-ID (autre que root) de supprimer tous ses privilèges utilisateur, d'effectuer un travail non privilégié, puis de réengager l'ID utilisateur effectif d'origine de manière sécurisée.
Si l'utilisateur est root ou que le programme est set-user-ID-root, une attention particulière doit être prise. La fonction setuid () vérifie l'ID utilisateur effectif de l'appelant et s'il s'agit du superutilisateur, tous les ID utilisateur liés au processus sont définis sur uid. Après cela, il est impossible pour le programme de récupérer les privilèges root.
Ainsi, un programme set-user-ID-root souhaitant supprimer temporairement les privilèges root, assumer l'identité d'un utilisateur non privilégié, puis retrouver ensuite les privilèges root ne peut pas utiliser setuid (). Vous pouvez accomplir cela avec seteuid (2).
(extrait du Manuel du programmeur Linux, 2014-09-21, page setuid.2
)
Oh! Ces fonctions sont difficiles à utiliser correctement.
La page de manuel indique que setuid changera l'uid réel, enregistré et efficace. Donc, après le setuid appelant (1000), tous les trois passent à 1000.
C'est le cas si et seulement si vous êtes euid 0. Au moment où vous appelez setuid(0)
, cependant, vous êtes euid 1000 et enregistré uid 0 (vérifiez getresuid(2)
, par exemple). C'est pourquoi vous pouvez retrouver des privilèges.
Code:
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
void print_uid(char *str, int ret)
{
uid_t ruid;
uid_t euid;
uid_t suid;
getresuid(&ruid, &euid, &suid);
printf("%s ret:%d\n"
"Real:%4d Effective:%4d Saved:%4d\n",
str, ret, ruid, euid, suid);
}
int main(void)
{
int ret = 0;
print_uid("init", ret); /* Real:1000 Effective: 0 Saved: 0 */
ret = seteuid(600);
print_uid("seteuid(600)", ret); /* Real:1000 Effective: 600 Saved: 0 */
ret = setuid(1000);
print_uid("setuid(1000)", ret); /* Real:1000 Effective:1000 Saved: 0 */
ret = setuid(0);
print_uid("setuid(0)", ret); /* Real:1000 Effective: 0 Saved: 0 */
ret = setuid(1000);
print_uid("setuid(1000)", ret); /* Real:1000 Effective:1000 Saved:1000 */
ret = setuid(0);
print_uid("setuid(0)", ret); /* Real:1000 Effective:1000 Saved:1000 */
return 0 ;
}
Sudo chown root setuid_feature
Sudo chmod + s setuid_feature
Il y a trois uids pour un processus sous Linux: REAL uid, EFFECTIVE uid, SAVED uid.
Cond 1. Lorsque euid est root, setuid ou seteuid peut être défini sur n'importe quel uid, mais il y a un effet secondaire, lorsque vous utilisez setuid (pas seteuid), tous les trois peuvent être définis sur le même uid qui n'est pas ROOT, puis le processus ne peut pas récupérer le privilège ROOT.
Cond 2. Lorsque euid n'est pas root, setuid ou seteuid peut être défini sur ruid ou suid, et seul euid est modifié.
| seteuid | setuid
Cond 1. (euid == root) | set euid to any uid | set all three uids to any uid
Cond 2. (euid != root) | set euid to ruid or suid | set euid to ruid or suid
donc, il y a 5 processus setuid ou seteuid dans le code, permettez-moi de les classer:
1. seteuid (600): Cond 1, définissez euid sur 600
2. setuid (1000): Cond 2, définissez euid sur 1000
3. setuid (0): Cond 2, définissez euid sur 0 (suid)
4. setuid (1000): Cond 1, définissez les trois uids sur 1000
5. setuid (0): Cond 2, les trois uid ne sont pas égaux à 0, donc ne peuvent pas être mis à 0, échec avec ret = -1