2012-12-02 02:59:06

By Tim Brown

Background: A colleague asked me about fuzzing PHP. Verbatim from my email to security@php.net back in 2007:

I've been doing some work on fuzzing the PHP 4 and PHP 5 parsers and wanted to share my results with you. I know PHP 4 won't be supported for much longer and I have no idea whether any of these examples are directly exploitable however before I make them public I thought it was only right to give you a chance to comment. Feel free to ignore me if you so choose, but I'll be putting them up on my blog in 14 days unless I hear otherwise. Although I'm reporting them against the CGI binary, I've had similar results running these snippets against the module too. In each case, I can cause the process (either the CGI binary or the module) to crash. The fuzzers I've written are also available if you so wish.

I never did do anything with them but they're all dead now.

PHP 4 (PHP 4.4.4-9+lenny1 (cgi-fcgi) (built: May 24 2007 20:14:24))

  • wordwrap("x00", 0, "%1000n" xor true);
Program received signal SIGFPE, Arithmetic exception.
0x08122ab0 in zif_wordwrap ()
(gdb) bt
#0  0x08122ab0 in zif_wordwrap ()
#1  0x081a8304 in execute ()
#2  0x0818dfc6 in zend_execute_scripts ()
#3  0x0815bd8e in php_execute_script ()
#4  0x081b3094 in main ()
(gdb) i r
eax            0x2      2
ecx            0x0      0
edx            0x0      0
ebx            0x8323014        137506836
esp            0xbf9fb4c0       0xbf9fb4c0
ebp            0xbf9fb558       0xbf9fb558
esi            0x0      0
edi            0x0      0
eip            0x8122ab0        0x8122ab0 <zif_wordwrap+928>
eflags         0x210202 [ IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
  • ob_start($dummyvariable, 0xd34adb33f . false, undef);
Program received signal SIGSEGV, Segmentation fault.
0x0817dc40 in _efree ()
(gdb) bt
#0  0x0817dc40 in _efree ()
#1  0x0817109b in php_end_ob_buffer ()
#2  0x0817169e in php_end_ob_buffers ()
#3  0x0815c592 in php_request_shutdown ()
#4  0x081b255a in main ()
(gdb) i r
eax            0x0      0
ecx            0x0      0
edx            0x0      0
ebx            0x8323014        137506836
esp            0xbfdb5410       0xbfdb5410
ebp            0xbfdb5428       0xbfdb5428
esi            0xfffffff4       -12
edi            0x8332ec0        137572032
eip            0x817dc40        0x817dc40 <_efree+32>
eflags         0x210293 [ CF AF SF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
  • dcngettext(undef == undef, undef, 1, true xor "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", $dummyvariable);
Program received signal SIGSEGV, Segmentation fault.
0x080a377d in zif_dcngettext ()
(gdb) bt
#0  0x080a377d in zif_dcngettext ()
#1  0x081a8304 in execute ()
#2  0x0818dfc6 in zend_execute_scripts ()
#3  0x0815bd8e in php_execute_script ()
#4  0x081b3094 in main ()
(gdb) i r
eax            0x1      1
ecx            0x31     49
edx            0xa      10
ebx            0x8323014        137506836
esp            0xbfef5190       0xbfef5190
ebp            0xbfef51d8       0xbfef51d8
esi            0xbfef52c4       -1074834748
edi            0x839864c        137987660
eip            0x80a377d        0x80a377d <zif_dcngettext+621>
eflags         0x210286 [ PF SF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

PHP 5 (PHP 5.2.4-1+b1 with Suhosin-Patch 0.9.6.2 (cgi-fcgi) (built: Oct 1 2007 10:29:46))

  • jdmonthname(0xd34adb33f, -1);
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb78dd8c0 (LWP 16731)]
0xb7aa4503 in strlen () from /lib/i686/cmov/libc.so.6
(gdb) bt
#0  0xb7aa4503 in strlen () from /lib/i686/cmov/libc.so.6
#1  0x080aecdf in zif_jdmonthname ()
#2  0x082e8f5b in ?? ()
#3  0x00000002 in ?? ()
#4  0x086923a0 in ?? ()
#5  0x00000000 in ?? ()
(gdb) i r
eax            0x0      0
ecx            0x0      0
edx            0xfffffffe       -2
ebx            0x855a20c        139829772
esp            0xbfb0175c       0xbfb0175c
ebp            0xbfb017a8       0xbfb017a8
esi            0x0      0
edi            0x86923a0        141108128
eip            0xb7aa4503       0xb7aa4503 <strlen+51>
eflags         0x210246 [ PF ZF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
  • cal_from_jd(0xd34adb33f, true / false);
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb78a78c0 (LWP 23742)]
0xb7a6e503 in strlen () from /lib/i686/cmov/libc.so.6
(gdb) bt
#0  0xb7a6e503 in strlen () from /lib/i686/cmov/libc.so.6
#1  0x082c25bb in add_assoc_string_ex ()
#2  0x080afc0c in zif_cal_from_jd ()
#3  0x082e8f5b in ?? ()
#4  0x00000002 in ?? ()
#5  0x08692408 in ?? ()
#6  0x00000000 in ?? ()
(gdb) i r
eax            0x0      0
ecx            0x0      0
edx            0x8581b44        139991876
ebx            0x855a20c        139829772
esp            0xbf9b9dac       0xbf9b9dac
ebp            0xbf9b9df8       0xbf9b9df8
esi            0x8692890        141109392
edi            0x0      0
eip            0xb7a6e503       0xb7a6e503 <strlen+51>
eflags         0x210246 [ PF ZF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

The fuzzer code can be found below, although it doesn't seem to trigger any bugs in recent releases of PHP:

# The following can be used to fingerprint what functions are available
# for ($arraycounter = 0; $arraycounter < count($function); $arraycounter ++) {
# 	if (function_exists($function[$arraycounter])) {
# 		echi $function[$arraycounter] . " OK\n";
# 	}
# }
# What shall we fuzz today?
$function = array(<list of PHPfunctions to fuzz>);
# Mmm, input :)
$data = 
array("\"PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP\"", 
"-1", "0xd34adb33f", "0", "1", "true", "false", "undef", "\"%1000n\"", "$dummyvariable", "\"\x00\"", "0xd34db33f");
# Operations to perform
$uoperator = array("++", "--");
$doperator = array("+", "-", "+=", "-=", "/", "*",  "&&", "||", ".", "!=", "&", "^", "<=", ">=", "==", "xor", "and", "or");
set_time_limit(0);
# Seed the fuzzer and it will bear fruit
$seed = time();
srand($seed);
echo "seed was: " . $seed . "\n";
while (1) {
	# getting deep and meaningful
	$maxdepth = 5;
	$expression = makeExpression();
	echo $expression . "\n";
	eval($expression);
}

function makeExpression() {
	$expression = makeFunction() . ";";
	return $expression;
}

function makeFunction() {
	global $function;
	do {
		$rndfunction = rand(0, count($function) - 1);
		if (function_exists($function[$rndfunction])) {
			$expression = $function[$rndfunction];
		} else {
			$function = array_remove($function, $rndfunction);
		}
	} while ($expression == "");
	$expression = $expression . "(";
	$maxparameter = rand(1, 10);
	for ($parameter = 1; $parameter < $maxparameter; $parameter ++) {
		$expression = $expression . makeParameter() . ", ";
	}
	$expression = $expression . makeParameter() . ")";
	return $expression;
}

function makeParameter() {
	global $data, $uoperator, $doperator, $maxdepth;
	if ($maxdepth > 0) {
		$operator = rand(1, 4);
	} else {
		$operator = rand(1, 3);
	}
	switch ($operator) {
		case 1:
			$rnddata = rand(0, count($data) - 1);
			$parameter = $data[$rnddata];
			break;
		case 2:
			$rnddata = rand(0, count($data) - 1);
			$parameter = $data[$rnddata];
			$rnduoperator = rand(0, count($uoperator) - 1);
			$parameter = $parameter . " " . $uoperator[$rnduoperator];
			break;
		case 3:
			$rnddata = rand(0, count($data) - 1);
			$parameter = $data[$rnddata];
			$rnddoperator = rand(0, count($doperator) - 1);
			$parameter = $parameter . " " . $doperator[$rnddoperator];
			$rnddata = rand(0, count($data) - 1);
			$parameter = $parameter . " " . $data[$rnddata];
			break;
		case 4:
			$maxdepth --;
			$parameter = makeFunction();
			break;
	}
	return $parameter;
}

function array_remove($array, $arrayindex) {
	$newarray = array();
	$arraycounter = 0;
	for ($arraycounter = 0; $arraycounter < count($array); $arraycounter ++) 
	{
		if ($arraycounter != $arrayindex) {
			$newarray[] = $array[$arraycounter];
		}
	}
	return $newarray;
}

Mood: Relaxed

Music: Nothing playing right now

You are unknown, comment