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
Ferramentas/GitHubGitHub/lrh2000/stackrot
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoCTFExploração de Binários
GitHublrh2000/stackrot

StackRot

CVE-2023-3269: Vulnerabilidade de escalonamento de privilégios no kernel Linux

Ver Repositório
499371há 6 mesesRevisado 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

StackRot (CVE-2023-3269): vulnerabilidade de escalonamento de privilégios no kernel Linux

GitHub CI (Exploit verificado pelo GitHub-CI)

Demo

Foi encontrada uma falha no tratamento da expansão da pilha no kernel Linux 6.1 até 6.4, também conhecida como "Stack Rot". A árvore de bordo (maple tree), responsável por gerenciar as áreas de memória virtual, pode passar por uma substituição de nós sem adquirir adequadamente o bloqueio de escrita do MM, gerando problemas de uso-após-liberação. Um usuário local sem privilégios poderia usar essa falha para comprometer o kernel e escalar seus privilégios.

Como o StackRot é uma vulnerabilidade do kernel Linux encontrada no subsistema de gerenciamento de memória, ela afeta quase todas as configurações do kernel e exige capacidades mínimas para ser acionada. No entanto, deve-se observar que os nós de bordo são liberados usando callbacks RCU, atrasando a desalocação real de memória até depois do período de graça do RCU. Consequentemente, explorar essa vulnerabilidade é considerado desafiador.

Até onde sei, atualmente não existem exploits publicamente disponíveis que visem bugs de uso-após-liberação-por-RCU (UAFBR). Esta é a primeira instância em que bugs UAFBR foram comprovadamente exploráveis, mesmo sem a presença das configurações CONFIG_PREEMPT ou CONFIG_SLAB_MERGE_DEFAULT. Notavelmente, este exploit foi demonstrado com sucesso no ambiente fornecido pelo Google kCTF VRP (bzImage_upstream_6.1.25, config).

A vulnerabilidade StackRot está presente no kernel Linux desde a versão 6.1, quando a estrutura da árvore VMA foi alterada de árvores rubro-negras para árvores de bordo.

Contexto

Sempre que a chamada de sistema mmap() é utilizada para estabelecer um mapeamento de memória, o kernel gera uma estrutura chamada vm_area_struct para representar a área de memória virtual (VMA) correspondente. Essa estrutura armazena diversas informações, incluindo flags, propriedades e outros detalhes pertinentes relacionados ao mapeamento.```c struct vm_area_struct { long unsigned int vm_start; /* 0 8 / long unsigned int vm_end; / 8 8 / struct mm_struct * vm_mm; / 16 8 / pgprot_t vm_page_prot; / 24 8 / long unsigned int vm_flags; / 32 8 / union { struct { struct rb_node rb attribute((aligned(8))); / 40 24 / / --- cacheline 1 boundary (64 bytes) --- / long unsigned int rb_subtree_last; / 64 8 / } attribute((aligned(8))) shared attribute((aligned(8))); / 40 32 / struct anon_vma_name * anon_name; / 40 8 / } attribute((aligned(8))); / 40 32 / / --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- / struct list_head anon_vma_chain; / 72 16 / struct anon_vma * anon_vma; / 88 8 / const struct vm_operations_struct * vm_ops; / 96 8 / long unsigned int vm_pgoff; / 104 8 / struct file * vm_file; / 112 8 / void * vm_private_data; / 120 8 / / --- cacheline 2 boundary (128 bytes) --- / atomic_long_t swap_readahead_info; / 128 8 / struct vm_userfaultfd_ctx vm_userfaultfd_ctx; / 136 0 */

root@kitploit:~
    /* size: 136, cachelines: 3, members: 14 */
    /* forced alignments: 1 */
    /* last cacheline: 8 bytes */

} attribute((aligned(8)));

root@kitploit:~
Posteriormente, quando o kernel encontra falhas de página ou outras chamadas de sistema
relacionadas à memória, ele exige uma busca rápida da VMA baseada apenas no endereço.
Anteriormente, as VMAs eram gerenciadas usando árvores rubro-negras. No entanto, a partir
da versão 6.1 do kernel Linux, ocorreu a migração para maple trees. [Maple
trees][mt] são estruturas de dados B-tree seguras para RCU, otimizadas para armazenar
intervalos sem sobreposição. No entanto, sua natureza intrincada adiciona complexidade ao
código e introduz a vulnerabilidade StackRot.

 [mt]: https://docs.kernel.org/6.4/core-api/maple_tree.html

Em sua essência, uma maple tree é composta por nós maple. Embora a estrutura da árvore
possa ser complexa, é importante observar que essa complexidade não tem nada a ver com
o bug StackRot. Portanto, ao longo deste artigo, assume-se que a maple tree consiste
em apenas um nó, ou seja, o nó raiz.

Esse nó raiz pode conter até 16 intervalos. Esses intervalos podem
representar uma lacuna ou apontar para uma VMA. Como as lacunas também contam
como intervalos, todos os intervalos são conectados sequencialmente, resultando
na necessidade de apenas 15 extremidades, também conhecidas como pivôs, dentro
da estrutura do nó. Observe que a extremidade mais à esquerda e a extremidade
mais à direita são omitidas, pois podem ser recuperadas do nó pai.```c
struct maple_range_64 {
        struct maple_pnode *       parent;               /*     0     8 */
        long unsigned int          pivot[15];            /*     8   120 */
        /* --- cacheline 2 boundary (128 bytes) --- */
        union {
                void *             slot[16];             /*   128   128 */
                struct {
                        void *     pad[15];              /*   128   120 */
                        /* --- cacheline 3 boundary (192 bytes) was 56 bytes ago --- */
                        struct maple_metadata meta;      /*   248     2 */
                };                                       /*   128   128 */
        };                                               /*   128   128 */

