Linux 安全 - SUID机制

这篇具有很好参考价值的文章主要介绍了Linux 安全 - SUID机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、简介

1.1 简介

最初 UNIX 为文件分配了九个允许位,对应三类用户(同主、同组、其他),三种操作(读、写、执行)。后来,UNIX 又增加了三个允许位:set-user-bit(又称 set-user-id 或 setuid)、set-group-bit(又称 set-group-id 或 setgid)、set-other-bit(又称 sticky bit)。看起来似乎是为文件新增加了一种set 操作,但实际上不是这样,这三个允许位与操作许可无关。先说 setuid。进程调用 execve 执行了一个允许位 setuid 为 1 的文件后,进程的 euid,还有Linux 特有的 fsuid,被改变为所执行文件的属主 id。效果就是进程执行文件不仅将文件的内容读入进程的代码区内存和数据区内存,还将文件属性中的部分数据读入进程凭证。于是,进程可以操作一些以前不能操作的客体。

引入 setuid 更深层的目的是特权提升。

UNIX 的设计是简单的,一些任务只能由特权用户来做,而特权用户只有一个,就是用户id 为 0 的 root。如果普通用户需要做一些特权操作,那么正规的做法是请求管理员代劳,比如用户需要加载一块新硬盘。但是有的特权操作使用频繁,又不会对系统带来危害,比如passwd命令,普通用户可以在不具备修改密码文件权限的情况下,通过执行该命令来更改自己的密码。
它实现起来是这样的:
(1)passwd 二进制文件的属主是 root。
(2)passwd 二进制文件的 setuid 位被置位。
(3)passwd 二进制文件的允许位设置为所有人都可以执行。

1.2 文件权限位

UNIX 在诞生之初就采用了一种相对简单的方式来管理操作许可,这种方式被 Linux 继承,以文件为例:
(1)在每个文件的属性中存储文件所属的用户(属主)和所属的用户组(属组)。
(2)根据用户的属主和属组将所有用户分为三类:同主用户、同组用户、其他用户。
(3)在每个文件的属性中为三类用户分别存储访问许可。

(1)

$ ls -l text.txt 
-rw-rw-r-- 1 yl yl 0 Sep 28 16:25 text.txt

其中第一个字段-rw-rw-r–,我们可以把它分为四部分看:

-rw-rw-r--
1-     :第一个字符表示文件的类型
(2)rw-   :第 2-4 个字符表示文件所有者权限位
(3)rw-   :5-7 个字符表示所属组权限位
(4)r--	   :8 -10 个字符表示其他用户权限位

每个文件和目录都有一组权限位,用于控制对其的访问和操作权限。文件权限由九个位组成,分为三组,每组三个位,分别代表文件所有者、所属组和其他用户的权限。

这三组权限位分别是:
(1)文件所有者权限位:这组权限位控制文件所有者对文件的权限。它们的顺序是读取(r,位值为4)、写入(w,位值为2)和执行(x,位值为1)。文件所有者权限位的符号表示为:例如,rw-表示文件所有者具有读取和写入权限,但没有执行权限。

(2)所属组权限位:这组权限位控制与文件属于相同组的其他用户对文件的权限。它们的顺序和符号表示与文件所有者权限位相同。

(3)其他用户权限位:这组权限位控制所有其他用户对文件的权限,即除了文件所有者和所属组之外的用户。同样,它们的顺序和符号表示与文件所有者权限位相同。

内核代码采用一个 bit 来表示一个操作许可,对于文件就需要 9 个 bit 来表示文件的操作许可:同主读、同主写、同主执行、同组读、同组写、同组执行、其他读、其他写、其他执行。这些表示操作许可的比特位合在一起就成为权限位。

在文件权限中,每个用户类型可以具有读取、写入和执行的权限组合。对于目录而言,读取权限允许查看目录内容,写入权限允许创建、删除和重命名目录中的文件,执行权限允许进入目录并访问其内容。

