Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
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-0847 — Exploit de prova de conceito e análise detalhada da vulnerabilidade de escalonamento de privilégios no kernel Linux CVE-2022-0847 (Dirty Pipe), incluindo ambiente Docker para teste e depuração. | Kitploit
Ferramentas/GitHubGitHub/chenaotian/cve-2022-0847
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoAprendizado e EducaçãoExploração de BináriosLabs e Prática
GitHubchenaotian/cve-2022-0847

CVE-2022-0847

Exploit de prova de conceito e análise detalhada da vulnerabilidade de escalonamento de privilégios no kernel Linux CVE-2022-0847 (Dirty Pipe), incluindo ambiente Docker para teste e depuração.

Ver Repositório
25766há 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

Análise da escalada de privilégios no kernel Linux CVE-2022-0847 Dirty Pipe

[toc]

Este artigo foi publicado originalmente na conta pública de segurança da Huawei; esta é a versão do blog (mais completa).

Link da publicação original: https://mp.weixin.qq.com/s/6VhWBOzJ7uu80nzFxe5jpg

Introdução à vulnerabilidade

Identificador da vulnerabilidade: CVE-2022-0847 (também conhecida como: Dirty Pipe)

Produto vulnerável: kernel Linux - syscall splice

Versões afetadas: Linux 5.8 (introduzida pelo patch f6dd975583bd) com correção em 5.16.11, 5.15.25, 5.10.102

Impacto: permite gravar até uma página de conteúdo em qualquer arquivo legível (o que é suficiente) e possibilita escalada local de privilégios.

Configuração do ambiente

Docker para análise da vulnerabilidade: chenaotian/cve-2022-0847 (se ainda não conseguir acessar, é porque ainda não terminei de enviar).

Fornece:

  • Kernel 5.13 vulnerável compilado e configurável para depuração
  • qemu, gdb e código-fonte do kernel linux 5.13
  • exp

Inicialização:

cd ~/cve-2022-0847
gcc exp.c -o exp --static && cp exp ./rootfs && cd rootfs
find . | cpio -o --format=newc > ../rootfs.img
cd ../ 
./boot.sh

Depuração:

gdb ./vmlinux
target remote :10086
directory /root/linux-5.13
b do_splice
b copy_page_to_iter_pipe 
b pipe_write
ignore 3 15
...
p *(struct pipe_inode_info *) pipe
p (struct pipe_buffer)pipe->bufs[0]

Princípio da vulnerabilidade

O princípio resumido da vulnerabilidade é que a chamada da função splice pode enviar arquivos para o pipe na forma de "cópia zero". No nível do código, a cópia zero consiste em usar diretamente a página de cache do arquivo (page cache) como página buf do pipe. Porém, isso introduz uma vulnerabilidade de variável não inicializada, fazendo com que a página de cache do arquivo seja tratada, posteriormente, no canal do pipe, como uma página comum de cache do pipe e seja "continuada a escrita", sendo assim adulterada. No entanto, nesse caso, o kernel não considera essa página de cache como uma "página suja" (dirty page), e ela não será gravada no disco por um curto período (até a próxima reinicialização, por exemplo). Durante esse período, todos os acessos ao arquivo usarão a página de cache adulterada, alcançando assim uma operação de "escrita arbitrária por um curto período em qualquer arquivo legível". Isso possibilita a escalada local de privilégios.

Ponto de ocorrência da vulnerabilidade

De acordo com o patch, o ponto de ocorrência da vulnerabilidade está na função copy_page_to_iter_pipe, onde foi adicionada a inicialização de buf->flags; portanto, trata-se de uma vulnerabilidade de variável não inicializada.

image-20220308170149137

O ponto de chamada de copy_page_to_iter_pipe ocorre na chamada de sistema splice. A função splice (chamada de sistema) entrega o conteúdo do arquivo ao pipe por meio de um método de "cópia zero". Isso tem melhor desempenho do que o método tradicional de enviar o conteúdo do arquivo diretamente para o pipe. Os detalhes são apresentados a seguir.

Princípio do pipe e pipe_write

Primeiramente, como o apelido da vulnerabilidade é "Dirty Pipe", vamos entender o pipe. O pipe é um canal de comunicação fornecido pelo kernel, criado pelas funções pipe/pipe2, retornando dois descritores de arquivo: um para enviar dados e outro para recebê-los, como as duas extremidades de um cano. Não vou falar muito sobre o uso específico.

Falando brevemente sobre a implementação no kernel: normalmente o espaço total do buffer do pipe é de 65536 bytes, gerenciado na forma de páginas, totalizando 16 páginas (4096 bytes por página). As páginas não são contíguas; elas são gerenciadas por um array, formando uma lista circular. São mantidos dois ponteiros da lista: um para escrita (pipe->head) e um para leitura (pipe->tail). Aqui vamos analisar principalmente a função pipe_write:

