Overhaul of the processing and selection of vulnerabilities and output formatting. Adds support for secure services, which will be fulfilled if a vulnerability of that type has not already beed allocated to a system. Vulnerability selection now filters on vulnerability CVE, access level, etc. Enforces a limit of one service or vulnerability of each type on each system, to resolve potential conflict (for example, one FTP server). Some general code cleanup.

This commit is contained in:
Z. Cliffe Schreuders
2015-01-29 08:41:25 -08:00
parent d14062400b
commit 9c74651eb7
10 changed files with 319 additions and 171 deletions

View File

@@ -13,6 +13,7 @@ class FileCreator
def initialize(systems)
@systems = systems
end
def generate(system)
Dir::mkdir("#{PROJECTS_DIR}") unless File.exists?("#{PROJECTS_DIR}")
@@ -20,7 +21,7 @@ class FileCreator
build_number = count.next
p "the system is now creating the Project#{build_number}"
puts "The system is now creating the Project#{build_number}"
Dir::mkdir("#{PROJECTS_DIR}/Project#{build_number}") unless File.exists?("#{PROJECTS_DIR}/#{build_number}")
# initialises box before creation
@@ -29,13 +30,14 @@ class FileCreator
controller = ERBController.new
controller.systems = system
vagrant_template = ERB.new(File.read(VAGRANT_TEMPLATE_FILE))
p "#{PROJECTS_DIR}/Project#{build_number}/VagrantFile file has been created"
File.open("#{PROJECTS_DIR}/Project#{build_number}/VagrantFile", 'w') { |file| file.write(vagrant_template.result(controller.get_binding)) }
vagrant_template = ERB.new(File.read(VAGRANT_TEMPLATE_FILE), 0, '<>')
File.delete("#{PROJECTS_DIR}/Project#{build_number}/Vagrantfile")
puts "#{PROJECTS_DIR}/Project#{build_number}/Vagrantfile file has been created"
File.open("#{PROJECTS_DIR}/Project#{build_number}/Vagrantfile", 'w') { |file| file.write(vagrant_template.result(controller.get_binding)) }
report_template = ERB.new(File.read(REPORT_TEMPLATE_FILE))
p "#{PROJECTS_DIR}/Project#{build_number}/Report file has been created"
report_template = ERB.new(File.read(REPORT_TEMPLATE_FILE), 0, '<>')
puts "#{PROJECTS_DIR}/Project#{build_number}/Report file has been created"
File.open("#{PROJECTS_DIR}/Project#{build_number}/Report", 'w'){ |file| file.write(report_template.result(controller.get_binding)) }
return build_number
@@ -53,4 +55,4 @@ class ERBController
def get_binding
return binding
end
end
end

View File

