【问题标题】:RealUID, Saved UID, Effective UID. What's going on?RealUID,保存的 UID,有效的 UID。这是怎么回事?
【发布时间】:2019-04-11 01:00:31
【问题描述】:

这是一个 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  

我的问题

手册页指出 setuid 将更改真实的、保存的和有效的 uid。 所以在调用setuid(1000) 之后,三个都变为1000setuid(0) 怎么样让我把euid 改成0

【问题讨论】:

    标签: linux privileges setuid


    【解决方案1】:

    有两种情况,

    1. 您想在执行 setuid 程序时暂时放弃 root 权限
    2. 您想在执行 setuid 程序时永久放弃 root 权限...
    • 您可以暂时将 euid 设置为真实用户 id,然后将 uid 更改为您想要的任何内容。稍后当您需要恢复 root 权限时,您可以将 setuid 设置为 root,有效用户 ID 将更改回 root .这是因为保存的用户 ID 没有改变。
    • 您可以通过直接将 uid 更改为特权较低的用户 ID 来永久放弃特权。在此之后,无论如何您都无法恢复 root 权限。

    案例一:

    setuid 程序开始执行后

    1.seteuid(600);
    2.setuid(1000);
    3.setuid(0);
    

    在这种情况下,可以再次获得 root 权限。

                  +----+------+------------+
                  | uid|euid  |saved-uid   |
                  |----|------|------------|
                1.|1000| 0    | 0          |
                2.|1000| 600  | 0          |
                3.|1000| 1000 | 0          |
                4.|1000|  0   | 0          |
                  |    |      |            |
                  +------------------------+
    

    案例2:

    setuid程序开始执行后

    1.setuid(1000);
    2.setuid(0);
    
    
    
                   +----+------+------------+
                   | uid|euid  |saved-uid   |
                   |----|------|------------|
                 1.|1000|0     | 0          |
                 2.|1000|1000  | 1000       |
                   |    |      |            |
                   +------------------------+
    

    在这种情况下,您无法取回 root 权限。 这可以通过以下命令验证,

    cat /proc/PROCID/task/PROCID/status |少

    Uid:    1000    0       0       0
    Gid:    1000    0       0       0
    

    这个命令会显示一个 Uid 和 Gid,它有 4 个字段(前三个字段是我们关心的)。类似上面的东西

    三个字段分别代表uid、euid和saved-user-id。您可以在 setuid 程序中引入暂停(来自用户的输入)并检查cat /proc/PROCID/task/PROCID/status | less 命令的每个步骤。在每个步骤中,您都可以检查保存的 uid 是否发生了上述变化。

    如果您是 euid 是 root 并且您更改了 uid,则权限将被永久删除。如果有效用户 id 不是 root,则保存的用户 id 永远不会被触及,您可以重新获得 root 权限任何时候你想在你的程序。

    【讨论】:

    • 你能告诉我 Uid: 1000 0 0 0 的最后一列代表什么
    • 当我尝试使用示例代码对其进行测试时,第四个值显示与有效用户 ID 相同。猜猜它一定是有效的用户ID,但不确定为什么它会打印两次有效的用户ID。
    • 对于那些试图在没有 groovy 的情况下跟随的人(例如在 bash shell 中),试试这个:cat /proc/$$/task/$$/status | grep -A2 TracerPid
    • 看来场景是much more complicated在不同系统下_POSIX_SAVED_IDS定义与否
    【解决方案2】:

    描述 setuid() 设置调用进程的有效用户 ID。如果调用者的有效 UID 是 root,真实的 UID 和保存的 set-user-ID 也都设置好了。

    在 Linux 下,setuid() 的实现方式与具有 _POSIX_SAVED_IDS 功能的 POSIX 版本类似。这个 允许 set-user-ID(root 除外)程序放弃其所有用户权限,执行一些非特权工作,然后以安全方式重新使用原始有效用户 ID。

    如果用户是 root 或程序是 set-user-ID-root,则必须特别小心。 setuid() 函数检查调用者的有效用户 ID,如果是超级用户,则所有与进程相关的用户 ID 设置为 uid。发生这种情况后,程序将无法重新获得 root 权限。

    因此,一个希望暂时放弃 root 权限的 set-user-ID-root 程序,假设其身份为 非特权用户,然后重新获得 root 特权,不能使用 setuid()。你可以完成 这与 seteuid(2).

    (摘自 Linux 程序员手册,2014-09-21,第 setuid.2 页)

    【讨论】:

    • 根据你所说的最后一段输出应该显示Effective = 1000。但它能够回到0。这正是我的问题。
    • 我相信这是由于您正在拨打seteuid;它会导致内核“保护”您将来重新获得root 的能力。删除该调用消除了“后门”效应。
    • 是的,@ajai 的回答也提供了证明。无论如何感谢您的回答
    • 这种行为往往会造成这种漏洞:youtu.be/-wGtxJ8opa8
    【解决方案3】:

    哦!这些功能很难正确使用。

    手册页指出 setuid 将更改真实的、保存的和有效的 uid。所以在调用 setuid(1000) 之后,三个都变成了 1000。

    当且仅当您是 euid 0 时才会出现这种情况。但是,当您调用 setuid(0) 时,您是 euid 1000 并且 已保存 uid 0(例如,检查 getresuid(2) )。这就是您能够重新获得特权的原因。

    【讨论】:

    • 你是对的。 getresuid() 似乎工作正常。但它不符合POSIX。但在这种情况下,这并不重要。谢谢..
    【解决方案4】:

    代码:

    #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

    Linux 中的进程有三个 uid:REAL uid、EFFECTIVE uid、SAVED uid。
    条件1.euid为root时,setuid或seteuid可以设置为任意uid,但是有一个副作用,使用setuid(not seteuid)时,可以将三者都设置为同一个非ROOT的uid,并且则进程无法重新获得ROOT权限。
    条件2.euid不是root时,setuid或者seteuid可以设置为ruid或者suid,只改变euid。

                           |      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  
    

    所以,代码中有 5 个 setuid 或 seteuid 进程,让我对它们进行分类:
    1. seteuid(600):条件1,设置euid为600
    2. setuid(1000):条件2,设置euid为1000
    3. setuid(0) : Cond 2,设置euid为0(suid)
    4. setuid(1000): Cond 1,将三个uid全部设置为1000
    5. setuid(0) : Cond 2,三个uid都不等于0,所以不能设置为0,失败,ret = -1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-25
      • 2020-08-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多