Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
Pixel_GPU_Exploit — Android 14 kernel exploit for Pixel7/8 Pro | Kitploit
Ferramentas/GitHubGitHub/0x36/pixel_gpu_exploit
Android SecurityPrivilege EscalationMemory ForensicsVulnerability AnalysisExploitationLearning & EducationBinary Exploitation
GitHub0x36/pixel_gpu_exploit

Pixel_GPU_Exploit

Android 14 kernel exploit for Pixel7/8 Pro

Ver Repositório
55788há 2 anosRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Mali GPU Kernel LPE

Este artigo fornece uma análise aprofundada de duas vulnerabilidades do kernel na GPU Mali, acessíveis a partir da sandbox padrão de aplicativos, que identifiquei e reportei de forma independente ao Google. Inclui um exploit do kernel que alcança capacidades de leitura/escrita arbitrárias do kernel. Consequentemente, desabilita o SELinux e eleva privilégios para root nos modelos Google Pixel 7 e 8 Pro executando as seguintes versões do Android 14:

  • Pixel 8 Pro: google/husky/husky:14/UD1A.231105.004/11010374:user/release-keys
  • Pixel 7 Pro: google/cheetah/cheetah:14/UP1A.231105.003/11010452:user/release-keys
  • Pixel 7 Pro: google/cheetah/cheetah:14/UP1A.231005.007/10754064:user/release-keys
  • Pixel 7: google/panther/panther:14/UP1A.231105.003/11010452:user/release-keys (por m4b4 (Marcel))

Vulnerabilidades

Este exploit aproveita duas vulnerabilidades: um estouro de inteiro resultante de um patch incompleto no comando ioctl gpu_pixel_handle_buffer_liveness_update_ioctl, e um vazamento de informações nos buffers de mensagens do fluxo da timeline.

Buffer Underflow em gpu_pixel_handle_buffer_liveness_update_ioctl() Devido a Correção Incorreta de Estouro de Inteiro

Google abordou um estouro de inteiro no comando ioctl gpu_pixel_handle_buffer_liveness_update_ioctl neste commit. Inicialmente, quando reportei este problema, pensei que o bug era causado por um problema no patch descrito anteriormente. Após revisar o relatório, percebi que minha análise da vulnerabilidade estava incorreta. Apesar da minha suposição inicial de que o patch estava incompleto, ele efetivamente resolve e previne um underflow no cálculo. Isso me levou a suspeitar que a alteração não foi aplicada nas compilações de produção. No entanto, embora eu possa causar um underflow no cálculo, não é possível causar um overflow. Isso sugere que o comando ioctl foi parcialmente corrigido, embora não com o patch mostrado acima. Analisando o IDA revelou que outro patch incompleto foi enviado nas versões de produção, e este patch não está presente em nenhum branch git do módulo do kernel da GPU Mali.

Esta vulnerabilidade foi descoberta pela primeira vez na versão mais recente do Android e reportada em 19 de novembro de 2023. O Google posteriormente me informou que já a havia identificado internamente e atribuído CVE-2023-48409 no Boletim de Segurança do Android de dezembro, rotulando-a como uma duplicata.

Embora eu tenha conseguido verificar que o bug foi identificado internamente meses antes do meu relatório (com base na data do commit por volta de 30 de agosto), ainda há confusão. Especificamente, é estranho que os Níveis de Patch de Segurança (SPL) de outubro e novembro dos dispositivos mais recentes ainda estivessem afetados por esta vulnerabilidade — não investiguei versões anteriores a essas. Portanto, não consigo determinar conclusivamente se isso foi realmente uma duplicata e se o patch apropriado estava de fato agendado para dezembro antes da minha submissão, ou se houve uma falha na correção desta vulnerabilidade.

De qualquer forma, o que torna este bug poderoso é o seguinte:

  • O buffer info.live_ranges é totalmente controlado pelo usuário.
  • Os valores de estouro são entrada controlada pelo usuário, portanto, podemos causar um estouro no cálculo para que o ponteiro info.live_ranges possa estar em um deslocamento arbitrário anterior ao início do endereço do kernel buff.
  • O tamanho da alocação também é entrada controlada pelo usuário, o que dá a capacidade de solicitar uma alocação de memória de qualquer alocador de slabs de propósito geral.

