这是一个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权限...
情况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 |
| | | |
+------------------------+
在这种情况下,您无法取回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权限。
描述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权限。
因此,一个set-user-ID-root程序希望暂时删除root权限,假定一个非特权用户的身份,然后重新获得root权限后不能使用setuid()。你可以用seteuid(2)完成这个。
(摘自Linux程序员手册,2014-09-21,页面setuid.2
)
哦!这些功能很难正确使用。
手册页指出setuid将改变真实,保存和有效的uid。因此在调用setuid(1000)之后,所有三个都变为1000。
当且仅当你是euid 0时就是这种情况。然而,当你调用setuid(0)
时,你是euid 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:%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。 Cond 1.当euid是root时,setuid或seteuid可以设置为任何uid,但是有副作用,当使用setuid(不是seteuid)时,所有这三个都可以设置为不是ROOT的同一个uid,并且那么这个过程就无法重新获得ROOT特权。 Cond 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,所以不能设置为0,失败,ret = -1