Suid

简介

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
  1. Real UserID:对于一个进程,这个ID是启动这个进程的用户的用户ID,这个ID定义了这个进程有权访问那些文件。
  2. 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
    
  3. 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用户启动:

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
  printf("%s\n", argv[1]);
  return system(argv[1]);
}

其中通过system函数执行用户传入的命令,system实现可以 在这找到,可以看到实际执行的是/bin/sh -c command,加入攻击者传入恶意命令,尝试通过该程序以root权限执行命令,最终以/bin/sh -c command的形式执行命令。

#define	SHELL_PATH	"/bin/sh"	/* Path of the shell.  */
#define	SHELL_NAME	"sh"		/* Name to give it.  */
do_system (const char *line)
{
  int status = -1;
  int ret;
  pid_t pid;
  struct sigaction sa;
#ifndef _LIBC_REENTRANT
  struct sigaction intr, quit;
#endif
 .....
  __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

Created at 2023-12-08T10:30:07+08:00

创建于:Friday, December 8,2023
最后修改于: Friday, December 8,2023