Lucene search

K
metasploitTodb <[email protected]>, hdm <[email protected]>MSF:AUXILIARY-SCANNER-UPNP-SSDP_MSEARCH-
HistoryNov 09, 2010 - 6:24 a.m.

UPnP SSDP M-SEARCH Information Discovery

2010-11-0906:24:32
www.rapid7.com
31

6.4 Medium

AI Score

Confidence

Low

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.974 High

EPSS

Percentile

99.9%

Discover information from UPnP-enabled systems

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::UDPScanner

  def initialize
    super(
      'Name'        => 'UPnP SSDP M-SEARCH Information Discovery',
      'Description' => 'Discover information from UPnP-enabled systems',
      'Author'      => [ 'todb', 'hdm'], # Original scanner module and vuln info reporter, respectively
      'License'     => MSF_LICENSE,
      'References'  =>
        [
          ['CVE', '2012-5958'],
          ['CVE', '2012-5959'],
          ['CVE', '2013-0230'],
          ['CVE', '2013-0229']
        ]
    )

    register_options( [
      Opt::RPORT(1900),
      OptBool.new('REPORT_LOCATION', [true, 'This determines whether to report the UPnP endpoint service advertised by SSDP', false ])
    ])
  end

  def rport
    datastore['RPORT']
  end

  def setup
    super
    @msearch_probe =
      "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"
  end

  def scanner_prescan(batch)
    print_status("Sending UPnP SSDP probes to #{batch[0]}->#{batch[-1]} (#{batch.length} hosts)")
    @results = {}
  end

  def scan_host(ip)
    vprint_status "#{ip}:#{rport} - SSDP - sending M-SEARCH probe"
    scanner_send(@msearch_probe, ip, datastore['RPORT'])
  end

  def scanner_postscan(batch)
    print_status "No SSDP endpoints found." if @results.empty?

    @results.each_pair do |skey,res|
      sinfo = res[:service]
      next unless sinfo

      bits = []

      [ :server, :location, :usn ].each do |k|
        bits << res[:info][k] if res[:info][k]
      end

      desc = bits.join(" | ")
      sinfo[:info] = desc

      res[:vulns] = []

      if res[:info][:server].to_s =~ /MiniUPnPd\/1\.0([\.\,\-\~\s]|$)/mi
        res[:vulns] << {
          :name => "MiniUPnPd ProcessSSDPRequest() Out of Bounds Memory Access Denial of Service",
          :refs => [ 'CVE-2013-0229' ]
        }
      end

      if res[:info][:server].to_s =~ /MiniUPnPd\/1\.[0-3]([\.\,\-\~\s]|$)/mi
        res[:vulns] << {
          :name  => "MiniUPnPd ExecuteSoapAction memcpy() Remote Code Execution",
          :refs  => [ 'CVE-2013-0230' ],
          :port  => res[:info][:ssdp_port] || 80,
          :proto => 'tcp'
        }
      end

      if res[:info][:server].to_s =~ /Intel SDK for UPnP devices.*|Portable SDK for UPnP devices(\/?\s*$|\/1\.([0-5]\..*|8\.0.*|(6\.[0-9]|6\.1[0-7])([\.\,\-\~\s]|$)))/mi
        res[:vulns] << {
          :name => "Portable SDK for UPnP Devices unique_service_name() Remote Code Execution",
          :refs => [ 'CVE-2012-5958', 'CVE-2012-5959' ]
        }
      end

      if res[:vulns].length > 0
        vrefs = []
        res[:vulns].each do |v|
          v[:refs].each do |r|
            vrefs << r
          end
        end

        print_good("#{skey} SSDP #{desc} | vulns:#{res[:vulns].count} (#{vrefs.join(", ")})")
      else
        print_status("#{skey} SSDP #{desc}")
      end

      report_service( sinfo )

      res[:vulns].each do |v|
        report_vuln(
          :host  => sinfo[:host],
          :port  => v[:port]  || sinfo[:port],
          :proto => v[:proto] || 'udp',
          :name  => v[:name],
          :info  => res[:info][:server],
          :refs  => v[:refs]
        )
      end

      if res[:info][:ssdp_host]
        report_service(
          :host  => res[:info][:ssdp_host],
          :port  => res[:info][:ssdp_port],
          :proto => 'tcp',
          :name  => 'upnp',
          :info  => res[:info][:location].to_s
        ) if datastore['REPORT_LOCATION']
      end
    end
  end

  def scanner_process(data, shost, sport)

    skey = "#{shost}:#{datastore['RPORT']}"

    @results[skey] ||= {
      :info    => { },
      :service => {
        :host  => shost,
        :port  => datastore['RPORT'],
        :proto => 'udp',
        :name  => 'ssdp'
      }
    }

    if data =~ /^Server:[\s]*(.*)/i
      @results[skey][:info][:server] = $1.strip
    end

    ssdp_host = nil
    ssdp_port = 80
    location_string = ''
    if data =~ /^Location:[\s]*(.*)/i
      location_string = $1
      @results[skey][:info][:location] = $1.strip
      if location_string[/(https?):\x2f\x2f([^\x5c\x2f]+)/]
        ssdp_host,ssdp_port = $2.split(":") if $2.respond_to?(:split)
        if ssdp_port.nil?
          ssdp_port = ($1 == "http" ? 80 : 443)
        end

        if ssdp_host and ssdp_port
          @results[skey][:info][:ssdp_host] = ssdp_host
          @results[skey][:info][:ssdp_port] = ssdp_port.to_i
        end

      end
    end

    if data =~ /^USN:[\s]*(.*)/i
      @results[skey][:info][:usn] = $1.strip
    end

  end


end

6.4 Medium

AI Score

Confidence

Low

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.974 High

EPSS

Percentile

99.9%