        /* size: 256, cachelines: 4, members: 3 */
};

A estrutura maple_range_64, conforme mostrado acima, representa um nó maple. Além dos pivôs, os slots são usados para referenciar a estrutura VMA quando o nó atua como um nó folha, ou para outros nós maple quando o nó atua como um nó interno. Se um intervalo corresponder a uma lacuna, o slot simplesmente conterá um valor NULL. A disposição dos pontos de pivô e dos slots pode ser visualizada conforme ilustrado abaixo:``` Slots -> | 0 | 1 | 2 | ... | 12 | 13 | 14 | 15 | ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ └─ Implied maximum │ │ │ │ │ │ │ └─ Pivot 14 │ │ │ │ │ │ └─ Pivot 13 │ │ │ │ │ └─ Pivot 12 │ │ │ │ └─ Pivot 11 │ │ │ └─ Pivot 2 │ │ └─ Pivot 1 │ └─ Pivot 0 └─ Implied minimum

root@kitploit:~
Em relação à modificação concorrente, a árvore maple impõe uma restrição específica, ou seja, um bloqueio exclusivo deve ser mantido pelos escritores (*Regra W*). No caso da árvore VMA, o bloqueio exclusivo corresponde ao bloqueio de escrita do MM. Quanto aos leitores, duas opções estão disponíveis. A primeira opção envolve manter o bloqueio de leitura do MM (*Regra A1*), o que resulta no bloqueio do escritor pelo bloqueio de leitura-escrita do MM. Alternativamente, a segunda opção é entrar na seção crítica RCU (*Regra A2*). Ao fazer isso, o escritor não é bloqueado, e os leitores podem continuar suas operações, uma vez que a árvore maple é segura para RCU. Embora a maioria dos acessos VMA existentes opte pela primeira opção (ou seja, Regra A1), a Regra A2 é empregada em alguns cenários críticos de desempenho, como falhas de página sem bloqueio.

Contudo, há um aspecto adicional que requer atenção particular, o qual diz respeito à expansão da pilha. A pilha representa uma área de memória que é mapeada com a flag MAP_GROWSDOWN, indicando expansão automática quando um endereço abaixo da região é acessado. Nesses casos, o endereço inicial da VMA correspondente é ajustado, bem como o intervalo associado dentro da árvore maple. Notavelmente, esses ajustes são feitos sem manter o bloqueio de escrita do MM.```c
static inline
void do_user_addr_fault(struct pt_regs *regs,
                        unsigned long error_code,
                        unsigned long address)
{
	// ...

	if (unlikely(!mmap_read_trylock(mm))) {
		// ...
	}
	// ...
	if (unlikely(expand_stack(vma, address))) {
		// ...
	}

	// ...
}

