简介
SUID全称Set owner User ID up on execution,是Linux给可执行文件的一个属性,设置了s位的程序在运行时其Effective UID将会设置为这个程序的所有者。比如,/bin/ping
这个程序的所有者是0(root),它设置了s位,那么普通用户在运行ping时其Effective UID就是0,等同于拥有了root权限。
➜ c ls -ldb $(which pkexec)
-rwsr-xr-x 1 root root 30872 2023年 2月13日 /usr/bin/pkexec
SUID文件的出现是为了解决一些操作只能由root权限进行,但普通权限用户也需要能通过某种方式进行调用,比如passwd,/etc/shadow只有root可写,但用户自己显然需要可以修改密码,所以passwd被设置为SUID程序,使得普通用户能通过passwd临时获取到修改shadow文件的能力。
-rw-r----- 1 root shadow 1411 2023年 5月10日 /etc/shadow
-rwsr-xr-x 1 root root 68248 2022年11月11日 /usr/bin/passwd
Linux中每个用户都有独一无二的ID,称为UserID。为进程定义了三个ID:
-
Real UserID
-
Effective UserID
-
Saved UserID
-
Real UserID:对于一个进程,这个ID是启动这个进程的用户的用户ID,这个ID定义了这个进程有权访问哪些文件。
-
Effective UserID:通常这个ID和Real UserID相同,但有时会不一样,来允许非特权用户访问只能由特权用户访问的文件。当非特权的用户运行此文件时,euid是文件所属的用户id,ruid才是当前用户的id
-rwsr-xr-x 1 root root 68248 2022年11月11日 /usr/bin/passwd
┌──(chestnut㉿chestnut)-[/root/code/c]
└─$ passwd
为 chestnut 更改 STRESS 密码。
➜ c ps -eo pid,euid,ruid | grep 1692693
1692693 0 1000 -
Saved UserID,当进程以提升权限运行时,需要做一些非特权的操作,可以通过临时切换到非特权账户来实现。在执行低权限工作时,将Effective UID 更改为某个较低的权限值,并将 euid 保存到Saved userID(suid),以便在任务完成时用于切换回特权账户。
https://www.geeksforgeeks.org/real-effective-and-saved-userid-in-linux/
查找SUID程序
find / -perm -4000 -type f -exec ls -ldb {} ;
-perm -4000 查找权限为4000
-type f 只查找普通文件,过滤掉目录等其他类型
-[/root/code/c/suid.c
/root/code/c/CMakeLists.txt](vscode-remote://ssh-remote%2B192.168.59.211/root/code/c/suid.c)exec ls -ldb {} ; 对找到的文件执行ls -ldb命令,显示文件详细信息。{}表示find找到的文件名,会逐个代入。;表示-exec选项命令结束。
; 的作用就是隔离 find 命令行和 -exec 指定的命令,避免解析错误。
-rwsr-xr-x 1 root root 30872 2023年 2月13日 /usr/bin/pkexec
-rwsr-xr-x 1 root root 14888 2023年 1月 3日 /usr/bin/vmware-user-suid-wrapper
-rwsr-xr-- 1 root kismet 146216 2022年12月27日 /usr/bin/kismet_cap_nxp_kw41z
-rwsr-xr-- 1 root kismet 142120 2022年12月27日 /usr/bin/kismet_cap_nrf_51822
-rwsr-xr-x 1 root root 59704 2023年 2月13日 /usr/bin/mount
-rwsr-xr-- 1 root kismet 216392 2022年12月27日 /usr/bin/kismet_cap_linux_wifi
-rwsr-xr-- 1 root kismet 142120 2022年12月27日 /usr/bin/kismet_cap_ubertooth_one
-rwsr-xr-- 1 root kismet 146216 2022年12月27日 /usr/bin/kismet_cap_rz_killerbee
-rwsr-xr-x 1 root root 68248 2022年11月11日 /usr/bin/passwd
-rwsr-xr-- 1 root kismet 146216 2022年12月27日 /usr/bin/kismet_cap_nrf_mousejack
-rwsr-xr-- 1 root kismet 142120 2022年12月27日 /usr/bin/kismet_cap_nrf_52840
-rwsr-xr-x 1 root root 88496 2022年11月11日 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 35128 2023年 2月13日 /usr/bin/umount
-rwsr-xr-- 1 root kismet 154408 2022年12月27日 /usr/bin/kismet_cap_linux_bluetooth
-rwsr-xr-- 1 root kismet 146216 2022年12月27日 /usr/bin/kismet_cap_ti_cc_2531
shell中的SUID细节
在https://www.leavesongs.com/PENETRATION/linux-suid-privilege-escalation.html中提到Ubuntu对dash进行了patch。
找到patch地址为https://launchpadlibrarian.net/240241543/dash_0.5.8-2.1ubuntu2.diff.gz,代码如下,当on=1时会略过权限检查,当on不为1时,会通过geteuid和getegid获取当前进程的effective user ID和effective group ID,并与通过getuid和getgid获取的real user ID和real group ID进行比较。如果当前进程对应的可执行文件为SUID文件且当前运行这个文件的用户不是文件属主时,会重新通过setuid和setgid将当前进程的权限设置为real ID,即运行这个可执行文件的用户的权限。当文件不是SUID文件时rid和eid相等,不会进入if内,或者文件是SUID文件并且运行这个文件的用户是文件属主rid和eid也会相等,不会进入if内。
+diff -Naurp dash-0.5.7.ori/src/main.c dash-0.5.7/src/main.c
+--- dash-0.5.7.ori/src/main.c 2015-06-03 10:45:22.766472281 -0400
++++ dash-0.5.7/src/main.c 2015-06-03 10:58:56.484258181 -0400
+@@ -97,11 +97,16 @@ main(int argc, char **argv)
+ struct jmploc jmploc;
+ struct stackmark smark;
+ int login;
++ uid_t uid;
++ gid_t gid;
+
+ #ifdef __GLIBC__
+ dash_errno = __errno_location();
+ #endif
+
++ uid = getuid();
++ gid = getgid();
+diff -Naurp dash-0.5.7.ori/src/priv.c dash-0.5.7/src/priv.c
+--- dash-0.5.7.ori/src/priv.c 1969-12-31 19:00:00.000000000 -0500
++++ dash-0.5.7/src/priv.c 2015-06-03 11:00:31.097386153 -0400
+@@ -0,0 +1,27 @@
++#include <unistd.h>
++
++#include "priv.h"
++#include "var.h"
++
++uid_t uid;
++gid_t gid;
++
++void setprivileged(int on)
++{
++ static int is_privileged = 1;
++ if (is_privileged == on)
++ return;
++
++ is_privileged = on;
++
++ /*
++ * To limit bogus system(3) or popen(3) calls in setuid binaries, require
++ * -p flag to work in this situation.
++ */
++ if (!on && (uid != geteuid() || gid != getegid())) {
++ setuid(uid);
++ setgid(gid);
++ /* PS1 might need to be changed accordingly. */
++ choose_ps1();
++ }
++}
那么为什么要这样实现呢?为什么如果需要继承默认的effective user ID和effective group ID需要显式的使用-p参数呢?假设我们有如下suid程序,并且该程序由www-data用户启动:
int main(int argc, char *argv[]) {
printf("%sn", argv[1]);
return system(argv[1]);
}
其中通过system函数执行用户传入的命令,system实现可以在这找到(https://codebrowser.dev/glibc/glibc/sysdeps/posix/system.c.html),可以看到实际执行的是/bin/sh -c command
,加入攻击者传入恶意命令,尝试通过该程序以root权限执行命令,最终以/bin/sh -c command
的形式执行命令。
do_system (const char *line)
{
int status = -1;
int ret;
pid_t pid;
struct sigaction sa;
struct sigaction intr, quit;
.....
__posix_spawnattr_init (&spawn_attr);
__posix_spawnattr_setsigmask (&spawn_attr, &omask);
__posix_spawnattr_setsigdefault (&spawn_attr, &reset);
__posix_spawnattr_setflags (&spawn_attr,
POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);
ret = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr,
(char *const[]){ (char *) SHELL_NAME,
(char *) "-c",
(char *) line, NULL },
__environ);
如果没有前面说的措施,那么攻击者可以成功以root权限执行恶意命令,但通过上面的措施,则会出现如下:因为进程的rid为www-data,eid为root,而通过system函数执行命令不能显式设置-p参数,导致在执行命令时,不能通过if判断,命令的权限会被降为www-data权限,从而一定程度上缓解了攻击。
参考资料
https://www.leavesongs.com/PENETRATION/linux-suid-privilege-escalation.html
原文始发于微信公众号(闲聊趣说):浅聊SUID
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论