Esta vulnerabilidade compartilha semelhanças com a vulnerabilidade de Buffer underflow em DeCxt::RasterizeScaleBiasData() que encontrei e explorei no kernel do iOS 15 em 2022.

Vazamento de Ponteiros do Kernel em Buffers de Mensagens do Fluxo da Timeline

A GPU Mali implementa um timeline stream personalizado projetado para coletar informações, serializá-las e subsequentemente escrevê-las em um buffer circular seguindo um formato específico. Os usuários podem invocar o comando ioctl kbase_api_tlstream_acquire para obter um descritor de arquivo, permitindo-lhes ler deste buffer circular. O formato das mensagens é o seguinte:

  • Um cabeçalho de pacote
  • Um ID de mensagem
  • Um buffer de mensagem serializado, onde o conteúdo específico depende do ID da mensagem.

Por exemplo, a função __kbase_tlstream_tl_kbase_kcpuqueue_enqueue_fence_wait serializa os ponteiros do kernel kbase_kcpu_command_queue e dma_fence no buffer de mensagens, resultando no vazamento de ponteiros do kernel para o processo de espaço do usuário.```c void __kbase_tlstream_tl_kbase_kcpuqueue_enqueue_fence_wait( struct kbase_tlstream *stream, const void *kcpu_queue, const void *fence ) { const u32 msg_id = KBASE_TL_KBASE_KCPUQUEUE_ENQUEUE_FENCE_WAIT; const size_t msg_size = sizeof(msg_id) + sizeof(u64) + sizeof(kcpu_queue) + sizeof(fence) ; char *buffer; unsigned long acq_flags; size_t pos = 0;

root@kitploit:~
buffer = kbase_tlstream_msgbuf_acquire(stream, msg_size, &acq_flags);

pos = kbasep_serialize_bytes(buffer, pos, &msg_id, sizeof(msg_id));
pos = kbasep_serialize_timestamp(buffer, pos);
pos = kbasep_serialize_bytes(buffer,
	pos, &kcpu_queue, sizeof(kcpu_queue));
pos = kbasep_serialize_bytes(buffer,
	pos, &fence, sizeof(fence));

kbase_tlstream_msgbuf_release(stream, acq_flags);

}

root@kitploit:~
A prova de conceito do exploit vaza o endereço do objeto `kbase_kcpu_command_queue` monitorando o ID da mensagem `KBASE_TL_KBASE_NEW_KCPUQUEUE`, que é despachado pela função `kbasep_kcpu_queue_new` sempre que um novo objeto de fila kcpu é alocado.

O Google me informou que a vulnerabilidade foi reportada em março de 2023 e recebeu o identificador [CVE-2023-26083](https://source.android.com/docs/security/bulletin/2023-07-01) em seu boletim de segurança. No entanto, consegui reproduzir o problema nos dispositivos Pixel mais recentes com os Pacotes de Nível de Segurança (SPL) de outubro e novembro, indicando que a correção não foi aplicada corretamente ou não foi aplicada. Posteriormente, o Google corrigiu rapidamente o problema no Boletim de Atualização de Segurança de dezembro sem dar créditos, e depois me informou que o problema foi considerado duplicado. A justificativa para rotular este problema como duplicado, no entanto, permanece questionável.

## Exploração
---
Então, tenho duas vulnerabilidades interessantes. A primeira oferece uma capacidade poderosa de modificar o conteúdo de qualquer endereço do kernel alinhado a 16 bytes que venha antes do endereço do ~buff~ alocado. A segunda vulnerabilidade fornece pistas sobre as possíveis localizações de objetos na memória do kernel.

### Notas sobre os valores de buffer_count e live_ranges_count
Com controle total sobre os campos `buffer_count` e `live_ranges_count`, tenho flexibilidade para selecionar a slab alvo e o deslocamento preciso que pretendo escrever. No entanto, selecionar valores para `buffer_count` e `live_ranges_count` requer consideração cuidadosa devido a várias restrições e fatores:
- Ambos os valores estão relacionados, e o estouro só ocorrerá se todas as novas verificações introduzidas forem ignoradas.
- A exigência de que o deslocamento negativo seja alinhado a 16 bytes restringe a capacidade de escrever em qualquer local escolhido. No entanto, isso geralmente não é um obstáculo significativo.
- Optar por um deslocamento maior leva a uma grande quantidade de dados sendo escrita em áreas da memória que podem não ser os alvos pretendidos. Por exemplo, se o tamanho da alocação transbordar para `0x3004`, o ponteiro `live_ranges` seria definido para `-0x4000` bytes do espaço alocado do objeto `buff`. A função `copy_from_user` então escreveria `0x7004` bytes, com base no cálculo de `update->live_ranges_count` vezes 4. Consequentemente, esta operação resultaria na sobrescrita de dados controlados pelo usuário na área de memória entre o ponteiro `live_ranges` e a alocação do `buff`. É essencial, portanto, garantir cuidadosamente que nenhum objeto crítico do sistema dentro desse intervalo seja acidentalmente sobrescrito. Dado que a operação envolve uma chamada `copy_from_user`, pode-se considerar acionar um `EFAULT` desmapeando deliberadamente a região de memória indesejada após o buffer de origem do usuário para evitar que dados sejam escritos em locais sensíveis. No entanto, esta abordagem é ineficaz, pois se a função `raw_copy_from_user` falhar, ela zerará os bytes restantes no buffer de destino do kernel. Esse comportamento é implementado para garantir que, em caso de cópia parcial devido a um erro, o restante do buffer do kernel não contenha dados não inicializados.```c
static inline __must_check unsigned long
_copy_from_user(void *to, const void __user *from, unsigned long n)
{
	unsigned long res = n;
	might_fault();
	if (!should_fail_usercopy() && likely(access_ok(from, n))) {
		instrument_copy_from_user(to, from, n);
		res = raw_copy_from_user(to, from, n);
	}
	if (unlikely(res))
		memset(to + (n - res), 0, res);
	return res;
}

