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/postgres_spec.rb
Views: 11766
require 'acceptance_spec_helper'12RSpec.describe 'Postgres sessions and postgres modules' do3include_context 'wait_for_expect'45tests = {6postgres: {7target: {8session_module: "auxiliary/scanner/postgres/postgres_login",9type: 'PostgreSQL',10platforms: [:linux, :osx, :windows],11datastore: {12global: {},13module: {14username: ENV.fetch('POSTGRES_USERNAME', 'postgres'),15password: ENV.fetch('POSTGRES_PASSWORD', 'password'),16rhost: ENV.fetch('POSTGRES_RHOST', '127.0.0.1'),17rport: ENV.fetch('POSTGRES_RPORT', '5432'),18}19}20},21module_tests: [22{23name: "post/test/postgres",24platforms: [:linux, :osx, :windows],25targets: [:session],26skipped: false,27},28{29name: "auxiliary/scanner/postgres/postgres_hashdump",30platforms: [:linux, :osx, :windows],31targets: [:session, :rhost],32skipped: false,33lines: {34all: {35required: [36" Username Hash",37" -------- ----",38# postgres SCRAM-SHA-256$4096:UfTJGaMUW+DtXay1UUD+zA==$0C01mPHaruGTqKJFt5qdITvM+nwLsCgxukO3MIbKugU=:iNBXVE5Vqnoa+dGhmEGMQ0cy+nNXDOzg0F3YNcrtRyE=39/ postgres \w+/40]41},42}43},44{45name: "auxiliary/scanner/postgres/postgres_version",46platforms: [:linux, :osx, :windows],47targets: [:session, :rhost],48skipped: false,49lines: {50all: {51required: [52/Version PostgreSQL \d+.\d+/53]54},55}56},57{58name: "auxiliary/admin/postgres/postgres_readfile",59platforms: [:linux],60targets: [:session, :rhost],61skipped: false,62lines: {63all: {64# Module reads /etc/passwd by default:65required: [66/root:x:\d+:\d+:root:/,67/postgres:x:\d+:\d+::/68]69},70}71},72{73name: "auxiliary/admin/postgres/postgres_sql",74platforms: [:linux, :osx, :windows],75targets: [:session, :rhost],76skipped: false,77lines: {78all: {79required: [80# Default module query81"Query Text: 'select version()'",82# Result83/PostgreSQL \d+.\d+/,84]85},86}87}88]89}90}9192allure_test_environment = AllureRspec.configuration.environment_properties9394let_it_be(:current_platform) { Acceptance::Session::current_platform }9596# Driver instance, keeps track of all open processes/payloads/etc, so they can be closed cleanly97let_it_be(:driver) do98driver = Acceptance::ConsoleDriver.new99driver100end101102# Opens a test console with the test loadpath specified103# @!attribute [r] console104# @return [Acceptance::Console]105let_it_be(:console) do106console = driver.open_console107108# Load the test modules109console.sendline('loadpath test/modules')110console.recvuntil(/Loaded \d+ modules:[^\n]*\n/)111console.recvuntil(/\d+ auxiliary modules[^\n]*\n/)112console.recvuntil(/\d+ exploit modules[^\n]*\n/)113console.recvuntil(/\d+ post modules[^\n]*\n/)114console.recvuntil(Acceptance::Console.prompt)115116# Read the remaining console117# console.sendline "quit -y"118# console.recv_available119120features = %w[121postgresql_session_type122]123124features.each do |feature|125console.sendline("features set #{feature} true")126console.recvuntil(Acceptance::Console.prompt)127end128129console130end131132# Run the given block in a 'test harness' which will handle all of the boilerplate for asserting module results, cleanup, and artifact tracking133# This doesn't happen in a before/after block to ensure that allure's report generation is correctly attached to the correct test scope134def with_test_harness(module_test)135begin136replication_commands = []137138known_failures = module_test.dig(:lines, :all, :known_failures) || []139known_failures += module_test.dig(:lines, current_platform, :known_failures) || []140known_failures = known_failures.flat_map { |value| Acceptance::LineValidation.new(*Array(value)).flatten }141142required_lines = module_test.dig(:lines, :all, :required) || []143required_lines += module_test.dig(:lines, current_platform, :required) || []144required_lines = required_lines.flat_map { |value| Acceptance::LineValidation.new(*Array(value)).flatten }145146yield replication_commands147148# XXX: When debugging failed tests, you can enter into an interactive msfconsole prompt with:149# console.interact150151# Expect the test module to complete152module_type = module_test[:name].split('/').first153test_result = console.recvuntil("#{module_type.capitalize} module execution completed")154155# Ensure there are no failures, and assert tests are complete156aggregate_failures("#{target.type} target and passes the #{module_test[:name].inspect} tests") do157# Skip any ignored lines from the validation input158validated_lines = test_result.lines.reject do |line|159is_acceptable = known_failures.any? do |acceptable_failure|160is_matching_line = is_matching_line.value.is_a?(Regexp) ? line.match?(acceptable_failure.value) : line.include?(acceptable_failure.value)161is_matching_line &&162acceptable_failure.if?(test_environment)163end || line.match?(/Passed: \d+; Failed: \d+/)164165is_acceptable166end167168validated_lines.each do |test_line|169test_line = Acceptance::Session.uncolorize(test_line)170expect(test_line).to_not include('FAILED', '[-] FAILED', '[-] Exception', '[-] '), "Unexpected error: #{test_line}"171end172173# Assert all expected lines are present174required_lines.each do |required|175next unless required.if?(test_environment)176if required.value.is_a?(Regexp)177expect(test_result).to match(required.value)178else179expect(test_result).to include(required.value)180end181end182183# Assert all ignored lines are present, if they are not present - they should be removed from184# the calling config185known_failures.each do |acceptable_failure|186next if acceptable_failure.flaky?(test_environment)187next unless acceptable_failure.if?(test_environment)188189expect(test_result).to include(acceptable_failure.value)190end191end192rescue RSpec::Expectations::ExpectationNotMetError, StandardError => e193test_run_error = e194end195196# Test cleanup. We intentionally omit cleanup from an `after(:each)` to ensure the allure attachments are197# still generated if the session dies in a weird way etc198199console_reset_error = nil200current_console_data = console.all_data201begin202console.reset203rescue => e204console_reset_error = e205Allure.add_attachment(206name: 'console.reset failure information',207source: "Error: #{e.class} - #{e.message}\n#{(e.backtrace || []).join("\n")}",208type: Allure::ContentType::TXT209)210end211212target_configuration_details = target.as_readable_text(213default_global_datastore: default_global_datastore,214default_module_datastore: default_module_datastore215)216217replication_steps = <<~EOF218## Load test modules219loadpath test/modules220221#{target_configuration_details}222223## Replication commands224#{replication_commands.empty? ? 'no additional commands run' : replication_commands.join("\n")}225EOF226227Allure.add_attachment(228name: 'payload configuration and replication',229source: replication_steps,230type: Allure::ContentType::TXT231)232233Allure.add_attachment(234name: 'console data',235source: current_console_data,236type: Allure::ContentType::TXT237)238239test_assertions = JSON.pretty_generate(240{241required_lines: required_lines.map(&:to_h),242known_failures: known_failures.map(&:to_h),243}244)245Allure.add_attachment(246name: 'test assertions',247source: test_assertions,248type: Allure::ContentType::TXT249)250251raise test_run_error if test_run_error252raise console_reset_error if console_reset_error253end254255tests.each do |runtime_name, test_config|256runtime_name = "#{runtime_name}#{ENV.fetch('RUNTIME_VERSION', '')}"257258describe "#{Acceptance::Session.current_platform}/#{runtime_name}", focus: test_config[:focus] do259test_config[:module_tests].each do |module_test|260describe(261module_test[:name],262if: (263Acceptance::Session.supported_platform?(module_test)264)265) do266let(:target) { Acceptance::Target.new(test_config[:target]) }267268let(:default_global_datastore) do269{270}271end272273let(:test_environment) { allure_test_environment }274275let(:default_module_datastore) do276{277lhost: '127.0.0.1'278}279end280281# The shared session id that will be reused across the test run282let(:session_id) do283console.sendline "use #{target.session_module}"284console.recvuntil(Acceptance::Console.prompt)285286# Set global options287console.sendline target.setg_commands(default_global_datastore: default_global_datastore)288console.recvuntil(Acceptance::Console.prompt)289290console.sendline target.run_command(default_module_datastore: { PASS_FILE: nil, USER_FILE: nil, CreateSession: true })291292session_id = nil293# Wait for the session to open, or break early if the payload is detected as dead294wait_for_expect do295session_opened_matcher = /#{target.type} session (\d+) opened[^\n]*\n/296session_message = ''297begin298session_message = console.recvuntil(session_opened_matcher, timeout: 1)299rescue Acceptance::ChildProcessRecvError300# noop301end302303session_id = session_message[session_opened_matcher, 1]304expect(session_id).to_not be_nil305end306307session_id308end309310before :each do |example|311next unless example.respond_to?(:parameter)312313# Add the test environment metadata to the rspec example instance - so it appears in the final allure report UI314test_environment.each do |key, value|315example.parameter(key, value)316end317end318319after :all do320driver.close_payloads321console.reset322end323324context "when targeting a session", if: module_test[:targets].include?(:session) do325it(326"#{Acceptance::Session.current_platform}/#{runtime_name} session opens and passes the #{module_test[:name].inspect} tests"327) do328with_test_harness(module_test) do |replication_commands|329# 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 dies330expect(session_id).to_not(be_nil, proc do331"There should be a session present"332end)333334use_module = "use #{module_test[:name]}"335run_module = "run session=#{session_id} Verbose=true"336337replication_commands << use_module338console.sendline(use_module)339console.recvuntil(Acceptance::Console.prompt)340341replication_commands << run_module342console.sendline(run_module)343344# Assertions will happen after this block ends345end346end347end348349context "when targeting an rhost", if: module_test[:targets].include?(:rhost) do350it(351"#{Acceptance::Session.current_platform}/#{runtime_name} rhost opens and passes the #{module_test[:name].inspect} tests"352) do353with_test_harness(module_test) do |replication_commands|354use_module = "use #{module_test[:name]}"355run_module = "run #{target.datastore_options(default_module_datastore: default_module_datastore)} Verbose=true"356357replication_commands << use_module358console.sendline(use_module)359console.recvuntil(Acceptance::Console.prompt)360361replication_commands << run_module362console.sendline(run_module)363364# Assertions will happen after this block ends365end366end367end368end369end370end371end372end373374375