Exploit-DB #38649
Description: Google AdWords API PHP client library 6.2.0 - Arbitrary PHP Code Execution
Type: webapps
Platform: php
Date: 2015-11-07
Author: Dawid Golunski

Exploit:
# Date: 06.11.2015
# Title: Google AdWords API PHP client library <= 6.2.0 Arbitrary PHP Code Execution
# Exploit Author: Dawid Golunski
# Vendor Homepage: https://developers.google.com/adwords/api/docs/clientlibraries
# Software Link: https://github.com/googleads/googleads-php-lib
# Version: <=6.2.0


=============================================
- Release date: 06.11.2015
- Discovered by: Dawid Golunski
- Severity: Medium/High
=============================================

 
I. VULNERABILITY
-------------------------

Google AdWords API PHP client library <= 6.2.0    Arbitrary PHP Code Execution
(googleads-php-lib)
 
II. BACKGROUND
-------------------------

- AdWords API

https://developers.google.com/adwords/api/docs/

"The AdWords API is a collection of web services that you can use to build 
applications that manage AdWords accounts and their associated campaign data.
While the AdWords API is based on SOAP 1.1, high-level client libraries are 
provided to help you develop applications more quickly."

AdWords API client libraries are available for different platforms
such as PHP, .NET, Java etc. 
These can be found at:

https://developers.google.com/adwords/api/docs/clientlibraries
 
III. INTRODUCTION
-------------------------

The Google AdWords API client library for PHP contains a WSDL Interpreter
class which is described in a comment within the source code as:

"
* The main class for handling WSDL interpretation.
 *
 * The WSDLInterpreter is utilized for the parsing of a WSDL document for rapid
 * and flexible use within the context of PHP 5 scripts.
"

The class contains a function savePHP() which allows to convert the WSDL 
document received from a remote end into a PHP file. 
The funcion is vulnerable to Path Traversal and Code Execution vulnerabilities.

IV. DESCRIPTION
-------------------------
 
googleads-php-lib contains the following function which is meant to load WSDL 
document (XML data) from a remote Google AdWords server:

---[ build_lib/WSDLInterpreter/WSDLInterpreter.php ]---

  protected function loadWsdl($wsdlUri, $proxy = null) {
    // Set proxy.
    if ($proxy) {
      $opts = array(
          'http' => array(
              'proxy' => $proxy,
              'request_fulluri' => true
          )
      );
      $context = stream_context_get_default($opts);
      libxml_set_streams_context($context);
    }

    $this->dom = new DOMDocument();
    $this->dom->load($wsdlUri,
        LIBXML_DTDLOAD|LIBXML_DTDATTR|LIBXML_NOENT|LIBXML_XINCLUDE);

-------------------------------------------------------

For security reasons Google AdWords API should only be accessed via HTTPS. 
However, the above code does not set appropriate SSL settings on the 
https:// stream context. 
It fails to assign Certificate Authority (CA), turn the verify_peer 
option to ON, specify allowed ciphers etc.
It uses the stream_context_get_default() function to get the default context,
which on all PHP versions below PHP 5.6.x (see references), does not validate 
the CA by default. 
Because of this, application may retrieve data from untrusted sources 
pretending to be adwords.google.com.

Further on, the WSDLInterpreter class contains the following savePHP function:

---[ build_lib/WSDLInterpreter/WSDLInterpreter.php ]---

  /**
   * Saves the PHP source code that has been loaded to a target directory.
   *
   * Services will be saved by their validated name, and classes will be
   * included with each service file so that they can be utilized independently.
   *
   * @param string $outputDirectory the destination directory for the source
   *    code
   * @return array array of source code files that were written out
   * @throws WSDLInterpreterException problem in writing out service sources
   */
  public function savePHP($outputDirectory) {
    if (!count($this->servicePHPSources)) {
      throw new WSDLInterpreterException("No services loaded");
    }
    $namespace = $this->enableNamespaces ? sprintf("namespace %s;\n\n",
        $this->utils->getNamespace()) : '';
    $require = sprintf("require_once \"%s\";\n\n", $this->soapClientClassPath);
    $classSource = join("\n\n", $this->classPHPSources);
    $outputFiles = 
    foreach ($this->servicePHPSources as $serviceName => $serviceCode) {
      $filename = sprintf('%s/%s.php', $outputDirectory, $serviceName);
      $success = file_put_contents($filename, sprintf(
          "<?php\n%s%s%s%s\n\n%s\n\n", $this->getFileHeader(), $namespace,
              $require, $classSource, $serviceCode));
...

-------------------------------------------------------

The function does not perform sufficient sanitisation of the WSDL document
received from a remote end. It allows to inject '../' sequence, which can be
used by attackers to  save the resulting translated PHP file into an arbitrary
directory on the system.
It also fails to validate the Name spaces provided within WSDL XML document,
making it possible to inject arbitrary PHP code via encoding it in hex.

For the attack to be successful, the attacker needs to perform a MitM attack 
to impersonate adwords.google.com server (eg. via DNS poisoning/spoofing/proxy
attacks, ARP spoofing, etc. ) to inject malicious XML code.
 

V. PROOF OF CONCEPT
-------------------------
 
Below is a test application that makes use of of PHP Google AdWords API library.
The application simply connects to the AdWords API endpoint to retrieve the 
Google API WSDL document and translates it into a PHP file.

---[ testAPI.php ]---

<?php
// Test application reading WSDL from Google AdWords 

set_include_path('./build_lib/WSDLInterpreter/');
require_once 'WSDLInterpreter.php';

$wsdlUri = 'https://adwords.google.com/api/adwords/cm/v201502/'
	  .'CampaignService?wsdl';

$wsdlInterpreter = new WSDLInterpreter($wsdlUri, "AdWordsSoapClient",null, 
null, "CampaignService",