Considerando isso, precisamos selecionar cuidadosamente o objeto a ser sobrescrito e os dados a serem escritos.

Choosing the Right Object to Overwrite

Por estar preso a essa verificação infeliz, minha estratégia é identificar um objeto que, se anulado, não produza nenhum resultado indesejado. Mas, antes de chegar lá, há outra questão a ser tratada. Lembra quando eu disse na última parte que posso escolher qualquer tamanho de alocação e, portanto, qualquer alocador de cache slab de propósito geral para atender meu buffer de alocação? Isso não está correto, por causa do copy_from_user novamente! É devido à mitigação CONFIG_HARDENED_USERCOPY. Ela proíbe especificar um tamanho que não corresponda ao tamanho do cache slab correspondente onde o buffer de destino do kernel corresponde (neste caso) de um objeto de heap. Ela determina se a página do buffer é uma página slab e, se for, recupera o kmem_cache->size correspondente e determina se o tamanho fornecido pelo usuário não o excederá; caso contrário, o kernel apenas trava devido à incompatibilidade de tamanho. Então, em outras palavras, não posso atingir objetos que pertencem ao alocador de propósito geral, MAS ainda posso atingir objetos que possuem tamanhos grandes (ou seja, aqueles servidos diretamente pelo alocador de páginas).

O primeiro pensamento que veio à mente foi usar a técnica pipe_buffer, que é uma técnica muito elegante para obter primitivas de leitura/escrita arbitrárias. Não entrarei em detalhes sobre a técnica, mas os leitores são incentivados a ler este blog fantástico do Interrupt Labs. Ao construir um objeto pipe, o objeto pipe_buffer é inicialmente criado em um array de 16 elementos; no entanto, o tamanho do array pode ser ajustado usando fcntl(F_SETPIPE_SZ). Portanto, a alocação do array pipe_buffer pode ser ajustada de modo que seja atendida pelo alocador de páginas, tornando-o um objeto alvo perfeito para atacar. Após selecionar o objeto pipe_buffer como candidato alvo, o próximo passo para alcançar leitura/escrita no kernel é sobrescrever seu conteúdo com a vulnerabilidade de underflow, o que me permitirá ler/escrever de/para qualquer localização de memória cuja página esteja sobrescrevendo o campo pipe_buffer->page. Como a vulnerabilidade me permite escrever dados arbitrários, posso controlar todo o conteúdo do 'pipe_buffer', incluindo seu campo page, e para fazer isso, preciso alocar o array pipe_buffer antes do objeto vulnerável kbuff e eles devem estar lado a lado.

