diff --git a/lib/helpers/constants.rb b/lib/helpers/constants.rb
index 21122bfaa..0b7ca14b5 100644
--- a/lib/helpers/constants.rb
+++ b/lib/helpers/constants.rb
@@ -1,4 +1,4 @@
-## FILE CONSTANTS ##
+## FILE / PATH CONSTANTS ##
# Root directory of SecGen file structure
ROOT_DIR = File.expand_path('../../../',__FILE__)
@@ -12,6 +12,7 @@ VULNERABILITY_SCHEMA_FILE = "#{ROOT_DIR}/lib/schemas/vulnerability_metadata_sche
SERVICE_SCHEMA_FILE = "#{ROOT_DIR}/lib/schemas/service_metadata_schema.xsd"
UTILITY_SCHEMA_FILE = "#{ROOT_DIR}/lib/schemas/utility_metadata_schema.xsd"
GENERATOR_SCHEMA_FILE = "#{ROOT_DIR}/lib/schemas/generator_metadata_schema.xsd"
+ENCODER_SCHEMA_FILE = "#{ROOT_DIR}/lib/schemas/encoder_metadata_schema.xsd"
NETWORK_SCHEMA_FILE = "#{ROOT_DIR}/lib/schemas/network_metadata_schema.xsd"
BASE_SCHEMA_FILE = "#{ROOT_DIR}/lib/schemas/base_metadata_schema.xsd"
@@ -21,17 +22,16 @@ PROJECTS_DIR = "#{ROOT_DIR}/projects"
# Path to environments directory
ENVIRONMENTS_PATH = "#{ROOT_DIR}/modules/build/environments"
-
-## PATH CONSTANTS ##
-
# Path to modules directories
MODULES_PATH = "#{ROOT_DIR}/modules/"
VULNERABILITIES_PATH = "#{MODULES_PATH}vulnerabilities/"
SERVICES_PATH = "#{MODULES_PATH}services/"
UTILITIES_PATH = "#{MODULES_PATH}utilities/"
GENERATORS_PATH = "#{MODULES_PATH}generators/"
+ENCODERS_PATH = "#{MODULES_PATH}encoders/"
NETWORKS_PATH = "#{MODULES_PATH}networks/"
BASES_PATH = "#{MODULES_PATH}bases/"
+MODULE_LOCAL_CALC_PATH = '/secgen_local/local.rb'
# Path to documentation (Make sure documentation directory is already deleted with rake yard_clean before changing this)
DOCUMENTATION_PATH = "#{ROOT_DIR}/documentation/yard/doc"
diff --git a/lib/objects/module.rb b/lib/objects/module.rb
index aedb8f7fa..f65bf0847 100644
--- a/lib/objects/module.rb
+++ b/lib/objects/module.rb
@@ -11,13 +11,16 @@ class Module
# Module *selectors*, store filters in the attributes hash.
# XML validity ensures valid and complete information.
+ attr_accessor :write_to_module_with_id
attr_accessor :write_outputs_to
+ attr_accessor :output
attr_accessor :unique_id
attr_accessor :conflicts
attr_accessor :requires
attr_accessor :puppet_file
attr_accessor :puppet_other_path
+ attr_accessor :local_calc_file
# @param [Object] module_type: such as 'vulnerability', 'base', 'service', 'network'
def initialize(module_type)
@@ -25,6 +28,7 @@ class Module
self.conflicts = []
self.requires = []
self.attributes = {}
+ self.output = "dynamic"
# self.attributes['module_type'] = module_type # add as an attribute for filtering
end
@@ -33,7 +37,6 @@ class Module
(<<-END)
#{module_type}: #{module_path}
attributes: #{attributes.inspect}
- inputs: #{inputs.inspect}
conflicts: #{conflicts.inspect}
requires: #{requires.inspect}
puppet file: #{puppet_file}
@@ -46,7 +49,6 @@ class Module
(<<-END)
# #{module_type}: #{module_path}
# attributes: #{attributes.inspect}
- # inputs: #{inputs.inspect}
# conflicts: #{conflicts.inspect}
# requires: #{requires.inspect}
END
@@ -64,6 +66,13 @@ class Module
module_path_name.gsub!('/','_')
end
+ # pre-calculate any secgen_local/local.rb outputs
+ def local_processing
+ if self.local_calc_file
+ self.output = `#{self.local_calc_file}`.chomp
+ end
+ end
+
# @return [Object] a list of attributes that can be used to re-select the same modules
def attributes_for_scenario_output
attr_flattened = {}
@@ -78,61 +87,61 @@ class Module
attr_flattened
end
- # resolve randomisation of inputs
- def select_inputs
- inputs.each do |input|
- # TODO TODO
- Print.verbose "Input #{input["name"][0]}"
- Print.verbose "Rand type: #{input["randomisation_type"][0]}"
- case input["randomisation_type"][0]
- when "one_from_list"
- if input["value"].size == 0
- Print.err "Randomisation not possible for #{module_path} (one_from_list with no values)"
- exit
- end
- one_value = [input["value"].shuffle![0]]
- input["value"] = one_value
- when "flag_value"
- # if no value suppied, generate one
- unless input["value"]
- input["value"] = ["THE_FLAG_IS:#{SecureRandom.hex}"]
- else
- input["value"] = ["THE_FLAG_IS:#{input["value"][0]}"]
- end
- when "none"
- # nothing...
-
- end
-
- # if an encoding is specified
- if input["encoding"]
- if input["encoding"].size > 1
- input["encoding"] = [input["encoding"].shuffle![0]]
- else
- enc = input["encoding"][0]
- end
- #
- # TODO?? case enc
- # when "base64_encode"
- # require "base64"
- # unless input["value"]
- # input["value"] = [Base64.encode64(SecureRandom.hex)]
- # else
- # input["value"] = [Base64.encode64(input["value"][0])]
- # end
- # when "MD5_calc_hash"
- # unless input["value"]
- # input["value"] = [Digest::MD5.hexdigest(SecureRandom.hex)]
- # else
- # input["value"] = [Digest::MD5.hexdigest(input["value"][0])]
- # end
- # end
- end
-
- end
-
- Print.err inputs.inspect
- end
+ # # resolve randomisation of inputs
+ # def select_inputs
+ # inputs.each do |input|
+ # # TODO TODO
+ # Print.verbose "Input #{input["name"][0]}"
+ # Print.verbose "Rand type: #{input["randomisation_type"][0]}"
+ # case input["randomisation_type"][0]
+ # when "one_from_list"
+ # if input["value"].size == 0
+ # Print.err "Randomisation not possible for #{module_path} (one_from_list with no values)"
+ # exit
+ # end
+ # one_value = [input["value"].shuffle![0]]
+ # input["value"] = one_value
+ # when "flag_value"
+ # # if no value suppied, generate one
+ # unless input["value"]
+ # input["value"] = ["THE_FLAG_IS:#{SecureRandom.hex}"]
+ # else
+ # input["value"] = ["THE_FLAG_IS:#{input["value"][0]}"]
+ # end
+ # when "none"
+ # # nothing...
+ #
+ # end
+ #
+ # # if an encoding is specified
+ # if input["encoding"]
+ # if input["encoding"].size > 1
+ # input["encoding"] = [input["encoding"].shuffle![0]]
+ # else
+ # enc = input["encoding"][0]
+ # end
+ # #
+ # # TODO?? case enc
+ # # when "base64_encode"
+ # # require "base64"
+ # # unless input["value"]
+ # # input["value"] = [Base64.encode64(SecureRandom.hex)]
+ # # else
+ # # input["value"] = [Base64.encode64(input["value"][0])]
+ # # end
+ # # when "MD5_calc_hash"
+ # # unless input["value"]
+ # # input["value"] = [Digest::MD5.hexdigest(SecureRandom.hex)]
+ # # else
+ # # input["value"] = [Digest::MD5.hexdigest(input["value"][0])]
+ # # end
+ # # end
+ # end
+ #
+ # end
+ #
+ # Print.err inputs.inspect
+ # end
# A one directional test for conflicts
# Returns whether this module specifies it conflicts with the other_module.
diff --git a/lib/objects/system.rb b/lib/objects/system.rb
index 8ebd7bf54..9fafb0b4a 100644
--- a/lib/objects/system.rb
+++ b/lib/objects/system.rb
@@ -30,7 +30,7 @@ class System
# for each module specified in the scenario
module_selectors.each do |module_filter|
- selected_modules += select_modules(module_filter.module_type, module_filter.attributes, available_modules, selected_modules)
+ selected_modules += select_modules(module_filter.module_type, module_filter.attributes, available_modules, selected_modules, module_filter.write_outputs_to, module_filter.unique_id)
end
selected_modules
@@ -62,7 +62,7 @@ class System
# returns a list containing a module (plus dependencies recursively) of the module type with the required attributes
# modules are selected from the list of available modules and will be checked against previously selected modules for conflicts
# raises an exception when unable to resolve and the retry limit has not been reached
- def select_modules(module_type, required_attributes, available_modules, previously_selected_modules)
+ def select_modules(module_type, required_attributes, available_modules, previously_selected_modules, write_outputs_to, unique_id)
# select based on selected type, access, cve...
search_list = available_modules.clone
@@ -92,7 +92,18 @@ class System
Print.err 'Could not find a matching module. Please check the scenario specification'
else
# use from the top of the randomised list
- selected = search_list[0]
+ selected = search_list[0].clone
+
+ # propagate module relationships
+ selected.write_outputs_to = write_outputs_to
+ selected.unique_id = unique_id
+
+ # pre-calculate any secgen_local/local.rb outputs
+ if selected.local_calc_file
+ Print.verbose "Module includes local calculation of output. Processing..."
+ selected.output = `#{selected.local_calc_file}`.chomp
+ Print.verbose "Output: #{selected.output}"
+ end
# add any modules that the selected module requires
dependencies = select_required_modules(selected, available_modules, previously_selected_modules + [selected])
@@ -148,7 +159,7 @@ class System
Print.verbose "Dependency satisfied by previously selected module: #{existing.printable_name}"
else
Print.verbose 'Adding required modules...'
- modules_to_add += select_modules('any', required, available_modules, modules_to_add + selected_modules)
+ modules_to_add += select_modules('any', required, available_modules, modules_to_add + selected_modules, '', '')
end
end
modules_to_add
diff --git a/lib/output/xml_report_generator.rb b/lib/output/xml_report_generator.rb
index a33a42600..09e9e0089 100644
--- a/lib/output/xml_report_generator.rb
+++ b/lib/output/xml_report_generator.rb
@@ -40,6 +40,10 @@ class XMLReportGenerator
xml.service(selected_module.attributes_for_scenario_output)
when 'utility'
xml.utility(selected_module.attributes_for_scenario_output)
+ when 'encoder'
+ xml.encoder(selected_module.attributes_for_scenario_output)
+ when 'generator'
+ xml.generator(selected_module.attributes_for_scenario_output)
when 'network'
xml.network(selected_module.attributes_for_scenario_output)
else
diff --git a/lib/readers/module_reader.rb b/lib/readers/module_reader.rb
index ce03f9d75..d019179dc 100644
--- a/lib/readers/module_reader.rb
+++ b/lib/readers/module_reader.rb
@@ -30,6 +30,11 @@ class ModuleReader
return read_modules('generator', GENERATORS_PATH, GENERATOR_SCHEMA_FILE, true)
end
+ # reads in all utilities
+ def self.read_encoders
+ return read_modules('encoder', ENCODERS_PATH, ENCODER_SCHEMA_FILE, true)
+ end
+
# reads in all networks
def self.read_networks
return read_modules('network', NETWORKS_PATH, NETWORK_SCHEMA_FILE, false)
@@ -87,6 +92,12 @@ class ModuleReader
new_module.puppet_file = "#{ROOT_DIR}/#{module_path}/#{module_filename}.pp"
new_module.puppet_other_path = "#{ROOT_DIR}/#{module_path}/manifests"
+ # save executable path of any pre-calculation for outputs
+ local = "#{module_path}#{MODULE_LOCAL_CALC_PATH}"
+ if File.file?(local)
+ new_module.local_calc_file = local
+ end
+
# check that the expected puppet files exist
if require_puppet
unless File.file?("#{new_module.puppet_file}")
diff --git a/lib/readers/system_reader.rb b/lib/readers/system_reader.rb
index 32b0e21b7..3a6c9d422 100644
--- a/lib/readers/system_reader.rb
+++ b/lib/readers/system_reader.rb
@@ -51,7 +51,7 @@ class SystemReader
end
# for each module selection
- system_node.xpath('//vulnerability | //service | //utility | //network | //base | //generator').each do |module_node|
+ system_node.xpath('//vulnerability | //service | //utility | //network | //base | //encoder | //generator').each do |module_node|
# create a selector module, which is a regular module instance used as a placeholder for matching requirements
module_selector = Module.new(module_node.name)
@@ -61,10 +61,7 @@ class SystemReader
module_node.xpath('parent::input').each do |input|
# Parent is input -- needs to send write value somewhere
input.xpath('..').each do |input_parent|
- # Print.verbose " -- Sends output to " + input_parent.path.gsub(/[^a-zA-Z0-9]/, '')
-
- #TODO propagate unique ids and writes to to selected modules
-
+ module_selector.write_to_module_with_id = input_parent.path.gsub(/[^a-zA-Z0-9]/, '')
module_selector.write_outputs_to = input_parent.path.gsub(/[^a-zA-Z0-9]/, '') + '_' + input.xpath('@into').to_s
end
end
@@ -78,11 +75,25 @@ class SystemReader
Print.verbose " - #{attr[0].to_s} ~= #{attr[1].to_s}"
end
end
- if module_selector.write_outputs_to
+
+ # insert into module list
+ # if this module feeds another...
+ if module_selector.write_outputs_to != nil
Print.verbose " -- writes to: " + module_selector.write_outputs_to
+ # insert into module list before the module we are writing to
+ insert_pos = -1 # end of list
+ for i in 0..module_selectors.size-1
+ if module_selector.write_to_module_with_id == module_selectors[i].unique_id
+ # found position of earlier module this one feeds into, so put this one first
+ insert_pos = i
+ end
+ end
+ module_selectors.insert(insert_pos, module_selector)
+ else
+ # otherwise just append module to end of list
+ module_selectors << module_selector
end
- module_selectors << module_selector
end
systems << System.new(system_name, system_attributes, module_selectors)
end
diff --git a/lib/schemas/scenario_schema.xsd b/lib/schemas/scenario_schema.xsd
index 2b8000e33..f260b9e25 100644
--- a/lib/schemas/scenario_schema.xsd
+++ b/lib/schemas/scenario_schema.xsd
@@ -15,10 +15,11 @@
-
-
+
+
-
+
+
@@ -36,8 +37,8 @@
-
-
+
+
@@ -97,7 +98,10 @@
-
+
+
+
+
diff --git a/lib/templates/Vagrantfile.erb b/lib/templates/Vagrantfile.erb
index a672df966..3f1450710 100644
--- a/lib/templates/Vagrantfile.erb
+++ b/lib/templates/Vagrantfile.erb
@@ -10,7 +10,6 @@ VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
<% @systems.each do |system| %>
config.vm.define "<%= system.name %>" do |<%= system.name %>|
- # REMOVE: < %= system.name %>.vm.synced_folder "< %= MOUNT_DIR %>", "/mount"
config.vm.provider :virtualbox do |vb|
vb.gui = true
end
@@ -28,21 +27,25 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
<% else -%>
<%= system.name %>.vm.network :<%= selected_module.attributes['type'][0] %>, ip: "<%= selected_module.attributes['range'][0] %>"
<% end -%>
-<% when 'vulnerability', 'service', 'utility' -%>
+<% when 'vulnerability', 'service', 'utility', 'encoder', 'generator' -%>
<% module_name = selected_module.module_path_name -%>
<%= system.name %>.vm.provision "puppet" do | <%=module_name%> |
+ <%=module_name%>.facter = {
+ "write_to" => "<%=selected_module.write_outputs_to%>",
+<% if selected_module.write_outputs_to -%>
+ "<%=selected_module.write_outputs_to%>" => "<%=selected_module.output%>",
+<% end -%>
+<% if selected_module.attributes['read_fact'] != nil
+ selected_module.attributes['read_fact'].each do |fact| -%>
+ "<%=fact%>_location" => "<%=selected_module.unique_id + "_" + fact%>",
+<% end
+ end -%>
+ }
<%=module_name%>.module_path = "<%="puppet/#{system.name}/modules"%>"
<%=module_name%>.environment_path = "<%="#{ENVIRONMENTS_PATH}"%>"
<%=module_name%>.environment = "production"
<%=module_name%>.manifests_path = "<%="#{ROOT_DIR}/#{selected_module.module_path}/"%>"
<%=module_name%>.manifest_file = "<%="#{selected_module.module_path_end}.pp"%>"
-
- <%=module_name%>.facter = {
-<% selected_module.inputs.each do |input| -%>
- "<%="#{module_name}_#{input["name"][0]}"%>" => <%=input["value"].inspect%>,
-<% end -%>
-
- }
end
<% end -%>
<% end -%>
diff --git a/modules/encoders/string/rot13/lib/facter/customfact.rb b/modules/encoders/string/rot13/lib/facter/customfact.rb
new file mode 100644
index 000000000..358b038a9
--- /dev/null
+++ b/modules/encoders/string/rot13/lib/facter/customfact.rb
@@ -0,0 +1,16 @@
+Facter.add(:rot13_encoded_value) do
+ setcode do
+ # distid = Facter.value(:lsbdistid)
+ # case distid
+ # when /RedHatEnterprise|CentOS|Fedora/
+ # 'redhat'
+ # when 'ubuntu'
+ # 'debian'
+ # else
+ # distid
+ # end
+
+ # Facter::Core::Execution.exec('/bin/uname --hardware-platform')
+ "TEST".tr!("A-Za-z", "N-ZA-Mn-za-m")
+ end
+end
diff --git a/modules/encoders/string/rot13/rot13.pp b/modules/encoders/string/rot13/rot13.pp
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/encoders/string/rot13/secgen_metadata.xml b/modules/encoders/string/rot13/secgen_metadata.xml
new file mode 100644
index 000000000..914331114
--- /dev/null
+++ b/modules/encoders/string/rot13/secgen_metadata.xml
@@ -0,0 +1,48 @@
+
+
+
+ ROT13 Encoder
+ Z. Cliffe Schreuders
+ MIT
+ Rotates each A-Z character by 13 places. Applying a second time reveals the cleartext.
+
+
+ string_encoder
+ linux
+ windows
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ strings_to_encode
+
+ rot13_options
+
+ encoded_strings
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/generators/hello_world/hello_world.pp b/modules/generators/hello_world/hello_world.pp
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/generators/hello_world/lib/facter/generated_strings.rb b/modules/generators/hello_world/lib/facter/generated_strings.rb
new file mode 100644
index 000000000..a201da47a
--- /dev/null
+++ b/modules/generators/hello_world/lib/facter/generated_strings.rb
@@ -0,0 +1,5 @@
+Facter.add(:generated_strings) do
+ setcode do
+ "Hello, world!"
+ end
+end
diff --git a/modules/generators/hello_world/secgen_local/local.rb b/modules/generators/hello_world/secgen_local/local.rb
new file mode 100644
index 000000000..b67462f5f
--- /dev/null
+++ b/modules/generators/hello_world/secgen_local/local.rb
@@ -0,0 +1,3 @@
+#!/usr/bin/ruby
+
+puts "Hello, world!"
diff --git a/modules/generators/hello_world/secgen_metadata.xml b/modules/generators/hello_world/secgen_metadata.xml
new file mode 100644
index 000000000..25a84614c
--- /dev/null
+++ b/modules/generators/hello_world/secgen_metadata.xml
@@ -0,0 +1,19 @@
+
+
+
+ Hello, World! Generator
+ Z. Cliffe Schreuders
+ MIT
+ Generates a simple "Hello, world!" message.
+
+
+ string_generator
+ local_calculation
+ linux
+ windows
+
+ generated_strings
+
+
\ No newline at end of file
diff --git a/modules/generators/random_base64/random_base64.pp b/modules/generators/random_base64/random_base64.pp
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/generators/random_base64/secgen_local/local.rb b/modules/generators/random_base64/secgen_local/local.rb
new file mode 100644
index 000000000..6543e8609
--- /dev/null
+++ b/modules/generators/random_base64/secgen_local/local.rb
@@ -0,0 +1,3 @@
+#!/usr/bin/ruby
+require 'securerandom'
+puts SecureRandom.base64
diff --git a/modules/generators/random_base64/secgen_metadata.xml b/modules/generators/random_base64/secgen_metadata.xml
new file mode 100644
index 000000000..a32ff38f1
--- /dev/null
+++ b/modules/generators/random_base64/secgen_metadata.xml
@@ -0,0 +1,20 @@
+
+
+
+ Random Base64 Generator
+ Z. Cliffe Schreuders
+ MIT
+ Uses Ruby's SecureRandom to generate a message made up of base64 digits (A-Z, a-z, 0-9, +, / and =).
+
+ string_generator
+ local_calculation
+ linux
+ windows
+
+ http://ruby-doc.org/stdlib-2.2.2/libdoc/securerandom/rdoc/SecureRandom.html#method-c-base64
+
+ generated_strings
+
+
\ No newline at end of file
diff --git a/modules/generators/random_hex/random_hex.pp b/modules/generators/random_hex/random_hex.pp
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/generators/random_hex/secgen_local/local.rb b/modules/generators/random_hex/secgen_local/local.rb
new file mode 100644
index 000000000..bb0e850de
--- /dev/null
+++ b/modules/generators/random_hex/secgen_local/local.rb
@@ -0,0 +1,3 @@
+#!/usr/bin/ruby
+require 'securerandom'
+puts SecureRandom.hex
diff --git a/modules/generators/random_hex/secgen_metadata.xml b/modules/generators/random_hex/secgen_metadata.xml
new file mode 100644
index 000000000..36fff87ef
--- /dev/null
+++ b/modules/generators/random_hex/secgen_metadata.xml
@@ -0,0 +1,20 @@
+
+
+
+ Random Hex Generator
+ Z. Cliffe Schreuders
+ MIT
+ Uses Ruby's SecureRandom to generate a message made up of hex digits (a-f0-9).
+
+ string_generator
+ local_calculation
+ linux
+ windows
+
+ http://ruby-doc.org/stdlib-2.2.2/libdoc/securerandom/rdoc/SecureRandom.html#method-c-hex
+
+ generated_strings
+
+
\ No newline at end of file
diff --git a/secgen.rb b/secgen.rb
index 848652f81..f67349d93 100644
--- a/secgen.rb
+++ b/secgen.rb
@@ -58,13 +58,17 @@ def build_config(scenario, out_dir)
all_available_generators = ModuleReader.read_generators
Print.std "#{all_available_generators.size} generator modules loaded"
+ Print.info 'Reading available encoder modules...'
+ all_available_encoders = ModuleReader.read_encoders
+ Print.std "#{all_available_encoders.size} encoder modules loaded"
+
Print.info 'Reading available network modules...'
all_available_networks = ModuleReader.read_networks
Print.std "#{all_available_networks.size} network modules loaded"
Print.info 'Resolving systems: randomising scenario...'
# for each system, select modules
- all_available_modules = all_available_bases + all_available_vulnerabilties + all_available_services + all_available_utilities + all_available_generators + all_available_networks
+ all_available_modules = all_available_bases + all_available_vulnerabilties + 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)