linux 提权攻击姿势

流程

用户态 —-> 内核态

I.切换GS段寄存器
	通过 `swapgs` 切换 GS 段寄存器,将 GS 寄存器值和一个特定位置的值进行交换,目的是保存 GS 值,同时将该位置的值作为内核执行时的 GS 值使用
II.保存用户态栈帧信息
	将当前栈顶(用户空间栈顶)记录在 CPU 独占变量区域里,将 CPU 独占区域里记录的内核栈顶放入 rsp/esp
III.保存用户态寄存器信息
	通过 push 保存各寄存器值到栈上,以便后续“着陆”回用户态
IV.通过汇编指令判断是否为32位
V.控制权转交内核,执行系统调用
	在这里用到一个全局函数表`sys_call_table`,其中保存着系统调用的函数指针

内核态 —-> 用户态

由内核态重新“着陆”回用户态只需要恢复用户空间信息即可:
- `swapgs`指令恢复用户态GS寄存器
- `sysretq`或者`iretq`恢复到用户空间

方式一

进程权限凭证结构体cred ,它用于 保存每个进程的权限定义如下:

struct cred {
	atomic_t	usage;
	atomic_t	subscribers;	/* number of processes subscribed */
	void		*put_addr;
	unsigned	magic;
	kuid_t		uid;		/* 实际用户id */
	kgid_t		gid;		/* 实际用户组id */
	kuid_t		suid;		/* 保存的用户uid */
	kgid_t		sgid;		/* 保存的用户组gid */
	kuid_t		euid;		/* 真正有效的用户id */
	kgid_t		egid;		/* 真正有效的用户组id */
	kuid_t		fsuid;		
	kgid_t		fsgid;		
	unsigned	securebits;	/* 安全管理标识;用来控制凭证的操作与继承 */
	kernel_cap_t	cap_inheritable; /* execve时可以继承的权限 */
	kernel_cap_t	cap_permitted;	/* 可以(通过capset)赋予cap_effective的权限 */
	kernel_cap_t	cap_effective;	/* 进程实际使用的权限 */
	kernel_cap_t	cap_bset;	/* capability bounding set */
	kernel_cap_t	cap_ambient;	/* Ambient capability set */
  //。。。。。。
	};
} __randomize_layout;

真实用户ID(real UID):标识一个进程启动时的用户ID

保存用户ID(saved UID):标识一个进程最初的有效用户ID

有效用户ID(effective UID):标识一个进程正在运行时所属的用户ID,一个进程在运行途中是可以改变自己所属用户的,因而权限机制也是通过有效用户ID进行认证的,内核通过 euid 来进行特权判断;为了防止用户一直使用高权限,当任务完成之后,euid 会与 suid 进行交换,恢复进程的有效权限

文件系统用户ID(UID for VFS ops):标识一个进程创建文件时进行标识的用户ID

我们知道root身份的id组id都是0表示最高权限,一般我们使用的管理员用户就是1000

那么我们在内核创建一个新的进程时,改变进程的cred结构体uid和gid都为0也就完成了提权到root

方法二

struct cred* prepare_kernel_cred(struct task_struct* daemon):该函数用以拷贝一个进程的cred结构体,并返回一个新的cred结构体,需要注意的是daemon参数应为有效的进程描述符地址或NULL

int commit_creds(struct cred *new):该函数用以将一个新的cred结构体应用到进程

struct cred *prepare_kernel_cred(struct task_struct *daemon)
{
	const struct cred *old;
	struct cred *new;

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

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

	if (daemon)
		old = get_task_cred(daemon);
	else   //为NULL则从init_crea初创建一个默认的crea结构体
		old = get_cred(&init_cred);

	validate_creds(old);
//。。。。
	return NULL;
}

int commit_creds(struct cred *new)
{
	struct task_struct *task = current;
	const struct cred *old = task->real_cred;
//。。。

	get_cred(new); /* we will require a ref for the subj creds too */

	/* dumpability changes */
	if (!uid_eq(old->euid, new->euid) ||
	    !gid_eq(old->egid, new->egid) ||
	    !uid_eq(old->fsuid, new->fsuid) ||
	    !gid_eq(old->fsgid, new->fsgid) ||
	    !cred_cap_issubset(old, new)) {
		if (task->mm)
			set_dumpable(task->mm, suid_dumpable);
		task->pdeath_signal = 0;
		smp_wmb();
	}

	/* alter the thread keyring */
	if (!uid_eq(new->fsuid, old->fsuid))
		key_fsuid_changed(task);
	if (!gid_eq(new->fsgid, old->fsgid))
		key_fsgid_changed(task);

	/* do it
	 * RLIMIT_NPROC limits on user->processes have already been checked
	 * in set_user().
	 */
	alter_cred_subscribers(new, 2);
	if (new->user != old->user)
		atomic_inc(&new->user->processes);
	rcu_assign_pointer(task->real_cred, new);
	rcu_assign_pointer(task->cred, new);
	if (new->user != old->user)
		atomic_dec(&old->user->processes);
	alter_cred_subscribers(old, -2);

	/* send notifications */
	if (!uid_eq(new->uid,   old->uid)  ||
	    !uid_eq(new->euid,  old->euid) ||
	    !uid_eq(new->suid,  old->suid) ||
	    !uid_eq(new->fsuid, old->fsuid))
		proc_id_connector(task, PROC_EVENT_UID);

	if (!gid_eq(new->gid,   old->gid)  ||
	    !gid_eq(new->egid,  old->egid) ||
	    !gid_eq(new->sgid,  old->sgid) ||
	    !gid_eq(new->fsgid, old->fsgid))
		proc_id_connector(task, PROC_EVENT_GID);

	/* release the old obj and subj refs both */
	put_cred(old);
	put_cred(old);
	return 0;
}

那么在内核空间执行commit_creds(prepare_kernel_cred(NULL))则可以将该进程提权root

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值