Positioning pipe_buffer and buff Objects Adjacently

Eu espalhei a memória do kernel com muitos objetos kbase_kcpu_command_queue seguidos por um monte de arrays pipe_buffer. Não posso usar apenas os arrays pipe_buffer como fonte principal de spray devido à limitação imposta por pipe_max_size. Portanto, decidi começar o spray com o objeto kbase_kcpu_command_queue. Escolher o objeto kbase_kcpu_command_queue foi por dois motivos: seu tamanho de alocação é 0x38C8, portanto tratado pelo alocador de páginas, e posso obter deterministicamente seu endereço de kernel usando o bug de vazamento de informações do kernel, tornando-o um bom objeto para spray bem como um bom objeto alvo (como veremos na próxima seção).

Como mencionado antes, usei fcntl(F_SETPIPE_SZ) para aumentar o tamanho da alocação do array pipe_buffer para que possa ser atendida pelo alocador de páginas. Para ser mais específico, escolhi o tamanho de alocação como ==0x4000 bytes (4 * PAGE_SIZE)== para ser consistente com as alocações de kbase_kcpu_command_queue.

Obtaining a struct page Address

Para usar corretamente o pipe_buffer, é necessário um endereço de página. Ser capaz de identificar o endereço de kernel de um objeto kbase_kcpu_command_queue que posso deliberadamente criar e destruir o torna um bom candidato para usar, e encontrar seu struct page correspondente pode ser alcançado usando virt_to_page .

Contents to Write in the pipe_buffer

Então o objeto pipe_buffer é o seguinte:```c struct pipe_buffer { struct page *page; unsigned int offset, len; const struct pipe_buf_operations *ops; unsigned int flags; unsigned long private; };

root@kitploit:~
Conforme mencionado anteriormente, o campo `page` deve incluir um endereço de página válido. Os campos `offset` e `len` não devem exceder `PAGE_SIZE`, caso contrário, o pipe incrementará os contadores head/tail, resultando no uso de um novo objeto `pipe_buffer` e na perda de controle sobre o buffer pipe falso. 
Além disso, as `flags` devem ser `PIPE_BUF_FLAG_CAN_MERGE` para que as chamadas subsequentes a `pipe_write`, em vez de incrementar cegamente o contador head e usar o próximo pipe buffer, primeiro verifiquem se há espaço no `pipe_buffer` atual que comporte a solicitação de gravação ou não, e se houver, simplesmente anexarão os dados ao mesmo pipe buffer começando a partir do valor armazenado no campo `len`. 
Para evitar que o dispositivo trave em `pipe_buf_confirm`, que é chamado por `pipe_write` e `pipe_read`, o ponteiro `ops` também deve ser um endereço de kernel válido com o campo `ops->confirm` definido como _NULL_. Eu posso simplesmente usar um offset dentro do objeto `kbase_kcpu_command_queue` vazado que seja NULL e não mude em nenhuma circunstância.

### Escolhendo o Valor de Offset Ideal para Underflow
Embora os tamanhos de alocação de `buff` ,`kbase_kcpu_command_queue` e `pipe_buffer` sejam ~0x4000~ bytes, eu optei por fazer underflow do buffer com **0x8000** bytes. Por quê?

Vamos dar uma breve olhada em como `pipe_buffers` são atualizados durante operações de leitura e escrita. Suponha que possamos moldar o `pipe_buffer` para ficar assim:```c
struct pipe_buffer {
	.page = virt_to_page(addr),
	.offset =  0,
	.len = 0x40,
	.ops = kcpu_addr + 0x50,
	.flags = PIPE_BUF_FLAG_CAN_MERGE,
	unsigned long private = 0
};

