漏洞分析
函数调用链
__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, ¶m);
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 > 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
Jumping into Gigabet is super easy with gigabetlogin. No hassles, just straight to the games. The way it should be! Easy Access: gigabetlogin
Bay888casino, now we’re talking! The colours are bright, they make it feel like I’m going to win. Good and fun, check out bay888casino ASAP and you might get lucky too!
Found Goagamesapk last week with the best games ever. It’s now my main source of enjoyment, give it a go guys! You won’t regret it! Download it over at website: goagamesapk
Been playing on Taya3653 for a while now. Pretty decent selection of games, and the site is easy to navigate. Recommended! Start playing here: taya3653
Yo check out Winph99, heard they giving out free spins. Its time to try my luck and win big today! To know more about this platform check :winph99
Heard some buzz about shbet.80. Guess it’s worth checking out. Someone told me they had a decent win there last week so who knows maybe I will too. shbet.80
Hey, I stumbled upon 7j777 the other day. Nothing too fancy, but worth a look if you’re bored. Here’s the link: 7j777
Yo, check it out! Found 777casinonl. Looks like a fun little spot for some online gambling. Ready to win? Join 777casinonl.
Needed to login to 777bdlogin and it was quick and painless. Good to go! Let’s get started by clicking on 777bdlogin.