You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SetUID C++程序执行groups命令显示真实用户组而非有效用户组

问题

我正在编写一个C++程序,充当无脸账户的sudo,目标是让命令以cadtools账户身份运行,而非调用命令的用户身份。计划是编译程序后将所有者改为cadtools,再设置SetUID位实现权限切换。程序大部分功能正常,但groups命令返回的是我自身的用户组,而非cadtools的组,导致结果异常。

当前代码片段:

uid_t euid, ruid;

ruid = getuid();
euid = geteuid();
seteuid(ruid);

int childPid = fork();
if(childPid == 0){
    seteuid(euid);
    execvp(command, arguments);
} else {
    wait(childPid);
}

编译并修改权限后,文件信息:

[user@home]$ ls -ltr ../bin/.sudo
-rwsr-xr-x 1 cadtools vendor_tools 13224 Feb 21 18:23 ../bin/.sudo
[user@home]$

测试结果:

  • 运行whoami正常返回目标账户:
[user@home]$ ../bin/.sudo /usr/bin/whoami
cadtools
[user@home]$
  • 运行groups异常,返回当前用户的组,而非cadtools的组:
[user@home]$ ../bin/.sudo /usr/bin/groups
domain users vpn_users linux-admins hw engineering wifi_eng rtm_admins hw_er
[user@home]$ /usr/bin/groups
domain users vpn_users linux-admins hw engineering wifi_eng rtm_admins hw_er
[user@home]$ /usr/bin/groups cadtools
cadtools : domain users vendor_tools engineering hw
[user@home]$

尝试过保留或移除fork和seteuid调用,结果一致。

原因与解决方法

问题出在你只修改了用户ID(UID)相关的值,但未处理组ID(GID)的设置。whoami仅读取有效UID,因此能正确返回目标账户名;但groups会读取真实GID和附属组列表,这些值仍属于原用户,未切换到目标账户的组信息。

要完全切换到目标账户身份,需在子进程中完成以下操作:

  1. 获取目标账户(cadtools)的真实GID和附属组列表
  2. 同步设置真实GID、有效GID、保存的GID为目标账户的GID
  3. 更新附属组列表为目标账户的组

修改后的代码示例:

#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <sys/wait.h>

// ... 其他业务代码 ...

int childPid = fork();
if(childPid == 0){
    // 获取目标用户的passwd结构体信息
    struct passwd *pw = getpwnam("cadtools");
    if(pw == NULL){
        _exit(1); // 错误处理,直接退出子进程
    }

    // 同步设置真实、有效、保存的GID
    if(setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1){
        _exit(1);
    }

    // 获取目标用户的附属组数量并分配内存
    int ngroups = 0;
    getgrouplist(pw->pw_name, pw->pw_gid, NULL, &ngroups);
    gid_t *groups = new gid_t[ngroups];
    getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups);

    // 设置当前进程的附属组列表
    if(setgroups(ngroups, groups) == -1){
        delete[] groups;
        _exit(1);
    }
    delete[] groups;

    // 同步设置真实、有效、保存的UID
    if(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1){
        _exit(1);
    }

    // 执行目标命令
    execvp(command, arguments);
    // 若execvp返回,说明命令执行失败
    _exit(1);
} else {
    waitpid(childPid, NULL, 0);
}

关键说明:

  • 使用setresgid和setresuid可一次性同步设置真实、有效、保存的ID,确保所有身份标识完全切换到目标账户
  • getgrouplist配合setgroups,能将目标用户的所有附属组同步到当前进程,让groups命令返回正确结果
  • 子进程中切换身份后,避免保留原用户的任何权限,降低安全风险

内容的提问来源于stack exchange,提问作者Bob Swan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:36:09