(2)
特殊权限:权限格式中可以存在三个特殊权限字符:
“s”(setuid、setgid):当出现在所有者或所属组的执行权限中时,表示设置了 setuid 或 setgid 权限。如果相应的读取或写入权限未设置,则用 “S” 表示。
“t”(粘着位):当出现在目录的其他用户执行权限中时,表示粘着位。通常用于限制只有文件所有者能删除目录中的文件。

当文件设置了SUID权限时,文件的权限位会显示为包含"S"的特殊形式。具体来说,文件所有者权限位中的执行权限位 “x” 将被替换为 “s”。这表示当执行该文件时,它将以文件所有者的权限运行。

以下是SUID权限位的不同组合及其含义:
-rwS------:文件所有者具有读写权限,并且设置了SUID权限。其他用户没有任何权限。
-rwSr-xr-x:文件所有者具有读写权限,并且设置了SUID权限。所属组和其他用户具有读取和执行权限。
-rwsr-xr-x:文件所有者具有读写执行权限,并且设置了SUID权限。所属组和其他用户具有读取和执行权限。

要设置SUID权限,可以使用chmod命令与数字或符号表示法。例如,chmod 4755 file将文件的权限设置为-rwsr-xr-x,其中SUID权限被设置为文件所有者的执行权限。

二、SUID简介

在Linux中,SUID(Set User ID)是一种权限机制,用于赋予程序在执行时临时获取文件所有者的权限。当一个可执行文件具有SUID权限时,无论是哪个用户执行该文件,该程序都会以文件所有者的权限运行,而不是执行者自身的权限。

SUID机制允许普通用户执行特定的程序或命令,以便执行特权操作,例如修改系统配置或访问受限资源。一个典型的例子是/usr/bin/passwd命令,它具有SUID权限,因此普通用户可以在不具备修改密码文件权限的情况下,通过执行该命令来更改自己的密码。

类似的还有 sudo、su命令等,比如在普通用户下执行sudo就可以执行root权限可以执行的操作。
Linux 安全 - SUID机制,系统安全,linux,安全,服务器
SUID权限位:SUID由文件权限位中用户部分的"s"表示。如果一个文件被设置了SUID位,会表现在所有者(文件的属主)的权限的可执行位上(x)。

所有者的权限的可执行位(x)上定义了一个补充的s位,如果文件设置了SUID,那么它在执行的时候,会把进程的权限(euid)设置成文件属主的uid。
当设置了SUID时,还需要所有者的执行权限。当s这个标志出现在文件所有者的x权限上时,则就被称为Set UID。

可以使用chmod命令的数字符号(“4”)或符号符号(“u+s”)来设置SUID位。

#define S_ISUID  0004000

执行上下文:当用户执行一个启用了SUID的程序时,该程序以文件所有者的有效用户ID(EUID)运行。这暂时提升了运行程序的用户的权限级别到文件所有者的级别。

SUID权限仅对二进制程序(binary program)有效,不能用在脚本上(script)。

这个SUID机制就是专门为提升/切换用户权限而设计的,切换用户也必须先提升到root用户才能切换到其他用户。在这类文件被执行后,不需要验证密码,进程的euid被设置成文件属主的uid,如果文件属主是root用户当前进程就有了root权限,同时这时进程的uid和euid也不相等了。

因此当我们以普通用户在终端上执行 passwd,sudo 等命令时,比如普通用户的uid是1000,那么终端上的bash进程是普通用户的uid1000,当执行 passwd,sudo 等命令时, passwd,sudo 等二进制文件设置了SUID位,这么bash进程的euid以文件所有者的有效用户ID(EUID)运行,而 passwd,sudo 等二进制文件是由root用户管理的,其文件属主的用户名是 root 用户,因此passwd,sudo 等二进制文件的 EUID 等于 root 用户的uid = 0,因此bash进程的euid以文件所有者的有效用户ID = 0运行,这暂时提升了bash程序的用户的权限级别到文件所有者的级别。这样就达到了提升用户权限的作用了。