Embora o bug ofereça a capacidade de controlar arbitrariamente o conteúdo deste objeto, ele só o faz uma vez porque o objeto com underflow é liberado imediatamente após a chamada ioctl terminar. Isso na verdade apresenta um problema, pois preciso atualizar manualmente o objeto pipe_buffer para torná-lo utilizável novamente, já que cada operação de leitura/escrita do pipe:

  • O campo .page não é atualizado; permanece o mesmo, e quando o buffer está vazio, ele é liberado, o que não quero que aconteça porque o campo .ops não está configurado corretamente.
  • Como o pipe_buffer atualiza o campo .offset em uma operação de leitura, portanto, não consigo ler a mesma região de memória novamente.
  • Os dados escritos no pipe_buffer serão anexados ao buffer a partir do valor .len (assumindo que a flag PIPE_BUF_FLAG_CAN_MERGE esteja definida) e o .len é atualizado de acordo. Ou seja, não podemos escrever dados no endereço exato duas vezes.

Como resultado, a menos que eu atualize corretamente o pipe_buffer após cada operação de leitura ou escrita, não consigo ler e escrever no mesmo pipe ao mesmo tempo. É por isso que fazer underflow com 0x8000 bytes é muito mais prático, porque, em vez de sobrescrever um único pipe_buffer, sobrescreverei duas instâncias distintas de pipe_buffer de dois objetos pipe distintos: uma será considerada para operações de leitura e a outra para operações de escrita.```c #define PIPE_BUF_FLAG_CAN_MERGE 0x10 /* can merge buffers */

pipe_read = (struct pipe_buffer *)( ptr); pipe_read->page = virt_to_page(ta->kcpu_kaddr); pipe_read->offset = 0; pipe_read->len = 0xfff; pipe_read->ops = (const void *)(ta->kcpu_kaddr + 0x50); pipe_read->flags = PIPE_BUF_FLAG_CAN_MERGE; pipe_read->private = 0;

pipe_write = (struct pipe_buffer )( ptr + 0x4000); pipe_write->page = virt_to_page(ta->kcpu_kaddr); pipe_write->offset = 0; pipe_write->len = 0; / This is the starting position of the pipe_write */ pipe_write->ops = (const void *)(ta->kcpu_kaddr + 0x50); pipe_write->flags = PIPE_BUF_FLAG_CAN_MERGE; pipe_write->private = 0;

root@kitploit:~
O `pipe_read` é um buffer de pipe falso que será usado para ler dados da página alvo começando em `.offset = 0` até `0xfff` bytes, enquanto `pipe_write` é um `pipe_buffer` falso que será usado para escrever dados começando em `.len = 0` até `0xfff` bytes.
Também é muito importante mencionar novamente que escrever mais de `PAGE_SIZE` bytes fará o pipe incrementar o contador head, portanto usando um `pipe_buffer` recém-alocado e perdendo o controle sobre nosso `pipe_write` falso. Por outro lado, esvaziar (ler 0xfff dados do) buffer `fake_read` diz ao kernel para liberar a página real chamando `ops→release`, causando uma falha no kernel porque ainda não tenho um endereço de texto do kernel.
Embora eu tenha conseguido segregar as operações de leitura e escrita do pipe para que realizar uma escrita em uma extremidade do pipe não interfira no outro buffer do pipe e vice-versa, ainda não resolvi o problema central: Como atualizar o buffer do pipe de forma confiável? A resposta óbvia que veio à mente foi apenas repetir o processo de spray repetidamente após cada chamada de leitura ou escrita do pipe. E isso não faz sentido porque teria um impacto significativo na confiabilidade do exploit. Na seção a seguir, dividirei o objetivo em dois sub-objetivos: para começar, focarei apenas no campo `.page`, seguido pelos campos `.len/.offset` depois.

### Modificando o campo pipe_buffer→page
Para minha surpresa, não tenho ou preciso atualizar o `.page`, isso porque posso sobrescrever o `pipe_buffer→page` para apontar para o endereço da página do `kbase_kcpu_command_queue` vazado. Portanto, **Tudo que preciso fazer é liberar o objeto `kbase_kcpu_command_queue` e sobrepô-lo com um novo objeto `pipe_buffer`. Sim! Agora tenho um `pipe_buffer→page` que aponta para um objeto `pipe_buffer` legítimo!
Substituir `kbase_kcpu_command_queue` por `pipe_buffer` nos dá a capacidade de manipular um buffer de pipe legítimo sem ter que atualizar regularmente o campo `.page`. No entanto, ainda tenho que lidar com os campos `.len` e `.offset`.

### Modificando os campos pipe_buffer→len/offset
Como mencionei anteriormente, fazer leitura/escrita no pipe atualiza os campos `.len` e `.offset`, tornando inutilizáveis operações subsequentes de leitura/escrita na mesma página, mesmo se realizadas através dos dois pipes distintos. Aqui vai outro truque: **existe uma técnica para ler/escrever dados sem nem tocar nos campos `.len/.offset`!**. E é possível alcançar isso causando falha nas chamadas `copy_page_from_iter` e `copy_page_to_iter` em `pipe_read/write`! Sim, assim como `copy_to/from_user`, `copy_page_to/from_iter` copia dados de/para o espaço do usuário que são passados através da estrutura `iov_iter`, e pode falhar.

Para continuar com o exemplo anterior, se desejamos escrever 8 bytes de dados em um endereço, o tamanho do buffer de espaço do usuário fornecido deve ser 8, seguido por uma área de memória não mapeada ou não legível, e então passar `9` como argumento de tamanho para a chamada de sistema `write`, indicando a quantidade de dados que queremos escrever. Esta operação escreverá 8 bytes e falhará no nono porque encontra uma localização de memória não mapeada/não legível. Como resultado, os dados foram efetivamente escritos no buffer de destino do kernel e o campo `.len` não foi modificado. A função `pipe_write` do kernel simplesmente retornará sem atualizar o campo `buf->len`.```c
		if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&
		    offset + chars <= PAGE_SIZE) {
			ret = pipe_buf_confirm(pipe, buf);
			if (ret)
				goto out;

			ret = copy_page_from_iter(buf->page, offset, chars, from);
			if (unlikely(ret < chars)) {
				ret = -EFAULT;
				goto out;
			}

			buf->len += ret;
			if (!iov_iter_count(from))
				goto out;
		}

