CVE-2010-4335: Improper Input Validation (CWE-20)
value: The _validatePost function in libs/controller/components/security.php in CakePHP 1.3.x through 1.3.5 and 1.2.8 allows remote attackers to modify the internal Cake cache and execute arbitrary code via a crafted data[_Token][fields] value that is processed by the unserialize function, as demonstrated by modifying the file_map cache to execute arbitrary local files.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Published: 2011-01-14T23:00Z
Repository: https://github.com/cakephp/cakephp
Language: PHP
Files changed: cake/libs/controller/components/security.php, cake/tests/cases/libs/controller/components/security.test.php
Commit date: 2010-11-07 20:53:04 -0500
Commit message: Fixing issue found by Felix Wilhelm(flxm) where users could send potentially dangerous or corrupted serialized objects to SecurityComponent, potentially allowing manipulation of file map caches.  Test case added.

Diff: diff --git a/cake/libs/controller/components/security.php b/cake/libs/controller/components/security.php
index c267c6ce1..7e5b3b28f 100644
--- a/cake/libs/controller/components/security.php
+++ b/cake/libs/controller/components/security.php
@@ -616,14 +616,19 @@ class SecurityComponent extends Object {
 		if (strpos($token, ':')) {
 			list($token, $locked) = explode(':', $token, 2);
 		}
 		unset($check['_Token']);
 
+		$locked = str_rot13($locked);
+		if (preg_match('/(\A|;|{|})O\:[0-9]+/', $locked)) {
+			return false;
+		}
+
 		$lockedFields = array();
 		$fields = Set::flatten($check);
 		$fieldList = array_keys($fields);
-		$locked = unserialize(str_rot13($locked));
+		$locked = unserialize($locked);
 		$multi = array();
 
 		foreach ($fieldList as $i => $key) {
 			if (preg_match('/\.\d+$/', $key)) {
 				$multi[$i] = preg_replace('/\.\d+$/', '', $key);
diff --git a/cake/tests/cases/libs/controller/components/security.test.php b/cake/tests/cases/libs/controller/components/security.test.php
index 883f1c810..bbd514100 100644
--- a/cake/tests/cases/libs/controller/components/security.test.php
+++ b/cake/tests/cases/libs/controller/components/security.test.php
@@ -606,10 +606,34 @@ DIGEST;
 			'_Token' => compact('fields')
 		);
 		$result = $this->Controller->Security->validatePost($this->Controller);
 		$this->assertFalse($result, 'validatePost passed when key was missing. %s');
 	}
+
+/**
+ * Test that objects can't be passed into the serialized string. This was a vector for RFI and LFI 
+ * attacks. Thanks to Felix Wilhelm
+ *
+ * @return void
+ */
+	function testValidatePostObjectDeserialize() {
+		$this->Controller->Security->startup($this->Controller);
+		$key = $this->Controller->params['_Token']['key'];
+		$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877';
+
+		// a corrupted serialized object, so we can see if it ever gets to deserialize
+		$attack = 'O:3:"App":1:{s:5:"__map";a:1:{s:3:"foo";s:7:"Hacked!";s:1:"fail"}}';
+		$fields .= urlencode(':' . str_rot13($attack));
+
+		$this->Controller->data = array(
+			'Model' => array('username' => 'mark', 'password' => 'foo', 'valid' => '0'),
+			'_Token' => compact('key', 'fields')
+		);
+		$result = $this->Controller->Security->validatePost($this->Controller);
+		$this->assertFalse($result, 'validatePost passed when key was missing. %s');
+	}
+
 /**
  * Tests validation of checkbox arrays
  *
  * @access public
  * @return void

Vulnerable code: 		$locked = unserialize(str_rot13($locked));
Fixed code: 		$locked = str_rot13($locked);
		if (preg_match('/(\A|;|{|})O\:[0-9]+/', $locked)) {
			return false;
		}

		$locked = unserialize($locked);

/**
 * Test that objects can't be passed into the serialized string. This was a vector for RFI and LFI 
 * attacks. Thanks to Felix Wilhelm
 *
 * @return void
 */
	function testValidatePostObjectDeserialize() {
		$this->Controller->Security->startup($this->Controller);
		$key = $this->Controller->params['_Token']['key'];
		$fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877';

		// a corrupted serialized object, so we can see if it ever gets to deserialize
		$attack = 'O:3:"App":1:{s:5:"__map";a:1:{s:3:"foo";s:7:"Hacked!";s:1:"fail"}}';
		$fields .= urlencode(':' . str_rot13($attack));

		$this->Controller->data = array(
			'Model' => array('username' => 'mark', 'password' => 'foo', 'valid' => '0'),
			'_Token' => compact('key', 'fields')
		);
		$result = $this->Controller->Security->validatePost($this->Controller);
		$this->assertFalse($result, 'validatePost passed when key was missing. %s');
	}