Linux 安全 - SUID机制,系统安全,linux,安全,服务器

三、源码解析

接下来分析内核源码SUID机制的实现:
在终端bash进程执行exec系统调用时:

execve
	-->do_execve
		-->do_execveat_common
			 /*
			 * sys_execve() executes a new program.
			 */
			-->__do_execve_file
				-->prepare_bprm_creds
				-->prepare_binprm

3.1 prepare_bprm_creds

/*
 * This structure is used to hold the arguments that are used when loading binaries.
 */
struct linux_binprm {
	struct cred *cred;	/* new credentials */
}
/*
 * Prepare credentials and lock ->cred_guard_mutex.
 * install_exec_creds() commits the new creds and drops the lock.
 * Or, if exec fails before, free_bprm() should release ->cred and
 * and unlock.
 */
static int prepare_bprm_creds(struct linux_binprm *bprm)
{
	if (mutex_lock_interruptible(&current->signal->cred_guard_mutex))
		return -ERESTARTNOINTR;

	bprm->cred = prepare_exec_creds();
	if (likely(bprm->cred))
		return 0;

	mutex_unlock(&current->signal->cred_guard_mutex);
	return -ENOMEM;
}

prepare_bprm_creds函数用于准备执行新的二进制程序所需的凭证(credentials),确保在执行新程序时具备正确的身份验证和权限信息。

prepare_bprm_creds
	-->prepare_exec_creds
		-->prepare_creds
/**
 * prepare_creds - Prepare a new set of credentials for modification
 *
 * Prepare a new set of task credentials for modification.  A task's creds
 * shouldn't generally be modified directly, therefore this function is used to
 * prepare a new copy, which the caller then modifies and then commits by
 * calling commit_creds().
 *
 * Preparation involves making a copy of the objective creds for modification.
 *
 * Returns a pointer to the new creds-to-be if successful, NULL otherwise.
 *
 * Call commit_creds() or abort_creds() to clean up.
 */
struct cred *prepare_creds(void)
{
	struct task_struct *task = current;
	const struct cred *old;
	struct cred *new;

	validate_process_creds();

	new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
	if (!new)
		return NULL;

	kdebug("prepare_creds() alloc %p", new);

	old = task->cred;
	memcpy(new, old, sizeof(struct cred));

	new->non_rcu = 0;
	atomic_set(&new->usage, 1);
	set_cred_subscribers(new, 0);
	get_group_info(new->group_info);
	get_uid(new->user);
	get_user_ns(new->user_ns);

#ifdef CONFIG_KEYS
	key_get(new->session_keyring);
	key_get(new->process_keyring);
	key_get(new->thread_keyring);
	key_get(new->request_key_auth);
#endif

#ifdef CONFIG_SECURITY
	new->security = NULL;
#endif

	if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
		goto error;
	validate_creds(new);
	return new;

error:
	abort_creds(new);
	return NULL;
}
EXPORT_SYMBOL(prepare_creds);

为对任务的凭证进行修改准备一个新的副本。在一般情况下,任务的凭证不应直接被修改,而是通过先准备一个新的副本,然后由调用者对副本进行修改,最后通过调用 commit_creds() 来提交修改。

函数的主要步骤如下:
首先,它验证当前任务的凭证是否有效。

然后,它使用 kmem_cache_alloc 分配内存来创建一个新的 struct cred 对象,并将其赋值给 new。

接着,它通过调用 memcpy 将当前任务的凭证内容复制到新的凭证对象中。

设置新的凭证对象的一些属性,如 non_rcu、usage、subscribers 等。

获取相关的用户组信息、用户ID以及用户命名空间。

如果系统启用了 CONFIG_KEYS,则增加对会话、进程、线程和请求密钥的引用计数。

如果系统启用了 CONFIG_SECURITY,则将 security 字段设置为 NULL。

最后,如果 security_prepare_creds() 失败,它将通过 abort_creds() 清理并释放新凭证对象的内存,并返回 NULL。否则,它将验证新凭证对象的有效性,并返回指向新凭证的指针。

