GSoC/Meterpreter_Web_Console
christopher lee 2018-04-02 08:08:23 -05:00
parent f386ae0ba3
commit 3aed6d6666
43 changed files with 347 additions and 37 deletions

View File

@ -1,7 +1,6 @@
require 'open3'
require 'rex/ui'
require 'rex/logging'
require 'metasploit/framework/data_service/remote/http/core'
require 'metasploit/framework/data_service/proxy/data_proxy_auto_loader'
#

View File

@ -14,12 +14,22 @@ module HostDataProxy
end
end
# TODO: Shouldn't this proxy to RemoteHostDataService#find_or_create_host ?
# It's currently skipping the "find" part
def find_or_create_host(opts)
host = get_host(opts)
return host unless host.nil?
report_host(opts)
end
def get_host(opts)
begin
data_service = self.get_data_service()
data_service.get_host(opts)
rescue Exception => e
self.log_error(e, "Problem reporting host")
end
end
def report_host(opts)
return unless valid(opts)

View File

@ -31,6 +31,16 @@ class RemoteHTTPDataService
build_client_pool(5)
end
def online?
begin
response = get_data(ONLINE_TEST_URL)
return response.expected
rescue Exception => e
end
return false
end
def connection_established?
true
end

View File

@ -11,6 +11,10 @@ module RemoteHostDataService
json_to_mdm_object(self.get_data(HOST_API_PATH, nil, opts), HOST_MDM_CLASS, [])
end
def get_host(opts)
json_to_mdm_object(self.post_data(HOST_SEARCH_PATH, opts), HOST_MDM_CLASS, []).first
end
def report_host(opts)
json_to_mdm_object(self.post_data(HOST_API_PATH, opts), HOST_MDM_CLASS, []).first
end

View File

@ -0,0 +1,77 @@
require 'singleton'
require 'metasploit/framework/data_service/remote/http/core'
module Metasploit
module Framework
module DataService
class ManagedDBWS
include Singleton
def running?
return @running
end
def remote_data_service
return @remote_host_data_service
end
def start(opts)
@mutex.synchronize do
return if @running
# started with no signal to prevent ctrl-c from taking out db
db_script = File.join( Msf::Config.install_root, opts[:process_name])
wait_t = Open3.pipeline_start(db_script)
@pid = wait_t[0].pid
puts "Started process with pid #{@pid}"
endpoint = "http://#{opts[:host]}:#{opts[:port]}"
@remote_host_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(endpoint)
count = 0
loop do
count = count + 1
if count > 10
raise 'Unable to start remote data service'
end
sleep(1)
if @remote_host_data_service.online?
break
end
end
@running = true
end
end
def stop
@mutex.synchronize do
return unless @running
begin
Process.kill("TERM", @pid)
@running = false
rescue Exception => e
puts "Unable to kill db process: #{e.message}"
end
end
end
#######
private
#######
def initialize
@mutex = Mutex.new
@running = false
end
end
end
end
end

View File

@ -1,5 +1,9 @@
module HostDataService
def get_host(opts)
raise 'HostDataService#get_host is not implemented'
end
def report_host(opts)
raise 'HostDataService#report_host is not implemented'
end

View File

@ -3,4 +3,8 @@ module LootDataService
def report_loot(opts)
raise 'LootDataService#report_loot is not implemented'
end
def loot(opts)
raise 'LootDataService#loots is not implemented'
end
end

View File

@ -12,7 +12,8 @@ module Metasploit
#
# Number of allowed threads when threads are counted in `after(:suite)` or `before(:suite)`
EXPECTED_THREAD_COUNT_AROUND_SUITE = 1
EXPECTED_THREAD_COUNT_AROUND_SUITE = if ENV['REMOTE_DB'] then 2 else 1 end
# `caller` for all Thread.new calls
LOG_PATHNAME = Pathname.new('log/metasploit/framework/spec/threads/suite.log')
# Regular expression for extracting the UUID out of {LOG_PATHNAME} for each Thread.new caller block

View File

@ -8,11 +8,16 @@ module HostServlet
"#{HostServlet.api_path}/?:id?"
end
def self.api_search_path
"#{HostServlet.api_path}/search"
end
def self.registered(app)
app.get HostServlet.api_path_with_id, &get_host
app.post HostServlet.api_path, &report_host
app.put HostServlet.api_path_with_id, &update_host
app.delete HostServlet.api_path, &delete_host
app.post HostServlet.api_search_path, &search
end
#######
@ -71,4 +76,20 @@ module HostServlet
}
end
def self.search
lambda {
begin
opts = parse_json_request(request, false)
opts.each {|key, value|
puts "#{key}:#{value}"
}
data = get_db().get_host(opts)
set_json_response(data)
rescue Exception => e
set_error_on_response(e)
end
}
end
end

