40

这是一个 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),三个都变成了1000。那setuid(0)让我怎么euid0

4

4 回答 4

36

有两种情况,

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

情况1:

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          |
              |    |      |            |
              +------------------------+

案例二:

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 权限。

于 2011-12-14T05:04:24.453 回答
11

说明 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 权限、假设为非特权用户的身份、然后重新获得 root 权限的 set-user-ID-root 程序不能使用 setuid()。您可以使用 seteuid(2) 完成此操作。

(来自 Linux 程序员手册,2014 年 9 月 21 日,第 页setuid.2

于 2011-12-14T04:01:42.970 回答
5

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

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

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

于 2011-12-14T05:19:06.323 回答
3

代码:

#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):Cond 1,将 euid 设置为 600
2. setuid(1000):Cond 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,所以可以' t 设置为 0,失败,ret = -1

于 2019-04-10T17:00:31.527 回答