metasploit-framework icon indicating copy to clipboard operation
metasploit-framework copied to clipboard

restrict analyze creds to host workspace

Open jmartin-tech opened this issue 2 years ago • 0 comments

When evaluating available data to target a host via the analyze command only credentials in the current workspace are consider valid for use however since the workspace is not passed into the method call for creds the workspace gets looked up from the framework object. In cases where framework is running as a service process an internally cached workspace object held by a messaging thread may be returned in the call to @framework.db.creds().

Further another framework instance communicating to the same database can delete the entry for the cached workspace. This can lead to an exception when attempting to lookup credentials based on a workspace id that was removed resulting in an error returned to an RPC or JSON request to db.analyze_host.

Another edge case I have only been able to reproduce once can lead to an incorrect lookup of credential information from the workspace held by the cached thread and report incorrectly that a module is READY_FOR_TEST when it should be REQUIRES_CRED due to credentials existing in the cached workspace when a request for analysis of a host in another workspace is made.

By passing the specific workspace of the host being analyzed, the possibility of getting credentials for a workspace not connected to the host is removed and the risk of attempting to lookup a deleted workspace is reduced. Race conditions do still exists where the workspace in which the current host is contained is deleted in the middle of analysis, however since that would also delete the host being processed, that edge case is of less concern.

Fully addressing the thread cached workspace is beyond the scope of this adjustment since the workspace is required in a call of db.analyze_host expectations that workspace requested will exist for the full transaction are assumed.

List the steps needed to make sure this thing works

In terminal A:

  • [ ] Start msfconsole
  • [ ] workspace -a other
  • [ ] use auxiliary/scanner/smb/smb_ms17_010
  • [ ] run rhost=<metasploitable3 ip>
  • [ ] workspace -a more

In terminal B:

  • [ ] Start msfrpcd -P aaaa
  • [ ] Start msfrpc -P aaaa -a localhost
  • [ ] rpc.call('db.workspaces')
  • [ ] rpc.call('db.set_workspace', 'more')
  • [ ] rpc.call('db.analyze_host', { host: '<metasploitable3 ip>', workspace: 'other' } )

In terminal A:

  • [ ] workspace default
  • [ ] workspace -d more

In terminal B:

  • [ ] rpc.call('db.analyze_host', { host: '<metasploitable3 ip>', workspace: 'other' } )

Pre patch

Terminal A:

msf6 > workspace -a other
[*] Added workspace: other
[*] Workspace: other
msf6 > use auxiliary/scanner/smb/smb_ms17_010
msf6 auxiliary(scanner/smb/smb_ms17_010) > run rhost=192.0.2.138

[+] 192.0.2.138:445   - Host is likely VULNERABLE to MS17-010! - Windows Server 2008 R2 Standard 7601 Service Pack 1 x64 (64-bit)
[*] 192.0.2.138:445   - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/smb/smb_ms17_010) > workspace -a more
[*] Added workspace: more
[*] Workspace: more
msf6 auxiliary(scanner/smb/smb_ms17_010) > workspace default

Terminal B:

msf-tester:metasploit-framework jmartin$ ./msfrpcd -P aaaa
[*] MSGRPC starting on 0.0.0.0:55553 (SSL):Msg...
[*] MSGRPC backgrounding at 2022-08-19 16:11:14 -0500...
[*] MSGRPC background PID 9551
msf-tester:metasploit-framework jmartin$ ./msfrpc -P aaaa -a localhost
[*] The 'rpc' object holds the RPC client interface
[*] Use rpc.call('group.command') to make RPC calls

>> rpc.call('db.workspaces')
=>
{"workspaces"=>
  [{"id"=>1, "name"=>"default", "created_at"=>1660943366, "updated_at"=>1660943366},
   {"id"=>2, "name"=>"other", "created_at"=>1660943397, "updated_at"=>1660943397},
   {"id"=>3, "name"=>"more", "created_at"=>1660943457, "updated_at"=>1660943457}]}