@@ -1,6 +1,10 @@
______________________________________________________________________________
| |
| Security Simulator |
| Created By Lewis Ardern |
| Leeds Met Final Year Project |
|______________________________________________________________________________|
_____ _ _ _____ _ _ _
/ ____| (_) | / ____(_) | | | |
| (___ ___ ___ _ _ _ __ _| |_ _ _| (___ _ _ __ ___ _ _| | __ _| |_ ___ _ __
\___ \ / _ \/ __| | | | '__| | __| | | |\___ \| | '_ ` _ \| | | | |/ _` | __| / _ \| '__|
____) | __/ (__| |_| | | | | |_| |_| |____) | | | | | | | |_| | | (_| | |_ | (_) | |
|_____/ \___|\___|\__,_|_| |_|\__|\__, |_____/|_|_| |_| |_|\__,_|_|\__,_|\__| \___/|_|
__/ |
Licensed GPLv3 |___/ Creates virtualised security scenarios
2014-15 By Lewis Ardern and Z.Cliffe.S

View File

@@ -1,37 +1,43 @@
This document has been automated for build
<%if systems.count == 1%>
There was only 1 system generated for this project.
<%else %>
There were <%systems.count%> systems generated for this project.
<%end%>
There were <%systems.count%> systems generated for this project. <%end%>
The module files for puppet can be found here: "<%=ROOT_DIR%>/mount/puppet/modules"
The manifest files for puppet can be found here: "<%=ROOT_DIR%>/mount/puppet/manifests"
The module files for puppet can be found here: "<%=ROOT_DIR%>/mount/puppet/modules"
The manifest files for puppet can be found here: "<%=ROOT_DIR%>/mount/puppet/manifests"
<% systems.each do |s| %>
====System: <%=s.id%>====
<%=s.id%> uses <%=s.basebox%> a distro of <%=s.os%> which can be downloaded from <%=s.url%>
<% s.networks.each do |n| %> <%grab_system_number = s.id.gsub(/[^0-9]/i, "") %> <% n.range[9..9] = grab_system_number %>
ip address for <%=s.id%> = <%=n.range%>0 <% end %>
==Secure services==
<% s.services.each do |v| %>
Here is a summary of the service <%=v.name%>:
Type: <%=v.type%>.
Name: <%= v.name %>.
Details: <%= v.details %>.
<% v.puppets.each do |p| %>
Puppet "<%=p%>.pp" has been used to create this service.
<% end %>
<% end %>
<%=s.id%> uses <%=s.basebox%> a distro of <%=s.os%> which can be downloaded from <%=s.url%>
<% s.networks.each do |n| %>
<%grab_system_number = s.id.gsub(/[^0-9]/i, "") %>
<% n.range[9..9] = grab_system_number %>
ip address for <%=s.id%> = <%=n.range%>0
<% end %>
<% s.vulns.each do |v| %>
Here is a summary of the vulnerability <%=v.type%>:
Type: <%=v.type%>
Details: <%= v.details %>
privilege: <%= v.privilege %>
access: <%= v.access %>
<%if not v.cve == ""%>
cve: <%= v.cve %>
<% end %>
<% v.puppets.each do |p| %>
Puppet "<%=p%>.pp" has been used to create these vulnerabiliies
<% end %>
<% v.ports.each do |port| %>
Web server runs on port <%=port%>
<% end %>
<% end %>
==Vulnerabilities==
<% s.vulns.each do |v| %>
Here is a summary of the vulnerability <%=v.type%>:
Type: <%=v.type%>.
Details: <%= v.details %>.
privilege: <%= v.privilege %>.
access: <%= v.access %>.
<%if not v.cve == ""%>
cve: <%= v.cve %>.
<% end %>
<% v.puppets.each do |p| %>
Puppet "<%=p%>.pp" has been used to create this vulnerability.
<% end %>
<% v.ports.each do |port| %>
Runs on port <%=port%>
<% end %>
<% end %>
<% end %>

View File

@@ -5,43 +5,52 @@
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
<% systems.each do |s| %>
<% systems.each do |s| %>
config.vm.define "<%=s.id%>" do |<%=s.id%>|
<%=s.id%>.vm.box = "<%=s.basebox%>"
<%=s.id%>.vm.box_url = "<%=s.url%>"
<% s.networks.each do |n| %>
<%grab_system_number = s.id.gsub(/[^0-9]/i, "") %>
<% n.range[9..9] = grab_system_number %>
<%=s.id%>.vm.network :public_network
<%=s.id%>.vm.network :private_network, :ip => "<%=n.range%>0"
<% end %>
<% s.networks.each do |n| %>
<%grab_system_number = s.id.gsub(/[^0-9]/i, "") %>
<% n.range[9..9] = grab_system_number %>
<%=s.id%>.vm.network :public_network
<%=s.id%>.vm.network :private_network, :ip => "<%=n.range%>0"
<% end %>
<%=s.id%>.vm.synced_folder "<%=MOUNT_DIR%>", "/mount"
end
config.vm.provision :shell, :inline => "apt-get update --fix-missing"
<%s.vulns.each do |v|%>
<%v.puppets.each do |p|%>
config.vm.provision "puppet" do |<%=p%>|
<%=p%>.module_path = "<%=ROOT_DIR%>/mount/puppet/modules"
<%=p%>.manifests_path = "<%=ROOT_DIR%>/mount/puppet/manifests"
# Add secure services
<%s.services.each do |v|%>
<%v.puppets.each do |p|%>
config.vm.provision "puppet" do |<%=p%>|
<%=p%>.module_path = "<%=ROOT_DIR%>/mount/puppet/modules"
<%=p%>.manifests_path = "<%=ROOT_DIR%>/mount/puppet/manifests"
<%=p%>.manifest_file = "<%=p%>.pp"
end
<% end %>
<% end %>
<%=p%>.manifest_file = "<%=p%>.pp"
end
<% end %>
<% end %>
# Add vulnerabilities
<%s.vulns.each do |v|%>
<%v.puppets.each do |p|%>
config.vm.provision "puppet" do |<%=p%>|
<%=p%>.module_path = "<%=ROOT_DIR%>/mount/puppet/modules"
<%=p%>.manifests_path = "<%=ROOT_DIR%>/mount/puppet/manifests"
<%=p%>.manifest_file = "<%=p%>.pp"
end
<% end %>
<% end %>
# clean up script which clears history from the VMs and clobs files together
config.vm.provision "puppet" do |cleanup|
cleanup.module_path = "<%=ROOT_DIR%>/mount/puppet/modules"
cleanup.manifests_path = "<%=ROOT_DIR%>/mount/puppet/manifests"
cleanup.manifest_file = "cleanup.pp"
end
# clean up script which clears history and clobs files together
config.vm.provision :shell, :inline => "history -c && history -w"
config.vm.provision :shell, :inline => "umount /mount/"
<% end %>
<% end %>
end

View File

@@ -1,10 +1,30 @@
<systems>
<system id="system2" os="linux" basebox="puppettest" url="" >
<!-- an example remote storage system, with a remotely exploitable vulnerability that can then be escalated to root -->
<system id="storage-server" os="linux" basebox="puppettest" url="" >
<vulnerabilities>
<vulnerability privilege="user" access="remote" type="ftpbackdoor" cve=""></vulnerability>
<vulnerability privilege="user" access="remote" type="ftp" cve=""></vulnerability>
<vulnerability privilege="user" access="remote" type="" cve=""></vulnerability>
<vulnerability privilege="root" access="local" type="" cve=""></vulnerability>
</vulnerabilities>
<!-- secure services will be provided, if matching insecure ones have not been selected -->
<services>
<service type="ftp"></service>
<service type="smb"></service>
<service type="nfs"></service>
</services>
<networks>
<network name="homeonly" ></network>
<network name="homeonly"></network>
</networks>
</system>
<!-- an example remote web server, with a remotely exploitable root vulnerability -->
<system id="web-server" os="linux" basebox="puppettest" url="" >
<vulnerabilities>
<vulnerability privilege="root" access="remote" type="www" cve=""></vulnerability>
</vulnerabilities>
<networks>
<network name="homeonly"></network>
</networks>
</system>
</systems>

View File

@@ -10,7 +10,7 @@
</puppets>
</vulnerability>
<vulnerability
type="commandinjection"
type="www"
cve=""
privilege="user"
access="remote"
@@ -33,7 +33,7 @@
</puppets>
</vulnerability>
<vulnerability
type="samba"
type="smb"
cve=""
privilege="user"
access="remote"
@@ -43,10 +43,10 @@
</puppets>
</vulnerability>
<vulnerability
type="writeableshadow"
type="local-config"
cve=""
privilege="user"
access="remote"
privilege="root"
access="local"
details="This puppet module edits the chmod of the shadow file to 777.">
<puppets>
<puppet>writeableshadow</puppet>
@@ -62,8 +62,8 @@
<puppet>distcc</puppet>
</puppets>
</vulnerability>
<vulnerability
type="ftpbackdoor"
<vulnerability
type="ftp"
cve=""
privilege="user"
access="remote"
@@ -73,7 +73,7 @@
</puppets>
</vulnerability>
<vulnerability
type="sqlinjection"
type="www"
cve=""
privilege="user"
access="remote"

View File

@@ -15,67 +15,74 @@ require_relative 'filecreator.rb'
require_relative 'systemreader.rb'
require_relative 'vagrant.rb'
# coloured logo
puts "\e[34m"
File.open('lib/commandui/logo/logo.txt', 'r') do |f1|
while line = f1.gets
puts line
end
while line = f1.gets
puts line
end
end
puts "\e[0m"
def usage
puts 'Usage:
puts 'Usage:
' + $0 + ' [options]
run - creates virtual machines e.g run 10
kill - destoys current session
ssh - creates a ssh session for specifiec box e.g ssh box1
All options options are:
--help -h: show
--run -r: run
OPTIONS:
--run, -r: builds vagrant config and then builds the VMs
--build-config, -c: builds vagrant config, but does not build VMs
--build-vms, -v: builds VMs from previously generated vagrant config
--help, -h: shows this usage information
'
exit
exit
end
def build_config
puts 'Reading configuration file for virtual machines you want to create'
# uses nokogoiri to grab all the system information from boxes.xml
systems = SystemReader.new(BOXES_XML).systems
puts 'Creating vagrant file'
# create's vagrant file / report a starts the vagrant installation'
create_files = FileCreator.new(systems)
build_number = create_files.generate(systems)
end
def build_vms
vagrant = VagrantController.new
vagrant.vagrant_up(build_number)
end
def run
puts 'reading configuration file on how many virtual machines you want to create'
puts 'creating vagrant file'
# uses nokogoiri to grab all the system information from boxes.xml
systems = SystemReader.new(BOXES_XML).systems
# create's vagrant file / report a starts the vagrant installation'
create_files = FileCreator.new(systems)
build_number = create_files.generate(systems)
vagrant = VagrantController.new
vagrant.vagrant_up(build_number)
build_config()
build_vms()
end
def config
if ARGV.length < 1
puts 'Please enter a command option.'
puts
usage
end
opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--run', '-r', GetoptLong::NO_ARGUMENT ],
[ '--config', '-c', GetoptLong::NO_ARGUMENT ]
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--run', '-r', GetoptLong::NO_ARGUMENT ],
[ '--build-config', '-c', GetoptLong::NO_ARGUMENT ],
[ '--build-vms', '-v', GetoptLong::NO_ARGUMENT ]
)
opts.each do |opt, arg|
case opt
when '--help'
usage
when '--run'
run
when '--config'
#do a box count increment to next one
#create template config file!
config
end
case opt
when '--help'
usage
when '--run'
run
when '--build-config'
build_config()
when '--build-vms'
build_vms()
end
end

171
system.rb
View File

@@ -5,21 +5,23 @@ 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
attr_accessor :id, :os, :url,:basebox, :networks, :vulns, :services
#initalizes system variables
def initialize(id, os, basebox, url, vulns=[], networks=[])
#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
@@ -60,6 +62,78 @@ class Network
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)
@@ -136,46 +210,50 @@ class Vulnerability
end
class VulnerabilityManager
# the user will either specify a blank vulnerability or will check it against vulns.xml and will append
# specific information to system if the system information is empty
def self.process(vulns,valid_vulns)
new_vulns = {}
# 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 = valid_vulns & vulns
vulns.each do |vuln|
legal_vulns = all_vulns.clone
want_vulns.each do |vulnerability_query|
# select based on selected type, access, cve...
if vuln.type == ""
random = valid_vulns.sample
# valid vulnerability into a new hash map of vulnerabilities
new_vulns[random.id] = random
else
has_found = false
# shuffle randomly selects first match of ftp or nfs and then abandon
legal_vulns.shuffle.each do |valid|
if vuln.type == valid.type
vuln.puppets = valid.puppets unless not vuln.puppets.empty?
vuln.ports = valid.ports unless not vuln.ports.empty?
vuln.cve = valid.cve unless not vuln.cve.empty?
vuln.privilege = valid.privilege unless not vuln.privilege.empty?
vuln.access = valid.access unless not vuln.access.empty?
vuln.details = valid.details
# valid vulnerability into a new hash map of vulnerabilities
new_vulns[vuln.id] = vuln
has_found = true
break
end
end
if not has_found
STDERR.puts "vulnerability was not found please check the xml boxes.xml"
exit
end
end
end
return new_vulns.values
end
# 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
#loop through vulns, fill in missing details if not enough info, choose one at random fill in vulns..
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
@@ -202,15 +280,22 @@ class Conf
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
obj = cls.new
# checks to see if there are children puppet and add string to obj.puppets
# move this to vulnerabilities class
# 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? }
@@ -225,4 +310,4 @@ class Conf
end
return itemlist
end
end
end