这段代码用于创建一个新的任务凭证的副本,以备修改和提交。它确保了在修改任务凭证时能够进行适当的拷贝和处理,并提供了必要的清理机制以防出现错误。

3.2 prepare_binprm

/*
 * Fill the binprm structure from the inode.
 * Check permissions, then read the first BINPRM_BUF_SIZE bytes
 *
 * This may be called multiple times for binary chains (scripts for example).
 */
int prepare_binprm(struct linux_binprm *bprm)
{
	int retval;
	loff_t pos = 0;

	bprm_fill_uid(bprm);

	/* fill in binprm security blob */
	retval = security_bprm_set_creds(bprm);
	if (retval)
		return retval;
	bprm->called_set_creds = 1;

	memset(bprm->buf, 0, BINPRM_BUF_SIZE);
	return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
}

EXPORT_SYMBOL(prepare_binprm);

prepare_binprm函数作用是填充 struct linux_binprm 结构,该结构用于执行二进制程序。调用 bprm_fill_uid() 函数填充 bprm 结构的用户ID信息。

3.2.1 bprm_fill_uid

static void bprm_fill_uid(struct linux_binprm *bprm)
{
	struct inode *inode;
	unsigned int mode;
	kuid_t uid;
	kgid_t gid;

	/*
	 * Since this can be called multiple times (via prepare_binprm),
	 * we must clear any previous work done when setting set[ug]id
	 * bits from any earlier bprm->file uses (for example when run
	 * first for a setuid script then again for its interpreter).
	 */
	bprm->cred->euid = current_euid();
	bprm->cred->egid = current_egid();

	if (!mnt_may_suid(bprm->file->f_path.mnt))
		return;

	if (task_no_new_privs(current))
		return;

	inode = bprm->file->f_path.dentry->d_inode;
	mode = READ_ONCE(inode->i_mode);
	if (!(mode & (S_ISUID|S_ISGID)))
		return;

	/* Be careful if suid/sgid is set */
	inode_lock(inode);

	/* reload atomically mode/uid/gid now that lock held */
	mode = inode->i_mode;
	uid = inode->i_uid;
	gid = inode->i_gid;
	inode_unlock(inode);

	/* We ignore suid/sgid if there are no mappings for them in the ns */
	if (!kuid_has_mapping(bprm->cred->user_ns, uid) ||
		 !kgid_has_mapping(bprm->cred->user_ns, gid))
		return;

	if (mode & S_ISUID) {
		bprm->per_clear |= PER_CLEAR_ON_SETID;
		bprm->cred->euid = uid;
	}

	if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
		bprm->per_clear |= PER_CLEAR_ON_SETID;
		bprm->cred->egid = gid;
	}
}

bprm_fill_uid函数根据文件的权限和设置,填充 struct linux_binprm 结构的用户ID信息。

具体步骤如下:
(1)将 bprm->cred->euid 和 bprm->cred->egid 设置为当前进程的有效用户ID和有效组ID。
(2)获取文件的inode,即获取passwd,sudo 等可执行二进制文件的inode,并读取其权限模式。如果文件的权限模式中没有设置 setuid/setgid 位,则函数直接返回。

/*
 * Keep mostly read-only and often accessed (especially for
 * the RCU path lookup and 'stat' data) fields at the beginning
 * of the 'struct inode'
 */
struct inode {
	umode_t			i_mode;
	......
	kuid_t			i_uid;
	kgid_t			i_gid;
}
/*
 * This structure is used to hold the arguments that are used when loading binaries.
 */
struct linux_binprm {
	struct file * file;
}
struct file {
	struct path		f_path;
}
struct path {
	struct dentry *dentry;
} __randomize_layout;
struct dentry {
	struct inode *d_inode;		/* Where the name belongs to - NULL is  negative */
}