View File

@ -24,6 +24,7 @@ module WorkspaceServlet
#includes = 'hosts: {only: :count}, services: {only: :count}, vulns: {only: :count}, creds: {only: :count}, loots: {only: :count}, notes: {only: :count}'
else
data = get_db().find_workspace(opts[:workspace_name])
puts "Getting data with name #{opts[:workspace_name]}"
end
set_json_response(data, includes)

View File

@ -51,8 +51,8 @@ module Msf
def auto_target_host
return nil unless self.respond_to?(:rhost)
return nil unless framework.db.active
current_workspace = framework.db.find_workspace(self.workspace)
current_workspace.hosts.where(address: rhost).first
host = framework.db.get_host({workspace: self.workspace, address: rhost})
return host
end
# Returns the best matching Targets based on the target host's

View File

@ -48,8 +48,11 @@ RSpec.describe Msf::DBManager do
it_should_behave_like 'Msf::DBManager::Web'
it_should_behave_like 'Msf::DBManager::Workspace'
it { is_expected.to respond_to :check }
it { is_expected.to respond_to :error }
it { is_expected.to respond_to :initialize_database_support }
it { is_expected.to respond_to :service_name_map }
# TODO: Evaluate need in remote db
unless ENV['REMOTE_DB']
it { is_expected.to respond_to :check }
it { is_expected.to respond_to :error }
it { is_expected.to respond_to :service_name_map }
end
end

View File

@ -5,6 +5,11 @@ require 'msf/ui'
require 'msf/ui/console/command_dispatcher/creds'
RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do
if ENV['REMOTE_DB']
before {skip("Awaiting credentials port")}
end
include_context 'Msf::DBManager'
include_context 'Msf::UIDriver'

View File

