mirror of
https://github.com/cliffe/SecGen.git
synced 2026-02-20 13:50:45 +00:00
Output files -- admin passwords for vms, IP addresses, CyBOK per flag challenge and project, and simplified hints
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
# datastore related global variables
|
||||
$datastore = {}
|
||||
$datastore_iterators = {} # keeps track of previous access to datastore elements datastorevariablename => prev_index_accessed
|
||||
$cybok_coverage = [] # array of XML nodes
|
||||
|
||||
## FILE / DIR CONSTANTS ##
|
||||
|
||||
@@ -55,8 +56,12 @@ SAMP_DBS_DIR = "#{ROOT_DIR}/lib/resources/sample_databases"
|
||||
LOCAL_PUPPET_DIR = "#{MODULES_DIR}build/puppet"
|
||||
SECGEN_FUNCTIONS_PUPPET_DIR = "#{MODULES_DIR}build/puppet/secgen_functions"
|
||||
|
||||
# Filename for flags
|
||||
# Filename for flags, etc
|
||||
FLAGS_FILENAME = "flag_hints.xml"
|
||||
CYBOK_FILENAME = "cybok.xml"
|
||||
SPOILER_ADMIN_FILENAME = "spoiler_admin_pass"
|
||||
IP_ADDRESSES_FILENAME = "IP_addresses.json"
|
||||
|
||||
|
||||
## PACKER CONSTANTS ##
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ class Module
|
||||
|
||||
attr_accessor :conflicts
|
||||
attr_accessor :requires
|
||||
attr_accessor :cybok_coverage # a list of xml doc nodes
|
||||
attr_accessor :puppet_file
|
||||
attr_accessor :puppet_other_path
|
||||
attr_accessor :local_calc_file
|
||||
@@ -34,6 +35,7 @@ class Module
|
||||
self.module_type = module_type
|
||||
self.conflicts = []
|
||||
self.requires = []
|
||||
self.cybok_coverage = []
|
||||
self.attributes = {}
|
||||
self.output = []
|
||||
self.write_to_module_with_id = write_output_variable = ''
|
||||
@@ -96,13 +98,17 @@ class Module
|
||||
def attributes_for_scenario_output
|
||||
attr_flattened = {}
|
||||
|
||||
attributes.each do |key, array|
|
||||
unless "#{key}" == 'module_type' || "#{key}" == 'conflict' || "#{key}" == 'default_input' || "#{key}" == 'requires'
|
||||
# creates a valid regexp that can match the original module
|
||||
attr_flattened["#{key}"] = Regexp.escape(array.join('~~~')).gsub(/\n\w*/, '.*').gsub(/\\ /, ' ').gsub(/~~~/, '|')
|
||||
end
|
||||
end
|
||||
# this alternative approach populates all the filters in the generated senario,
|
||||
# but this means that changes to the metadata breaks the selection
|
||||
# attributes.each do |key, array|
|
||||
# unless "#{key}" == 'module_type' || "#{key}" == 'conflict' || "#{key}" == 'default_input' || "#{key}" == 'requires'
|
||||
# # creates a valid regexp that can match the original module
|
||||
# attr_flattened["#{key}"] = Regexp.escape(array.join('~~~')).gsub(/\n\w*/, '.*').gsub(/\\ /, ' ').gsub(/~~~/, '|')
|
||||
# end
|
||||
# end
|
||||
|
||||
# just specify modules by their path
|
||||
attr_flattened["module_path"] = Regexp.escape(attributes["module_path"][0])
|
||||
attr_flattened
|
||||
end
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ require 'erb'
|
||||
require_relative '../helpers/constants.rb'
|
||||
require_relative 'xml_scenario_generator.rb'
|
||||
require_relative 'xml_marker_generator.rb'
|
||||
require_relative 'xml_cybok_generator.rb'
|
||||
require_relative 'ctfd_generator.rb'
|
||||
require 'fileutils'
|
||||
require 'librarian'
|
||||
require 'zip/zip'
|
||||
require 'json'
|
||||
|
||||
class ProjectFilesCreator
|
||||
# Creates project directory, uses .erb files to create a report and the vagrant file that will be used
|
||||
@@ -127,16 +129,25 @@ class ProjectFilesCreator
|
||||
Print.std "Creating scenario definition file: #{xfile}"
|
||||
write_data_to_file(xml, xfile)
|
||||
|
||||
write_data_to_file(@systems.to_s, "#{@out_dir}/systems")
|
||||
write_data_to_file(@scenario.to_s, "#{@out_dir}/scenario")
|
||||
|
||||
|
||||
# Create the marker xml file
|
||||
x2file = "#{@out_dir}/#{FLAGS_FILENAME}"
|
||||
|
||||
xml_marker_generator = XmlMarkerGenerator.new(@systems, @scenario, @time)
|
||||
xml = xml_marker_generator.output
|
||||
Print.std "Creating flags and hints file: #{x2file}"
|
||||
write_data_to_file(xml, x2file)
|
||||
|
||||
Print.std "Saving spoiler/admin records..."
|
||||
# Create the CyBOK xml file
|
||||
x3file = "#{@out_dir}/#{CYBOK_FILENAME}"
|
||||
xml_cybok_generator = XmlCybokGenerator.new(@systems, @scenario, @time)
|
||||
xml = xml_cybok_generator.output
|
||||
Print.std "Creating flags and hints file: #{x3file}"
|
||||
write_data_to_file(xml, x3file)
|
||||
|
||||
Print.std "Saving spoiler/admin records..."
|
||||
jfile = "#{@out_dir}/datastores"
|
||||
Print.std "Saving datastore records: #{jfile}"
|
||||
json = JSON.generate($datastore)
|
||||
@@ -145,14 +156,14 @@ class ProjectFilesCreator
|
||||
if $datastore.has_key? "IP_addresses"
|
||||
system_names = @systems.map { |system| system.name }
|
||||
system_ips = Hash[system_names.zip($datastore["IP_addresses"])]
|
||||
jfile = "#{@out_dir}/IP_addresses.json"
|
||||
jfile = "#{@out_dir}/#{IP_ADDRESSES_FILENAME}"
|
||||
Print.std "Saving IP addresses: #{jfile}"
|
||||
json = JSON.generate(system_ips)
|
||||
write_data_to_file(json, jfile)
|
||||
end
|
||||
|
||||
if $datastore.has_key? "spoiler_admin_pass"
|
||||
pfile = "#{@out_dir}/spoiler_admin_pass"
|
||||
pfile = "#{@out_dir}/#{SPOILER_ADMIN_FILENAME}"
|
||||
Print.std "Saving spoiler/admin passwords: #{pfile}"
|
||||
pass_notes = $datastore["spoiler_admin_pass"].join("\n")
|
||||
write_data_to_file(pass_notes, pfile)
|
||||
|
||||
30
lib/output/xml_cybok_generator.rb
Normal file
30
lib/output/xml_cybok_generator.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
require 'nokogiri'
|
||||
# Convert systems objects into xml
|
||||
class XmlCybokGenerator
|
||||
|
||||
# @param [Object] systems the list of systems
|
||||
# @param [Object] scenario the scenario file used to generate
|
||||
# @param [Object] time the current time as a string
|
||||
def initialize(systems, scenario, time)
|
||||
@systems = systems
|
||||
@scenario = scenario
|
||||
@time = time
|
||||
end
|
||||
|
||||
# outputs a XML CyBOK file that can be used to track CyBOK
|
||||
# even for randomised challenges, where CyBOK is defined per module
|
||||
# @return [Object] xml string
|
||||
def output
|
||||
# $cybok_coverage starts with the cybok from the scenario, and then we also
|
||||
# add all the cybok from modules that are selected
|
||||
@systems.each { |system|
|
||||
system.module_selections.each { |selected_module|
|
||||
$cybok_coverage.push *selected_module.cybok_coverage
|
||||
}
|
||||
}
|
||||
coverage = "<cybokmapping>" + $cybok_coverage.map { |c| "\n " + c.to_xml.gsub(/\R/, "\n ").gsub(/\t/, ' ') }.uniq.join("\n") + "\n " + "</cybokmapping>"
|
||||
|
||||
doc = Nokogiri.XML(coverage)
|
||||
doc.to_xml
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
require 'nokogiri'
|
||||
|
||||
require 'irb'
|
||||
# Convert systems objects into xml
|
||||
class XmlMarkerGenerator
|
||||
|
||||
@@ -15,6 +15,7 @@ class XmlMarkerGenerator
|
||||
# outputs a XML marker file that can be used to mark flags and provide hints
|
||||
# @return [Object] xml string
|
||||
def output
|
||||
@processed_hints = []
|
||||
ns = {
|
||||
'xmlns' => "http://www.github/cliffe/SecGen/marker",
|
||||
'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
|
||||
@@ -37,10 +38,16 @@ class XmlMarkerGenerator
|
||||
# flag has to be the only thing in the parameter string (not within some text)
|
||||
if output_value.match(/\Aflag{.*\z/)
|
||||
xml.challenge{
|
||||
xml.flag(output_value)
|
||||
|
||||
system.module_selections.each { |search_module|
|
||||
if search_module.unique_id == selected_module.write_to_module_with_id
|
||||
# special case check for flag that's fed into a parameter that isn't defined within the receiving module
|
||||
if search_module.attributes["read_fact"].include? selected_module.write_output_variable
|
||||
xml.flag(output_value)
|
||||
else
|
||||
Print.warn "Ignoring flag generated but fed into a fact that the module doesn't read: #{selected_module.write_to_module_with_id}.#{selected_module.write_output_variable} #{output_value}"
|
||||
Print.warn "This likely isn't an issue, especially if fed into strings_to_pre_leak which doesn't always exist"
|
||||
end
|
||||
module_hints(search_module, xml, system.module_selections)
|
||||
end
|
||||
}
|
||||
@@ -69,6 +76,10 @@ class XmlMarkerGenerator
|
||||
}
|
||||
end
|
||||
|
||||
if search_module.cybok_coverage&.size > 0
|
||||
add_cybok(search_module, xml)
|
||||
end
|
||||
|
||||
case search_module.module_type
|
||||
when "vulnerability"
|
||||
case search_module.attributes['access'].first
|
||||
@@ -130,10 +141,22 @@ class XmlMarkerGenerator
|
||||
end
|
||||
|
||||
def add_hint(hint_text, hint_id, hint_type, xml)
|
||||
xml.hint {
|
||||
xml.hint_text(hint_text)
|
||||
xml.hint_type(hint_type)
|
||||
xml.hint_id(hint_id)
|
||||
# due to the nested structure of components a specific hint may lead to
|
||||
# multiple next steps -- but we just record each hint once to simplify things
|
||||
# without this condition, the same hint will appear multiple times
|
||||
unless @processed_hints.include? hint_id
|
||||
@processed_hints << hint_id
|
||||
xml.hint {
|
||||
xml.hint_text(hint_text)
|
||||
xml.hint_type(hint_type)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def add_cybok(search_module, xml)
|
||||
xml.cybok_coverage {
|
||||
# quick and dirty conversion of saved nodes back to tidy xml
|
||||
xml << search_module.cybok_coverage.map { |c| "\n " + c.to_xml.gsub(/\R/, "\n ").gsub(/\t/, ' ') }.join("\n") + "\n "
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -160,6 +160,11 @@ class ModuleReader < XMLReader
|
||||
new_module.requires.push(require)
|
||||
end
|
||||
|
||||
# for each CyBOK in the module -- we just store the xml node for later
|
||||
doc.xpath("/#{module_type}/CyBOK").each do |cybok_doc|
|
||||
new_module.cybok_coverage.push(cybok_doc.clone)
|
||||
end
|
||||
|
||||
# for each default input
|
||||
doc.xpath("/#{module_type}/default_input").each do |inputs_doc|
|
||||
inputs_doc.xpath('descendant::vulnerability | descendant::service | descendant::utility | descendant::network | descendant::base | descendant::encoder | descendant::generator').each do |module_node|
|
||||
@@ -219,4 +224,4 @@ class ModuleReader < XMLReader
|
||||
return modules
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -16,6 +16,11 @@ class SystemReader < XMLReader
|
||||
# Parse and validate the schema
|
||||
doc = parse_doc(scenario_file, SCENARIO_SCHEMA_FILE, 'scenario')
|
||||
|
||||
# for each CyBOK in the module
|
||||
doc.xpath("/scenario/CyBOK").each do |cybok_doc|
|
||||
$cybok_coverage.push(cybok_doc.clone)
|
||||
end
|
||||
|
||||
doc.xpath('/scenario/system').each_with_index do |system_node, system_index|
|
||||
module_selectors = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user