rapid7/metasploit-framework

View on GitHub
lib/msf/core/auxiliary/prometheus.rb

Summary

Maintainability
F
4 days
Test Coverage
# -*- coding: binary -*-

module Msf
  ###
  #
  # This module provides methods for working with Prometheus node exporter
  #
  ###
  module Auxiliary::Prometheus
    include Msf::Auxiliary::Report

    # returns username, password
    def process_authorization(auth)
      if auth['credentials']
        # credential foobar
        return '', auth['credentials']
      elsif auth['credentials_file']
        # type: Bearer
        # credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        return auth['type'], auth['credentials_file']
      end
    end

    # processes a generic URI for creds
    def process_embedded_uri(uri, job_name, config_name)
      uri = URI(uri)
      cred = credential_data
      cred[:port] = uri.port
      cred[:address] = uri.host
      cred[:username] = uri.user
      cred[:private_data] = uri.password
      cred[:service_name] = uri.scheme
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        config_name,
        uri.host,
        uri.port,
        uri.user,
        uri.password,
        ''
      ]
    end

    def credential_data
      {
        # these 4 need to be changed every time
        # address: thost,
        # port: tport,
        # username: username
        # private_data: hash
        protocol: 'tcp',
        workspace_id: myworkspace_id,
        origin_type: :service,
        private_type: :password,
        service_name: '',
        module_fullname: fullname,
        status: Metasploit::Model::Login::Status::UNTRIED
      }
    end

    def process_dns_sd_configs(job_name, dns_sd_configs)
      dns_sd_configs['names']&.each do |name|
        username = dns_sd_configs.dig('basic_auth', 'username')
        password = dns_sd_configs.dig('basic_auth', 'password')
        password = dns_sd_configs.dig('basic_auth', 'password_file') if dns_sd_configs.dig('basic_auth', 'password_file')
        uri = URI("#{dns_sd_configs['scheme']}://#{name}")
        cred = credential_data
        cred[:port] = uri.port
        cred[:address] = uri.host
        cred[:username] = dns_sd_configs.dig('basic_auth', 'username')
        cred[:private_data] = password
        cred[:service_name] = dns_sd_configs['scheme']
        create_credential_and_login(cred)
        @table_creds << [
          job_name,
          'dns_sd_configs',
          uri.host,
          uri.port,
          username,
          password,
          ''
        ]
      end
    end

    def process_consul_sd_configs(job_name, consul_sd_configs)
      uri = URI("#{consul_sd_configs['scheme']}://#{consul_sd_configs['server']}")
      cred = credential_data
      cred[:port] = uri.port
      cred[:address] = uri.host
      cred[:username] = ''
      cred[:private_data] = consul_sd_configs['token']
      cred[:service_name] = consul_sd_configs['scheme']
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'consul_sd_configs',
        uri.host,
        uri.port,
        '',
        consul_sd_configs['token'],
        "Path Prefix: #{consul_sd_configs['path_prefix']}"
      ]
    end

    def process_kubernetes_sd_configs(job_name, kubernetes_sd_configs)
      username = kubernetes_sd_configs.dig('basic_auth', 'username')
      password = kubernetes_sd_configs.dig('basic_auth', 'password')
      password = kubernetes_sd_configs.dig('basic_auth', 'password_file') if kubernetes_sd_configs.dig('basic_auth', 'password_file')

      uri = URI(kubernetes_sd_configs['api_server'])
      cred = credential_data
      cred[:port] = uri.port
      cred[:address] = uri.host
      cred[:username] = username
      cred[:private_data] = password
      cred[:service_name] = uri.scheme
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'kubernetes_sd_configs',
        uri.host,
        uri.port,
        username,
        password,
        "Role: #{kubernetes_sd_configs['role']}"
      ]
    end

    def process_kuma_sd_configs(job_name, targets)
      return if targets['server'].nil?
      return unless targets['server'].include? '@'

      process_embedded_uri(targets['server'], job_name, 'kuma_sd_configs')
    end

    def process_marathon_sd_configs(job_name, marathon_sd_configs)
      marathon_sd_configs['servers']&.each do |servers|
        uri = URI(servers)
        cred = credential_data
        cred[:port] = uri.port
        cred[:address] = uri.host
        cred[:username] = ''
        cred[:private_data] = marathon_sd_configs['auth_token']
        cred[:service_name] = uri.scheme
        create_credential_and_login(cred)
        @table_creds << [
          job_name,
          'marathon_sd_configs',
          uri.host,
          uri.port,
          '',
          marathon_sd_configs['auth_token'],
          ''
        ]
      end
    end

    def process_nomad_sd_configs(job_name, targets)
      return if targets['server'].nil?
      return unless targets['server'].include? '@'

      process_embedded_uri(targets['server'], job_name, 'nomad_sd_configs')
    end

    def process_ec2_sd_configs(job_name, ec2_sd_configs)
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = ec2_sd_configs['access_key']
      cred[:private_data] = ec2_sd_configs['secret_key']
      cred[:service_name] = ''
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'ec2_sd_configs',
        '',
        '',
        ec2_sd_configs['access_key'],
        ec2_sd_configs['secret_key'],
        "Region: #{ec2_sd_configs['region']}, Profile: #{ec2_sd_configs['profile']}"
      ]
    end

    def process_lightsail_sd_configs(job_name, lightsail_sd_configs)
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = lightsail_sd_configs['access_key']
      cred[:private_data] = lightsail_sd_configs['secret_key']
      cred[:service_name] = ''
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'lightsail_sd_configs',
        '',
        '',
        lightsail_sd_configs['access_key'],
        lightsail_sd_configs['secret_key'],
        "Region: #{lightsail_sd_configs['region']}, Profile: #{lightsail_sd_configs['profile']}"
      ]
    end

    def process_azure_sd_configs(job_name, azure_sd_configs)
      cred = credential_data
      cred[:port] = azure_sd_configs['port']
      cred[:address] = ''
      cred[:username] = azure_sd_configs['client_id']
      cred[:private_data] = azure_sd_configs['client_secret']
      cred[:service_name] = azure_sd_configs['authentication_method']
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'azure_sd_configs',
        '',
        azure_sd_configs['port'],
        azure_sd_configs['client_id'],
        azure_sd_configs['client_secret'],
        "Environment: #{azure_sd_configs['environment']}, Subscription ID: #{azure_sd_configs['subscription_id']}, Resource Group: #{azure_sd_configs['resource_group']}, Tenant ID: #{azure_sd_configs['tenant_id']}"
      ]
    end

    def process_http_sd_configs(job_name, http_sd_configs)
      return if http_sd_configs['url'].nil?
      return unless http_sd_configs['url'].include? '@'

      process_embedded_uri(http_sd_configs['url'], job_name, 'http_sd_configs')
    end

    def process_digitalocean_sd_configs(job_name, digitalocean_sd_configs)
      username, password = process_authorization(digitalocean_sd_configs['authorization'])
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = username
      cred[:private_data] = password
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'digitalocean_sd_configs',
        '',
        '',
        username,
        password,
        ''
      ]
    end

    def process_hetzner_sd_configs(job_name, hetzner_sd_configs)
      username = hetzner_sd_configs.dig('basic_auth', 'username')
      password = hetzner_sd_configs.dig('basic_auth', 'password')

      username, password = process_authorization(hetzner_sd_configs['authorization']) if hetzner_sd_configs.dig('authorization', 'credentials')

      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = username
      cred[:private_data] = password
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'hetzner_sd_configs',
        '',
        '',
        username,
        password,
        hetzner_sd_configs['role']
      ]
    end

    def process_eureka_sd_configs(job_name, eureka_sd_configs)
      return if eureka_sd_configs['server'].nil?
      return unless eureka_sd_configs['server'].include? '@'

      process_embedded_uri(eureka_sd_configs['server'], job_name, 'eureka_sd_configs')
    end

    def process_ovhcloud_sd_configs(job_name, ovhcloud_sd_configs)
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ovhcloud_sd_configs['endpoint']
      cred[:username] = ovhcloud_sd_configs['application_key']
      cred[:private_data] = ovhcloud_sd_configs['application_secret']
      cred[:service_name] = ovhcloud_sd_configs['service']
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'ovhcloud_sd_configs',
        ovhcloud_sd_configs['endpoint'],
        '',
        ovhcloud_sd_configs['application_key'],
        ovhcloud_sd_configs['application_secret'],
        "Consumer Key: #{ovhcloud_sd_configs['consumer_key']}, Service: #{ovhcloud_sd_configs['service']}"
      ]
    end

    def process_scaleway_sd_configs(job_name, scaleway_sd_configs)
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = scaleway_sd_configs['access_key']
      cred[:private_data] = scaleway_sd_configs['secret_key']
      cred[:service_name] = scaleway_sd_configs['role']
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'scaleway_sd_configs',
        '',
        '',
        scaleway_sd_configs['access_key'],
        scaleway_sd_configs['secret_key'],
        "Project ID: #{scaleway_sd_configs['project_id']}, Role: #{scaleway_sd_configs['role']}"
      ]
    end

    def process_linode_sd_configs(job_name, linode_sd_configs)
      username, password = process_authorization(linode_sd_configs['authorization'])
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = username
      cred[:private_data] = password
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'linode_sd_configs',
        '',
        '',
        username,
        password,
        ''
      ]
    end

    def process_uyuni_sd_configs(job_name, uyuni_sd_configs)
      uri = URI(uyuni_sd_configs['server'])
      cred = credential_data
      cred[:port] = uri.port
      cred[:address] = uri.host
      cred[:username] = uyuni_sd_configs['username']
      cred[:private_data] = uyuni_sd_configs['password']
      cred[:service_name] = uri.scheme
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'uyuni_sd_configs',
        uri.host,
        uri.port,
        uyuni_sd_configs['username'],
        uyuni_sd_configs['password'],
        ''
      ]
    end

    def process_ionos_sd_configs(job_name, ionos_sd_configs)
      _username, password = process_authorization(ionos_sd_configs['authorization'])
      # we may hit an issue here where we have a type stored in username, but use datacenter_id
      # as the username
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = ionos_sd_configs['datacenter_id']
      cred[:private_data] = password
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'ionos_sd_configs',
        '',
        '',
        ionos_sd_configs['datacenter_id'],
        password,
        ''
      ]
    end

    def process_vultr_sd_configs(job_name, vultr_sd_configs)
      username, password = process_authorization(vultr_sd_configs['authorization'])
      cred = credential_data
      cred[:port] = ''
      cred[:address] = ''
      cred[:username] = username
      cred[:private_data] = password
      create_credential_and_login(cred)
      @table_creds << [
        job_name,
        'vultr_sd_configs',
        '',
        '',
        username,
        password,
        ''
      ]
    end

    def prometheus_config_eater(yamlconf)
      @table_creds = Rex::Text::Table.new(
        'Header' => 'Credentials',
        'Indent' => 2,
        'Columns' =>
        [
          'Name',
          'Config',
          'Host',
          'Port',
          'Public/Username',
          'Private/Password/Token',
          'Notes'
        ]
      )

      yamlconf['scrape_configs']&.each do |scrape|
        # check for targets which have creds built in to the URL
        if scrape['static_configs']
          scrape['static_configs']&.each do |static|
            static['targets']&.each do |target|
              if target.include? '@'
                uri = URI(target)
                cred = credential_data
                cred[:port] = uri.port
                cred[:address] = uri.host
                cred[:username] = uri.user
                cred[:private_data] = uri.password
                cred[:service_name] = uri.scheme
                create_credential_and_login(cred)
                @table_creds << [
                  scrape['job_name'],
                  'static_configs Target',
                  uri.host,
                  uri.port,
                  uri.user,
                  uri.password,
                  ''
                ]
              end
            end
          end
        elsif scrape['dns_sd_configs']
          scrape['dns_sd_configs']&.each do |dns_sd_configs|
            # pass in basic_auth from the level above
            if dns_sd_configs['basic_auth'].nil? && scrape['basic_auth']
              dns_sd_configs['basic_auth'] = {}
              dns_sd_configs['basic_auth']['username'] = scrape.dig('basic_auth', 'username') if scrape.dig('basic_auth', 'username')
              dns_sd_configs['basic_auth']['password'] = scrape.dig('basic_auth', 'password') if scrape.dig('basic_auth', 'password')
              dns_sd_configs['basic_auth']['password_file'] = scrape.dig('basic_auth', 'password_file') if scrape.dig('basic_auth', 'password_file')
            end

            # pass in the 'scheme' from a level above to properly build the URI
            if dns_sd_configs['scheme'].nil? && scrape['scheme']
              dns_sd_configs['scheme'] = scrape['scheme']
            end

            process_dns_sd_configs(scrape['job_name'], dns_sd_configs)
          end
        elsif scrape['consul_sd_configs']
          scrape['consul_sd_configs']&.each do |consul_sd_configs|
            process_consul_sd_configs(scrape['job_name'], consul_sd_configs)
          end
        elsif scrape['authorization']
          username, password = process_authorization(scrape['authorization'])
          cred = credential_data
          cred[:port] = ''
          cred[:address] = ''
          cred[:username] = username
          cred[:private_data] = password
          create_credential_and_login(cred)
          @table_creds << [
            scrape['job_name'],
            'authorization',
            '',
            '',
            username,
            password,
            ''
          ]
        elsif scrape['kubernetes_sd_configs']
          scrape['kubernetes_sd_configs']&.each do |kubernetes_sd_configs|
            next unless kubernetes_sd_configs['api_server']

            # if scrape has basic auth, but the individual config doesn't
            # add it to the individual config
            if kubernetes_sd_configs['basic_auth'].nil? && scrape['basic_auth']
              kubernetes_sd_configs['basic_auth'] = {}
              kubernetes_sd_configs['basic_auth']['username'] = scrape.dig('basic_auth', 'username') if scrape.dig('basic_auth', 'username')
              kubernetes_sd_configs['basic_auth']['password'] = scrape.dig('basic_auth', 'password') if scrape.dig('basic_auth', 'password')
              kubernetes_sd_configs['basic_auth']['password'] = scrape.dig('basic_auth', 'password_file') if scrape.dig('basic_auth', 'password_file')
            end

            process_kubernetes_sd_configs(scrape['job_name'], kubernetes_sd_configs)
          end
        elsif scrape['kuma_sd_configs']
          scrape['kuma_sd_configs']&.each do |targets|
            process_kuma_sd_configs(scrape['job_name'], targets)
          end
        elsif scrape['marathon_sd_configs']
          scrape['marathon_sd_configs']&.each do |marathon_sd_configs|
            process_marathon_sd_configs(scrape['job_name'], marathon_sd_configs)
          end
        elsif scrape['nomad_sd_configs']
          scrape['nomad_sd_configs']&.each do |targets|
            process_nomad_sd_configs(scrape['job_name'], targets)
          end
        elsif scrape['ec2_sd_configs']
          scrape['ec2_sd_configs']&.each do |ec2_sd_configs|
            process_ec2_sd_configs(scrape['job_name'], ec2_sd_configs)
          end
        elsif scrape['lightsail_sd_configs']
          scrape['lightsail_sd_configs']&.each do |lightsail_sd_configs|
            process_lightsail_sd_configs(scrape['job_name'], lightsail_sd_configs)
          end
        elsif scrape['azure_sd_configs']
          scrape['azure_sd_configs']&.each do |azure_sd_configs|
            process_azure_sd_configs(scrape['job_name'], azure_sd_configs)
          end
        elsif scrape['http_sd_configs']
          scrape['http_sd_configs']&.each do |http_sd_configs|
            process_http_sd_configs(scrape['job_name'], http_sd_configs)
          end
        elsif scrape['digitalocean_sd_configs']
          scrape['digitalocean_sd_configs']&.each do |digitalocean_sd_configs|
            process_digitalocean_sd_configs(scrape['job_name'], digitalocean_sd_configs)
          end
        elsif scrape['hetzner_sd_configs']
          scrape['hetzner_sd_configs']&.each do |hetzner_sd_configs|
            process_hetzner_sd_configs(scrape['job_name'], hetzner_sd_configs)
          end
        elsif scrape['eureka_sd_configs']
          scrape['eureka_sd_configs']&.each do |eureka_sd_configs|
            process_eureka_sd_configs(scrape['job_name'], eureka_sd_configs)
          end
        elsif scrape['ovhcloud_sd_configs']
          scrape['ovhcloud_sd_configs']&.each do |ovhcloud_sd_configs|
            process_ovhcloud_sd_configs(scrape['job_name'], ovhcloud_sd_configs)
          end
        elsif scrape['scaleway_sd_configs']
          scrape['scaleway_sd_configs']&.each do |scaleway_sd_configs|
            process_scaleway_sd_configs(scrape['job_name'], scaleway_sd_configs)
          end
        elsif scrape['linode_sd_configs']
          scrape['linode_sd_configs']&.each do |linode_sd_configs|
            process_linode_sd_configs(scrape['job_name'], linode_sd_configs)
          end
        elsif scrape['uyuni_sd_configs']
          scrape['uyuni_sd_configs']&.each do |uyuni_sd_configs|
            process_uyuni_sd_configs(scrape['job_name'], uyuni_sd_configs)
          end
        elsif scrape['ionos_sd_configs']
          scrape['ionos_sd_configs']&.each do |ionos_sd_configs|
            process_ionos_sd_configs(scrape['job_name'], ionos_sd_configs)
          end
        elsif scrape['vultr_sd_configs']
          scrape['vultr_sd_configs']&.each do |vultr_sd_configs|
            process_vultr_sd_configs(scrape['job_name'], vultr_sd_configs)
          end
        end
      end
      print_good(@table_creds.to_s) if !@table_creds.rows.empty?
    end

    def process_results_page(page)
      # data is in a strange 'label{optional_kv_hash-ish} value' format.
      return nil if page.nil?

      results = []
      page.scan(/^(?<name>\w+)(?:{(?<labels>[^}]+)})? (?<value>[\w.+-]+)/).each do |hit|
        result = {}
        value = { 'value' => hit[2], 'labels' => {} }
        if hit[1]
          hit[1].scan(/(?<key>[^=]+?)="(?<value>[^"]*)",?/).each do |label|
            value['labels'][label[0]] = label[1]
          end
        end
        result[hit[0]] = value
        results.append(result)
      end
      return results
    end
  end
end