Normalmente, existe uma lacuna entre a VMA da pilha e sua VMA vizinha, pois o kernel impõe uma proteção de pilha (stack guard). Nesse cenário, ao expandir a pilha, apenas o valor de pivô no nó maple precisa ser atualizado, um processo que pode ser realizado atomicamente. No entanto, se a VMA vizinha também possuir o flag MAP_GROWSDOWN, nenhuma proteção de pilha é imposta.```c int expand_downwards(struct vm_area_struct *vma, unsigned long address) { // ...

root@kitploit:~
if (prev) {
	if (!(prev->vm_flags & VM_GROWSDOWN) &&
	    vma_is_accessible(prev) &&
	    (address - prev->vm_end < stack_guard_gap))
		return -ENOMEM;
}

// ...

}

root@kitploit:~
Como resultado, a expansão da pilha pode eliminar a lacuna. Em tais situações, o
intervalo de lacuna dentro do nó maple deve ser removido. Como a árvore maple é
segura para RCU, não é possível sobrescrever o nó in-place. Em vez disso, um novo nó é
criado, disparando a substituição do nó, e o nó antigo é posteriormente
destruído usando um callback de RCU.```c
static inline void mas_wr_modify(struct ma_wr_state *wr_mas)
{
	// ...

	if ((wr_mas->offset_end - mas->offset <= 1) &&
	    mas_wr_slot_store(wr_mas))           // <-- in-place update
		return;
	else if (mas_wr_node_store(wr_mas))      // <-- node replacement
		return;

	// ...
}

O callback de RCU é invocado somente após todas as seções críticas de RCU pré-existentes terem concluído. No entanto, o problema surge ao acessar VMAs, pois apenas o lock de leitura do MM é mantido, e ele não entra na seção crítica de RCU (de acordo com a Regra A1). Consequentemente, em teoria, o callback poderia ser invocado a qualquer momento, resultando na liberação do antigo nó maple. No entanto, ponteiros para o nó antigo podem já ter sido obtidos, levando a um bug de use-after-free ao tentar acessá-lo posteriormente.

O backtrace onde ocorre o use-after-free (UAF) é mostrado abaixo:```

  • CPU 0 - - CPU 1 -

mm_read_lock() mm_read_lock() expand_stack() find_vma_prev() expand_downwards() mas_walk() mas_store_prealloc() mas_state_walk() mas_wr_story_entry() mas_start() mas_wr_modify() mas_root() mas_wr_node_store() node = rcu_dereference_check() mas_replace() [ The node pointer is recorded ] mas_free() ma_free_rcu() call_rcu(&mt_free_rcu) [ The node is dead ] mm_read_unlock()

[ Wait for the next RCU grace period.. ] rcu_do_batch() mas_prev() mt_free_rcu() mas_prev_entry() kmem_cache_free() mas_prev_nentry() [ The node is freed ] mas_slot() mt_slot() rcu_dereference_check(node->..) [ UAF occurs here ] mm_read_unlock()

root@kitploit:~
## Fix

Relatei esta vulnerabilidade à equipe de segurança do kernel Linux em 15 de junho.
Em seguida, o processo de correção desse bug foi liderado por Linus Torvalds.
Dada a sua complexidade, levou quase duas semanas para desenvolver um conjunto de patches que
obteve consenso.

Em 28 de junho, durante a janela de merge do kernel Linux 6.5, a correção foi mesclada
na árvore do Linus. Linus forneceu uma [mensagem de merge abrangente][fix] para
elucidar a série de patches do ponto de vista técnico.

 [fix]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9471f1f2f50282b9e8f59198ec6bb738b4ccc009

Esses patches foram subsequentemente backportados para os kernels estáveis ([6.1.37][6.1],
[6.3.11][6.3] e [6.4.1][6.4]), resolvendo efetivamente o bug "Stack Rot" em
1º de julho.

 [6.1]: https://lore.kernel.org/stable/2023070133-create-stainless-9a8c@gregkh/T/
 [6.3]: https://lore.kernel.org/stable/2023070146-endearing-bounding-d21a@gregkh/T/
 [6.4]: https://lore.kernel.org/stable/2023070140-eldercare-landlord-133c@gregkh/T/

## Exploit

O exploit foca principalmente no desafio Google kCTF, especificamente quando
nem CONFIG_PREEMPT nem CONFIG_SLAB_MERGE_DEFAULT estão definidos. Para explorar
o StackRot, a tarefa mais importante é localizar uma iteração VMA que atenda
aos seguintes critérios:
 1. O tempo da iteração pode ser controlado. Esse controle nos permite garantir
    que o período de graça da RCU termine durante a iteração VMA.
 2. A iteração recupera informações específicas da estrutura VMA e
    retorna as informações para o espaço do usuário. Esse recurso nos permite
    explorar a vulnerabilidade UAF do nó maple para vazar alguns endereços de
    kernel.
 3. A iteração invoca certos ponteiros de função na estrutura VMA. Essa
    capacidade específica nos permite explorar o UAF do nó maple para
    controlar o contador de programa (PC) em modo kernel.

A iteração VMA escolhida é a iteração responsável por gerar o
conteúdo de `/proc/[pid]/maps`. As seções a seguir mostrarão como essa
iteração satisfaz os critérios acima.

### Passo 0: De UAFBR para UAF

Durante qualquer iteração VMA, a referência ao nó raiz da árvore VMA é
obtida, e a iteração prossegue através de seus slots. Assim, ao acionar
a expansão da pilha em outra thread em uma CPU separada durante a iteração VMA,
a substituição do nó pode ser iniciada simultaneamente. Nesse ponto, acessar
o nó antigo é considerado uma situação de use-after-free-by-RCU (UAFBR). No entanto,
os problemas reais surgem apenas quando o nó antigo é realmente liberado, o que ocorre no
callback da RCU.

Isso apresenta dois desafios: (i) determinar quando o nó antigo é liberado e
(ii) garantir que a iteração VMA não termine antes de o nó antigo ser
liberado.

A primeira questão é relativamente simples. No kernel, a
função `synchronize_rcu()` pode ser empregada para aguardar até que o período de graça da RCU
termine, garantindo que todos os callbacks RCU pré-existentes tenham sido invocados. No
espaço do usuário, chamadas de sistema que, em última análise, chamam `synchronize_rcu()` podem ser
utilizadas para o mesmo propósito. Assim, quando tais chamadas de sistema terminam, é
sabido que o nó antigo foi liberado. Notavelmente, há uma chamada de sistema,
`membarrier(MEMBARRIER_CMD_GLOBAL, 0, -1)`, que invoca exclusivamente
`synchronize_rcu()`.```c
SYSCALL_DEFINE3(membarrier, int, cmd, unsigned int, flags, int, cpu_id)
{
	// ...

	switch (cmd) {
	// ...
	case MEMBARRIER_CMD_GLOBAL:
		/* MEMBARRIER_CMD_GLOBAL is not compatible with nohz_full. */
		if (tick_nohz_full_enabled())
			return -EINVAL;
		if (num_online_cpus() > 1)
			synchronize_rcu();
		return 0;
	// ...
	}
}