(3)在获取文件的inode之前,对其进行加锁。在持有锁的情况下,原子地重新加载文件的权限模式、用户ID和组ID。解锁inode。
(4)如果文件的权限模式中设置了 setuid 位,则将 bprm->cred->euid 设置为文件的用户ID,并将 bprm->per_clear 的 PER_CLEAR_ON_SETID 标志设置为1。
(5)如果文件的权限模式中设置了 setgid 位且组执行位也同时设置了,则将 bprm->cred->egid 设置为文件的组ID,并将 bprm->per_clear 的 PER_CLEAR_ON_SETID 标志设置为1。

这段代码用于根据文件的权限模式和设置,将适当的用户ID和组ID填充到 struct linux_binprm 结构中。它会检查文件的 setuid 和 setgid 位,并根据情况设置相关的用户ID和组ID。

3.2.2 security_bprm_set_creds

static inline int security_bprm_set_creds(struct linux_binprm *bprm)
{
	return cap_bprm_set_creds(bprm);
}
/**
 * cap_bprm_set_creds - Set up the proposed credentials for execve().
 * @bprm: The execution parameters, including the proposed creds
 *
 * Set up the proposed credentials for a new execution context being
 * constructed by execve().  The proposed creds in @bprm->cred is altered,
 * which won't take effect immediately.  Returns 0 if successful, -ve on error.
 */
int cap_bprm_set_creds(struct linux_binprm *bprm)
{
	//把bash普通进程的suid和sgid替换为passwd,sudo 等可执行二进制文件的uid,gid
	new->suid = new->fsuid = new->euid;
	new->sgid = new->fsgid = new->egid;
}

新凭证中的用户和组ID设置为有效用户和组ID。

3.3 install_exec_creds

/*
 * sys_execve() executes a new program.
 */
 __do_execve_file
 	-->exec_binprm
 		-->search_binary_handler
 		{
			struct linux_binfmt *fmt;
			fmt->load_binary(bprm);
		}

对于加载 elf 二进制可执行文件来说:

static struct linux_binfmt elf_format = {
	.load_binary	= load_elf_binary,
};
/*
 * These are the functions used to load ELF style executables and shared
 * libraries.  There is no binary dependent code anywhere else.
 */

static int load_elf_binary(struct linux_binprm *bprm)
{
	install_exec_creds(bprm);
}
/*
 * install the new credentials for this executable
 */
void install_exec_creds(struct linux_binprm *bprm)
{
	security_bprm_committing_creds(bprm);

	commit_creds(bprm->cred);
	bprm->cred = NULL;

	/*
	 * Disable monitoring for regular users
	 * when executing setuid binaries. Must
	 * wait until new credentials are committed
	 * by commit_creds() above
	 */
	if (get_dumpable(current->mm) != SUID_DUMP_USER)
		perf_event_exit_task(current);
	/*
	 * cred_guard_mutex must be held at least to this point to prevent
	 * ptrace_attach() from altering our determination of the task's
	 * credentials; any time after this it may be unlocked.
	 */
	security_bprm_committed_creds(bprm);
	mutex_unlock(&current->signal->cred_guard_mutex);
}
EXPORT_SYMBOL(install_exec_creds);

commit_creds替换当前进程的凭证来安装新的执行凭证。

总结

除了以用户和用户组控制权限,还有以下凭证机制:

Capabilities
Secure management flags (securebits)
Keys and keyrings
LSM
AF_KEY

详细请参考:Linux 安全 - Credentials

参考资料

Linux 5.4.18

Linux DAC 权限管理详解
Linux 内核安全模块深入剖析文章来源地址https://www.toymoban.com/news/detail-729190.html

