Lucene search
+L

Amazon Web Services EC2 instance enumeration

🗓️ 20 Jun 2019 20:08:30Reported by Aaron Soto <[email protected]>, RageLtMan <rageltman@sempervictus>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 41 Views

AWS EC2 instance enumeration using Metasploi

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

require 'aws-sdk-ec2'

class MetasploitModule < Msf::Auxiliary
  include Msf::Auxiliary::Report
  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Amazon Web Services EC2 instance enumeration',
        'Description' => %q{
          Provided AWS credentials, this module will call the authenticated
          API of Amazon Web Services to list all EC2 instances associated
          with the account
        },
        'Author' => [
          'Aaron Soto <[email protected]>',
          'RageLtMan <rageltman[at]sempervictus>'
        ],
        'License' => MSF_LICENSE,
        'Notes' => {
          'SideEffects' => [IOC_IN_LOGS],
          'Stability' => [CRASH_SAFE],
          'Reliability' => []
        }
      )
    )

    register_options(
      [
        OptInt.new('LIMIT', [false, 'Only return the specified number of results from each region']),
        OptString.new('REGION', [false, 'AWS Region (eg. "us-west-2")']),
        OptString.new('ACCESS_KEY_ID', [true, 'AWS Access Key ID (eg. "AKIAXXXXXXXXXXXXXXXX")', '']),
        OptString.new('SECRET_ACCESS_KEY', [true, 'AWS Secret Access Key (eg. "CA1+XXXXXXXXXXXXXXXXXXXXXX6aYDHHCBuLuV79")', ''])
      ]
    )
  end

  def enumerate_regions
    regions = []

    ec2 = Aws::EC2::Resource.new(
      region: 'us-west-1',
      access_key_id: datastore['ACCESS_KEY_ID'],
      secret_access_key: datastore['SECRET_ACCESS_KEY']
    )

    ec2_regions = ec2.client.describe_regions.data.regions
    ec2_regions.each do |r|
      regions.append(r.region_name)
    end

    regions
  end

  def describe_ec2_instance(inst)
    print_good "  #{inst.id} (#{inst.state.name})"
    print_good "    Creation Date:  #{inst.launch_time}"
    print_good "    Public IP:      #{inst.public_ip_address} (#{inst.public_dns_name})"
    print_good "    Private IP:     #{inst.private_ip_address} (#{inst.private_dns_name})"
    # Report hosts and info
    mac_addr = inst.network_interfaces.select do |iface|
      iface.private_ip_address == inst.private_ip_address
    end.first.mac_address
    iname = inst.tags.find { |t| t.key == 'Name' } ? inst.tags.find { |t| t.key == 'Name' }.value : inst.private_dns_name
    iinfo = inst.tags.find { |t| t.key == 'Description' } ? inst.tags.find { |t| t.key == 'Description' }.value : nil
    report_host(
      host: inst.private_ip_address,
      mac: mac_addr,
      os_name: inst.platform_details,
      os_flavor: inst.architecture,
      name: iname,
      info: iinfo,
      comments: "ec2-id: #{inst.id} (#{inst.placement.availability_zone})"
    )
    if inst.public_ip_address
      report_note(
        host: inst.private_ip_address,
        type: 'ec2.public_ip',
        data: inst.public_ip_address
      )
    end
    #eips = inst.network_interfaces.map {|i| i.association && i.association.public_ip}.compact # <-- works in pry, breaks at runtime in AWS SDK
    #report_note(
    #  host: inst.private_ip_address,
    #  type: 'ec2.public_ips',
    #  data: eips.join(' ')
    #) unless eips.empty?
    if inst.public_ip_address && !inst.public_dns_name.empty?
      report_note(
        host: inst.private_ip_address,
        type: 'ec2.public_dns',
        data: "#{inst.public_dns_name} #{inst.public_ip_address}"
      )
    end
    if inst.hypervisor
      report_note(
        host: inst.private_ip_address,
        type: 'ec2.hypervisor',
        data: inst.hypervisor
      )
    end
    inst.security_groups.each do |s|
      print_good "    Security Group: #{s.group_id}"
      report_note(
        host: inst.private_ip_address,
        type: "ec2.#{s.group_id}",
        data: s.group_name
      )
    end
    inst.tags.each do |t|
      print_good "    Tag: #{t.key} = #{t.value}"
      report_note(
        host: inst.private_ip_address,
        type: "ec2.tag #{t.key}",
        data: t.value
      )
    end
  end

  def run
    all_regions = enumerate_regions
    if datastore['REGION'].blank?
      regions = all_regions
    elsif !all_regions.include?(datastore['REGION'])
      fail_with(Failure::BadConfig, "Invalid AWS region: #{datastore['REGION']}")
    else
      regions = [datastore['REGION']]
    end

    regions.uniq.each do |region|
      vprint_status "Checking #{region}..."
      ec2 = Aws::EC2::Resource.new(
        region: region,
        access_key_id: datastore['ACCESS_KEY_ID'],
        secret_access_key: datastore['SECRET_ACCESS_KEY']
      )

      instances = datastore['LIMIT'] ? ec2.instances.limit(datastore['LIMIT']) : ec2.instances
      print_status "Found #{ec2.instances.count} instances in #{region}"

      instances.each do |i|
        describe_ec2_instance(i)
      end
    end
  rescue Seahorse::Client::NetworkingError => e
    print_error e.message
    print_error 'Confirm region name (eg. us-west-2) is valid or blank before retrying'
  rescue Aws::EC2::Errors::ServiceError => e
    fail_with(Failure::UnexpectedReply, e.message)
  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