Parameterisation

This commit is contained in:
Z. Cliffe Schreuders
2016-08-17 23:10:31 +01:00
parent 0185837c7b
commit edc1e28d68
22 changed files with 282 additions and 88 deletions

View File

@@ -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"

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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}")

View File

@@ -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

View File

@@ -15,10 +15,11 @@
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name='vulnerability' type='VulnerabilityType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='service' type='ServiceUtilityGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='utility' type='ServiceUtilityGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='service' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='utility' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='network' type='NetworkType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='generator' type='ServiceUtilityGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='generator' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='encoder' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
</xs:choice>
</xs:sequence>
<xs:attribute name='into' type='xs:string'/>
@@ -36,8 +37,8 @@
<xs:element name='base' type='BaseType' minOccurs='1' maxOccurs='1' />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name='vulnerability' type='VulnerabilityType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='service' type='ServiceUtilityGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='utility' type='ServiceUtilityGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='service' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='utility' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='network' type='NetworkType' minOccurs='0' maxOccurs='unbounded' />
</xs:choice>
</xs:sequence>
@@ -97,7 +98,10 @@
<xs:attribute name="solution" type="xs:string"/>
</xs:complexType>
<xs:complexType name="ServiceUtilityGeneratorType">
<xs:complexType name="ServiceUtilityEncoderGeneratorType">
<xs:sequence>
<xs:element name="input" type="InputElements" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="module_path" type="xs:string"/>
<xs:attribute name="name" type="xs:string"/>

View File

@@ -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 -%>

View File

@@ -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

View File

View File

@@ -0,0 +1,48 @@
<?xml version="1.0"?>
<encoder xmlns="http://www.github/cliffe/SecGen/encoder"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/encoder">
<name>ROT13 Encoder</name>
<author>Z. Cliffe Schreuders</author>
<module_license>MIT</module_license>
<description>Rotates each A-Z character by 13 places. Applying a second time reveals the cleartext.
</description>
<type>string_encoder</type>
<platform>linux</platform>
<platform>windows</platform>
<!--optional encoder details-->
<!--<difficulty>low</difficulty>-->
<!-- <reference>...</reference> -->
<!-- <software_name></software_name>
<software_license>GPL</software_license>-->
<!--optional hints-->
<!--TODO <hint>A simple transform</hint>-->
<!--<solution>Run rot13 on the cyphertext</solution>-->
<!-- <read_fact_required>strings_to_encode</read_fact>
<read_fact_optional>rot13_options</read_fact>-->
<!--<read_fact valid_match=".*" default_value="You found a message">-->
<read_fact>strings_to_encode</read_fact>
<!--valid_match="retain_caps|lower|upper" default_value="retain_caps"-->
<read_fact>rot13_options</read_fact>
<write_fact>encoded_strings</write_fact>
<!-- ensure a module has prepared input -->
<!-- <require>
<write_fact>strings_to_encode</write_fact>
</require>-->
<!--Cannot co-exist with other installations-->
<!-- <conflict>
<software_name>proftpd</software_name>
</conflict>-->
</encoder>

View File

@@ -0,0 +1,5 @@
Facter.add(:generated_strings) do
setcode do
"Hello, world!"
end
end

View File

@@ -0,0 +1,3 @@
#!/usr/bin/ruby
puts "Hello, world!"

View File

@@ -0,0 +1,19 @@
<?xml version="1.0"?>
<generator xmlns="http://www.github/cliffe/SecGen/generator"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/generator">
<name>Hello, World! Generator</name>
<author>Z. Cliffe Schreuders</author>
<module_license>MIT</module_license>
<description>Generates a simple "Hello, world!" message.
</description>
<type>string_generator</type>
<type>local_calculation</type>
<platform>linux</platform>
<platform>windows</platform>
<write_fact>generated_strings</write_fact>
</generator>

View File

@@ -0,0 +1,3 @@
#!/usr/bin/ruby
require 'securerandom'
puts SecureRandom.base64

View File

@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<generator xmlns="http://www.github/cliffe/SecGen/generator"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/generator">
<name>Random Base64 Generator</name>
<author>Z. Cliffe Schreuders</author>
<module_license>MIT</module_license>
<description>Uses Ruby's SecureRandom to generate a message made up of base64 digits (A-Z, a-z, 0-9, +, / and =).</description>
<type>string_generator</type>
<type>local_calculation</type>
<platform>linux</platform>
<platform>windows</platform>
<reference>http://ruby-doc.org/stdlib-2.2.2/libdoc/securerandom/rdoc/SecureRandom.html#method-c-base64</reference>
<write_fact>generated_strings</write_fact>
</generator>

View File

@@ -0,0 +1,3 @@
#!/usr/bin/ruby
require 'securerandom'
puts SecureRandom.hex

View File

@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<generator xmlns="http://www.github/cliffe/SecGen/generator"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/generator">
<name>Random Hex Generator</name>
<author>Z. Cliffe Schreuders</author>
<module_license>MIT</module_license>
<description>Uses Ruby's SecureRandom to generate a message made up of hex digits (a-f0-9).</description>
<type>string_generator</type>
<type>local_calculation</type>
<platform>linux</platform>
<platform>windows</platform>
<reference>http://ruby-doc.org/stdlib-2.2.2/libdoc/securerandom/rdoc/SecureRandom.html#method-c-hex</reference>
<write_fact>generated_strings</write_fact>
</generator>

View File

@@ -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)