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和附属组列表,这些值仍属于原用户,未切换到目标账户的组信息。
要完全切换到目标账户身份,需在子进程中完成以下操作:
- 获取目标账户(
cadtools)的真实GID和附属组列表 - 同步设置真实GID、有效GID、保存的GID为目标账户的GID
- 更新附属组列表为目标账户的组
修改后的代码示例:
#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
相关产品推荐
相关产品推荐