View File

@@ -18,6 +18,7 @@ class SystemReader
url = system["url"]
vulns = []
networks = []
services = []
system.css('vulnerabilities vulnerability').each do |v|
vulnerability = Vulnerability.new
@@ -28,23 +29,37 @@ class SystemReader
vulns << vulnerability
end
system.css('services service').each do |v|
service = Service.new
service.name = v['name']
service.details = v['details']
service.type = v['type']
services << service
end
system.css('networks network').each do |n|
network = Network.new
network.name = n['name']
networks << network
end
# vulns / networks are passed through to their manager and the program will create valid vulnerabilities / networks
# depending on what the user has specified these two will return valid vulns to be used in vagrant file creation.
new_vulns = VulnerabilityManager.process(vulns, Conf.vulnerabilities)
new_networks = NetworkManager.process(networks, Conf.networks)
puts "Processing system: " + id
# vulns / networks are passed through to their manager and the program will create valid vulnerabilities / networks
# depending on what the user has specified these two will return valid vulns to be used in vagrant file creation.
new_vulns = VulnerabilityManager.process(vulns, Conf.vulnerabilities)
#puts new_vulns.inspect
new_networks = NetworkManager.process(networks, Conf.networks)
# pass in the already selected set of vulnerabilities, and additional secure services to find
new_services = ServiceManager.process(services, Conf.services, new_vulns)
s = System.new(id, os, basebox, url, new_vulns, new_networks)
if s.is_valid_base == false
BaseManager.generate_base(s,Conf.bases)
end
s = System.new(id, os, basebox, url, new_vulns, new_networks, new_services)
if s.is_valid_base == false
BaseManager.generate_base(s,Conf.bases)
end
systems << s
systems << s
end
return systems
end
end
end

View File

@@ -4,8 +4,8 @@ class VagrantController
def vagrant_up(build_number)
#executes vagrant up from the current build.
p 'building now.....'
puts 'Building now.....'
command = "cd #{PROJECTS_DIR}/Project#{build_number}/; vagrant up"
exec command
end
end
end