A segunda questão exige uma análise mais aprofundada. Várias soluções potenciais são as seguintes:

  1. A tarefa de iteração é preemptada, o período de graça do RCU termina e a iteração retoma a execução. No entanto, essa abordagem é ineficaz se CONFIG_PREEMPT não estiver definido.
  2. A tarefa de iteração entra em um estado de suspensão (por exemplo, aguardando E/S), o período de graça do RCU termina e a iteração continua. Atualmente, não tenho conhecimento de nenhuma iteração de VMA que atenda a esse requisito e possa ser explorada para vazar endereços do kernel e controlar o contador de programa (PC). Ela pode existir, mas é necessária uma investigação minuciosa.
  3. A tarefa de iteração sofre uma interrupção (por exemplo, interrupção de temporizador), durante a qual o período de graça do RCU é concluído. É possível empregar timerfd para criar vários temporizadores de hardware que, ao expirar durante a iteração de VMA, podem acionar uma interrupção demorada. No entanto, essa abordagem não é viável porque o manipulador de interrupções opera com interrupções desabilitadas e, se uma CPU não puder lidar com interrupções entre processadores (IPIs), o período de graça do RCU não terminará.
  4. A tarefa de iteração é deliberadamente prolongada, permitindo que o período de graça do RCU expire. Esta é a solução escolhida. Se o período de graça atual do RCU ultrapassar jiffies_till_first_fqs (com padrão de vários jiffies), uma interrupção entre processadores (IPI) será enviada à CPU vítima e acionará preempção voluntária. No caso da iteração de VMA, a preempção voluntária pode fazer com que o período de graça do RCU termine e libere o nó maple, convertendo efetivamente UAFBR em um cenário genuíno de use-after-free (UAF).