到了这里,关于Linux 安全 - SUID机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux 服务器如何做好安全防护?

    Linux服务器安全技巧有: 网络安全重磅福利:入门进阶全套282G学习资源包免费分享! 1. 物理系统的安全性 配置BIOS,禁用从CD/DVD、外部设备、软驱启动。下一步,启用BIOS密码,同时启用GRUB的密码保护,这样可以限制对系统的物理访问。 2. 磁盘分区 使用不同的分区很重要,

    2024年02月22日
    浏览(43)
  • linux服务器远程控制安全配置

    一、设置linux服务器用户登录错误次数锁定配置         为防止遭受恶意暴力破解,设置账户登录尝试次数并进行锁定,有效保护账户的安全。         1、登录失败处理功能策略(服务器终端)                 注:用户锁定期间,无论在输入正确还是错误的密码

    2024年04月12日
    浏览(36)
  • 如何保护linux服务器远程使用的安全

    服务器安全是一个非常敏感的问题,因服务器远程入侵导致数据丢失的安全问题频频出现,一旦服务器入侵就会对个人和企业造成巨大的损失。因此,在日常使用服务器的时候,我们需要采取一些安全措施来保障服务器的安全性。 目前服务器系统使用到比较多的就是Linux,大

    2024年01月25日
    浏览(42)
  • Linux中FTP服务器搭建与安全配置

    FTP(File Transfer Protocol)是一种用于在计算机之间传输文件的标准协议。在Linux系统上,您可以搭建自己的FTP服务器,以便轻松共享和传输文件。本文将提供详细的步骤和示例代码,帮助大家安全地搭建和配置Linux FTP服务器。 首先,需要选择并安装一个FTP服务器软件。在Linux中

    2024年02月04日
    浏览(42)
  • 利用Ansible实现批量Linux服务器安全配置

    1.摘要 在上一篇初步利用Ansible实现批量服务器自动化管理文章中, 我初步实现了通过编写清单和剧本来实现多台服务器的自动化管理,在本章节中, 我将利用Ansible的剧本来实现更实用、更复杂一点的功能, 主要功能包括三个:1.同时在三台服务器中增加IP访问控制,只允许192.168.2

    2024年01月20日
    浏览(41)
  • 【安全狗】linux免费服务器防护软件安全狗详细安装教程

    在费用有限的基础上,复杂密码+云服务器基础防护+常见端口替换+安全软件,可以防护绝大多数攻击 官方提供了两个下载方式,本文采用的是 方式2 wget安装 方法1:在安全狗官网直接下载软件安装包(.tar.gz 格式:safedog_linux64.tar.gz) 方法2:采取wget方式下载发布包: tar xzv

    2024年02月16日
    浏览(41)
  • 配置安全的linux-apache服务器(5)

    实验简介 实验所属系列:Linux网络服务配置与安全 实验对象: 本科/专科信息安全专业、网络工程 相关课程及专业:系统安全配置、服务器配置、计算机网络 实验时数(学分):2学时 实验类别:实践实验类 预备知识 虚拟主机简介 虚拟主机(Virtual Host)是指在一台主机上运

    2024年02月03日
    浏览(36)
  • 等保2.0 测评 linux服务器加固 基本安全配置手册

    禁止所有默认的被操作系统本身启动的且不需要的帐号,当你第一次装上系统时就应该做此检查,Linux提供了各种帐号,你可能不需要,如果你不需要这个帐号,就移走它,你有的帐号越多,就越容易受到攻击。 #为删除你系统上的用户,用下面的命令: [root@c1gstudio]# userdel

    2024年03月14日
    浏览(56)
  • 保障Linux服务器安全:多人共用环境下的防护策略

    引言 在多人共用的Linux服务器环境中,确保系统安全至关重要。不仅要防止外部攻击,还要防止内部用户的不当操作或恶意行为。本文将探讨在多人共用Linux服务器时,如何采取有效策略来保障系统安全,防止“坏事”发生。 使用sudo和权限分组 :避免给予用户过多的权限。

    2024年04月10日
    浏览(46)
  • Linux系统安装NFS服务器

    NFS是一种网络文件系统,英文全称Network File System,通过NFS可以让不同的主机系统之间共享文件或目录。通过NFS,用户可以直接在本地NFS客户端读写NFS服务端上的文件,是非常好的共享存储工具。本篇文章将介绍如何在CentOS7上安装NFS服务器,包括服务端和客户端安装两部分。

    2024年01月21日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包