100 lines
3.2 KiB
Ruby
100 lines
3.2 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'aws-sdk-s3'
|
|
|
|
class MetasploitModule < Msf::Auxiliary
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Amazon Web Services S3 instance enumeration',
|
|
'Description' => %q(
|
|
Provided AWS credentials, this module will call the authenticated
|
|
API of Amazon Web Services to list all S3 buckets associated
|
|
with the account
|
|
),
|
|
'Author' => ['Aaron Soto <aaron.soto@rapid7.com>'],
|
|
'License' => MSF_LICENSE
|
|
)
|
|
)
|
|
|
|
register_options(
|
|
[
|
|
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 handle_aws_errors(e)
|
|
if e.class.module_parents.include?(Aws)
|
|
fail_with(Failure::UnexpectedReply, e.message)
|
|
else
|
|
raise e
|
|
end
|
|
end
|
|
|
|
def describe_s3_bucket(i)
|
|
print_good " Name: #{i.name}"
|
|
print_good " Creation Date: #{i.creation_date}"
|
|
print_good " # of Objects: #{@s3.list_objects_v2(bucket: i.name).contents.length}"
|
|
print_good " Region: #{@s3.get_bucket_location(bucket: i.name).location_constraint}"
|
|
|
|
begin
|
|
print_good " Website: /#{@s3.get_bucket_website(bucket: i.name).index_document.suffix}"
|
|
rescue Aws::S3::Errors::NoSuchWebsiteConfiguration
|
|
print_good " Website: (None)"
|
|
end
|
|
|
|
acl = @s3.get_bucket_acl(bucket: i.name)
|
|
print_good " Owner: #{acl.owner.display_name}"
|
|
print_good " Permissions:"
|
|
acl.grants.each do |i|
|
|
grantee = i.grantee.type == "CanonicalUser" ? "User" : i.grantee.type
|
|
grantee << " '#{i.grantee.display_name}'"
|
|
grantee << " (#{i.grantee.email_address})" unless i.grantee.email_address.nil?
|
|
grantee << " (#{i.grantee.uri})" unless i.grantee.uri.nil?
|
|
print_good " #{grantee} granted #{i.permission}"
|
|
end
|
|
print_status ''
|
|
end
|
|
|
|
def run
|
|
region = datastore['REGION']
|
|
|
|
@s3 = Aws::S3::Client.new(
|
|
region: "us-west-2", # This doesn't actually filter anything, but
|
|
# it's still required. Thanks AWS. :-(
|
|
access_key_id: datastore['ACCESS_KEY_ID'],
|
|
secret_access_key: datastore['SECRET_ACCESS_KEY']
|
|
)
|
|
|
|
buckets = @s3.list_buckets.buckets
|
|
unless buckets.length > 0
|
|
print_status 'No buckets found.'
|
|
return
|
|
end
|
|
|
|
print_good "Found #{buckets.count} buckets."
|
|
if region.nil?
|
|
buckets.each do |i|
|
|
describe_s3_bucket(i)
|
|
end
|
|
else
|
|
print_good "Listing buckets that match REGION '#{datastore['REGION']}':"
|
|
buckets.each do |i|
|
|
if @s3.get_bucket_location(bucket: i.name).location_constraint.starts_with? region
|
|
describe_s3_bucket(i)
|
|
end
|
|
end
|
|
end
|
|
print_status 'Done.'
|
|
rescue ::Exception => e
|
|
handle_aws_errors(e)
|
|
end
|
|
end
|