O mesmo é verdade para operações de leitura; se desejamos ler 8 bytes, torne o nono byte do buffer ilegível e então simplesmente alegue que queremos ler 9 bytes, os dados serão copiados para o buffer do usuário sem alterar o campo .offset. Como resultado, somos capazes de realizar operações ilimitadas de leitura/escrita em qualquer endereço de memória do kernel sem ter que passar repetidamente pelo processo de spray.

Obtendo root

Agora que tenho uma primitiva forte de leitura/escrita arbitrária, apenas examinei todos os struct page no array VMEMMAP_START para determinar o endereço inicial do texto do kernel usando a técnica descrita no post do blog Interrupt Labs. Então percebi que init_task é anulado nas Atualizações de Segurança de Novembro do Android, então usei kthreadd_task em vez disso. Ter o endereço do kernel de kthreadd_task permitiu-me percorrer a lista task->tasks e obter o endereço do kernel da minha própria tarefa current, e então zerar a estrutura cred para obter privilégios de root.

Mais tarde, percebi que escanear todos os endereços de página era desnecessário porque eu já tinha o endereço de texto do kernel anon_pipe_buf_ops de um objeto pipe_buffer. Com esta informação, pude deduzir o endereço base do texto do kernel, contornando efetivamente o KASLR.

Desabilitar SELinux

O exploit também desabilita o SELinux. Com o endereço base do texto do kernel, preciso apenas encontrar a localização da estrutura global selinux_state e então zerar o valor .enforcing.

Prova de Conceito

A prova de conceito que acompanha o relatório foi testada em dispositivos Pixel 7 e 8 Pro rodando Android 14 com os ASBs de Outubro e Novembro, alcançando uma taxa de sucesso de quase 100%. Também é importante mencionar que o exploit não funcionará imediatamente em outros dispositivos devido ao uso de alguns offsets fixos. Para adicionar suporte a um novo dispositivo, é necessário fornecer o seguinte:

  • kthreadd_task offset from the kernel base address.
  • selinux_state offset from the kernel base address.
  • task_struct->cred , task_struct->pid and task_struct->tasks structure offsets.
  • anon_pipe_buf_ops offset from the kernel base address.