Uma observação significativa é que, durante a iteração de VMA para /proc/[pid]/maps, é gerado o caminho completo do arquivo para regiões de memória mapeadas por arquivo. Embora o nome do diretório seja tipicamente restrito a um máximo de 255 caracteres, não há limitação na profundidade do diretório. Isso significa que, ao criar um arquivo com uma profundidade de diretório extremamente grande e estabelecer um mapeamento de memória para esse arquivo, o acesso a /proc/[pid]/maps pode levar um tempo considerável durante a iteração de VMA. Consequentemente, essa duração estendida possibilita a conclusão do período de graça do RCU e a aquisição da primitiva de UAF.```c static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) { // ...

root@kitploit:~
/*
 * Print the dentry name for named mappings, and a
 * special [heap] marker for the heap:
 */
if (file) {
	seq_pad(m, ' ');
	/*
	 * If user named this anon shared memory via
	 * prctl(PR_SET_VMA ..., use the provided name.
	 */
	if (anon_name)
		seq_printf(m, "[anon_shmem:%s]", anon_name->name);
	else
		seq_file_path(m, file, "\n");
	goto done;
}

// ...

}

root@kitploit:~
Este passo é ilustrado na figura a seguir:

![Passo 0: De UAFBR para UAF](https://assets.kitploit.com/production/public/readmes/28620/6791d2c105f1fc504247b668317c7d23c3141c92456dab2fb2e54f7f70e16e15.png)

### Passo 1: De UAF de slab para UAF de página

Agora que a UAF está funcionando dentro de um slab. Se CONFIG_SLAB_MERGE_DEFAULT estiver habilitada e o slab de nós maple se fundir com kmalloc-256, o conteúdo dentro do nó antigo pode ser controlado alocando uma nova estrutura a partir de kmalloc-256 e preenchendo-a com dados do espaço do usuário. No entanto, se CONFIG_SLAB_MERGE_DEFAULT não estiver definida, será necessária uma abordagem alternativa. Nesse caso, é necessário devolver a página do nó liberado ao alocador de páginas, permitindo que o nó antigo seja controlado alocando uma nova página e preenchendo-a de acordo.

Lembre-se de que a árvore VMA conterá apenas um nó. Portanto, ao utilizar `fork()`/`clone()`, múltiplas árvores VMA e um número igual de nós maple são gerados. Supondo que um slab abranja M nós maple, e que um nó por M nós seja retido enquanto todos os outros nós são liberados via `exit()`, os nós restantes tornam-se os únicos nós dentro de seus respectivos slabs. Inicialmente, esses slabs residem na lista parcial da CPU. Quando a lista parcial atinge sua capacidade, os slabs são descarregados de volta para a lista parcial do nó NUMA correspondente.

Se o último nó maple dentro de um slab for liberado, o slab fica vazio. Se esse slab residir na lista parcial de um nó NUMA, e a lista parcial desse nó NUMA específico já estiver na capacidade máxima, a página é imediatamente devolvida ao alocador de páginas. Consequentemente, a UAF de slab se transforma em um cenário de UAF de página. O conteúdo dentro da página liberada pode ser manipulado enviando alguns dados via `msgsnd()`, que aloca objetos elásticos e os preenche diretamente com os dados do usuário fornecidos.```c
static void __slab_free(struct kmem_cache *s, struct slab *slab,
			void *head, void *tail, int cnt,
			unsigned long addr)

{
	// ...

	if (unlikely(!new.inuse && n->nr_partial >= s->min_partial))
		goto slab_empty;

	// ...
	return;

slab_empty:
	// ...
	discard_slab(s, slab);
}

O número de nós maple por slab, M, depende do número de CPUs. A implementação do exploit considera uma situação com duas CPUs e, portanto, assume 16 como o valor de M, conforme ilustrado na seguinte figura:

Passo 1: De UAF de slab para UAF de página

Passo 2: De UAF para vazamento de endereço

Ao obter controle do nó maple, torna-se possível manipular os endereços de VMAs subsequentes que serão iterados posteriormente. Como a iteração alvo tem como objetivo gerar /proc/self/maps, certas informações de VMA, como os endereços inicial e final, que residem na estrutura VMA, são retornadas ao espaço do usuário.

Contudo, surge um desafio: o endereço de uma estrutura VMA no nó maple só pode ser definido adequadamente se alguns endereços já forem conhecidos. Felizmente, o CVE-2023-0597 serve diretamente a esse propósito. De acordo com o CVE-2023-0597, o endereço de cpu_entry_area não é randomizado. Embora essa vulnerabilidade tenha sido corrigida no Linux 6.2, ela não foi backportada para kernels estáveis anteriores até o momento da escrita. Consequentemente, ao sobrescrever o endereço da estrutura VMA com o da última entrada da IDT, a entrada que contém o endereço de asm_sysvec_spurious_apic_interrupt é diretamente vazada, revelando assim os endereços base do código do kernel e dos dados do kernel.

