diff --git a/lib/objects/system.rb b/lib/objects/system.rb
index 85ce9a969..17874416f 100644
--- a/lib/objects/system.rb
+++ b/lib/objects/system.rb
@@ -9,27 +9,81 @@ class System
attr_accessor :module_selections # (after resolution)
attr_accessor :num_actioned_module_conflicts
attr_accessor :system_networks
- attr_accessor :network_ranges # populated when provided via command line options
+ attr_accessor :options #(command line options hash)
# Initalizes System object
# @param [Object] name of the system
# @param [Object] attributes such as base box selection
# @param [Object] module_selectors these are modules that define filters for selecting the actual modules to use
- def initialize(name, attributes, module_selectors, network_ranges)
+ def initialize(name, attributes, module_selectors)
self.name = name
self.attributes = attributes
self.module_selectors = module_selectors
self.module_selections = []
self.num_actioned_module_conflicts = 0
self.system_networks = []
- self.network_ranges = network_ranges
+ self.options = {}
end
# selects from the available modules, based on the selection filters that have been specified
# @param [Object] available_modules all available modules (vulnerabilities, services, bases)
+ # @param [Object] opts command line options hash
# @return [Object] the list of selected modules
- def resolve_module_selection(available_modules)
+ def resolve_module_selection(available_modules, opts)
+ @options = opts
retry_count = 0
+
+ # Replace $IP_addresses with options ip_ranges if required
+ begin
+ if @options[:ip_ranges] and $datastore['IP_addresses'] and !$datastore['replaced_ranges']
+ unused_opts_ranges = @options[:ip_ranges].clone
+ option_range_map = {} # k = ds_range, v = opts_range
+ new_ip_addresses = []
+
+ # Iterate over the DS IPs
+ $datastore['IP_addresses'].each do |ds_ip_address|
+ # Split the IP into ['X.X.X', 'Y']
+ split_ip = ds_ip_address.split('.')
+ ds_ip_array = [split_ip[0..2].join('.'), split_ip[3]]
+ ds_range = ds_ip_array[0] + '.0'
+ # Check if we have encountered first 3 octets before i.e. look in option_range_map for key(ds_range)
+ if option_range_map.has_key? ds_range
+ # if we have, grab that value (opts_range)
+ opts_range = option_range_map[ds_range]
+ # replace first 3 in ds_ip with first 3 in opts_range
+ split_opts_range = opts_range.split('.')
+ split_opts_range[3] = ds_ip_array[1]
+ new_ds_ip = split_opts_range.join('.')
+ # save in $datastore['IP_addresses']
+ new_ip_addresses << new_ds_ip
+ else #(if we haven't seen the first 3 octets before)
+ # grab the first range that we haven't used yet from unused_opts_ranges with .shift (also removes the range)
+ opts_range = unused_opts_ranges.shift
+ # store the range mapping in option_range_map (ds_range => opts_range)
+ option_range_map[ds_range] = opts_range
+ # split the opts_range and replace last octet with last octet of ds_ip_address
+ split_opts_range = opts_range.split('.')
+ split_opts_range[3] = ds_ip_array[1]
+ new_ds_ip = split_opts_range.join('.')
+ # save in $datastore['IP_addresses']
+ new_ip_addresses << new_ds_ip
+ end
+ end
+ $datastore['IP_addresses'] = new_ip_addresses
+ $datastore['replaced_ranges'] = true
+ end
+ rescue NoMethodError
+ required_ranges = []
+ $datastore['IP_addresses'].each { |ip_address|
+ split_range = ip_address.split('.')
+ split_range[3] = 0
+ required_ranges << split_range.join('.')
+ }
+ required_ranges.uniq!
+ Print.err("Fatal: Not enough ranges were provided with --network-ranges. Provided: #{options[:ip_ranges].size} Required: #{required_ranges.uniq.size}")
+ exit
+ end
+
begin
selected_modules = []
@@ -88,30 +142,25 @@ class System
# filter to those that satisfy the attribute filters
# select based on selected type, access, cve...
- search_list.delete_if{|module_for_possible_exclusion|
+ search_list.delete_if { |module_for_possible_exclusion|
!module_for_possible_exclusion.matches_attributes_requirement(required_attributes)
}
Print.verbose "Filtered to modules matching: #{required_attributes.inspect} ~= (n=#{search_list.size})"
# remove non-options due to conflicts
- search_list.delete_if{|module_for_possible_exclusion|
+ search_list.delete_if { |module_for_possible_exclusion|
check_conflicts_with_list(module_for_possible_exclusion, previously_selected_modules)
}
# check if modules need to be unique
# write_module_path_to_datastore
if write_module_path_to_datastore != nil && $datastore[write_module_path_to_datastore] != nil
- search_list.delete_if{|module_for_possible_exclusion|
+ search_list.delete_if { |module_for_possible_exclusion|
($datastore[write_module_path_to_datastore] ||=[]).include? module_for_possible_exclusion.module_path
}
Print.verbose "Filtering to remove non-unique #{$datastore[write_module_path_to_datastore]} ~= (n=#{search_list.size})"
end
- # check if we have a network range
- if self.network_ranges != nil && ($datastore['network'] == nil or $datastore['network'].empty?)
- $datastore['network_override'] = network_ranges
- end
-
if search_list.length == 0
raise 'failed'
Print.err 'Could not find a matching module. Please check the scenario specification'
@@ -327,7 +376,7 @@ class System
if /^.*defaultinput/ =~ def_unique_id
def_unique_id = def_unique_id.gsub(/^.*defaultinput/, selected.unique_id)
end
-
+
default_modules_to_add.concat select_modules(module_to_add.module_type, module_to_add.attributes, available_modules, previously_selected_modules + default_modules_to_add, def_unique_id, module_to_add.write_output_variable, def_write_to, module_to_add.received_inputs, module_to_add.default_inputs_literals, module_to_add.write_to_datastore, module_to_add.received_datastores, module_to_add.write_module_path_to_datastore)
end
end
@@ -387,7 +436,7 @@ class System
end
def get_networks
- if (self.system_networks = []) # assign the networks
+ if (self.system_networks = []) # assign the networks
self.module_selections.each do |mod|
if mod.module_type == 'network'
self.system_networks << mod
diff --git a/lib/output/project_files_creator.rb b/lib/output/project_files_creator.rb
index acde9cab4..95cfe5736 100644
--- a/lib/output/project_files_creator.rb
+++ b/lib/output/project_files_creator.rb
@@ -39,13 +39,13 @@ class ProjectFilesCreator
if File.exists? "#{@out_dir}/Vagrantfile" or File.exists? "#{@out_dir}/puppet"
dest_dir = "#{@out_dir}/MOVED_#{Time.new.strftime("%Y%m%d_%H%M")}"
Print.warn "Project already built to this directory -- moving last build to: #{dest_dir}"
- Dir.glob( "#{@out_dir}/**/*" ).select { |f| File.file?( f ) }.each do |f|
+ Dir.glob("#{@out_dir}/**/*").select { |f| File.file?(f) }.each do |f|
dest = "#{dest_dir}/#{f}"
- FileUtils.mkdir_p( File.dirname( dest ) )
+ FileUtils.mkdir_p(File.dirname(dest))
if f =~ /\.vagrant/
- FileUtils.cp( f, dest )
+ FileUtils.cp(f, dest)
else
- FileUtils.mv( f, dest )
+ FileUtils.mv(f, dest)
end
end
end
@@ -80,7 +80,7 @@ class ProjectFilesCreator
if File.file? packerfile_path
Print.info "Would you like to use the packerfile to create the packerfile from the given url (y/n)"
- (Print.info "Exiting as vagrant needs the basebox to continue"; exit) unless ['y','yes'].include?(STDIN.gets.chomp.downcase)
+ (Print.info "Exiting as vagrant needs the basebox to continue"; exit) unless ['y', 'yes'].include?(STDIN.gets.chomp.downcase)
Print.std "Packerfile #{packerfile_path.split('/').last} found, building basebox #{url.split('/').last} via packer"
template_based_file_write(packerfile_path, packerfile_path.split(/.erb$/).first)
@@ -156,10 +156,16 @@ class ProjectFilesCreator
end
# Resolves the network based on the scenario and ip_range.
- def resolve_network(scenario_ip_range)
+ def resolve_network(network_module)
+ current_network = network_module
+ scenario_ip_range = network_module.attributes['range'].first
+
+ # Use datastore IP_address if we have them
+ if current_network.received_inputs.include? 'IP_address'
+ ip_address = current_network.received_inputs['IP_address'].first
+ elsif @options.has_key? :ip_ranges
# if we have options[:ip_ranges] we want to use those instead of the ip_range argument.
# Store the mappings of scenario_ip_ranges => @options[:ip_range] in @option_range_map
- if @options.has_key? :ip_ranges
# Have we seen this scenario_ip_range before? If so, use the value we've assigned
if @option_range_map.has_key? scenario_ip_range
ip_range = @option_range_map[scenario_ip_range]
@@ -170,10 +176,14 @@ class ProjectFilesCreator
@option_range_map[scenario_ip_range] = options_ips.first
ip_range = options_ips.first
end
+ ip_address = get_ip_from_range(ip_range)
else
- ip_range = scenario_ip_range
+ ip_address = get_ip_from_range(scenario_ip_range)
end
+ ip_address
+ end
+ def get_ip_from_range(ip_range)
# increment @scenario_networks{ip_range=>counter}
@scenario_networks[ip_range] += 1
diff --git a/lib/readers/system_reader.rb b/lib/readers/system_reader.rb
index b8b4d313f..dfc5c299d 100644
--- a/lib/readers/system_reader.rb
+++ b/lib/readers/system_reader.rb
@@ -10,7 +10,7 @@ class SystemReader
# This includes module filters, which are module objects that contain filters for selecting
# from the actual modules that are available
# @return [Array] Array containing Systems objects
- def self.read_scenario(scenario_file, network_ranges)
+ def self.read_scenario(scenario_file)
systems = []
Print.verbose "Reading scenario file: #{scenario_file}"
doc, xsd = nil
@@ -38,19 +38,6 @@ class SystemReader
# remove xml namespaces for ease of processing
doc.remove_namespaces!
- # hack for networks -- TODO: Remove me ASAP DO NOT MERGE TO MASTER
- ranges = []
- if network_ranges
- network_ranges.each { |range|
- doc.xpath('/scenario/system').size.times { |count|
- range_array = range.split('.')
- range_array[-1] = count+2
- ranges << range_array.join('.')
- }
- }
- network_ranges = ranges
- end
-
doc.xpath('/scenario/system').each_with_index do |system_node, system_index|
module_selectors = []
system_attributes = {}
@@ -159,7 +146,7 @@ class SystemReader
end
end
- systems << System.new(system_name, system_attributes, module_selectors, network_ranges)
+ systems << System.new(system_name, system_attributes, module_selectors)
end
return systems
diff --git a/lib/schemas/network_metadata_schema.xsd b/lib/schemas/network_metadata_schema.xsd
index b0571191e..98f8fb9b8 100644
--- a/lib/schemas/network_metadata_schema.xsd
+++ b/lib/schemas/network_metadata_schema.xsd
@@ -38,6 +38,8 @@
+
+
@@ -57,4 +59,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/schemas/scenario_schema.xsd b/lib/schemas/scenario_schema.xsd
index 25291a7bd..2dcf001b3 100644
--- a/lib/schemas/scenario_schema.xsd
+++ b/lib/schemas/scenario_schema.xsd
@@ -126,6 +126,9 @@
+
+
+
diff --git a/lib/templates/Vagrantfile.erb b/lib/templates/Vagrantfile.erb
index c786269a9..37f1a374a 100644
--- a/lib/templates/Vagrantfile.erb
+++ b/lib/templates/Vagrantfile.erb
@@ -114,6 +114,11 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
<% system.module_selections.each do |selected_module| -%>
<%= selected_module.to_s_comment -%>
+<% if selected_module.module_type == 'network' and selected_module.received_inputs.include? 'IP_address' %>
+<%= ' # This module has a datastore entry for IP_address, using that instead of the default.' -%>
+<% elsif selected_module.module_type == 'network' and @options.has_key? :ip_ranges -%>
+<%= ' # This module has a command line ip_range, using that instead of the default.' -%>
+<% end -%>
<% case selected_module.module_type
when 'base' -%>
<% if (@options.has_key? :ovirtuser) && (@options.has_key? :ovirtpass) %> # TODO
@@ -131,7 +136,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
<%= system.name %>.vm.network :forwarded_port, guest: 5985, host: 5985, id: "winrm", auto_correct: true
<% end %>
<% when 'network' -%>
-<% if selected_module.attributes['range'].first.nil? || selected_module.attributes['range'].first == "dhcp" -%>
+<% if (selected_module.attributes['range'].first.nil? || selected_module.attributes['range'].first == "dhcp") and (!selected_module.received_inputs.include? 'IP_address' and !@options[:ip_ranges])-%>
<% if (@options.has_key? :ovirtnetwork) && (@options.has_key? :ovirtuser) && (@options.has_key? :ovirtpass) %>
<%= system.name %>.vm.network :<%= selected_module.attributes['type'].first %>, type: "dhcp", :ovirt__network_name => '<%= "#{@options[:ovirtnetwork]}" %>'
<% else %>
@@ -140,16 +145,16 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
<% else -%>
<% if (@options.has_key? :ovirtuser) && (@options.has_key? :ovirtpass) %>
<% if @ovirt_template and @ovirt_template.include? 'kali_linux_msf' %>
- <%= system.name %>.vm.provision 'shell', inline: "echo \"auto lo\niface lo inet loopback\n\nauto eth0\niface eth0 inet static\n\taddress <%= resolve_network(selected_module.attributes['range'].first)%>\" > /etc/network/interfaces"
+ <%= system.name %>.vm.provision 'shell', inline: "echo \"auto lo\niface lo inet loopback\n\nauto eth0\niface eth0 inet static\n\taddress <%= resolve_network(selected_module)%>\" > /etc/network/interfaces"
<%= system.name %>.vm.provision 'shell', inline: "echo '' > /etc/environment"
<% elsif @ovirt_template and @ovirt_template.include? 'debian_desktop_kde' %>
- <%= system.name %>.vm.provision 'shell', inline: "echo \"\nauto eth1\niface eth1 inet static\n\taddress <%= resolve_network(selected_module.attributes['range'].first)%>\" >> /etc/network/interfaces"
+ <%= system.name %>.vm.provision 'shell', inline: "echo \"\nauto eth1\niface eth1 inet static\n\taddress <%= resolve_network(selected_module)%>\" >> /etc/network/interfaces"
<%= system.name %>.vm.provision 'shell', inline: "echo '' > /etc/environment"
<% else %>
- <%= system.name %>.vm.network :<%= selected_module.attributes['type'].first %>, :ovirt__ip => "<%= resolve_network(selected_module.attributes['range'].first)%>", :ovirt__network_name => '<%= "#{@options[:ovirtnetwork]}" %>'
+ <%= system.name %>.vm.network :<%= selected_module.attributes['type'].first %>, :ovirt__ip => "<%= resolve_network(selected_module)%>", :ovirt__network_name => '<%= "#{@options[:ovirtnetwork]}" %>'
<% end %>
<% else %>
- <%= system.name %>.vm.network :<%= selected_module.attributes['type'].first %>, ip: "<%= resolve_network(selected_module.attributes['range'].first)%>"
+ <%= system.name %>.vm.network :<%= selected_module.attributes['type'].first %>, ip: "<%= resolve_network(selected_module)%>"
<% end %>
<% end -%>
<% when 'vulnerability', 'service', 'utility', 'build' -%>
diff --git a/modules/networks/host_only/private_network_1/secgen_metadata.xml b/modules/networks/host_only/private_network_1/secgen_metadata.xml
index 2c3dd4e46..61d9bc16e 100644
--- a/modules/networks/host_only/private_network_1/secgen_metadata.xml
+++ b/modules/networks/host_only/private_network_1/secgen_metadata.xml
@@ -11,4 +11,6 @@
private_network
172.16.0.0
+ IP_address
+
\ No newline at end of file
diff --git a/modules/networks/host_only/private_network_2/secgen_metadata.xml b/modules/networks/host_only/private_network_2/secgen_metadata.xml
index 67fce352c..10ef7b52a 100644
--- a/modules/networks/host_only/private_network_2/secgen_metadata.xml
+++ b/modules/networks/host_only/private_network_2/secgen_metadata.xml
@@ -11,4 +11,6 @@
private_network
172.17.0.0
+ IP_address
+
\ No newline at end of file
diff --git a/secgen.rb b/secgen.rb
index 6eb53d762..5ced0ca26 100644
--- a/secgen.rb
+++ b/secgen.rb
@@ -63,7 +63,7 @@ def build_config(scenario, out_dir, options)
Print.info 'Reading configuration file for virtual machines you want to create...'
# read the scenario file describing the systems, which contain vulnerabilities, services, etc
# this returns an array/hashes structure
- systems = SystemReader.read_scenario(scenario, options[:ip_ranges])
+ systems = SystemReader.read_scenario(scenario)
Print.std "#{systems.size} system(s) specified"
Print.info 'Reading available base modules...'
@@ -104,12 +104,10 @@ def build_config(scenario, out_dir, options)
all_available_services + all_available_utilities + all_available_generators + all_available_encoders + all_available_networks
# update systems with module selections
systems.map! {|system|
- system.module_selections = system.resolve_module_selection(all_available_modules)
+ system.module_selections = system.resolve_module_selection(all_available_modules, options)
system
}
- validate_network_ranges(systems, options)
-
Print.info "Creating project: #{out_dir}..."
# create's vagrant file / report a starts the vagrant installation'
creator = ProjectFilesCreator.new(systems, out_dir, scenario, options)
@@ -280,26 +278,6 @@ def delete_all_projects
FileUtils.rm_r(Dir.glob("#{PROJECTS_DIR}/*"))
end
-# Ensure enough ranges are provided with --network-ranges
-def validate_network_ranges(systems, options)
- if options.has_key? :ip_ranges
- scenario_networks = []
- systems.each { |sys| scenario_networks << sys.get_networks }
- scenario_networks.flatten!
-
- # Remove dhcp networks
- scenario_networks.delete_if { |network| network.attributes['range'][0] == 'dhcp' }
-
- # Remove non-unique network ranges
- scenario_networks = Hash[*scenario_networks.map { |network| [network.attributes['range'], network] }.flatten].values
-
- if options[:ip_ranges].size < scenario_networks.size
- Print.err("Fatal: Not enough ranges were provided with --network-ranges. Provided: #{options[:ip_ranges].size} Required: #{scenario_networks.size}")
- exit
- end
- end
-end
-
# end of method declarations
# start of program execution