
Exploit de prova de conceito para LPE no Android Binder UAF que usa iovec spraying e sobrescrita de addr_limit para obter leitura/escrita arbitrária do kernel.
Um exploit de Prova de Conceito / Escalação Local de Privilégios (LPE) reescrito que visa CVE-2019-2215, uma vulnerabilidade de Use-After-Free no driver Binder do Android.
Disponibilizei a compilação do kernel vulnerável na pasta vulnerable_kernel_builds. Crie um emulador AOSP do Android 10 e execute o kernel com ele.
emulator -show-kernel -no-window -no-snapshot -wipe-data -avd research -kernel bzImage
Para os detalhes técnicos da falha, você pode aprendê-los neste blog incrível: https://projectzero.google/2019/11/bad-binder-android-in-wild-exploit.html
A estrutura task_struct possui um membro importante, addr_limit, do tipo mm_segment_t. addr_limit armazena o endereço válido mais alto do espaço do usuário. addr_limit faz parte de struct thread_info ou struct thread_struct, dependendo da arquitetura alvo. Como estamos lidando agora com um sistema x86_64, addr_limit é definido em struct thread_struct.

Se conseguirmos sobrescrever esse addr_limit com 0xFFFFFFFFFFFFFFFF, poderemos ler e escrever em qualquer parte da memória do espaço do kernel. Para uma melhor compatibilidade do exploit em x86_64 e arm64, é melhor definir addr_limit para 0xFFFFFFFFFFFFFFFE.
struct iovec é usada para Vectored I/O, também conhecida como Scatter/Gather I/O. Um dos principais problemas com struct iovec é que elas têm vida curta. Elas são alocadas por chamadas de sistema quando estão trabalhando com os buffers e liberadas imediatamente quando retornam ao modo usuário.
Queremos que a estrutura iovec permaneça no kernel quando acionarmos a operação de unlink e sobrescrevermos o ponteiro iov_base com o endereço de binder_thread->wait.head para obter leitura e escrita limitadas. Uma maneira é usar chamadas de sistema como readv, writev em um descritor de arquivo pipe, pois ele pode bloquear se o pipe estiver cheio ou vazio. pipe é um canal de dados unidirecional que pode ser usado para comunicação entre processos. O recurso de bloqueio do pipe nos dá uma janela de tempo significativa para corromper a estrutura iovec no espaço do kernel.
Da mesma forma, podemos usar a chamada de sistema recvmsg para bloquear, passando MSG_WAITALL como parâmetro de flag.
Como o tamanho da estrutura binder_thread é de 408 bytes, ela acabará no cache kmalloc-512.

Precisaremos empilhar 25 iovec estruturas para realocar o bloco pendente. 408 / 16 = 25.5

Como podemos ver na imagem acima, iovecStack[10].iov_len e iovecStack[11].iov_base serão sobrescritos.

Então, queremos processar iovecStack[10], bloquear a chamada de sistema writev e então acionar a operação de unlink. Isso garantirá que, quando iovecStack[11].iov_base for sobrescrito, retomaremos a chamada de sistema writev. Por fim, vazamos o conteúdo do bloco binder_thread de volta para o espaço do usuário e lemos o ponteiro task_struct dele.

Para alcançar uma write limitada, vamos usar a chamada de sistema recvmsg para bloquear, passando MSG_WAITALL como parâmetro de flag. A chamada de sistema recvmsg pode bloquear assim como a chamada de sistema writev.

Como o tamanho de mm_segment_t é de 0x8 bytes, queremos sobrescrevê-lo com 0xFFFFFFFFFFFFFFFE, pois é o endereço válido mais alto do espaço do kernel e não causará crash no processo se ocorrer uma page fault em um sistema arm64.