Passo 2: De UAF para vazamento de endereço (1)

O método discutido anteriormente pode ser usado recorrentemente para expor de forma incremental mais endereços da seção de dados do kernel. Por exemplo, o ponteiro init_task.tasks.prev na seção de dados aponta para a estrutura task_struct da tarefa mais recentemente criada, que sem dúvida é alocada no heap.

Passo 2: De UAF para vazamento de endereço (2)

Quando todas as tarefas recém-criadas são terminadas, suas estruturas task_struct posteriormente serão desalocadas. Se a quantidade dessas tarefas for grande o suficiente, as páginas correspondentes podem ser devolvidas ao alocador de páginas. Isso possibilita realocar essas páginas e preenchê-las com dados do usuário. No entanto, lembre-se de que as páginas liberadas geralmente pertencem à lista de páginas per-CPU (PCP). Para páginas presentes na lista PCP, elas podem ser realocadas exclusivamente na mesma ordem de páginas. Consequentemente, apenas mapear novas páginas no espaço do usuário, o que requer apenas páginas de ordem 0 do alocador de páginas, não cumprirá os objetivos.

Contudo, a chamada de sistema msgsnd solicitará pedaços de memória via kmalloc e preencherá esses pedaços com dados definidos pelo usuário. Quando o cache do kmalloc for esgotado, ela requisitará páginas do alocador de páginas em uma ordem específica. Se o tamanho da mensagem for ajustado com precisão, a ordem exata será a desejada. Assim, a página cujo endereço foi previamente vazado será realocada. Como resultado, torna-se possível obter uma página com um endereço conhecido e dados manipulados pelo usuário.

Passo 3: De UAF para privilégios de root

Agora é possível forjar a estrutura VMA na página de endereço conhecido e controlar o ponteiro de função vma->vm_ops->name. O próximo passo envolve encontrar gadgets adequados para escapar de containers e adquirir privilégios de root.```c static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) { // ...

root@kitploit:~
if (vma->vm_ops && vma->vm_ops->name) {
	name = vma->vm_ops->name(vma);
	if (name)
		goto done;
}

// ...

}

root@kitploit:~
![Passo 3: De UAF a privilégios de root](https://assets.kitploit.com/production/public/readmes/28620/d9c03475803bb799f2b594935d118d73f2547ea4dcf5b40c54e1454324575a0f.png)

As construções de gadgets são as seguintes:
 1. Stack pivot: `movq %rbx, %rsi; movq %rbp, %rdi; call
    __x86_indirect_thunk_r13` -> `pushq %rsi; jmp 46(%rsi)` -> `popq %rsp; ret`
    -> `popq %rsp; ret`, em que %rdi, %rbx e %r13 _inicialmente_ apontam para
    dados controláveis pelo usuário.
 2. Obter privilégios de root: `popq %rdi; ret` -> `prepare_kernel_cred` -> `popq
    %rdi; ret` -> `movq %rax, (%rdi); ret`, em que %rdi _agora_ aponta para o
    topo da pilha; `popq %rdi; ret` -> `commit_creds`, executando efetivamente
    `commit_creds(prepare_kernel_cred(&init_task))`.
 3. Escapar de containers: `popq %rdi; ret` -> `find_task_by_vpid` -> `popq %rdi;
    ret` -> `movq %rax, (%rdi); ret`, em que %rdi _agora_ aponta para o topo da pilha;
    `popq %rdi; ret` -> `popq %rsi; ret` -> `switch_task_namespaces`,
    executando efetivamente `switch_task_namespaces(find_task_by_vpid(1),
    &init_nsproxy)`.
 4. Desbloquear mm: `popq %rax; ret` -> `movq %rbp, %rdi; call
    __x86_indirect_thunk_rax`, em que %rbp aponta para o seq_file original;
    `popq %rax; ret` -> `m_stop`, executando efetivamente `m_stop(seq_file, ..)`.
 5. Retornar ao espaço do usuário: use `swapgs_restore_regs_and_return_to_usermode`, e
    chame `execve()` para obter o shell.

Por fim, usando `nsenter --mount=/proc/1/ns/mnt` para restaurar o namespace de montagem
e obter a flag via `cat /flag/flag`.

### Código-fonte

O código-fonte completo do exploit está disponível [aqui](https://github.com/lrh2000/stackrot/blob/master/exp). Para mais detalhes, consulte
o arquivo README.
Baixar ferramenta