
Explorando CVE-2022-0847 - escrito por: Antonius (w1sdom)
Dirty Pipe (CVE-2022-0847) é uma das vulnerabilidades de segurança mais significativas no Kernel Linux 5.8 – 5.15.24, descoberta por Max Kellermann em 2022. Esta vulnerabilidade permite que usuários comuns (sem privilégios especiais) sobrescrevam dados em arquivos que deveriam ser somente leitura. Entendendo Conceitos Principais
Antes de discutir Dirty Pipe em detalhes, aqui estão alguns conceitos internos do kernel Linux que precisam ser compreendidos:
1. Paginação (Paging)
Paginação é um mecanismo de gerenciamento de memória no kernel Linux onde o sistema de memória divide a memória física em pequenos blocos de tamanho fixo chamados page frames, e a memória virtual é dividida em blocos do mesmo tamanho chamados páginas.
Esse mecanismo permite que o kernel mapeie o espaço de endereço virtual dos processos para a memória física de forma não sequencial, o que é crucial para eficiência e segurança em sistemas modernos.
2. Página (Memória Virtual)
No Linux, uma página é a menor unidade de gerenciamento de memória física manipulada pelo kernel.
Analogia: A RAM é como um livro gigante. Uma página é uma folha de papel naquele livro. O kernel não move dados bit a bit, mas sim folha por folha (página por página).
Geralmente, em arquiteturas de sistemas modernos (como x86_64), o tamanho padrão de uma página é 4 KB (4096 bytes).
3. Page Cache (Cache de Páginas)
Esta é uma parte crucial. O Linux não lê arquivos diretamente do disco toda vez porque é lento. O kernel copia o conteúdo dos arquivos na RAM chamada Page Cache.
4. Pipe Buffer (Buffer de Pipe)
Pipe é um mecanismo de Comunicação entre Processos (IPC). Internamente, o kernel gerencia pipes usando a estrutura de dados pipe_inode_info. Os dados dentro de um pipe são armazenados em um "buffer" chamado Pipe Buffer.
5. Flag do Pipe Buffer (PIPE_BUF_FLAG_CAN_MERGE)
A flag PIPE_BUF_FLAG_CAN_MERGE foi introduzida no Kernel Linux versão 5.8.
É aqui que reside a principal vulnerabilidade. A flag chamada PIPE_BUF_FLAG_CAN_MERGE.
6. Splice
splice() é uma syscall para mover dados entre dois descritores de arquivo sem copiar os dados entre o espaço do kernel e o espaço do usuário. Isso é frequentemente chamado de mecanismo Zero-copy.
A syscall splice() é o "ator principal" no Dirty Pipe:
7. Copy on Write (CoW)
O mecanismo Copy-on-Write (CoW) é uma estratégia de otimização de gerenciamento de memória usada pelo kernel Linux para adiar a cópia de dados até que seja absolutamente necessário.
A relação entre Copy-on-Write (CoW) e o exploit Dirty Pipe (CVE-2022-0847) é sobre como um pequeno bug no kernel Linux "engana" com sucesso o mecanismo CoW, permitindo que dados sejam escritos em arquivos que deveriam ser somente leitura.
8. Dirty Page (Página Suja)
Uma dirty page é uma página de memória na RAM que foi modificada por uma aplicação, mas as alterações ainda não foram gravadas de volta no armazenamento secundário (como SSD ou disco rígido).
Análise da Vulnerabilidade Dirty Pipe
Dirty Pipe é um tipo de bug de lógica no tratamento do pipe buffer no kernel Linux 5.8 até o kernel Linux 5.15.24. O principal problema está no mecanismo Pipe (canal de comunicação entre processos) e como o kernel gerencia o Page Cache (memória que armazena cópias de dados de arquivos do disco). A questão central é um bug na flag PIPE_BUF_FLAG_CAN_MERGE.
O principal problema reside na falha do kernel em redefinir adequadamente essa flag (bug de lógica). Aqui está a análise do código: Nas funções copy_page_to_iter_pipe e push_to_pipe no kernel Linux anterior à versão 5.16.11, ao realizar operações splice, o kernel prepara a estrutura pipe_buffer mas esquece de limpar o membro .flags.
Estrutura de Código Vulnerável:
// Local do problema: fs/pipe.c ou include/linux/pipe_fs_i.h
struct pipe_buffer {
struct page *page;
unsigned int offset, len;
const struct pipe_buf_operations *ops;
unsigned int flags; // <--- ESTA FLAG NÃO É REDEFINIDA
unsigned long private;
};
Código Antes do Patch (Vulnerável):
// lib/iov_iter.c - Antes do patch do CVE-2022-0847
static size_t copy_page_to_iter_pipe(struct page *page,
size_t offset, size_t bytes, struct iov_iter *i) {
// ---------snip-----------
struct pipe_buffer *buf = &pipe->bufs[head & mask];