web-dev-qa-db-fra.com

RealUID, UID enregistré, UID efficace. Que se passe-t-il?

Ceci est un programme set-root-uid

$ls -l
-rwsr-sr-x 1 root root 7406 2011-12-13 22:37 ./x*

Le code source:

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 ;       
}

PRODUCTION

         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  

Ma question

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?

37
Lelouch Lamperouge

Il y a deux cas,

  1. Vous souhaitez supprimer temporairement le privilège root lors de l'exécution du programme setuid
  2. Vous souhaitez supprimer définitivement le privilège root lors de l'exécution du programme setuid ...
  • Vous pouvez le faire temporairement en définissant l'uid sur le véritable identifiant utilisateur, puis en changeant l'uid en tout ce que vous voulez. En effet, l'ID utilisateur enregistré n'est pas modifié.
  • Vous pouvez supprimer des privilèges de façon permanente en changeant immédiatement l'uid en un identifiant d'utilisateur moins privilégié. Après cela, peu importe ce que vous ne pouvez pas récupérer le privilège root.

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.

33
Ajai

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)

8
BRPocock

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.

4
pilcrow

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

1
J.Wu