datastores for storing and reusing calculated values

This commit is contained in:
Z. Cliffe Schreuders
2017-01-07 21:21:17 +00:00
parent ab8ff07201
commit f8ba19ad75
9 changed files with 159 additions and 7 deletions

View File

@@ -1,3 +1,6 @@
# ONE global variable
$datastore = {}
## FILE / DIR CONSTANTS ##
# Root directory of SecGen file structure

View File

@@ -12,10 +12,12 @@ class Module
# XML validity ensures valid and complete information.
attr_accessor :write_to_module_with_id # the module instance that this module writes to
attr_accessor :write_to_datastore # the datastore to store the result to
attr_accessor :write_output_variable # the variable/fact written to
attr_accessor :output # the result of local processing
attr_accessor :unique_id # the unique id for this module *instance*
attr_accessor :received_inputs # any locally calculated inputs fed into this module instance
attr_accessor :received_datastores # any datastores to be fed into this module instance
attr_accessor :conflicts
attr_accessor :requires
@@ -35,6 +37,7 @@ class Module
self.output = []
self.write_to_module_with_id = write_output_variable = ''
self.received_inputs = {}
self.received_datastores = {}
self.default_inputs_selectors = {}
self.default_inputs_literals = {}

View File

@@ -32,7 +32,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, module_filter.unique_id, module_filter.write_output_variable, module_filter.write_to_module_with_id, module_filter.received_inputs, module_filter.default_inputs_literals)
selected_modules += select_modules(module_filter.module_type, module_filter.attributes, available_modules, selected_modules, module_filter.unique_id, module_filter.write_output_variable, module_filter.write_to_module_with_id, module_filter.received_inputs, module_filter.default_inputs_literals, module_filter.write_to_datastore, module_filter.received_datastores)
end
selected_modules
@@ -64,7 +64,7 @@ class System
# returns a list containing a module (plus any default input modules and 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, unique_id, write_outputs_to, write_to_module_with_id, received_inputs, default_inputs_literals)
def select_modules(module_type, required_attributes, available_modules, previously_selected_modules, unique_id, write_outputs_to, write_to_module_with_id, received_inputs, default_inputs_literals, write_to_datastore, received_datastores)
default_modules_to_add = []
search_list = available_modules.clone
@@ -101,10 +101,23 @@ class System
# propagate module relationships established when the filter was created
selected.write_output_variable = write_outputs_to
selected.write_to_module_with_id = write_to_module_with_id
selected.write_to_datastore = write_to_datastore
selected.unique_id = unique_id
selected.received_inputs = received_inputs
selected.received_datastores = received_datastores
selected.default_inputs_literals = selected.default_inputs_literals.merge(default_inputs_literals)
# feed in input from any received datastores
if selected.received_datastores != {}
Print.verbose "Receiving datastores: #{selected.received_datastores}"
selected.received_datastores.each do |input_key, datastore_value|
datastore_value.each do |datastore|
(received_inputs[input_key] ||=[]).push(*$datastore[datastore])
Print.verbose "Adding #{input_key} - #{datastore} (#{$datastore[datastore]})"
end
end
end
# if no input received from the scenario, apply default input values/modules
default_modules_to_add += select_default_modules(selected, available_modules, previously_selected_modules + [selected])
@@ -131,6 +144,12 @@ class System
selected.output = JSON.parse(`ruby #{selected.local_calc_file} #{args_string}`.chomp)
end
# store the output of the module into a datastore, if specified
if selected.write_to_datastore && selected.write_to_datastore.size != 0
($datastore[selected.write_to_datastore] ||=[]).push(*selected.output)
Print.verbose "Storing into datastore: #{selected.write_to_datastore} = #{$datastore[selected.write_to_datastore].to_s}"
end
# add any modules that the selected module requires
dependencies = select_required_modules(selected, available_modules, previously_selected_modules + default_modules_to_add + [selected])
end
@@ -178,7 +197,7 @@ class System
default_module_selectors_to_add.each do |module_to_add|
module_to_add.write_to_module_with_id = selected.unique_id
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, module_to_add.unique_id, module_to_add.write_output_variable, module_to_add.write_to_module_with_id, module_to_add.received_inputs, module_to_add.default_inputs_literals)
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, module_to_add.unique_id, module_to_add.write_output_variable, module_to_add.write_to_module_with_id, module_to_add.received_inputs, module_to_add.default_inputs_literals, module_to_add.write_to_datastore, module_to_add.received_datastores)
end
end
else
@@ -230,7 +249,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

