Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/admin/aws/aws_launch_instances.rb
Views: 11655
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'metasploit/framework/aws/client'67class MetasploitModule < Msf::Auxiliary8include Metasploit::Framework::Aws::Client910def initialize(info = {})11super(12update_info(13info,14'Name' => "Launches Hosts in AWS",15'Description' => %q{16This module will attempt to launch an AWS instances (hosts) in EC2.17},18'License' => MSF_LICENSE,19'Author' => [20'Javier Godinez <godinezj[at]gmail.com>',21],22'References' => [23[ 'URL', 'https://drive.google.com/open?id=0B2Ka7F_6TetSNFdfbkI1cnJHUTQ'],24[ 'URL', 'https://published-prd.lanyonevents.com/published/rsaus17/sessionsFiles/4721/IDY-W10-DevSecOps-on-the-Offense-Automating-Amazon-Web-Services-Account-Takeover.pdf' ]25]26)27)28register_options(29[30OptString.new('AccessKeyId', [true, 'AWS access key', '']),31OptString.new('SecretAccessKey', [true, 'AWS secret key', '']),32OptString.new('Token', [false, 'AWS session token', '']),33OptString.new('RHOST', [true, 'AWS region specific EC2 endpoint', 'ec2.us-west-2.amazonaws.com']),34OptString.new('Region', [true, 'The default region', 'us-west-2' ]),35OptString.new("AMI_ID", [true, 'The Amazon Machine Image (AMI) ID', 'ami-1e299d7e']),36OptString.new("KEY_NAME", [true, 'The SSH key to be used for ec2-user', 'admin']),37OptString.new("SSH_PUB_KEY", [false, 'The public SSH key to be used for ec2-user, e.g., "ssh-rsa ABCDE..."', '']),38OptString.new("USERDATA_FILE", [false, 'The script that will be executed on start', 'tools/modules/aws-aggregator-userdata.sh'])39]40)41register_advanced_options(42[43OptPort.new('RPORT', [true, 'AWS EC2 Endpoint TCP Port', 443]),44OptBool.new('SSL', [true, 'AWS EC2 Endpoint SSL', true]),45OptString.new('INSTANCE_TYPE', [true, 'The instance type', 'm3.medium']),46OptString.new('ROLE_NAME', [false, 'The instance profile/role name', '']),47OptString.new('VPC_ID', [false, 'The EC2 VPC ID', '']),48OptString.new('SUBNET_ID', [false, 'The public subnet to use', '']),49OptString.new('SEC_GROUP_ID', [false, 'The EC2 security group to use', '']),50OptString.new('SEC_GROUP_CIDR', [true, 'EC2 security group network access CIDR', '0.0.0.0/0']),51OptString.new('SEC_GROUP_PORT', [true, 'EC2 security group network access PORT', 'tcp:22']),52OptString.new('SEC_GROUP_NAME', [false, 'Optional EC2 security group name', '']),53OptInt.new('MaxCount', [true, 'Maximum number of instances to launch', 1]),54OptInt.new('MinCount', [true, 'Minumum number of instances to launch', 1])55]56)57deregister_options('VHOST')58end5960def run61if datastore['AccessKeyId'].blank? || datastore['SecretAccessKey'].blank?62print_error("Both AccessKeyId and SecretAccessKey are required")63return64end65# setup creds for making IAM API calls66creds = {67'AccessKeyId' => datastore['AccessKeyId'],68'SecretAccessKey' => datastore['SecretAccessKey']69}70creds['Token'] = datastore['Token'] unless datastore['Token'].blank?7172create_keypair(creds) unless datastore['SSH_PUB_KEY'].blank?73vpc = datastore['VPC_ID'].blank? ? vpc(creds) : datastore['VPC_ID']74sg = datastore['SEC_GROUP_ID'].blank? ? create_sg(creds, vpc) : datastore['SEC_GROUP_ID']75subnet = datastore['SUBNET_ID'].blank? ? pub_subnet(creds, vpc) : datastore['SUBNET_ID']76unless subnet77print_error("Could not find a public subnet, please provide one")78return79end80instance_id = launch_instance(creds, subnet, sg)81action = 'DescribeInstances'82doc = call_ec2(creds, 'Action' => action, 'InstanceId.1' => instance_id)83doc = print_results(doc, action)84begin85# need a better parser so we can avoid shit like this86ip = doc['reservationSet']['item']['instancesSet']['item']['networkInterfaceSet']['item']['privateIpAddressesSet']['item']['association']['publicIp']87print_status("Instance #{instance_id} has IP address #{ip}")88rescue NoMethodError89print_error("Could not retrieve instance IP address")90end91end9293def opts(action, subnet, sg)94opts = {95'Action' => action,96'ImageId' => datastore['AMI_ID'],97'KeyName' => datastore['KEY_NAME'],98'InstanceType' => datastore['INSTANCE_TYPE'],99'NetworkInterface.1.SubnetId' => subnet,100'NetworkInterface.1.SecurityGroupId.1' => sg,101'MinCount' => datastore['MinCount'].to_s,102'MaxCount' => datastore['MaxCount'].to_s,103'NetworkInterface.1.AssociatePublicIpAddress' => 'true',104'NetworkInterface.1.DeviceIndex' => '0'105}106opts['IamInstanceProfile.Name'] = datastore['ROLE_NAME'] unless datastore['ROLE_NAME'].blank?107unless datastore['USERDATA_FILE'].blank?108if File.exist?(datastore['USERDATA_FILE'])109opts['UserData'] = URI::DEFAULT_PARSER.escape(Base64.encode64(open(datastore['USERDATA_FILE'], 'r').read).strip)110else111print_error("Could not open userdata file: #{datastore['USERDATA_FILE']}")112end113end114opts115end116117def launch_instance(creds, subnet, sg)118action = 'RunInstances'119print_status("Launching instance(s) in #{datastore['Region']}, AMI: #{datastore['AMI_ID']}, key pair name: #{datastore['KEY_NAME']}, security group: #{sg}, subnet ID: #{subnet}")120doc = call_ec2(creds, opts(action, subnet, sg))121doc = print_results(doc, action)122return if doc.nil?123# TODO: account for multiple instances124if doc['instancesSet']['item'].instance_of?(Array)125instance_id = doc['instancesSet']['item'].first['instanceId']126else127instance_id = doc['instancesSet']['item']['instanceId']128end129print_status("Launched instance #{instance_id} in #{datastore['Region']} account #{doc['ownerId']}")130action = 'DescribeInstanceStatus'131loop do132sleep(15)133doc = call_ec2(creds, 'Action' => action, 'InstanceId' => instance_id)134doc = print_results(doc, action)135if doc['instanceStatusSet'].nil?136print_error("Error, could not get instance status, instance possibly terminated")137break138end139status = doc['instanceStatusSet']['item']['systemStatus']['status']140print_status("instance #{instance_id} status: #{status}")141break if status == 'ok' || status != 'initializing'142end143instance_id144end145146def create_keypair(creds)147action = 'ImportKeyPair'148doc = call_ec2(creds, 'Action' => action, 'KeyName' => datastore['KEY_NAME'], 'PublicKeyMaterial' => Rex::Text.encode_base64(datastore['SSH_PUB_KEY']))149if doc['Response'].nil?150doc = print_results(doc, action)151if doc['keyName'].nil? || doc['keyFingerprint'].nil?152print_error("Error creating key using provided key material (SSH_PUB_KEY)")153else154print_status("Created #{doc['keyName']} (#{doc['keyFingerprint']})")155end156else157if doc['Response']['Errors'] && doc['Response']['Errors']['Error']158print_error(doc['Response']['Errors']['Error']['Message'])159else160print_error("Error creating key using provided key material (SSH_PUB_KEY)")161end162end163end164165def pub_subnet(creds, vpc_id)166# First look for subnets that are configured to provision a public IP when instances are launched167action = 'DescribeSubnets'168doc = call_ec2(creds, 'Action' => action)169doc = print_results(doc, action)170vpc_subnets = doc['subnetSet']['item'].select { |x| x['vpcId'] == vpc_id }171pub_subnets = vpc_subnets.select { |x| x['mapPublicIpOnLaunch'] == 'true' }172return pub_subnets.first['subnetId'] if pub_subnets.count > 0173174# Second, try to retrieve public subnet id by looking through route tables to find subnets175# associated with an Internet gateway176action = 'DescribeRouteTables'177doc = call_ec2(creds, 'Action' => action)178doc = print_results(doc, action)179vpc_route_table = doc['routeTableSet']['item'].select { |x| x['vpcId'] == vpc_id }180vpc_route_table.each do |route_table|181next if route_table['associationSet'].nil? || route_table['routeSet'].nil?182entries = route_table['routeSet']['item']183if entries.instance_of?(Hash)184if entries['gatewayId'].start_with?('igw-')185return route_table['associationSet']['item'].first['subnetId']186end187else188route_table['routeSet']['item'].each do |route|189if route['gatewayId'] && route['gatewayId'].start_with?('igw-')190return route_table['associationSet']['item'].first['subnetId']191end192end193end194end195nil196end197198def create_sg(creds, vpc_id)199name = Rex::Text.rand_text_alphanumeric(8)200action = 'CreateSecurityGroup'201doc = call_ec2(creds, 'Action' => action, 'GroupName' => name, 'VpcId' => vpc_id, 'GroupDescription' => name)202doc = print_results(doc, action)203print_error("Could not create SG") && return if doc['groupId'].nil?204sg = doc['groupId']205proto, port = datastore['SEC_GROUP_PORT'].split(':')206cidr = URI::DEFAULT_PARSER.escape(datastore['SEC_GROUP_CIDR'])207action = 'AuthorizeSecurityGroupIngress'208doc = call_ec2(creds, 'Action' => action,209'IpPermissions.1.IpRanges.1.CidrIp' => cidr,210'IpPermissions.1.IpProtocol' => proto,211'IpPermissions.1.FromPort' => port,212'IpPermissions.1.ToPort' => port,213'GroupId' => sg)214doc = print_results(doc, action)215if doc['return'] && doc['return'] == 'true'216print_status("Created security group: #{sg}")217else218print_error("Failed creating security group")219end220sg221end222223def vpc(creds)224action = 'DescribeVpcs'225doc = call_ec2(creds, 'Action' => action)226doc = print_results(doc, action)227if doc['vpcSet'].nil? || doc['vpcSet']['item'].nil?228print_error("Could not determine VPC ID for #{datastore['AccessKeyId']} in #{datastore['RHOST']}")229return nil230end231item = doc['vpcSet']['item']232return item['vpcId'] if item.instance_of?(Hash)233return item.first['vpcId'] if item.instance_of?(Array) && !item.first['vpcId'].nil?234print_error("Could not determine VPC ID for #{datastore['AccessKeyId']} in #{datastore['RHOST']}")235nil236end237end238239240