这是一个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)
呢我把euid
改成0
有两种情况,
- 你想在执行setuid程序时暂时放弃root权限
- 你想在执行setuid程序时永久删除root权限…
- 您可以通过将uid设置为实际用户id,然后将uid更改为您想要的任何内容来临时做到这一点。以后当您需要恢复root权限时,您可以将id设置为root,并且有效的用户id将更改回root。这是因为保存的用户id没有更改。
- 您可以通过直接将uid更改为权限较低的用户id来永久删除特权。在此之后,无论如何您都无法恢复root权限。
Case 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 |
| | | |
+------------------------+
案例2:
当一个setuid程序开始执行时,
1.setuid(1000);
2.setuid(0);
+----+------+------------+
| uid|euid |saved-uid |
|----|------|------------|
1.|1000|0 | 0 |
2.|1000|1000 | 1000 |
| | | |
+------------------------+
在这种情况下,您无法获得根权限。这可以通过以下命令
进行验证cat/proc/procid/task/procid/status | less
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是否如前所述发生了更改。
如果您的uid是root并且您更改了uid,那么特权将被永久删除。如果有效用户id不是root,那么保存的用户id永远不会被触及,您可以随时在程序中重新获得root权限。
setuid()设置调用进程的有效用户ID。如果调用方的有效UID是同时设置真实UID和保存的set-user- id。
在Linux下,setuid()的实现与POSIX版本一样,具有_POSIX_SAVED_IDS特性。这允许set-user-ID(除了root)程序放弃它所有的用户权限,做一些没有特权的工作,然后以一种安全的方式重新使用原来的有效用户ID。
如果用户是root或者程序是set-user-ID-root,必须特别小心。setuid()函数检查调用者的有效用户ID,如果它是超级用户,则检查所有与进程相关的用户ID被设置为uid。在此之后,程序不可能重新获得root权限。
因此,如果一个set-user-ID-root程序希望暂时放弃root权限,那么它就假定是一个用户的身份无特权用户,然后重新获得根权限,不能使用setuid()。你可以完成
(来自Linux程序员手册,2014-09-21,page setuid.2
)
0 !这些函数很难正确使用。
手册页说明setuid将更改真实的、保存的和有效的uid。所以在调用setuid(1000)之后,这三个变量都变为1000。
当且仅当您的id为0时才会出现这种情况。但是,在调用setuid(0)
时,您的uid为1000,保存的uid为 0(例如,检查getresuid(2)
)。
代码:
#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:%dn"
"Real:%4d Effective:%4d Saved:%4dn",
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(而不是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):设置id为600
2. setuid(1000):第二个2,设置euid为1000
3.setuid(0):第二个2,设置euid为0(suid)
4. setuid(1000):第二个1,设置所有三个uid为1000
5. setuid(0):第二个2,所有三个uid不等于0,所以不能设置为0,失败的ret = -1