Skip to content
KitploitKITPLOIT
HerramientasBlog
Log in
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2012-0056 — escalada de privilegios en Linux | Kitploit
Herramientas/GitHubGitHub/pythonone/cve-2012-0056
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónShellcodeAprendizaje y EducaciónExplotación de Binarios
GitHubpythonone/cve-2012-0056

CVE-2012-0056

escalada de privilegios en Linux

Ver Repositorio
1613hace 10 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Escalada local de privilegios en Linux mediante escritura en /proc/pid/mem de SUID

Mempodipper

Presentamos Mempodipper, un exploit para CVE-2012-0056. /proc/pid/mem es una interfaz para leer y escribir directamente en la memoria de un proceso, desplazándose por las mismas direcciones que el espacio de memoria virtual del proceso. En 2.6.39, las protecciones contra el acceso no autorizado a /proc/pid/mem se consideraron suficientes, por lo que el #ifdef anterior que impedía la escritura en la memoria arbitraria de procesos se eliminó. Cualquiera con los permisos adecuados podía escribir en la memoria de un proceso. Resulta, por supuesto, que la comprobación de permisos estaba mal hecha. Esto significa que todos los kernels de Linux >=2.6.39 son vulnerables, hasta el commit que lo corrige de hace un par de días. Repasemos el antiguo código del kernel paso a paso y veamos cuál es el problema.

Cuando se abre /proc/pid/mem, se llama al siguiente código del kernel:

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;
}

No hay restricciones para abrirlo; cualquiera puede abrir el /proc/pid/mem fd de cualquier proceso (sujeto a las restricciones VFS ordinarias). Simplemente registra el self_exec_id del proceso original con el que se abrió y lo guarda para verificarlo después durante las lecturas y escrituras.

Sin embargo, las escrituras (y las lecturas) tienen restricciones de comprobación de permisos. Echemos un vistazo a la función de escritura:

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)
 */	

Así pues, hay dos comprobaciones relevantes para impedir escrituras no autorizadas: check_mem_permission y self_exec_id. Primero la primera y después la segunda.

El código de check_mem_permission simplemente llama a __check_mem_permission, así que este es su código:

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);
}

Hay dos formas de autorizar la escritura en memoria. O bien task == current, es decir, el proceso al que se escribe es el mismo proceso que escribe, o bien current (el proceso que escribe) tiene permisos esotéricos a nivel de ptrace para manipular a task (el proceso al que se escribe). ¿Quizá crees que puedes engañar al código de ptrace? Es tentador. Pero no lo sé. Mejor averigüemos cómo podemos hacer que un proceso escriba memoria arbitraria en sí mismo, de modo que task == current.

Ahora, naturalmente, queremos escribir en la memoria de los procesos suid, porque así podemos obtener root. Miremos esto:

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

su escupirá en stderr el texto que quieras, prefijado por "Unknown id:". Así que podemos abrir un fd a /proc/self/mem, hacer lseek al lugar correcto de la memoria para escribir (más sobre esto luego), usar dup2 para emparejar stderr con el fd de mem, y después exec a su $shellcode para escribir un lanzador de shell en la memoria del proceso, y entonces tenemos root. ¿De verdad? No tan fácil.

Aquí entra en juego la otra restricción. Después de pasar la prueba task == current, comprueba si el self_exec_id actual coincide con el self_exec_id con el que se abrió el fd. ¿Qué demonios es self_exec_id? Solo se referencia en unos pocos sitios del kernel. El más importante está 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);

self_exec_id se incrementa cada vez que un proceso hace exec. En este caso, funciona para que no puedas abrir el fd en un proceso no suid, hacer dup2 y luego exec a un proceso suid... que es exactamente lo que intentábamos hacer antes. Una forma bastante ingeniosa de frustrar nuestro ataque, ¿eh?

Así se sortea. Hacemos fork de un hijo y, dentro de ese hijo, hacemos exec a un proceso nuevo. El hijo inicial tras el fork tiene un self_exec_id igual al de su padre. Cuando hacemos exec a un proceso nuevo, self_exec_id se incrementa en uno. Mientras tanto, el propio padre está ocupado haciendo exec a nuestro proceso su que escribe el shellcode, de modo que su self_exec_id se incrementa al mismo valor. Así que lo que hacemos es lo siguiente: hacemos fork del hijo y exec a un proceso nuevo y, dentro de ese proceso nuevo, abrimos un fd a /proc/parent-pid/mem usando el pid del proceso padre, no el nuestro (como ocurría antes). Podemos abrir el fd de esta manera porque un simple open no tiene comprobación de permisos. Cuando se abre, su self_exec_id ya se ha incrementado al valor correcto que tendrá el self_exec_id del padre cuando hagamos exec a su. Finalmente, pasamos el fd abierto del proceso hijo de vuelta al proceso padre (usando algo de magia muy oscura de unix domain sockets), hacemos nuestro dup2 y hacemos exec a su con el shellcode.

Descargar herramienta