Compilação

Para compilar o exploit como um binário independente, use o seguinte comando e depois use adb shell para executá-lo:```sh $ aarch64-linux-androidXX-clang++ -static-libstdc++ -w -Wno-c++11-narrowing -DUSE_STANDALONE -o poc poc.cpp -llog $ adb push poc /data/local/tmp/ $ adb shell /data/local/tmp/poc

root@kitploit:~
Você também pode executar o exploit através de um aplicativo Android Studio, incorporando este diretório a ele e certifique-se de desabilitar os avisos inúteis de C++ adicionando `-w -Wno-c++11-narrowing` ao arquivo cmake.

### Demo```shell
$ adb logcat  |grep -i EXPLOIT
11-28 16:04:12.500  7989  7989 E EXPLOIT : [+] Target device: 'google/husky/husky:14/UD1A.231105.004/11010374:user/release-keys' 0xa9027bfdd10203ff 0xa90467faa9036ffc
11-28 16:04:15.563  7989  7989 E EXPLOIT : [+] Got the kcpu_id (0) kernel address = 0xffffff8901390000  from context (0x0)
11-28 16:04:18.441  7989  7989 E EXPLOIT : [+] Got the kcpu_id (255) kernel address = 0xffffff89b0bf8000  from context (0xff)
11-28 16:04:18.442  7989  7989 E EXPLOIT : [+] Found corrupted pipe with size 0xfff
11-28 16:04:18.442  7989  7989 E EXPLOIT : [+] SUCCESS! we have a fake pipe_buffer (0)!
11-28 16:04:18.444  7989  7989 E EXPLOIT : 10 00 39 01 89 FF FF FF  10 00 39 01 89 FF FF FF  | ..9.......9.....
11-28 16:04:18.444  7989  7989 E EXPLOIT : 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  | ................
11-28 16:04:18.444  7989  7989 E EXPLOIT : 00 B0 CD 12 C0 FF FF FF  00 00 00 00 00 00 00 00  | ................
11-28 16:04:18.444  7989  7989 E EXPLOIT : 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  | ................
11-28 16:04:18.445  7989  7989 E EXPLOIT : [+] Freeing kcpu_id = 0 (0xffffff8901390000)
11-28 16:04:18.446  7989  7989 E EXPLOIT : [+] Allocating 61 pipes with 256 slots
11-28 16:04:18.462  7989  7989 E EXPLOIT : [+] Successfully overlapped the kcpuqueue object with a pipe buffer
11-28 16:04:18.463  7989  7989 E EXPLOIT : 40 AB BA 26 FE FF FF FF  00 00 00 00 30 00 00 00  | @..&........0...
11-28 16:04:18.463  7989  7989 E EXPLOIT : 70 37 8D F1 DA FF FF FF  10 00 00 00 00 00 00 00  | p7..............
11-28 16:04:18.463  7989  7989 E EXPLOIT : 00 00 00 00 00 00 00 00                           | ........
11-28 16:04:18.463  7989  7989 E EXPLOIT : [+] pipe_buffer {.page = 0xfffffffe26baab40, .offset = 0x0, .len = 0x30, ops = 0xffffffdaf18d3770}
11-28 16:04:18.463  7989  7989 E EXPLOIT : [+] kernel base = 0xffffffdaf0010000, kthreadd_task = 0xffffff8002da3780 selinux_state = 0xffffffdaf28a3168
11-28 16:04:20.097  7989  7989 E EXPLOIT : [+] Found our own task struct 0xffffff88416c5c80
11-28 16:04:20.097  7989  7989 E EXPLOIT : [+] Successfully got root: getuid() = 0 getgid() = 0
11-28 16:04:20.097  7989  7989 E EXPLOIT : [+] Successfully disabled SELinux
11-28 16:04:20.102  7989  7989 E EXPLOIT : [+] Cleanup  ... OK
Baixar ferramenta