From 1fffa4b05c8bdc092300f71438d79130f87a704b Mon Sep 17 00:00:00 2001 From: ts Date: Sat, 2 Feb 2019 01:27:50 +0000 Subject: [PATCH] (WiP) Created proftpd_133c_backdoor testing script - Needs testing! --- lib/objects/post_provision_test.rb | 91 +++++++++++++++++++ lib/output/project_files_creator.rb | 4 + lib/templates/Vagrantfile.erb | 2 +- .../secgen_test/proftpd_133c_backdoor.rb | 30 ++++++ scenarios/tests/test_scenario.xml | 6 +- secgen.rb | 28 ++++++ 6 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 lib/objects/post_provision_test.rb create mode 100644 modules/vulnerabilities/unix/ftp/proftpd_133c_backdoor/secgen_test/proftpd_133c_backdoor.rb diff --git a/lib/objects/post_provision_test.rb b/lib/objects/post_provision_test.rb new file mode 100644 index 000000000..b7bf537f7 --- /dev/null +++ b/lib/objects/post_provision_test.rb @@ -0,0 +1,91 @@ +# Post Provision Testing +# +# This file will be copied into each project folder at creation time. +# It will be required from each of the modules/secgen_tests/module_name.rb test scripts +# +# Test classes must: require_relative '../../../../../lib/post_provision_test' + +require_relative "../../../lib/helpers/print.rb" +require 'json' +require 'base64' + +require 'socket' +require 'timeout' + +class PostProvisionTest + attr_accessor :project_path + attr_accessor :system_ip + attr_accessor :module_name + attr_accessor :module_path + attr_accessor :json_inputs + + def initialize + # self.project_path = + end + + def run + Print.info "Running tests for #{self.module_name}" + test_module + end + + def test_module + # Override me with testing details + end + + def get_system_ip(module_file_path) + # Get Vagrantfile + + end + + def get_json_inputs + json_inputs_path = "#{File.expand_path('../', self.module_path)}/secgen_functions/files/json_inputs/*" + Print.info "json_inputs_path: #{json_inputs_path}" + json_inputs_files = Dir.glob(json_inputs_path) + Print.info "json_input_files (pre delete): #{json_inputs_files}" + json_inputs_files.delete_if { |path| !path.include?(self.module_name) } + Print.info "json_input_files (post delete): #{json_inputs_files}" + JSON.parse(Base64.strict_decode64(File.read(json_inputs_files.first))) + end + + # Pass __FILE__ in from subclasses + def get_module_path(file_path) + "#{File.expand_path('..', File.dirname(file_path))}" + end + + # Note: returns proftpd_testing + def get_system_name + get_system_path.match(/.*?([^\/]*)$/i).captures[0] + end + + # Note: returns /home/thomashaw/git/SecGen/projects/SecGen20190202_010552/puppet/proftpd_testing + def get_system_path + "#{File.expand_path('../../', self.module_path)}" + end + + # Note: returns /home/thomashaw/git/SecGen/projects/SecGen20190202_010552/ + def get_project_path + "#{File.expand_path('../../../../', self.module_path)}" + end + + ############################## + ## Useful testing functions ## + ############################## + + def is_port_open?(ip, port) + begin + Timeout::timeout(1) do + begin + s = TCPSocket.new(ip, port) + s.close + return true + rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH + return false + end + end + rescue Timeout::Error + # ignored + end + false + end + +end \ No newline at end of file diff --git a/lib/output/project_files_creator.rb b/lib/output/project_files_creator.rb index 6f2e0a4b7..475e06abb 100644 --- a/lib/output/project_files_creator.rb +++ b/lib/output/project_files_creator.rb @@ -172,6 +172,10 @@ class ProjectFilesCreator abort end + # Copy the test superclass into the project/lib directory + Print.std "Copying post-provision testing class" + FileUtils.mkdir("#{@out_dir}/lib") + FileUtils.cp("#{ROOT_DIR}/lib/objects/post_provision_test.rb", "#{@out_dir}/lib/post_provision_test.rb") Print.std "VM(s) can be built using 'vagrant up' in #{@out_dir}" diff --git a/lib/templates/Vagrantfile.erb b/lib/templates/Vagrantfile.erb index 93d706ea2..ede0f1522 100644 --- a/lib/templates/Vagrantfile.erb +++ b/lib/templates/Vagrantfile.erb @@ -161,7 +161,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| b64_json_inputs = Base64.strict_encode64(json_inputs) # save the inputs in a randomly named file in the # project out directory of the secgen_functions module - json_inputs_filename = "#{module_name}_#{SecureRandom.hex(15).to_s}" + json_inputs_filename = "#{selected_module.module_path_end}_#{SecureRandom.hex(15).to_s}" dir = "#{@out_dir}/puppet/#{system.name}/modules/secgen_functions/files/json_inputs" FileUtils.mkdir_p(dir) unless File.exists?(dir) Print.verbose "Writing #{selected_module.module_path_name} input to: #{dir}/#{json_inputs_filename}" diff --git a/modules/vulnerabilities/unix/ftp/proftpd_133c_backdoor/secgen_test/proftpd_133c_backdoor.rb b/modules/vulnerabilities/unix/ftp/proftpd_133c_backdoor/secgen_test/proftpd_133c_backdoor.rb new file mode 100644 index 000000000..92bd87158 --- /dev/null +++ b/modules/vulnerabilities/unix/ftp/proftpd_133c_backdoor/secgen_test/proftpd_133c_backdoor.rb @@ -0,0 +1,30 @@ +require_relative '../../../../../lib/post_provision_test' + +class Proftpd133cBackdoorTest < PostProvisionTest + + attr_accessor :ftp_port + def initialize + super + self.module_name = 'proftpd_133c_backdoor' + self.module_path = get_module_path(__FILE__) + self.json_inputs = get_json_inputs + self.ftp_port = get_json_inputs['port'].to_i + # Print.info self.json_inputs + # Print.info "get_system_name: #{get_system_name}" + # Print.info "get_system_path: #{get_system_path}" + # Print.info "get_project_path: #{get_project_path}" + end + + def test_module + # TODO: Need to determine how to handle the output... see other Open3.capture3 module and stdout print pass/fail perhaps? + # TODO: Raise an exception? Return false? Print the PASSED / FAILED only? + + if is_port_open? "172.16.0.5", "21" + Print.info "PASSED: Port #{ftp_port} is open on #{get_system_name}!" + else + Print.err "FAILED: Port #{ftp_port} is closed on #{get_system_name}!" + end + end +end + +Proftpd133cBackdoorTest.new.run \ No newline at end of file diff --git a/scenarios/tests/test_scenario.xml b/scenarios/tests/test_scenario.xml index 3c84e680c..e3ef57c74 100644 --- a/scenarios/tests/test_scenario.xml +++ b/scenarios/tests/test_scenario.xml @@ -4,8 +4,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.github/cliffe/SecGen/scenario"> - empty_stretch - + proftpd_testing + + + 172.16.0.5 diff --git a/secgen.rb b/secgen.rb index 817dfac89..fb67ac42a 100644 --- a/secgen.rb +++ b/secgen.rb @@ -118,6 +118,8 @@ def build_vms(scenario, project_dir, options) while retry_count and !successful_creation vagrant_output = GemExec.exe('vagrant', project_dir, "#{command} #{system}") if vagrant_output[:status] == 0 + shutdown_cycle(project_dir) + post_provision_tests(project_dir) Print.info 'VMs created.' successful_creation = true if options[:shutdown] or OVirtFunctions::provider_ovirt?(options) @@ -329,6 +331,32 @@ def get_vm_names(scenario) vm_names end +def shutdown_cycle(project_dir) + Print.info 'Shutting down VMs.' + sleep(30) + GemExec.exe('vagrant', project_dir, 'halt') + sleep 5 + GemExec.exe('vagrant',project_dir,'up') + sleep 30 +end + +def post_provision_tests(project_dir) + # Get project files + Print.err "project_dir: #{project_dir}" + + # Get system names + test_script_paths = Dir.glob("#{project_dir}/puppet/*/modules/*/secgen_test/*.rb") + + test_script_paths.each {|test_file_path| + output = `bundle exec ruby #{test_file_path}` + Print.info output + if output.include? "FAILED" + raise "Post provision failure!" + end + } + Print.info 'Running post-provision tests...' +end + # end of method declarations # start of program execution