@@ -50,6 +50,19 @@ class SystemReader
system_attributes["#{attr.name}"] = attr.text unless attr.text.nil? || attr.text == ''
end
# literal values to store directly in a datastore
system_node.xpath('*[@into_datastore]/value').each do |value|
name = value.xpath('../@into_datastore').to_s
($datastore[name] ||= []).push(value.text)
end
# datastore in a datastore
if system_node.xpath('//*[@into_datastore]/datastore').to_s != ""
Print.err "WARNING: a datastore cannot capture the values from another datastore (this will be ignored)"
Print.err "The scenario has datastore(s) that try to save directly into another datastore -- currently this is only possible via an encoder"
sleep 2
end
# for each module selection
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
@@ -60,10 +73,18 @@ class SystemReader
# check if we need to be sending the module output to another module
module_node.xpath('parent::input').each do |input|
# Parent is input -- track that we need to send write value somewhere
input.xpath('..').each do |input_parent|
module_selector.write_output_variable = input.xpath('@into').to_s
module_selector.write_to_module_with_id = input_parent.path.gsub(/[^a-zA-Z0-9]/, '')
# if we need to feed results to parent module
if input.xpath('@into').to_s
input.xpath('..').each do |input_parent|
module_selector.write_output_variable = input.xpath('@into').to_s
module_selector.write_to_module_with_id = input_parent.path.gsub(/[^a-zA-Z0-9]/, '')
end
end
# check if we need to send the module output to a datastore
if input.xpath('@into_datastore').to_s
module_selector.write_to_datastore = input.xpath('@into_datastore').to_s
end
end
# check if we are being passed an input *literal value*
@@ -74,6 +95,14 @@ class SystemReader
(module_selector.received_inputs[variable] ||= []).push(value)
end
# check if we are being passed a datastore as input
module_node.xpath('input/datastore').each do |input_value|
variable = input_value.xpath('../@into').to_s
value = input_value.text
Print.verbose " -- datastore: #{variable} = #{value}"
(module_selector.received_datastores[variable] ||= []).push(value)
end
module_node.xpath('@*').each do |attr|
module_selector.attributes["#{attr.name}"] = [attr.text] unless attr.text.nil? || attr.text == ''

View File

@@ -21,9 +21,11 @@
<xs:element name='generator' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='encoder' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='value' type='xs:string' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='datastore' type='xs:string' minOccurs='0' maxOccurs='unbounded' />
</xs:choice>
</xs:sequence>
<xs:attribute name='into' type='xs:string'/>
<xs:attribute name='into_datastore' type='xs:string'/>
</xs:complexType>
<xs:complexType name="SystemType">
@@ -37,6 +39,7 @@
</xs:element>
<xs:element name='base' type='BaseType' minOccurs='1' maxOccurs='1' />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="input" type="InputElements" minOccurs="0" maxOccurs="unbounded" />
<xs:element name='vulnerability' type='VulnerabilityType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='service' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />
<xs:element name='utility' type='ServiceUtilityEncoderGeneratorType' minOccurs='0' maxOccurs='unbounded' />

View File

@@ -33,6 +33,9 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
end %>
end
# SecGen datastore
# <%= JSON.generate($datastore) %>
# SecGen modules
<% system.module_selections.each do |selected_module| -%>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<scenario xmlns="http://www.github/cliffe/SecGen/scenario"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/scenario">
<system>
<system_name>example_server</system_name>
<base platform="linux"/>
<input into_datastore="business_name">
<value>BusinessName1</value>
</input>
<!--select a vulnerability that leaks strings-->
<vulnerability read_fact="strings_to_leak">
<!--direct the output from below into strings_to_leak-->
<input into="strings_to_leak" into_datastore="flags">
<datastore>business_name</datastore>
<generator type="flag_generator" />
</input>
</vulnerability>
<network type="private_network" range="dhcp"/>
</system>
</scenario>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0"?>
<scenario xmlns="http://www.github/cliffe/SecGen/scenario"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/scenario">
<system>
<system_name>example_server</system_name>
<base platform="linux"/>
<input into_datastore="business_name">
<value>BusinessName1</value>
</input>
<input into_datastore="username">
<generator type="username_generator" />
<generator type="username_generator" />
<value>cliffe</value>
</input>
<!--select a vulnerability that leaks strings-->
<vulnerability read_fact="strings_to_leak">
<!--direct the output from below into strings_to_leak-->
<input into="strings_to_leak" into_datastore="flags">
<datastore>business_name</datastore>
<datastore>username</datastore>
<generator type="flag_generator" />
</input>
</vulnerability>
<network type="private_network" range="dhcp"/>
</system>
</scenario>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0"?>
<scenario xmlns="http://www.github/cliffe/SecGen/scenario"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.github/cliffe/SecGen/scenario">
<system>
<system_name>example_server</system_name>
<base platform="linux"/>
<!--select a vulnerability that leaks strings-->
<vulnerability read_fact="strings_to_leak">
<!--direct the output from below into strings_to_leak-->
<input into="strings_to_leak" into_datastore="flags">
<generator type="flag_generator" />
<generator type="flag_generator" />
</input>
</vulnerability>
<!--select a vulnerability that leaks strings-->
<vulnerability read_fact="strings_to_leak">
<!--direct the output from below into strings_to_leak-->
<input into="strings_to_leak" into_datastore="flags">
<generator type="flag_generator" />
<generator type="flag_generator" />
</input>
</vulnerability>
<network type="private_network" range="dhcp"/>
</system>
</scenario>