linux-5.13\fs\pipe.c : 400 : pipe_write

static ssize_t
pipe_write(struct kiocb *iocb, struct iov_iter *from)
{
	struct file *filp = iocb->ki_filp;
	struct pipe_inode_info *pipe = filp->private_data;
	unsigned int head;
	ssize_t ret = 0;
	size_t total_len = iov_iter_count(from);
	ssize_t chars;
	bool was_empty = false;
	bool wake_next_writer = false;

	··· ···
    ··· ···
	head = pipe->head;
	was_empty = pipe_empty(head, pipe->tail);
	chars = total_len & (PAGE_SIZE-1);
	if (chars && !was_empty) { 
        //[1]pipe 缓存不为空,则尝试是否能从当前最后一页"接着"写
		unsigned int mask = pipe->ring_size - 1;
		struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
		int offset = buf->offset + buf->len; 

		if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&
		    offset + chars <= PAGE_SIZE) { 
            /*[2]关键,如果PIPE_BUF_FLAG_CAN_MERGE 标志位存在,代表该页允许接着写
             *如果写入长度不会跨页,则接着写,否则直接另起一页 */
			ret = pipe_buf_confirm(pipe, buf);
			···
			ret = copy_page_from_iter(buf->page, offset, chars, from);
			···
			}
			buf->len += ret;
			···
		}
	}

	for (;;) {//[3]如果上一页没法接着写,则重新起一页
		··· ···
		head = pipe->head;
		if (!pipe_full(head, pipe->tail, pipe->max_usage)) {
			unsigned int mask = pipe->ring_size - 1;
			struct pipe_buffer *buf = &pipe->bufs[head & mask];
			struct page *page = pipe->tmp_page;
			int copied;

			if (!page) {//[4]重新申请一个新页
				page = alloc_page(GFP_HIGHUSER | __GFP_ACCOUNT);
				if (unlikely(!page)) {
					ret = ret ? : -ENOMEM;
					break;
				}
				pipe->tmp_page = page;
			}

			spin_lock_irq(&pipe->rd_wait.lock);

			head = pipe->head;
			··· ···
			pipe->head = head + 1;
			spin_unlock_irq(&pipe->rd_wait.lock);

			/* Insert it into the buffer array */
			buf = &pipe->bufs[head & mask];
			buf->page = page;//[5]将新申请的页放到页数组中
			buf->ops = &anon_pipe_buf_ops;
			buf->offset = 0;
			buf->len = 0;
			if (is_packetized(filp))
				buf->flags = PIPE_BUF_FLAG_PACKET;
			else
				buf->flags = PIPE_BUF_FLAG_CAN_MERGE;
            	//[6]设置flag,默认PIPE_BUF_FLAG_CAN_MERGE
			pipe->tmp_page = NULL;

			copied = copy_page_from_iter(page, 0, PAGE_SIZE, from); 
            //[7]拷贝操作
			··· ···
			ret += copied;
			buf->offset = 0;
			buf->len = copied;

			··· ···
		}
        ··· ···
    }
	··· ···
	return ret;
}
  1. Se o pipe atual não estiver vazio (o estado vazio é determinado por head==tail), significa que há dados não lidos no pipe; então obtém-se o ponteiro head, que aponta para a página mais recente usada para escrita, e verifica-se len e offset dessa página (para encontrar o final dos dados). Em seguida, tenta-se continuar escrevendo na página atual.
  2. Verifica se a página atual possui a flag PIPE_BUF_FLAG_CAN_MERGE; se ela não existir, não é permitido continuar escrevendo na página atual. Ou se os dados atuais, ao serem concatenados aos dados anteriores, ultrapassarem uma página (ou seja, a escrita cruza a fronteira da página), também não é possível continuar.
  3. Se não for possível continuar na página anterior, inicia-se uma nova página.
  4. alloc_page aloca uma nova página.
  5. Coloca a nova página no início do array (pode substituir a página original) e inicializa os valores.
  6. buf->flag é inicializado por padrão como PIPE_BUF_FLAG_CAN_MERGE, pois o estado padrão permite que a página continue sendo escrita.
  7. Copia os dados a serem escritos; se não forem copiados completamente, repete as operações acima.

O ponto-chave da exploração é a flag PIPE_BUF_FLAG_CAN_MERGE não inicializada no splice, que determina se podemos continuar escrevendo em uma página "não totalmente escrita" do pipe.

De splice a copy_page_to_iter_pipe

Baixar ferramenta