Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
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
CVE-2012-0056 — escalada de privilégios linux | Kitploit
Ferramentas/GitHubGitHub/pythonone/cve-2012-0056
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoShellcodeAprendizado e EducaçãoExploração de Binários
GitHubpythonone/cve-2012-0056

CVE-2012-0056

escalada de privilégios linux

Ver Repositório
1610há 10 anosAinda não revisado

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

Linux Local Privilege Escalation via SUID /proc/pid/mem Write

Mempodipper

Apresentando o Mempodipper, um exploit para o CVE-2012-0056. /proc/pid/mem é uma interface para ler e escrever diretamente a memória de processos, fazendo seek com os mesmos endereços do espaço de memória virtual do processo. Na versão 2.6.39, as proteções contra acesso não autorizado a /proc/pid/mem foram consideradas suficientes, e então o #ifdef anterior que impedia o suporte à escrita na memória arbitrária de processos foi removido. Qualquer pessoa com as permissões corretas podia escrever na memória de processos. Acontece que, claro, a verificação de permissões foi mal feita. Isso significa que todos os kernels Linux >=2.6.39 são vulneráveis, até o commit de correção, há alguns dias. Vamos examinar o código antigo do kernel passo a passo e entender o que há de errado com ele.

Quando /proc/pid/mem é aberto, este código do kernel é chamado:

static int mem_open(struct inode* inode, struct file* file)
{
	file->private_data = (void*)((long)current->self_exec_id);
	/* OK to pass negative loff_t, we can catch out-of-range */
	file->f_mode |= FMODE_UNSIGNED_OFFSET;
	return 0;
}

Não há restrições para a abertura; qualquer pessoa pode abrir o fd de /proc/pid/mem para qualquer processo (sujeito às restrições normais do VFS). Ele simplesmente registra o self_exec_id do processo original com o qual foi aberto e o armazena para verificação posterior durante leituras e escritas.

Escritas (e leituras), no entanto, têm restrições de verificação de permissões. Vamos dar uma olhada na função de escrita:

static ssize_t mem_write(struct file * file, const char __user *buf,
			 size_t count, loff_t *ppos)
{

/* unimportant code removed for blog post */	

	struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
	
/* unimportant code removed for blog post */

	mm = check_mem_permission(task);
	copied = PTR_ERR(mm);
	if (IS_ERR(mm))
		goto out_free;

/* unimportant code removed for blog post */	

	if (file->private_data != (void *)((long)current->self_exec_id))
		goto out_mm;

/* unimportant code removed for blog post
 * (the function here goes onto write the buffer into the memory)
 */	

Portanto, há duas verificações relevantes para impedir escritas não autorizadas: check_mem_permission e self_exec_id. Vamos tratar da primeira primeiro e da segunda depois.

O código de check_mem_permission simplesmente chama __check_mem_permission, então aqui está o código dele:

static struct mm_struct *__check_mem_permission(struct task_struct *task)
{
	struct mm_struct *mm;

	mm = get_task_mm(task);
	if (!mm)
		return ERR_PTR(-EINVAL);

	/*
	 * A task can always look at itself, in case it chooses
	 * to use system calls instead of load instructions.
	 */
	if (task == current)
		return mm;

	/*
	 * If current is actively ptrace'ing, and would also be
	 * permitted to freshly attach with ptrace now, permit it.
	 */
	if (task_is_stopped_or_traced(task)) {
		int match;
		rcu_read_lock();
		match = (ptrace_parent(task) == current);
		rcu_read_unlock();
		if (match && ptrace_may_access(task, PTRACE_MODE_ATTACH))
			return mm;
	}

	/*
	 * No one else is allowed.
	 */
	mmput(mm);
	return ERR_PTR(-EPERM);
}

Há duas maneiras de a escrita na memória ser autorizada. Ou task == current, significando que o processo sendo escrito é o processo que escreve, ou current (o processo que escreve) tem permissões esotéricas de nível ptrace para manipular task (o processo sendo escrito). Talvez você ache que pode enganar o código de ptrace? É tentador. Mas eu não sei. Vamos, em vez disso, descobrir como podemos fazer um processo escrever memória arbitrária para si mesmo, de modo que task == current.

Agora, naturalmente, queremos escrever na memória de processos suid, já que assim podemos obter root. Veja isto:

$ su "yeeeee haw I am a cowboy"
Unknown id: yeeeee haw I am a cowboy

O su vai despejar na stderr qualquer texto que você quiser, prefixado por "Unknown id:". Então, podemos abrir um fd para /proc/self/mem, fazer lseek para o lugar certo na memória para a escrita (mais sobre isso depois), usar dup2 para acoplar a stderr e o fd de memória, e então exec para su $shellcode a fim de escrever um spawner de shell na memória do processo, e aí temos root. Sério? Não é tão fácil.

Aqui a outra restrição entra em ação. Depois de passar no teste task == current, ele verifica se o self_exec_id atual corresponde ao self_exec_id com o qual o fd foi aberto. O que diabos é self_exec_id? Ele é referenciado em apenas alguns lugares no kernel. O mais importante acontece dentro de exec:

void setup_new_exec(struct linux_binprm * bprm)
{
/* massive amounts of code trimmed for the purpose of this blog post */

	/* An exec changes our domain. We are no longer part of the thread
	   group */

	current->self_exec_id++;
			
	flush_signal_handlers(current, 0);
	flush_old_files(current->files);
}
EXPORT_SYMBOL(setup_new_exec);

O self_exec_id é incrementado toda vez que um processo executa exec. Então, nesse caso, ele funciona para que você não possa abrir o fd em um processo não-suid, fazer dup2 e então exec para um processo suid... que é exatamente o que tentávamos fazer acima. Uma maneira bem inteligente de deter nosso ataque, hein?

Aqui está como contornar isso. Damos fork em um filho e, dentro desse filho, executamos exec para um novo processo. O fork filho inicial tem um self_exec_id igual ao de seu pai. Quando executamos exec para um novo processo, o self_exec_id é incrementado em um. Enquanto isso, o próprio pai está ocupado executando exec para o nosso processo su que escreve o shellcode, então o self_exec_id dele é incrementado para o mesmo valor. Então o que fazemos é: fazemos esse filho dar fork e exec para um novo processo e, dentro desse novo processo, abrimos um fd para /proc/parent-pid/mem usando o pid do processo pai, não nosso próprio processo (como era o caso anteriormente). Podemos abrir o fd assim porque não há verificação de permissões para uma mera abertura. Quando ele é aberto, seu self_exec_id já foi incrementado para o valor correto que o self_exec_id do pai terá quando executarmos exec para su. Então, finalmente, passamos nosso fd aberto do processo filho de volta para o processo pai (usando um pouco de magia negra de unix domain sockets), fazemos nosso dup2 e executamos exec para o su com o shellcode.

Baixar ferramenta