Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/spec/acceptance/mssql_spec.rb
Views: 11766
require 'acceptance_spec_helper'12RSpec.describe 'MSSQL sessions and MSSQL modules' do3include_context 'wait_for_expect'45tests = {6mssql: {7target: {8session_module: "auxiliary/scanner/mssql/mssql_login",9type: 'MSSQL',10platforms: [:linux, :osx, :windows],11datastore: {12global: {},13module: {14username: ENV.fetch('MSSQL_USER', 'sa'),15password: ENV.fetch('MSSQL_PASSWORD', 'yourStrong(!)Password'),16rhost: ENV.fetch('MSSQL_RHOST', '127.0.0.1'),17rport: ENV.fetch('MSSQL_RPORT', '1433'),18database: 'master'19}20}21},22module_tests: [23{24name: "post/test/mssql",25platforms: [:linux, :osx, :windows],26targets: [:session],27skipped: false,28},29{30name: "auxiliary/scanner/mssql/mssql_hashdump",31platforms: [:linux, :osx, :windows],32targets: [:session, :rhost],33skipped: false,34lines: {35all: {36required: [37/Instance Name: "\w+"/,38]39},40}41},42{43name: "auxiliary/scanner/mssql/mssql_version",44platforms: [:linux, :osx, :windows],45targets: [:session, :rhost],46skipped: false,47lines: {48all: {49required: [50/Version: \d+.\d+.\d+/,51/Encryption: (?:on|off|unsupported|required|unknown)/52]53},54}55},56{57name: "auxiliary/admin/mssql/mssql_enum",58platforms: [:linux, :osx, :windows],59targets: [:session, :rhost],60skipped: false,61lines: {62all: {63required: [64'Version:',65/Microsoft SQL Server \d+.\d+/,66'Databases on the server:',67'System Logins on this Server:'68]69},70}71},72{73name: "auxiliary/scanner/mssql/mssql_schemadump",74platforms: [:linux, :osx, :windows],75targets: [:session, :rhost],76skipped: false,77lines: {78all: {79required: [80/Instance Name: "\w+"/,81'Microsoft SQL Server Schema',82'Host:',83'Port:',84'Instance:',85'Version:'86]87},88}89},90{91name: "auxiliary/admin/mssql/mssql_sql",92platforms: [:linux, :osx, :windows],93targets: [:session, :rhost],94skipped: false,95lines: {96all: {97required: [98"Response",99"Microsoft SQL Server",100]101},102}103}104]105}106}107108allure_test_environment = AllureRspec.configuration.environment_properties109110let_it_be(:current_platform) { Acceptance::Session::current_platform }111112# Driver instance, keeps track of all open processes/payloads/etc, so they can be closed cleanly113let_it_be(:driver) do114driver = Acceptance::ConsoleDriver.new115driver116end117118# Opens a test console with the test loadpath specified119# @!attribute [r] console120# @return [Acceptance::Console]121let_it_be(:console) do122console = driver.open_console123124# Load the test modules125console.sendline('loadpath test/modules')126console.recvuntil(/Loaded \d+ modules:[^\n]*\n/)127console.recvuntil(/\d+ auxiliary modules[^\n]*\n/)128console.recvuntil(/\d+ exploit modules[^\n]*\n/)129console.recvuntil(/\d+ post modules[^\n]*\n/)130console.recvuntil(Acceptance::Console.prompt)131132# Read the remaining console133# console.sendline "quit -y"134# console.recv_available135136features = %w[137mssql_session_type138]139140features.each do |feature|141console.sendline("features set #{feature} true")142console.recvuntil(Acceptance::Console.prompt)143end144145console146end147148# Run the given block in a 'test harness' which will handle all of the boilerplate for asserting module results, cleanup, and artifact tracking149# This doesn't happen in a before/after block to ensure that allure's report generation is correctly attached to the correct test scope150def with_test_harness(module_test)151begin152replication_commands = []153154known_failures = module_test.dig(:lines, :all, :known_failures) || []155known_failures += module_test.dig(:lines, current_platform, :known_failures) || []156known_failures = known_failures.flat_map { |value| Acceptance::LineValidation.new(*Array(value)).flatten }157158required_lines = module_test.dig(:lines, :all, :required) || []159required_lines += module_test.dig(:lines, current_platform, :required) || []160required_lines = required_lines.flat_map { |value| Acceptance::LineValidation.new(*Array(value)).flatten }161162yield replication_commands163164# XXX: When debugging failed tests, you can enter into an interactive msfconsole prompt with:165# console.interact166167# Expect the test module to complete168module_type = module_test[:name].split('/').first169test_result = console.recvuntil("#{module_type.capitalize} module execution completed")170171# Ensure there are no failures, and assert tests are complete172aggregate_failures("#{target.type} target and passes the #{module_test[:name].inspect} tests") do173# Skip any ignored lines from the validation input174validated_lines = test_result.lines.reject do |line|175is_acceptable = known_failures.any? do |acceptable_failure|176is_matching_line = is_matching_line.value.is_a?(Regexp) ? line.match?(acceptable_failure.value) : line.include?(acceptable_failure.value)177is_matching_line &&178acceptable_failure.if?(test_environment)179end || line.match?(/Passed: \d+; Failed: \d+/)180181is_acceptable182end183184validated_lines.each do |test_line|185test_line = Acceptance::Session.uncolorize(test_line)186expect(test_line).to_not include('FAILED', '[-] FAILED', '[-] Exception', '[-] '), "Unexpected error: #{test_line}"187end188189# Assert all expected lines are present190required_lines.each do |required|191next unless required.if?(test_environment)192if required.value.is_a?(Regexp)193expect(test_result).to match(required.value)194else195expect(test_result).to include(required.value)196end197end198199# Assert all ignored lines are present, if they are not present - they should be removed from200# the calling config201known_failures.each do |acceptable_failure|202next if acceptable_failure.flaky?(test_environment)203next unless acceptable_failure.if?(test_environment)204205expect(test_result).to include(acceptable_failure.value)206end207end208rescue RSpec::Expectations::ExpectationNotMetError, StandardError => e209test_run_error = e210end211212# Test cleanup. We intentionally omit cleanup from an `after(:each)` to ensure the allure attachments are213# still generated if the session dies in a weird way etc214215console_reset_error = nil216current_console_data = console.all_data217begin218console.reset219rescue => e220console_reset_error = e221Allure.add_attachment(222name: 'console.reset failure information',223source: "Error: #{e.class} - #{e.message}\n#{(e.backtrace || []).join("\n")}",224type: Allure::ContentType::TXT225)226end227228target_configuration_details = target.as_readable_text(229default_global_datastore: default_global_datastore,230default_module_datastore: default_module_datastore231)232233replication_steps = <<~EOF234## Load test modules235loadpath test/modules236237#{target_configuration_details}238239## Replication commands240#{replication_commands.empty? ? '# no additional commands run' : replication_commands.join("\n")}241EOF242243Allure.add_attachment(244name: 'payload configuration and replication',245source: replication_steps,246type: Allure::ContentType::TXT247)248249Allure.add_attachment(250name: 'console data',251source: current_console_data,252type: Allure::ContentType::TXT253)254255test_assertions = JSON.pretty_generate(256{257required_lines: required_lines.map(&:to_h),258known_failures: known_failures.map(&:to_h),259}260)261Allure.add_attachment(262name: 'test assertions',263source: test_assertions,264type: Allure::ContentType::TXT265)266267raise test_run_error if test_run_error268raise console_reset_error if console_reset_error269end270271tests.each do |runtime_name, test_config|272runtime_name = "#{runtime_name}#{ENV.fetch('RUNTIME_VERSION', '')}"273274describe "#{Acceptance::Session.current_platform}/#{runtime_name}", focus: test_config[:focus] do275test_config[:module_tests].each do |module_test|276describe(277module_test[:name],278if: (279Acceptance::Session.supported_platform?(module_test)280)281) do282let(:target) { Acceptance::Target.new(test_config[:target]) }283284let(:default_global_datastore) do285{286}287end288289let(:test_environment) { allure_test_environment }290291let(:default_module_datastore) do292{293lhost: '127.0.0.1'294}295end296297# The shared session id that will be reused across the test run298let(:session_id) do299console.sendline "use #{target.session_module}"300console.recvuntil(Acceptance::Console.prompt)301302# Set global options303console.sendline target.setg_commands(default_global_datastore: default_global_datastore)304console.recvuntil(Acceptance::Console.prompt)305306console.sendline target.run_command(default_module_datastore: { PASS_FILE: nil, USER_FILE: nil, CreateSession: true })307308session_id = nil309# Wait for the session to open, or break early if the payload is detected as dead310wait_for_expect do311session_opened_matcher = /#{target.type} session (\d+) opened[^\n]*\n/312session_message = ''313begin314session_message = console.recvuntil(session_opened_matcher, timeout: 1)315rescue Acceptance::ChildProcessRecvError316# noop317end318319session_id = session_message[session_opened_matcher, 1]320expect(session_id).to_not be_nil321end322323session_id324end325326before :each do |example|327next unless example.respond_to?(:parameter)328329# Add the test environment metadata to the rspec example instance - so it appears in the final allure report UI330test_environment.each do |key, value|331example.parameter(key, value)332end333end334335after :all do336driver.close_payloads337console.reset338end339340context "when targeting a session", if: module_test[:targets].include?(:session) do341it(342"#{Acceptance::Session.current_platform}/#{runtime_name} session opens and passes the #{module_test[:name].inspect} tests"343) do344with_test_harness(module_test) do |replication_commands|345# Ensure we have a valid session id; We intentionally omit this from a `before(:each)` to ensure the allure attachments are generated if the session dies346expect(session_id).to_not(be_nil, proc do347"There should be a session present"348end)349350use_module = "use #{module_test[:name]}"351run_module = "run session=#{session_id} Verbose=true"352353replication_commands << use_module354console.sendline(use_module)355console.recvuntil(Acceptance::Console.prompt)356357replication_commands << run_module358console.sendline(run_module)359360# Assertions will happen after this block ends361end362end363end364365context "when targeting an rhost", if: module_test[:targets].include?(:rhost) do366it(367"#{Acceptance::Session.current_platform}/#{runtime_name} rhost opens and passes the #{module_test[:name].inspect} tests"368) do369with_test_harness(module_test) do |replication_commands|370use_module = "use #{module_test[:name]}"371run_module = "run #{target.datastore_options(default_module_datastore: default_module_datastore)} Verbose=true"372373replication_commands << use_module374console.sendline(use_module)375console.recvuntil(Acceptance::Console.prompt)376377replication_commands << run_module378console.sendline(run_module)379380# Assertions will happen after this block ends381end382end383end384end385end386end387end388end389390391