>> rpc.call('db.set_workspace', 'more')
=> {"result"=>"success"}
>> rpc.call('db.analyze_host', { host: '192.0.2.138', workspace: 'other' } )
=>
{"host"=>
  {"address"=>"192.0.2.138",
   "modules"=>
    [{"mtype"=>"exploit", "mname"=>"exploit/windows/smb/ms17_010_eternalblue", "state"=>"READY_FOR_TEST", "description"=>"ready for testing", "options"=>{"invalid"=>[], "missing"=>[]}},
     {"mtype"=>"exploit",
      "mname"=>"exploit/windows/smb/ms17_010_psexec",
      "state"=>"REQUIRES_CRED",
      "description"=>"credentials are required",
      "options"=>{"invalid"=>[], "missing"=>["credential"]}},
     {"mtype"=>"exploit", "mname"=>"exploit/windows/smb/smb_doublepulsar_rce", "state"=>"READY_FOR_TEST", "description"=>"ready for testing", "options"=>{"invalid"=>[], "missing"=>[]}}]}}

Terminal A (Continued):

[*] Workspace: default
msf6 auxiliary(scanner/smb/smb_ms17_010) > workspace -d more
[*] Deleted workspace: more
msf6 auxiliary(scanner/smb/smb_ms17_010) >

Terminal B (Continued):

>> rpc.call('db.analyze_host', { host: '192.0.2.138', workspace: 'other' } )
/metasploit/metasploit-framework/lib/msf/core/rpc/v10/client.rb:157:in `send_rpc_request': RuntimeError Couldn't find workspace more ["lib/msf/util/db_manager.rb:52:in `process_opts_workspace'", "lib/msf/core/db_manager/cred.rb:11:in `block in creds'", "lib/active_record/connection_adapters/abstract/connection_pool.rb:462:in `with_connection'", "lib/msf/core/db_manager/cred.rb:5:in `creds'", "lib/metasploit/framework/data_service/proxy/credential_data_proxy.rb:54:in `block in creds'", "lib/metasploit/framework/data_service/proxy/core.rb:164:in `data_service_operation'", "lib/metasploit/framework/data_service/proxy/credential_data_proxy.rb:52:in `creds'", "lib/msf/core/analyze.rb:80:in `block (3 levels) in suggest_modules_for_vulns'", "lib/msf/core/analyze.rb:76:in `each'", "lib/msf/core/analyze.rb:76:in `block (2 levels) in suggest_modules_for_vulns'", "lib/ruby/3.0.0/set.rb:344:in `each_key'", "lib/ruby/3.0.0/set.rb:344:in `each'", "lib/msf/core/analyze.rb:74:in `block in suggest_modules_for_vulns'", "lib/msf/core/analyze.rb:69:in `each'", "lib/msf/core/analyze.rb:69:in `suggest_modules_for_vulns'", "lib/msf/core/analyze.rb:16:in `host'", "lib/msf/core/rpc/v10/rpc_db.rb:789:in `block in rpc_analyze_host'", "lib/active_record/connection_adapters/abstract/connection_pool.rb:462:in `with_connection'", "lib/msf/core/rpc/v10/rpc_db.rb:775:in `rpc_analyze_host'", "lib/msf/core/rpc/v10/service.rb:143:in `block in process'", "lib/timeout.rb:179:in `block in timeout'", "lib/timeout.rb:36:in `block in catch'", "lib/timeout.rb:36:in `catch'", "lib/timeout.rb:36:in `catch'", "lib/timeout.rb:188:in `timeout'", "lib/msf/core/rpc/v10/service.rb:143:in `process'", "lib/msf/core/rpc/v10/service.rb:81:in `on_request_uri'", "lib/msf/core/rpc/v10/service.rb:62:in `block in start'", "lib/rex/proto/http/handler/proc.rb:38:in `on_request'", "lib/rex/proto/http/server.rb:369:in `dispatch_request'", "lib/rex/proto/http/server.rb:303:in `on_client_data'", "lib/rex/proto/http/server.rb:162:in `block in start'", "lib/rex/io/stream_server.rb:42:in `on_client_data'", "lib/rex/io/stream_server.rb:185:in `block in monitor_clients'", "lib/rex/io/stream_server.rb:184:in `each'", "lib/rex/io/stream_server.rb:184:in `monitor_clients'", "lib/rex/io/stream_server.rb:64:in `block in start'", "lib/rex/thread_factory.rb:22:in `block in spawn'", "lib/msf/core/thread_manager.rb:105:in `block in spawn'", "lib/logging/diagnostic_context.rb:474:in `block in create_with_logging_context'"] (Msf::RPC::ServerException)
	from /metasploit/metasploit-framework/lib/msf/core/rpc/v10/client.rb:104:in `call'
	from (irb):6:in `<main>'
	from /metasploit/metasploit-framework/lib/rex/ui/text/irb_shell.rb:53:in `block in run'
	from /metasploit/metasploit-framework/lib/rex/ui/text/irb_shell.rb:52:in `catch'
	from /metasploit/metasploit-framework/lib/rex/ui/text/irb_shell.rb:52:in `run'
	from ./msfrpc:91:in `<main>'

