CVE-2022-0185 Linux Kernel 提权漏洞分析

漏洞分析

函数调用链

__x64_sys_fsconfig()->vfs_fsconfig_locked()->vfs_parse_fs_param()->legacy_parse_param()

__x64_sys_fsconfig

SYSCALL_DEFINE5(fsconfig,
		int, fd,
		unsigned int, cmd,
		const char __user *, _key,
		const void __user *, _value,
		int, aux)
{
	struct fs_context *fc;
	struct fd f;
	int ret;
	int lookup_flags = 0;

	struct fs_parameter param = {
		.type	= fs_value_is_undefined,
	};

	if (fd < 0)
		return -EINVAL;

	switch (cmd) {
	...
	case FSCONFIG_SET_STRING:
		if (!_key || !_value || aux)
			return -EINVAL;
		break;
	...
	}
	...
	fc = f.file->private_data;
	...
	if (_key) {
		param.key = strndup_user(_key, 256);
		if (IS_ERR(param.key)) {
			ret = PTR_ERR(param.key);
			goto out_f;
		}
	}

	switch (cmd) {
	...
	case FSCONFIG_SET_STRING:
		param.type = fs_value_is_string;
		param.string = strndup_user(_value, 256);
		if (IS_ERR(param.string)) {
			ret = PTR_ERR(param.string);
			goto out_key;
		}
		param.size = strlen(param.string);
		break;
	...
	}

	ret = mutex_lock_interruptible(&fc->uapi_mutex);
	if (ret == 0) {
		ret = vfs_fsconfig_locked(fc, cmd, &param);
		mutex_unlock(&fc->uapi_mutex);
	}

	switch (cmd) {
	case FSCONFIG_SET_STRING:
		kfree(param.string);
		break;
	...
	}
	...
}
//https://elixir.bootlin.com/linux/v5.14.21/source/fs/fsopen.c#L314

 

将位于用户态的key字段以及value字段拷贝至内核并将指针写入该文件系统的结构体中

但是长度限制为小于0x100字节

调用下一步的vfs_fsconfig_locked函数

 

legacy_parse_param

static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
	struct legacy_fs_context *ctx = fc->fs_private;
	unsigned int size = ctx->data_size;
	size_t len = 0;
	int ret;

	...
	switch (param->type) {
	case fs_value_is_string:
		len = 1 + param->size;
		fallthrough;
	...
	}

	if (len &gt; PAGE_SIZE - 2 - size)				[1]
		return invalf(fc, "VFS: Legacy: Cumulative options too large");
	if (strchr(param->key, ',') ||
	    (param->type == fs_value_is_string &&
	     memchr(param->string, ',', param->size)))
		return invalf(fc, "VFS: Legacy: Option '%s' contained comma",
			      param->key);
	if (!ctx->legacy_data) {
		ctx->legacy_data = kmalloc(PAGE_SIZE, GFP_KERNEL);	[2]
		if (!ctx->legacy_data)
			return -ENOMEM;
	}

	ctx->legacy_data[size++] = ',';
	len = strlen(param->key);
	memcpy(ctx->legacy_data + size, param->key, len);
	size += len;
	if (param->type == fs_value_is_string) {			[3]
		ctx->legacy_data[size++] = '=';
		memcpy(ctx->legacy_data + size, param->string, param->size);
		size += param->size;
	}
	ctx->legacy_data[size] = '\0';
	ctx->data_size = size;
	ctx->param_type = LEGACY_FS_INDIVIDUAL_PARAMS;
	return 0;
}//https://elixir.bootlin.com/linux/v5.14.21/source/fs/fs_context.c#L525

 

该函数在[2]处为ctx->legacy_data字段初始化赋值一个kmalloc-4K大小的slab

而该字段对应唯一的文件描述符,即多次对同一文件描述符调用该函数即可对该字段对应的slab进行多次写入

在[3]处将拷贝至内核的key字符串以及val字符串memcpy至ctx->legacy_data上并添加,=字符

len为用户态所传输的字符串长度+1,size为当前的ctx->legacy_data所对应的字符串长度,因此二者皆为可控变量

而注意到在[1]处存在负溢出

当size>=0xFFF时,PAGE_SIZE-2-size会造成负溢出并通过len>PAGE_SIZE-2-size的检测

然后依然执行拷贝用户态数据至ctx->legacy_data指针对应slab的操作

然而此时的size已经超过了4K的slab的大小,因此存在kmalloc-4K的chunk_overlap

且溢出长度不限,并可以分多次溢出

但由于前面将用户态内容拷贝至内核时strndup的存在,存在\x00截断

 

漏洞利用

利用msg_msg进行任意地址读写

msg_msg结构体

struct msg_msg {
	struct list_head m_list;
	long m_type;
	size_t m_ts;		/* message text size */
	struct msg_msgseg *next;
	void *security;
	/* the actual message follows immediately */
};
//https://elixir.bootlin.com/linux/v5.14.21/source/include/linux/msg.h#L9

msgsnd()函数

函数调用链:

msgsnd()->ksys_msgsnd()->do_msgsnd()-> load_msg()->alloc_msg()
load_msg()->copy_from_user()

使用方法:

int make_queue(key_t key,int msgflg)
{
	int result;
	if ((result=msgget(key, msgflg))==-1)
	{
		perror("[X] Msgget Failure");
		exit(0);
	}
	return result;
}

void send_msg(int msqid,void *msgp,size_t msgsz,int msgflg)
{
	if (msgsnd(msqid, msgp, msgsz, msgflg)==-1)
	{
		perror("[X] Msgsend Failure");
		exit(0);
	}
	return;
}

qid=make_queue(IPC_PRIVATE,0666|IPC_CREAT);
send_msg(qid,message,size-0x30,0);

msgsrv()函数

函数调用链:

msgrcv()->ksys_msgrcv()->do_msgrcv()->find_msg()
do_msg_fill()->store_msg()
free_msg()

使用方法:

ssize_t get_msg(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg)
{
	ssize_t ret;
	result=msgrcv(msqid,msgp,msgsz,msgtyp,msgflg);
	if (result<0)
	{
		perror("[X] Msgrcv Failure");
		exit(0);
	}
	return result;
}

任意地址读

通过chunk_overlap或者UAF更改struct msg_msgseg *next字段,将其指向待泄漏内存区域即可完成泄漏

(该指针所指向的地址其需要为NULL,否则unlink时可能会出现段错误)

通过chunk_overlap泄漏kernel_base

发送长度为0x1018的msg_msg

其前0xFB0字节的内容会被分配在kmalloc-4K的空间

剩下的0x28字节与0x8字节的next指针则被分配在kmalloc-32的空间

通过大量堆喷射seq_operations在kmalloc-32空间,而此时第一个struct msg_msg结构体的next指针正好指向kmalloc-32空间

此时通过UAF或者chunk_overlap更改m_ts字段,伪造信息长度,即可将位于kmalloc-32空间的信息后面的内容泄漏

其中可能包含seq_operations结构体中所包含的指向某个全局结构体的指针,而该指针即使在开启了FG-KASLR的情况下其偏移依然是固定的,因此非常适合进行kernel_base地址泄漏

任意地址写

Poc:


 

参考文章:

[1] https://www.willsroot.io/2022/01/cve-2022-0185.html

[2] https://blog.csdn.net/panhewu9919/article/details/120820617

[3] https://elixir.bootlin.com/linux/v5.14.21/source

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