@ -213,13 +213,23 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Db do
]
end
end
describe "-p" do
describe "-S" do
before(:example) do
host = FactoryBot.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1")
FactoryBot.create(:mdm_service, :host => host, :port => 1024, name: 'Service1', proto: 'udp')
FactoryBot.create(:mdm_service, :host => host, :port => 1025, name: 'Service2', proto: 'tcp')
FactoryBot.create(:mdm_service, :host => host, :port => 1026, name: 'Service3', proto: 'udp')
@services = []
@services << framework.db.report_service({host: '192.168.0.1', port: 1024, name: 'service1', proto: 'udp'})
@services << framework.db.report_service({host: '192.168.0.1', port: 1025, name: 'service2', proto: 'tcp'})
@services << framework.db.report_service({host: '192.168.0.1', port: 1026, name: 'service3', proto: 'udp'})
end
after(:example) do
ids = []
@services.each{|service|
ids << service.id
}
framework.db.delete_service({ids: ids})
end
it "should list services that are on a given port" do
db.cmd_services "-S", "1024|1025"
expect(@output).to match_array [
@ -228,17 +238,17 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Db do
"",
"host port proto name state info",
"---- ---- ----- ---- ----- ----",
"192.168.0.1 1024 udp Service1 open ",
"192.168.0.1 1025 tcp Service2 open "
"192.168.0.1 1024 udp service1 open ",
"192.168.0.1 1025 tcp service2 open "
]
end
end
describe "-np" do
before(:example) do
host = FactoryBot.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1")
FactoryBot.create(:mdm_service, :host => host, :port => 1024)
FactoryBot.create(:mdm_service, :host => host, :port => 1025)
FactoryBot.create(:mdm_service, :host => host, :port => 1026)
framework.db.report_service({host: '192.168.0.1', port: 1024})
framework.db.report_service({host: '192.168.0.1', port: 1025})
framework.db.report_service({host: '192.168.0.1', port: 1026})
end
it "should list services that are not on a given port" do
skip {

View File

@ -9,6 +9,8 @@ ENV['RAILS_ENV'] = 'test'
# Must be explicit as activerecord is optional dependency
require 'active_record/railtie'
require 'metasploit/framework/data_service/remote/managed_dbws'
require 'metasploit/framework/database'
# check if database.yml is present
unless Metasploit::Framework::Database.configurations_pathname.try(:to_path)
@ -108,6 +110,23 @@ RSpec.configure do |config|
# # Equivalent to being in spec/controllers
# end
config.infer_spec_type_from_file_location!
if ENV['REMOTE_DB']
require 'metasploit/framework/data_service/remote/managed_dbws'
opts = {}
opts[:process_name] = 'msfdb_ws'
opts[:host] = 'localhost'
opts[:port] = '8080'
config.before(:suite) do
Metasploit::Framework::DataService::ManagedDBWS.instance.start(opts)
end
config.after(:suite) do
Metasploit::Framework::DataService::ManagedDBWS.instance.stop
end
end
end
Metasploit::Framework::Spec::Constants::Suite.configure!

View File

@ -6,6 +6,12 @@ RSpec.shared_context 'Msf::DBManager' do
end
let(:db_manager) do
if ENV['REMOTE_DB']
require 'metasploit/framework/data_service/remote/managed_dbws'
remote_data_service = Metasploit::Framework::DataService::ManagedDBWS.instance.remote_data_service
framework.db.register_data_service(remote_data_service, true)
end
framework.db.get_data_service
end

View File

@ -1,4 +1,8 @@
RSpec.shared_examples_for 'Msf::DBManager::Adapter' do
if ENV['REMOTE_DB']
before {skip("Awaiting evaluation")}
end
context 'CONSTANTS' do
context 'ADAPTER' do
subject(:adapter) {

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Client' do
if ENV['REMOTE_DB']
before {skip("Awaiting port")}
end
it { is_expected.to respond_to :find_or_create_client }
it { is_expected.to respond_to :get_client }
it { is_expected.to respond_to :report_client }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Connection' do
if ENV['REMOTE_DB']
before {skip("Not applicable for remote DB")}
end
it { is_expected.to respond_to :active }
it { is_expected.to respond_to :after_establish_connection }
it { is_expected.to respond_to :connect }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Cred' do
if ENV['REMOTE_DB']
before {skip("Awaiting cred port")}
end
it { is_expected.to respond_to :creds }
it { is_expected.to respond_to :each_cred }
it { is_expected.to respond_to :find_or_create_cred }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Event' do
if ENV['REMOTE_DB']
before {skip("Awaiting proper port")}
end
it { is_expected.to respond_to :events }
it { is_expected.to respond_to :report_event }
end

View File

@ -1,5 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do
it { is_expected.to respond_to :report_exploit }
if ENV['REMOTE_DB']
before {skip("Remote exploit needs its own testing")}
end
it { is_expected.to respond_to :report_exploit_attempt }
it { is_expected.to respond_to :report_exploit_failure }
it { is_expected.to respond_to :report_exploit_success }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::ExploitedHost' do
if ENV['REMOTE_DB']
before {skip("Not supported for remote DB")}
end
it { is_expected.to respond_to :each_exploited_host }
it { is_expected.to respond_to :exploited_hosts }
end

View File

@ -1,10 +1,18 @@
RSpec.shared_examples_for 'Msf::DBManager::Host' do
it { is_expected.to respond_to :del_host }
it { is_expected.to respond_to :each_host }
unless ENV['REMOTE_DB']
it { is_expected.to respond_to :each_host }
# TODO: Evaluate need
it { is_expected.to respond_to :del_host }
it { is_expected.to respond_to :update_host_via_sysinfo }
it { is_expected.to respond_to :has_host? }
end
it { is_expected.to respond_to :find_or_create_host }
it { is_expected.to respond_to :get_host }
it { is_expected.to respond_to :has_host? }
it { is_expected.to respond_to :hosts }
it { is_expected.to respond_to :report_host }
it { is_expected.to respond_to :update_host_via_sysinfo }
end

View File

@ -1,3 +1,8 @@
RSpec.shared_examples_for 'Msf::DBManager::HostDetail' do
if ENV['REMOTE_DB']
before {skip("HostDetail Port required")}
end
it { is_expected.to respond_to :report_host_details }
end

View File

@ -1,3 +1,8 @@
RSpec.shared_examples_for 'Msf::DBManager::HostTag' do
if ENV['REMOTE_DB']
before {skip("")}
end
it { is_expected.to respond_to :report_host_tag }
end

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Import' do
if ENV['REMOTE_DB']
before {skip("Awaiting import ticket")}
end
it { is_expected.to respond_to :dehex }
it { is_expected.to respond_to :emit }
it { is_expected.to respond_to :import }

View File

@ -2,6 +2,11 @@
require 'builder'
RSpec.shared_examples_for 'Msf::DBManager::Import::MetasploitFramework::XML' do
if ENV['REMOTE_DB']
before {skip("Awaiting a port of all components")}
end
# Serialized format from pro/modules/auxiliary/pro/report.rb
def serialize(object)
# FIXME https://www.pivotaltracker.com/story/show/46578647

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::IPAddress' do
if ENV['REMOTE_DB']
before {skip("Not applicable for remote DB")}
end
it { is_expected.to respond_to :ipv46_validator }
it { is_expected.to respond_to :ipv4_validator }
it { is_expected.to respond_to :ipv6_validator }

View File

@ -1,6 +1,10 @@
RSpec.shared_examples_for 'Msf::DBManager::Loot' do
it { is_expected.to respond_to :each_loot }
unless ENV['REMOTE_DB']
it { is_expected.to respond_to :each_loot }
end
it { is_expected.to respond_to :find_or_create_loot }
it { is_expected.to respond_to :loots }
it { is_expected.to respond_to :loot }
it { is_expected.to respond_to :report_loot }
end

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Migration' do
if ENV['REMOTE_DB']
before {skip("Migration is not tested for a remoted DB")}
end
it { is_expected.to be_a Msf::DBManager::Migration }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do
if ENV['REMOTE_DB']
before {skip("Module Cache methods will not be ported, instead the newer module metadata cache should be used")}
end
it { is_expected.to respond_to :match_values }
it { is_expected.to respond_to :module_to_details_hash }
it { is_expected.to respond_to :modules_cached }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Note' do
if ENV['REMOTE_DB']
before {skip("Awaiting port")}
end
it { is_expected.to respond_to :each_note }
it { is_expected.to respond_to :find_or_create_note }
it { is_expected.to respond_to :notes }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Ref' do
if ENV['REMOTE_DB']
before {skip("Not supported for remote DB")}
end
it { is_expected.to respond_to :find_or_create_ref }
it { is_expected.to respond_to :get_ref }
it { is_expected.to respond_to :has_ref? }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Report' do
if ENV['REMOTE_DB']
before {skip("Awaiting report port")}
end
it { is_expected.to respond_to :find_or_create_report }
it { is_expected.to respond_to :report_artifact }
it { is_expected.to respond_to :report_report }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Route' do
if ENV['REMOTE_DB']
before {skip("Awaiting evaluation")}
end
it { is_expected.to respond_to :report_session_route }
it { is_expected.to respond_to :report_session_route_remove }
end

View File

@ -1,8 +1,14 @@
RSpec.shared_examples_for 'Msf::DBManager::Service' do
it { is_expected.to respond_to :delete_service }
it { is_expected.to respond_to :each_service }
it { is_expected.to respond_to :find_or_create_service }
it { is_expected.to respond_to :get_service }
unless ENV['REMOTE_DB']
it { is_expected.to respond_to :each_service }
# TODO: This needs to be implemented on a service
it { is_expected.to respond_to :find_or_create_service }
it { is_expected.to respond_to :get_service }
end
it { is_expected.to respond_to :report_service }
it { is_expected.to respond_to :services }
end

View File

@ -1,6 +1,10 @@
RSpec.shared_examples_for 'Msf::DBManager::Session' do
it { is_expected.to respond_to :get_session }
if ENV['REMOTE_DB']
before {skip("Awaiting sessions port")}
end
context '#report_session' do
let(:options) do
{}

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Task' do
if ENV['REMOTE_DB']
before {skip("Not supported for remote DB")}
end
it { is_expected.to respond_to :find_or_create_task }
it { is_expected.to respond_to :report_task }
it { is_expected.to respond_to :tasks }

View File

@ -1,10 +1,16 @@
RSpec.shared_examples_for 'Msf::DBManager::Vuln' do
it { is_expected.to respond_to :each_vuln }
it { is_expected.to respond_to :find_or_create_vuln }
it { is_expected.to respond_to :find_vuln_by_details }
it { is_expected.to respond_to :find_vuln_by_refs }
it { is_expected.to respond_to :get_vuln }
it { is_expected.to respond_to :has_vuln? }
unless ENV['REMOTE_DB']
it { is_expected.to respond_to :each_vuln }
# TODO: Evaluate need
it { is_expected.to respond_to :find_vuln_by_refs }
it { is_expected.to respond_to :find_or_create_vuln }
it { is_expected.to respond_to :has_vuln? }
it { is_expected.to respond_to :get_vuln }
it { is_expected.to respond_to :find_vuln_by_details }
end
it { is_expected.to respond_to :report_vuln }
it { is_expected.to respond_to :vulns }
end

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::VulnDetail' do
if ENV['REMOTE_DB']
before {skip("Awaiting evaluation")}
end
it { is_expected.to respond_to :report_vuln_details }
it { is_expected.to respond_to :update_vuln_details }
end

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::Web' do
if ENV['REMOTE_DB']
before {skip("Awaiting web port")}
end
it { is_expected.to respond_to :report_web_form }
it { is_expected.to respond_to :report_web_page }
it { is_expected.to respond_to :report_web_site }

View File

@ -1,4 +1,9 @@
RSpec.shared_examples_for 'Msf::DBManager::WMAP' do
if ENV['REMOTE_DB']
before {skip("Awaiting wmap port")}
end
it { is_expected.to respond_to :create_request }
it { is_expected.to respond_to :create_target }
it { is_expected.to respond_to :delete_all_targets }