Post patch

Terminal A:

msf6 > workspace -a other
[*] Added workspace: other
[*] Workspace: other
msf6 > use auxiliary/scanner/smb/smb_ms17_010
msf6 auxiliary(scanner/smb/smb_ms17_010) > run rhost=192.0.2.138

[+] 192.0.2.138:445   - Host is likely VULNERABLE to MS17-010! - Windows Server 2008 R2 Standard 7601 Service Pack 1 x64 (64-bit)
[*] 192.0.2.138:445   - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/smb/smb_ms17_010) > workspace -a more
[*] Added workspace: more
[*] Workspace: more

Terminal B:

msf-tester:metasploit-framework jmartin$ kill 9551
msf-tester:metasploit-framework jmartin$ ./msfrpcd -P aaaa
[*] MSGRPC starting on 0.0.0.0:55553 (SSL):Msg...
[*] MSGRPC backgrounding at 2022-08-19 16:19:18 -0500...
[*] MSGRPC background PID 10034
msf-tester:metasploit-framework jmartin$ ./msfrpc -P aaaa -a localhost
[*] The 'rpc' object holds the RPC client interface
[*] Use rpc.call('group.command') to make RPC calls

>> rpc.call('db.workspaces')
=>
{"workspaces"=>
  [{"id"=>1, "name"=>"default", "created_at"=>1660943746, "updated_at"=>1660943746},
   {"id"=>2, "name"=>"other", "created_at"=>1660943873, "updated_at"=>1660943873},
   {"id"=>3, "name"=>"more", "created_at"=>1660943911, "updated_at"=>1660943911}]}
>> rpc.call('db.creds', { workspace: 'more' } )
=> {"creds"=>[]}
>> rpc.call('db.analyze_host', { host: '192.0.2.138', workspace: 'other' } )
=>
{"host"=>
  {"address"=>"192.0.2.138",
   "modules"=>
    [{"mtype"=>"exploit", "mname"=>"exploit/windows/smb/ms17_010_eternalblue", "state"=>"READY_FOR_TEST", "description"=>"ready for testing", "options"=>{"invalid"=>[], "missing"=>[]}},
     {"mtype"=>"exploit",
      "mname"=>"exploit/windows/smb/ms17_010_psexec",
      "state"=>"REQUIRES_CRED",
      "description"=>"credentials are required",
      "options"=>{"invalid"=>[], "missing"=>["credential"]}},
     {"mtype"=>"exploit", "mname"=>"exploit/windows/smb/smb_doublepulsar_rce", "state"=>"READY_FOR_TEST", "description"=>"ready for testing", "options"=>{"invalid"=>[], "missing"=>[]}}]}}
>> rpc.call('db.set_workspace', 'more')
=> {"result"=>"success"}

Terminal A (Continued):

msf6 auxiliary(scanner/smb/smb_ms17_010) > workspace -d more
[*] Deleted workspace: more
[*] Switched to workspace: default

Terminal B (Continued):

>> rpc.call('db.analyze_host', { host: '192.0.2.138', workspace: 'other' } )
=>
{"host"=>
  {"address"=>"192.0.2.138",
   "modules"=>
    [{"mtype"=>"exploit", "mname"=>"exploit/windows/smb/ms17_010_eternalblue", "state"=>"READY_FOR_TEST", "description"=>"ready for testing", "options"=>{"invalid"=>[], "missing"=>[]}},
     {"mtype"=>"exploit",
      "mname"=>"exploit/windows/smb/ms17_010_psexec",
      "state"=>"REQUIRES_CRED",
      "description"=>"credentials are required",
      "options"=>{"invalid"=>[], "missing"=>["credential"]}},
     {"mtype"=>"exploit", "mname"=>"exploit/windows/smb/smb_doublepulsar_rce", "state"=>"READY_FOR_TEST", "description"=>"ready for testing", "options"=>{"invalid"=>[], "missing"=>[]}}]}}

jmartin-tech avatar Aug 19 '22 21:08 jmartin-tech