
Exploit de Kernel para CVE-2016-6187 (Escalação de Privilégio Local)
Exploit de Kernel para CVE-2016-6187 (Escalação de Privilégio Local) (versões afetadas: < 4.6.5)
O exploit foi desenvolvido contra um kernel 4.6 compilado personalizado + buildroot (kconfig em breve)
e contorna KASLR, SMEP, SMAP (KPTI não disponível para 4.6)
A vulnerabilidade explorada é um estouro de um único byte nulo baseado em heap em um dos hooks LSM do AppArmor (setprocattr). Veja https://nvd.nist.gov/vuln/detail/CVE-2016-6187
Medições iniciais indicam 94,44% de chance de obter um shell root. Em 1 em cada 18 casos, o exploit causará um oops no kernel. Mais trabalho é necessário para estabilizar completamente o kernel após obter acesso root.
Abusamos do estouro de byte nulo para corromper o LSB de um ponteiro da freelist em kmalloc-128, fazendo com que
ele aponte para um objeto já alocado. Depois disso, liberamos esse objeto alocado, causando um cenário de double free.
Agora podemos alocar duas estruturas sobrepostas do cache kmalloc-128. A primeira estrutura é um objeto
cujo conteúdo pode ser lido de volta pelo espaço do usuário. Para isso, escolhi ip6_sf_socklist. A segunda estrutura,
que será alocada sobre ip6_sf_socklist, deve conter ponteiros do kernel - rfkill_data é uma boa opção.
Ela contém um ponteiro de heap (lista rfkill_data.events vazia) e um ponteiro de dados global para rfkill_fds (rfkill_data.list.prev).
Uma vez que rfkill_data é alocado sobre ip6_sf_socklist, podemos recuperar os ponteiros do kernel chamando
getsockopt(sock, IPPROTO_IPV6, MCAST_MSFILTER, gsf, &optlen). A base do texto do kernel pode então ser derivada de rfkill_fds.
Em seguida, causamos um segundo cenário de double free, mas desta vez abusamos do processo de vinculação da fila de mensagens para sobrescrever o ponteiro da freelist
e enganar o alocador para nos retornar um chunk de kmalloc-96 quando na verdade solicitamos um chunk de kmalloc-128.
Podemos então aproveitar a incompatibilidade de tamanho para estourar o ponteiro da freelist de um chunk kmalloc-96, dando-nos a primitiva para
alocar endereços arbitrários. No nosso caso, vamos para a estrutura ptmx_fops e sobrescrevemos o callback ptmx_fops.unlocked_ioctl
com um ponteiro para nosso gadget de stack pivot.
Chamar ioctl em /dev/ptmx aciona o gadget de pivot e movemos a pilha para uma pilha falsa em kmalloc-128, onde
já preparamos nossa cadeia rop de primeiro estágio.
Como temos espaço limitado na pilha falsa kmalloc-128 (não sabemos se o próximo chunk estará no mesmo slab), a tarefa
da cadeia rop de primeiro estágio é copiar a cadeia rop de segundo estágio do espaço do usuário para o buffer de log dmesg do kernel (__log_buf)
e mover a pilha para __log_buf.
A cadeia rop de segundo estágio então tenta reparar o kernel (restaurando ptmx_fops, ...) antes de chamar commit_creds(prepare_kernel_cred(NULL))
para escalar privilégios.
E finalmente ela sai do espaço do kernel via iretq e retornamos, com privilégios elevados, para nossa sub-rotina execve("/bin/sh", 0, 0).
Usuários sem privilégios devem conseguir pelo menos abrir /dev/rfkill. Algumas distribuições podem
não permitir isso. Verifiquei Ubuntu e Fedora, ambos permitem que usuários sem privilégios abram /dev/rfkill
(embora o Fedora use SELinux em vez de AppArmor)
A seção a seguir explica como reproduzir a escalação de privilégio local em qemu-x86_64.
Primeiro, você precisará obter um initrd e um kernel bzImage. Há duas maneiras:
A maneira mais fácil e rápida. Você pode encontrar todos os componentes necessários em prebuilt/.
Ou, alternativamente, você também pode compilar os componentes você mesmo. Basta baixar o
kernel 4.6 e usar
o x86_64_vuln_kern_defconfig fornecido em config/ para compilar o bzImage.
Para o initrd, você poderia, por exemplo, usar buildroot. Apenas certifique-se
de definir as permissões corretas para /dev/rfkill em seu init
chmod 664 /dev/rfkill
Isso é o mesmo que em Ubuntu-16.04 e muitas outras distribuições. Também não se esqueça de compilar
o exploit e colocá-lo em seu initrd
make
Depois de obter os componentes, inicialize a máquina vulnerável
./boot.sh <path/to/bzImage> <path/to/rootfs.cpio>
e execute o exploit
./exploit
Talvez seja necessário executar o exploit várias vezes, dependendo se zeramos um LSB que já era zero. Observe também que ainda há uma pequena chance (talvez 10%-20%?) de travar após obter acesso root - estou trabalhando nisso.
Divirta-se.
ptmx_fops para aumentar a estabilidadekmalloc-128 para aumentar a estabilidade