Files
SecGen/system.rb

314 lines
9.5 KiB
Ruby

require 'nokogiri'
# assign constants
ROOT_DIR = File.dirname(__FILE__)
BOXES_XML = "#{ROOT_DIR}/lib/xml/boxes.xml"
NETWORKS_XML = "#{ROOT_DIR}/lib/xml/networks.xml"
VULN_XML = "#{ROOT_DIR}/lib/xml/vulns.xml"
SERVICES_XML = "#{ROOT_DIR}/lib/xml/services.xml"
BASE_XML = "#{ROOT_DIR}/lib/xml/bases.xml"
MOUNT_DIR = "#{ROOT_DIR}/mount/"
class System
# can access from outside of class
attr_accessor :id, :os, :url,:basebox, :networks, :vulns, :services
#initalizes system variables
def initialize(id, os, basebox, url, vulns=[], networks=[], services=[])
@id = id
@os = os
@url = url
@basebox = basebox
@vulns = vulns
@networks = networks
@services = services
end
def is_valid_base
valid_base = Conf.bases
valid_base.each do |b|
if @basebox == b.vagrantbase
@url = b.url
return true
end
end
return false
end
end
class Network
attr_accessor :name, :range
def initialize(name="", range="")
@name = name
@range = range
end
def id
hash = @name + @range
return hash
# return string that connects everything to 1 massive string
end
def eql? other
# checks if name matches networks.xml from boxes.xml
other.kind_of?(self.class) && @name == other.name
end
def hash
@type.hash
end
end
class Service
attr_accessor :name, :type, :details, :puppets
def initialize(name="", type="", details="", puppets=[])
@name = name
@type = type
@details = details
@puppets = puppets
end
def eql? other
other.kind_of?(self.class) && @type == other.type
end
def hash
@type.hash
end
def id
return @type
end
end
class ServiceManager
# secure services are randomly selected from the definitions in services.xml (secure_services)
# based on the attributes optionally specified in boxes.xml (want_services)
# However, if the service type has already had a vulnerability assigned (selected_vulns), it is ignored here
def self.process(want_services, secure_services, selected_vulns=[])
return_services = {}
legal_services = secure_services.clone
wanted_services = want_services.clone
# remove all services that have already been selected as vulns (from both the wanted and secure lists)
selected_vulns.each do |a_vuln|
legal_services.delete_if{|x| x.type == a_vuln.type}
wanted_services.delete_if{|x| x.type == a_vuln.type}
end
wanted_services.each do |service_query|
# select based on selected type...
# copy services array
search_list = legal_services.clone
# shuffle order of available secure services
search_list.shuffle!
# remove all the services that don't match the current selection (type, etc)
if service_query.type.length > 0
puts "Searching for service matching type: " + service_query.type
search_list.delete_if{|x| x.type != service_query.type}
end
if search_list.length == 0
STDERR.puts "Matching service was not found please check the xml boxes.xml"
STDERR.puts "(note: you can only have one of each type of service per system)"
exit
else
# use from the top of the top of the randomised list
return_services[service_query.id] = search_list[0]
if search_list[0].type.length > 0
puts "Selected secure service : " + search_list[0].type
end
# enforce only one of any service type (remove from available)
legal_services.delete_if{|x| x.type == service_query.type}
end
end
return return_services.values
end
end
class NetworkManager
# the user will either specify a blank network type or a knownnetwork type
def self.process(networks,valid_network)
new_networks = {}
# intersection of valid networks / user defined networks
legal_networks = valid_network & networks
networks.each do |network|
# checks to see string is blank if so valid network into a new hash map of vulnerabilities
if network.name == ""
random = valid_network.sample
new_networks[random.id] = random
else
has_found = false
# shuffle randomly selects first match
legal_networks.shuffle.each do |valid|
if network.name == valid.name
network.range = valid.range unless not network.range.empty?
# valid network into a new hash map of networks
new_networks[network.id] = network
has_found = true
break
end
end
if not has_found
p "Network was not found please check the xml boxes.xml"
exit
end
end
end
return new_networks.values
end
end
class Basebox
attr_accessor :name, :os, :distro, :vagrantbase, :url
end
class BaseManager
def self.generate_base(system,bases)
# takes a sample from bases.xml and then assigns it to system
box = bases.sample
system.basebox = box.vagrantbase
system.url = box.url
return system
end
end
class Vulnerability
attr_accessor :type, :privilege, :access ,:puppets, :details, :ports, :cve
def eql? other
# checks if type matches vulns.xml from boxes.xml
other.kind_of?(self.class) && @type == other.type
end
def hash
@type.hash
end
def initialize(type="", privilege="", access="", puppets=[], details="", ports=[], cve="")
@type = type
@privilege = privilege
@access = access
@puppets = puppets
@details = details
@ports = ports
@cve = cve
end
def id
return @type + @privilege + @access
end
end
class VulnerabilityManager
# vulnerabilities are randomly selected from the definitions in vulns.xml (all_vulns)
# based on the attributes optionally specified in boxes.xml (want_vulns)
def self.process(want_vulns, all_vulns)
return_vulns = {}
legal_vulns = all_vulns.clone
want_vulns.each do |vulnerability_query|
# select based on selected type, access, cve...
# copy vulns array
search_list = legal_vulns.clone
# shuffle order of available vulnerabilities
search_list.shuffle!
# remove all the vulns that don't match the current selection (type, etc)
if vulnerability_query.type.length > 0
puts "Searching for vulnerability matching type: " + vulnerability_query.type
search_list.delete_if{|x| x.type != vulnerability_query.type}
end
if vulnerability_query.access.length > 0
puts "Searching for vulnerability matching access: " + vulnerability_query.access
search_list.delete_if{|x| x.access != vulnerability_query.access}
end
if vulnerability_query.cve.length > 0
puts "Searching for vulnerability matching CVE: " + vulnerability_query.cve
search_list.delete_if{|x| x.cve != vulnerability_query.cve}
end
if search_list.length == 0
STDERR.puts "Matching vulnerability was not found please check the xml boxes.xml"
STDERR.puts "(note: you can only have one of each type of vulnerability per system)"
exit
else
# use from the top of the top of the randomised list
return_vulns[vulnerability_query.id] = search_list[0]
if search_list[0].type.length > 0
puts "Selected vulnerability : " + search_list[0].type
end
# enforce only one of any vulnerability type (remove from available)
legal_vulns.delete_if{|x| x.type == vulnerability_query.type}
end
end
return return_vulns.values
end
end
class Conf
# this class uses nokogiri to grab all of the information from network.xml, bases.xml, and vulns.xml
# then adds them to their specific class to do checking for legal in Manager.process
def self.networks
if defined? @@networks
return @@networks
end
return @@networks = self._get_list(NETWORKS_XML, "//networks/network", Network)
end
def self.bases
if defined? @@bases
return @@bases
end
return @@bases = self._get_list(BASE_XML, "//bases/base", Basebox)
end
def self.vulnerabilities
if defined? @@vulnerabilities
return @@vulnerabilities
end
return @@vulnerabilities = self._get_list(VULN_XML, "//vulnerabilities/vulnerability", Vulnerability)
end
def self.services
if defined? @@services
return @@services
end
return @@services = self._get_list(SERVICES_XML, "//services/service", Service)
end
def self._get_list(xmlfile, xpath, cls)
itemlist = []
doc = Nokogiri::XML(File.read(xmlfile))
doc.xpath(xpath).each do |item|
# new class e.g networks
obj = cls.new
# checks to see if there are children puppet and add string to obj.puppets
# move this to vulnerabilities/services classes?
if defined? obj.puppets
item.xpath("puppets/puppet").each { |c| obj.puppets << c.text.strip if not c.text.strip.empty? }
item.xpath("ports/port").each { |c| obj.ports << c.text.strip if not c.text.strip.empty? }
end
# too specific move to vuln class end
item.each do |attr, value|
obj.send "#{attr}=", value
end
# vulnerability item
itemlist << obj
end
return itemlist
end
end