Metasploit Module: PHP 4 unserialize() ZVAL Reference Counter Overflow (Cookie)
Type: exploit
Path: modules/exploits/multi/php/php_unserialize_zval_cookie.rb
Platform: linux
Disclosure date: 2007-03-04
References: CVE-2007-1286, OSVDB-32771, URL-http://web.archive.org/web/[PII_REDACTED]0429/http://php-security.org/MOPB/MOPB-04-2007.html
Targets: Linux x86 Generic, Linux x86 phpBB2, Linux x86 punBB, Linux x86 WWWThreads, Linux x86 Deadman Redirect, Linux x86 PhpWebGallery, Linux x86 Ariadne-CMS, Linux x86 ProMA, Linux x86 eGroupware

Description:
This module exploits an integer overflow vulnerability in the unserialize()
          function of the PHP web server extension. This vulnerability was patched by
          Stefan in version 4.5.0 and applies all previous versions supporting this function.
          This particular module targets numerous web applications and is based on the proof
          of concept provided by Stefan Esser. This vulnerability requires approximately 900k
          of data to trigger due the multiple Cookie headers requirement. Since we
          are already assuming a fast network connection, we use a 2Mb block of shellcode for
          the brute force, allowing quick exploitation for those with fast networks.

          One of the neat things about this vulnerability is that on x86 systems, the EDI register points
          into the beginning of the hashtable string. This can be used with an egghunter to
          quickly exploit systems where the location of a valid "jmp EDI" or "call EDI" instruction
          is known. The EDI method is faster, but the bandwidth-intensive brute force used by this
          module is more reliable across a wider range of systems.

Check method (fingerprinting):
vprint_status('Checking for a vulnerable PHP version...')

    #
    # Pick the URI and Cookie name
    #
    cookie_name = datastore['COOKIENAME'] || target['DefaultCookie']
    uri_path = normalize_uri(datastore['URI']) || target['DefaultURI']

    if (!cookie_name)
      fail_with(Failure::Unknown, 'The COOKIENAME option must be set')
    end

    if (!uri_path)
      fail_with(Failure::Unknown, 'The URI option must be set')
    end

    res = send_request_cgi({
      'uri'	=> uri_path,
      'method'	=> 'GET'
    }, 5)

    php_bug = false

    if (!res)
      vprint_status('No response from the server')
      return Exploit::CheckCode::Unknown('No response from target, user should try again')
    end

    http_fingerprint({ response: res }) # check method

    if (res.code != 200)
      vprint_status("The server returned #{res.code} #{res.message}")
      return Exploit::CheckCode::Safe('Server returned non-200 response')
    end

    if (
        (res.headers['X-Powered-By'] and res.headers['X-Powered-By'] =~ %r{PHP/(.*)}) or
        (res.headers['Server'] and res.headers['Server'] =~ %r{PHP/(.*)})
      )

      php_raw = ::Regexp.last_match(1)
      php_ver = php_raw.split('.')

      if (php_ver[0].to_i == 4 and php_ver[1] and php_ver[2] and php_ver[1].to_i < 5)
        vprint_status("The server runs a vulnerable version of PHP (#{php_raw})")
        php_bug = true
      else
        vprint_status("The server runs a non-vulnerable version of PHP (#{php_raw})")