Commit 05f92cef authored by Sophie Brun's avatar Sophie Brun

New upstream version 1.5.9

parent 3d5a7f8c
...@@ -5,15 +5,15 @@ ...@@ -5,15 +5,15 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "bettercap" s.name = "bettercap"
s.version = "1.5.8" s.version = "1.5.9"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Simone Margaritelli"] s.authors = ["Simone Margaritelli"]
s.date = "2016-08-26" s.date = "2016-12-14"
s.description = "BetterCap is the state of the art, modular, portable and easily extensible MITM framework featuring ARP, DNS and ICMP spoofing, sslstripping, credentials harvesting and more." s.description = "BetterCap is the state of the art, modular, portable and easily extensible MITM framework featuring ARP, DNS and ICMP spoofing, sslstripping, credentials harvesting and more."
s.email = "evilsocket@gmail.com" s.email = "evilsocket@gmail.com"
s.executables = ["bettercap"] s.executables = ["bettercap"]
s.files = ["LICENSE.md", "README.md", "bin/bettercap", "lib/bettercap.rb", "lib/bettercap/banner", "lib/bettercap/context.rb", "lib/bettercap/discovery/agents/arp.rb", "lib/bettercap/discovery/agents/base.rb", "lib/bettercap/discovery/agents/icmp.rb", "lib/bettercap/discovery/agents/udp.rb", "lib/bettercap/discovery/thread.rb", "lib/bettercap/error.rb", "lib/bettercap/firewalls/base.rb", "lib/bettercap/firewalls/bsd.rb", "lib/bettercap/firewalls/linux.rb", "lib/bettercap/firewalls/redirection.rb", "lib/bettercap/loader.rb", "lib/bettercap/logger.rb", "lib/bettercap/memory.rb", "lib/bettercap/monkey/celluloid/actor.rb", "lib/bettercap/monkey/celluloid/io/udp_socket.rb", "lib/bettercap/monkey/em-proxy/proxy.rb", "lib/bettercap/monkey/openssl/server.rb", "lib/bettercap/monkey/packetfu/pcap.rb", "lib/bettercap/monkey/packetfu/utils.rb", "lib/bettercap/monkey/system.rb", "lib/bettercap/network/arp_reader.rb", "lib/bettercap/network/hw-prefixes", "lib/bettercap/network/network.rb", "lib/bettercap/network/packet_queue.rb", "lib/bettercap/network/protos/base.rb", "lib/bettercap/network/protos/dhcp.rb", "lib/bettercap/network/protos/mysql.rb", "lib/bettercap/network/protos/ntlm.rb", "lib/bettercap/network/protos/snmp.rb", "lib/bettercap/network/protos/teamviewer.rb", "lib/bettercap/network/servers/dnsd.rb", "lib/bettercap/network/servers/httpd.rb", "lib/bettercap/network/services", "lib/bettercap/network/target.rb", "lib/bettercap/network/validator.rb", "lib/bettercap/options/core_options.rb", "lib/bettercap/options/options.rb", "lib/bettercap/options/proxy_options.rb", "lib/bettercap/options/server_options.rb", "lib/bettercap/options/sniff_options.rb", "lib/bettercap/options/spoof_options.rb", "lib/bettercap/pluggable.rb", "lib/bettercap/proxy/http/module.rb", "lib/bettercap/proxy/http/modules/injectcss.rb", "lib/bettercap/proxy/http/modules/injecthtml.rb", "lib/bettercap/proxy/http/modules/injectjs.rb", "lib/bettercap/proxy/http/proxy.rb", "lib/bettercap/proxy/http/request.rb", "lib/bettercap/proxy/http/response.rb", "lib/bettercap/proxy/http/ssl/authority.rb", "lib/bettercap/proxy/http/ssl/bettercap-ca.pem", "lib/bettercap/proxy/http/ssl/server.rb", "lib/bettercap/proxy/http/sslstrip/cookiemonitor.rb", "lib/bettercap/proxy/http/sslstrip/lock.ico", "lib/bettercap/proxy/http/sslstrip/strip.rb", "lib/bettercap/proxy/http/streamer.rb", "lib/bettercap/proxy/stream_logger.rb", "lib/bettercap/proxy/tcp/module.rb", "lib/bettercap/proxy/tcp/proxy.rb", "lib/bettercap/proxy/thread_pool.rb", "lib/bettercap/shell.rb", "lib/bettercap/sniffer/parsers/base.rb", "lib/bettercap/sniffer/parsers/cookie.rb", "lib/bettercap/sniffer/parsers/creditcard.rb", "lib/bettercap/sniffer/parsers/custom.rb", "lib/bettercap/sniffer/parsers/dhcp.rb", "lib/bettercap/sniffer/parsers/dict.rb", "lib/bettercap/sniffer/parsers/ftp.rb", "lib/bettercap/sniffer/parsers/httpauth.rb", "lib/bettercap/sniffer/parsers/https.rb", "lib/bettercap/sniffer/parsers/irc.rb", "lib/bettercap/sniffer/parsers/mail.rb", "lib/bettercap/sniffer/parsers/mpd.rb", "lib/bettercap/sniffer/parsers/mysql.rb", "lib/bettercap/sniffer/parsers/nntp.rb", "lib/bettercap/sniffer/parsers/ntlmss.rb", "lib/bettercap/sniffer/parsers/pgsql.rb", "lib/bettercap/sniffer/parsers/post.rb", "lib/bettercap/sniffer/parsers/redis.rb", "lib/bettercap/sniffer/parsers/rlogin.rb", "lib/bettercap/sniffer/parsers/snmp.rb", "lib/bettercap/sniffer/parsers/snpp.rb", "lib/bettercap/sniffer/parsers/teamviewer.rb", "lib/bettercap/sniffer/parsers/url.rb", "lib/bettercap/sniffer/parsers/whatsapp.rb", "lib/bettercap/sniffer/sniffer.rb", "lib/bettercap/spoofers/arp.rb", "lib/bettercap/spoofers/base.rb", "lib/bettercap/spoofers/icmp.rb", "lib/bettercap/spoofers/none.rb", "lib/bettercap/update_checker.rb", "lib/bettercap/version.rb"] s.files = ["LICENSE.md", "README.md", "bin/bettercap", "lib/bettercap.rb", "lib/bettercap/banner", "lib/bettercap/context.rb", "lib/bettercap/discovery/agents/arp.rb", "lib/bettercap/discovery/agents/base.rb", "lib/bettercap/discovery/agents/icmp.rb", "lib/bettercap/discovery/agents/udp.rb", "lib/bettercap/discovery/thread.rb", "lib/bettercap/error.rb", "lib/bettercap/firewalls/base.rb", "lib/bettercap/firewalls/bsd.rb", "lib/bettercap/firewalls/linux.rb", "lib/bettercap/firewalls/redirection.rb", "lib/bettercap/loader.rb", "lib/bettercap/logger.rb", "lib/bettercap/memory.rb", "lib/bettercap/monkey/celluloid/actor.rb", "lib/bettercap/monkey/celluloid/io/udp_socket.rb", "lib/bettercap/monkey/em-proxy/proxy.rb", "lib/bettercap/monkey/openssl/server.rb", "lib/bettercap/monkey/packetfu/pcap.rb", "lib/bettercap/monkey/packetfu/utils.rb", "lib/bettercap/monkey/system.rb", "lib/bettercap/network/arp_reader.rb", "lib/bettercap/network/hw-prefixes", "lib/bettercap/network/network.rb", "lib/bettercap/network/packet_queue.rb", "lib/bettercap/network/protos/base.rb", "lib/bettercap/network/protos/dhcp.rb", "lib/bettercap/network/protos/mysql.rb", "lib/bettercap/network/protos/ntlm.rb", "lib/bettercap/network/protos/snmp.rb", "lib/bettercap/network/protos/teamviewer.rb", "lib/bettercap/network/servers/dnsd.rb", "lib/bettercap/network/servers/httpd.rb", "lib/bettercap/network/services", "lib/bettercap/network/target.rb", "lib/bettercap/network/validator.rb", "lib/bettercap/options/core_options.rb", "lib/bettercap/options/options.rb", "lib/bettercap/options/proxy_options.rb", "lib/bettercap/options/server_options.rb", "lib/bettercap/options/sniff_options.rb", "lib/bettercap/options/spoof_options.rb", "lib/bettercap/pluggable.rb", "lib/bettercap/proxy/http/module.rb", "lib/bettercap/proxy/http/modules/injectcss.rb", "lib/bettercap/proxy/http/modules/injecthtml.rb", "lib/bettercap/proxy/http/modules/injectjs.rb", "lib/bettercap/proxy/http/modules/redirect.rb", "lib/bettercap/proxy/http/proxy.rb", "lib/bettercap/proxy/http/request.rb", "lib/bettercap/proxy/http/response.rb", "lib/bettercap/proxy/http/ssl/authority.rb", "lib/bettercap/proxy/http/ssl/bettercap-ca.pem", "lib/bettercap/proxy/http/ssl/server.rb", "lib/bettercap/proxy/http/sslstrip/cookiemonitor.rb", "lib/bettercap/proxy/http/sslstrip/lock.ico", "lib/bettercap/proxy/http/sslstrip/strip.rb", "lib/bettercap/proxy/http/streamer.rb", "lib/bettercap/proxy/stream_logger.rb", "lib/bettercap/proxy/tcp/module.rb", "lib/bettercap/proxy/tcp/proxy.rb", "lib/bettercap/proxy/thread_pool.rb", "lib/bettercap/shell.rb", "lib/bettercap/sniffer/parsers/base.rb", "lib/bettercap/sniffer/parsers/cookie.rb", "lib/bettercap/sniffer/parsers/custom.rb", "lib/bettercap/sniffer/parsers/dhcp.rb", "lib/bettercap/sniffer/parsers/dict.rb", "lib/bettercap/sniffer/parsers/ftp.rb", "lib/bettercap/sniffer/parsers/httpauth.rb", "lib/bettercap/sniffer/parsers/https.rb", "lib/bettercap/sniffer/parsers/irc.rb", "lib/bettercap/sniffer/parsers/mail.rb", "lib/bettercap/sniffer/parsers/mpd.rb", "lib/bettercap/sniffer/parsers/mysql.rb", "lib/bettercap/sniffer/parsers/nntp.rb", "lib/bettercap/sniffer/parsers/ntlmss.rb", "lib/bettercap/sniffer/parsers/pgsql.rb", "lib/bettercap/sniffer/parsers/post.rb", "lib/bettercap/sniffer/parsers/redis.rb", "lib/bettercap/sniffer/parsers/rlogin.rb", "lib/bettercap/sniffer/parsers/snmp.rb", "lib/bettercap/sniffer/parsers/snpp.rb", "lib/bettercap/sniffer/parsers/teamviewer.rb", "lib/bettercap/sniffer/parsers/url.rb", "lib/bettercap/sniffer/parsers/whatsapp.rb", "lib/bettercap/sniffer/sniffer.rb", "lib/bettercap/spoofers/arp.rb", "lib/bettercap/spoofers/base.rb", "lib/bettercap/spoofers/icmp.rb", "lib/bettercap/spoofers/none.rb", "lib/bettercap/update_checker.rb", "lib/bettercap/version.rb"]
s.homepage = "http://github.com/evilsocket/bettercap" s.homepage = "http://github.com/evilsocket/bettercap"
s.licenses = ["GPL-3.0"] s.licenses = ["GPL-3.0"]
s.rdoc_options = ["--charset=UTF-8"] s.rdoc_options = ["--charset=UTF-8"]
......
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
=end =end
# Ensure bettercap is running with root privileges.
abort 'This software must run as root.' unless Process.uid.zero?
require 'bettercap' require 'bettercap'
begin begin
......
...@@ -17,52 +17,41 @@ ...@@ -17,52 +17,41 @@
Encoding.default_external = Encoding::UTF_8 Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8 Encoding.default_internal = Encoding::UTF_8
require 'base64' require 'packetfu'
require 'em-proxy'
require 'webrick'
require 'rubydns'
require 'colorize' require 'colorize'
require 'digest'
require 'ipaddr'
require 'json' require 'json'
require 'net/dns' require 'net/dns'
require 'net/http' require 'net/http'
require 'openssl'
require 'optparse' require 'optparse'
require 'packetfu'
require 'pcaprub'
require 'resolv'
require 'rubydns'
require 'socket'
require 'stringio'
require 'thread'
require 'uri'
require 'webrick'
require 'zlib'
require 'em-proxy'
Object.send :remove_const, :Config rescue nil Object.send :remove_const, :Config rescue nil
Config = RbConfig Config = RbConfig
def bettercap_autoload( path = '' ) def bettercap_autoload(path = '')
dir = File.dirname(__FILE__) + "/bettercap/#{path}" dir = File.dirname(__FILE__) + "/bettercap/#{path}"
deps = [] deps = []
files = [] files = []
monkey = [] monkey = []
Dir[dir+"**/*.rb"].each do |filename| Dir[dir + '**/*.rb'].each do |filename|
filename = filename.gsub( dir, '' ).gsub('.rb', '') filename = filename.gsub(dir, '').gsub('.rb', '')
filename = "bettercap/#{path}#{filename}" filename = "bettercap/#{path}#{filename}"
next if filename.include?('proxy/http/modules')
# Proxy modules must be loaded at runtime. # Proxy modules must be loaded at runtime.
unless filename =~ /.+\/inject[a-z]+$/i if filename.end_with?('/base') || filename.include?('pluggable')
if filename.end_with?('/base') or filename.include?('pluggable') deps << filename
deps << filename elsif filename.include?('monkey')
elsif filename.include?('monkey') monkey << filename
monkey << filename else
else files << filename
files << filename
end
end end
end end
( deps + files + monkey ).each do |file| (deps + files + monkey).each do |file|
require file require file
end end
end end
......
...@@ -40,8 +40,8 @@ class Context ...@@ -40,8 +40,8 @@ class Context
attr_reader :timeout attr_reader :timeout
# Instance of BetterCap::PacketQueue. # Instance of BetterCap::PacketQueue.
attr_reader :packets attr_reader :packets
# Instance of BetterCap::Memory. # Precomputed list of possible addresses on the current network.
attr_reader :memory attr_reader :endpoints
@@instance = nil @@instance = nil
...@@ -65,7 +65,6 @@ class Context ...@@ -65,7 +65,6 @@ class Context
@options = Options.new iface @options = Options.new iface
@discovery = Discovery::Thread.new self @discovery = Discovery::Thread.new self
@firewall = Firewalls::Base.get @firewall = Firewalls::Base.get
@memory = Memory.new
@iface = nil @iface = nil
@original_mac = nil @original_mac = nil
@gateway = nil @gateway = nil
...@@ -76,6 +75,7 @@ class Context ...@@ -76,6 +75,7 @@ class Context
@proxies = [] @proxies = []
@redirections = [] @redirections = []
@packets = nil @packets = nil
@endpoints = []
end end
# Update the Context state parsing network related informations. # Update the Context state parsing network related informations.
...@@ -105,6 +105,8 @@ class Context ...@@ -105,6 +105,8 @@ class Context
@gateway = Network::Target.new gw @gateway = Network::Target.new gw
@targets = @options.core.targets unless @options.core.targets.nil? @targets = @options.core.targets unless @options.core.targets.nil?
@iface = Network::Target.new( cfg[:ip_saddr], cfg[:eth_saddr], cfg[:ip4_obj], cfg[:iface] ) @iface = Network::Target.new( cfg[:ip_saddr], cfg[:eth_saddr], cfg[:ip4_obj], cfg[:iface] )
raise BetterCap::Error, "Could not determine MAC address of '#{@options.core.iface}', make sure this interface "\
'is active and connected.' unless Network::Validator::is_mac?(@iface.mac)
Logger.info "[#{@iface.name.green}] #{@iface.to_s(false)}" Logger.info "[#{@iface.name.green}] #{@iface.to_s(false)}"
...@@ -117,6 +119,21 @@ class Context ...@@ -117,6 +119,21 @@ class Context
@packets = Network::PacketQueue.new( @iface.name, @options.core.packet_throttle, 4 ) @packets = Network::PacketQueue.new( @iface.name, @options.core.packet_throttle, 4 )
# Spoofers need the context network data to be initialized. # Spoofers need the context network data to be initialized.
@spoofer = @options.spoof.parse_spoofers @spoofer = @options.spoof.parse_spoofers
if @options.core.discovery?
tstart = Time.now
Logger.info "[#{'DISCOVERY'.green}] Precomputing list of possible endpoints, this could take a while depending on your subnet ..."
net = ip = @iface.network
# loop each ip in our subnet and push it to the queue
while net.include?ip
if ip != @gateway.ip and ip != @iface.ip
@endpoints << ip
end
ip = ip.succ
end
tend = Time.now
Logger.info "[#{'DISCOVERY'.green}] Done in #{(tend - tstart) * 1000.0} ms"
end
end end
# Find a target given its +ip+ and +mac+ addresses inside the #targets # Find a target given its +ip+ and +mac+ addresses inside the #targets
......
...@@ -24,13 +24,8 @@ class Base ...@@ -24,13 +24,8 @@ class Base
@address = address @address = address
if @address.nil? if @address.nil?
net = ip = @ctx.iface.network @ctx.endpoints.each do |ip|
# loop each ip in our subnet and push it to the queue @ctx.packets.push( get_probe(ip) )
while net.include?ip
unless skip_address?(ip)
@ctx.packets.push( get_probe(ip) )
end
ip = ip.succ
end end
else else
if skip_address?(@address) if skip_address?(@address)
...@@ -46,21 +41,11 @@ class Base ...@@ -46,21 +41,11 @@ class Base
# Return true if +ip+ must be skipped during discovery, otherwise false. # Return true if +ip+ must be skipped during discovery, otherwise false.
def skip_address?(ip) def skip_address?(ip)
# don't send probes to the gateway if we already have its MAC. target = @ctx.find_target( ip.to_s, nil )
if ip == @ctx.gateway.ip # known target?
return !@ctx.gateway.mac.nil? return false if target.nil?
# don't send probes to our device # do we still need to get the mac for this target?
elsif ip == @ctx.iface.ip return ( target.mac.nil?? false : true )
return true
# don't stress endpoints we already discovered
else
target = @ctx.find_target( ip.to_s, nil )
# known target?
return false if target.nil?
# do we still need to get the mac for this target?
return ( target.mac.nil?? false : true )
end
end end
# Each Discovery::Agent::Base derived class should implement this method. # Each Discovery::Agent::Base derived class should implement this method.
......
...@@ -119,7 +119,6 @@ class Thread ...@@ -119,7 +119,6 @@ class Thread
prev = @ctx.targets prev = @ctx.targets
@ctx.memory.optimize!
sleep(1) if @ctx.options.core.discovery? sleep(1) if @ctx.options.core.discovery?
end end
end end
......
...@@ -70,7 +70,7 @@ module Logger ...@@ -70,7 +70,7 @@ module Logger
# Log a +message+ as it is. # Log a +message+ as it is.
def raw(message) def raw(message)
@@queue.push( formatted_message( message, nil ) ) @@queue.push( formatted_message( message, nil ) ) unless @@silent
end end
# Wait for the messages queue to be empty. # Wait for the messages queue to be empty.
......
...@@ -44,7 +44,6 @@ class PacketQueue ...@@ -44,7 +44,6 @@ class PacketQueue
# Push a packet to the queue. # Push a packet to the queue.
def push(packet) def push(packet)
@queue.push(packet) @queue.push(packet)
@ctx.memory.optimize! if ( @queue.size == 1 )
end end
# Wait for the packet queue to be empty. # Wait for the packet queue to be empty.
......
...@@ -157,7 +157,7 @@ class Options ...@@ -157,7 +157,7 @@ class Options
'https-proxy' => ( @proxies.proxy_https ? on : off ), 'https-proxy' => ( @proxies.proxy_https ? on : off ),
'sslstrip' => ( @proxies.sslstrip? ? on : off ), 'sslstrip' => ( @proxies.sslstrip? ? on : off ),
'http-server' => ( @servers.httpd ? on : off ), 'http-server' => ( @servers.httpd ? on : off ),
'dns-server' => ( @proxies.sslstrip? or @servers.dnsd ? on : off ) 'dns-server' => ( (@proxies.sslstrip? or @servers.dnsd) ? on : off )
} }
msg = "Starting [ " msg = "Starting [ "
...@@ -167,9 +167,6 @@ class Options ...@@ -167,9 +167,6 @@ class Options
msg += "] ...\n\n" msg += "] ...\n\n"
Logger.info msg Logger.info msg
Logger.warn "You are running an unstable/beta version of this software, please" \
" update to a stable one if available." if BetterCap::VERSION =~ /[\d\.+]b/
end end
end end
end end
...@@ -38,6 +38,8 @@ class ProxyOptions ...@@ -38,6 +38,8 @@ class ProxyOptions
attr_accessor :allow_local_connections attr_accessor :allow_local_connections
# If true, log HTTP responses too. # If true, log HTTP responses too.
attr_accessor :log_response attr_accessor :log_response
# If true, suppress HTTP requests logs.
attr_accessor :no_http_logs
# If true, TCP proxy will be enabled. # If true, TCP proxy will be enabled.
attr_accessor :tcp_proxy attr_accessor :tcp_proxy
# TCP proxy local port. # TCP proxy local port.
...@@ -72,6 +74,7 @@ class ProxyOptions ...@@ -72,6 +74,7 @@ class ProxyOptions
@sslstrip = true @sslstrip = true
@allow_local_connections = false @allow_local_connections = false
@log_response = false @log_response = false
@no_http_logs = false
@tcp_proxy = false @tcp_proxy = false
@tcp_proxy_port = 2222 @tcp_proxy_port = 2222
...@@ -103,7 +106,7 @@ class ProxyOptions ...@@ -103,7 +106,7 @@ class ProxyOptions
opts.on( '--tcp-proxy-module MODULE', "Ruby TCP proxy module to load." ) do |v| opts.on( '--tcp-proxy-module MODULE', "Ruby TCP proxy module to load." ) do |v|
@tcp_proxy_module = File.expand_path(v) @tcp_proxy_module = File.expand_path(v)
Proxy::TCP::Module.load( @tcp_proxy_module ) Proxy::TCP::Module.load( @tcp_proxy_module, opts )
end end
opts.on( '--tcp-proxy-port PORT', "Set local TCP proxy port, default to #{@tcp_proxy_port.to_s.yellow} ." ) do |v| opts.on( '--tcp-proxy-port PORT', "Set local TCP proxy port, default to #{@tcp_proxy_port.to_s.yellow} ." ) do |v|
...@@ -167,6 +170,10 @@ class ProxyOptions ...@@ -167,6 +170,10 @@ class ProxyOptions
@log_response = true @log_response = true
end end
opts.on( '--no-http-logs', 'Suppress HTTP requests and responses logs.' ) do
@no_http_logs = true
end
opts.on( '--proxy-module MODULE', "Ruby proxy module to load, either a custom file or one of the following: #{Proxy::HTTP::Module.available.map{|x| x.yellow}.join(', ')}." ) do |v| opts.on( '--proxy-module MODULE', "Ruby proxy module to load, either a custom file or one of the following: #{Proxy::HTTP::Module.available.map{|x| x.yellow}.join(', ')}." ) do |v|
Proxy::HTTP::Module.load(ctx, opts, v) Proxy::HTTP::Module.load(ctx, opts, v)
@proxy = true @proxy = true
......
...@@ -74,6 +74,11 @@ class SniffOptions ...@@ -74,6 +74,11 @@ class SniffOptions
@parsers = Parsers::Base.from_cmdline(v) @parsers = Parsers::Base.from_cmdline(v)
end end
opts.on( '--disable-parsers PARSERS', "Comma separated list of packet parsers to disable ( NOTE: Will set -X to true )" ) do |v|
@enabled = true
@parsers = Parsers::Base.from_exclusion_list(v)
end
opts.on( '--custom-parser EXPRESSION', 'Use a custom regular expression in order to capture and show sniffed data ( NOTE: Will set -X to true ).' ) do |v| opts.on( '--custom-parser EXPRESSION', 'Use a custom regular expression in order to capture and show sniffed data ( NOTE: Will set -X to true ).' ) do |v|
@enabled = true @enabled = true
@parsers = ['CUSTOM'] @parsers = ['CUSTOM']
......
...@@ -25,6 +25,8 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module ...@@ -25,6 +25,8 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module
@@iframe = nil @@iframe = nil
# HTML data to be injected. # HTML data to be injected.
@@data = nil @@data = nil
# Position of the injection, 0 = just after <body>, 1 = before </body>
@@position = 0
# Add custom command line arguments to the +opts+ OptionParser instance. # Add custom command line arguments to the +opts+ OptionParser instance.
def self.on_options(opts) def self.on_options(opts)
...@@ -45,6 +47,16 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module ...@@ -45,6 +47,16 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module
opts.on( '--html-iframe-url URL', 'URL of the iframe that will be injected, if this option is specified an "iframe" tag will be injected.' ) do |v| opts.on( '--html-iframe-url URL', 'URL of the iframe that will be injected, if this option is specified an "iframe" tag will be injected.' ) do |v|
@@iframe = v @@iframe = v
end end
opts.on( '--html-position POSITION', 'Position of the injection, valid values are START for injecting after the <body> tag and END to inject just before </body>.' ) do |v|
if v == 'START'
@@position = 0
elsif v == 'END'
@@position = 1
else
raise BetterCap::Error, "#{v} invalid position, only START or END values are accepted."
end
end
end end
# Create an instance of this module and raise a BetterCap::Error if command # Create an instance of this module and raise a BetterCap::Error if command
...@@ -61,12 +73,16 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module ...@@ -61,12 +73,16 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module
BetterCap::Logger.info "[#{'INJECTHTML'.green}] Injecting HTML code into #{request.to_url}" BetterCap::Logger.info "[#{'INJECTHTML'.green}] Injecting HTML code into #{request.to_url}"
if @@data.nil? if @@data.nil?
replacement = "<iframe src=\"#{@@iframe}\" frameborder=\"0\" height=\"0\" width=\"0\"></iframe></body>" replacement = "<iframe src=\"#{@@iframe}\" frameborder=\"0\" height=\"0\" width=\"0\"></iframe>"
else else
replacement = "#{@@data}</body>" replacement = "#{@@data}"
end end
response.body.sub!( /<\/body>/i ) { replacement } if @@position == 0
response.body.sub!( /<body([^>]*)>/i ) { "<body#{$1}>#{replacement}" }
else
response.body.sub!( /<\/body>/i ) { "#{replacement}</body>" }
end
end end
end end
end end
# encoding: UTF-8
=begin
BETTERCAP
Author : Simone 'evilsocket' Margaritelli
Email : evilsocket@gmail.com
Blog : http://www.evilsocket.net/
This project is released under the GPL 3 license.
=end
# This proxy module will redirect to a custom URL.
class Redirect < BetterCap::Proxy::HTTP::Module
meta(
'Name' => 'Redirect',
'Description' => 'This proxy module will redirect the target(s) to a custom URL.',
'Version' => '1.0.0',
'Author' => "Simone 'evilsocket' Margaritelli",
'License' => 'GPL3'
)
# URL to redirect the target(s) to.
@@url = nil
# Optional regex filter for redirections.
@@filter = nil
# Add custom command line arguments to the +opts+ OptionParser instance.
def self.on_options(opts)
opts.separator ""
opts.separator "Redirect Proxy Module Options:"
opts.separator ""
opts.on( '--redirect-url URL', 'URL to redirect the target(s) to.' ) do |v|
@@url = v
end
opts.on( '--redirect-filter EXPRESSION', 'Optional regex filter for redirections.' ) do |v|
@@filter = Regexp.new(v)
end
end
# Create an instance of this module and raise a BetterCap::Error if command
# line arguments weren't correctly specified.
def initialize
raise BetterCap::Error, "No --redirect-url option specified for the proxy module." if @@url.nil?
raise BetterCap::Error, "Invalid URL specified." unless @@url =~ /\A#{URI::regexp}\z/
end
def on_request( request, response )
if response.content_type =~ /^text\/html.*/ and !@@url.include?(request.host)
if @@filter.nil? or @@filter.match(request.to_url)
BetterCap::Logger.info "[#{'REDIRECT'.green}] Redirecting #{request.to_url} to #{@@url} ..."
response.redirect!(@@url)
end
end
end
end
...@@ -110,22 +110,22 @@ class Request ...@@ -110,22 +110,22 @@ class Request
name = $1 name = $1
value = $2 value = $2
case name case name.downcase
when 'Host' when 'host'
@host = value @host = value
if @host =~ /([^:]*):([0-9]*)$/ if @host =~ /([^:]*):([0-9]*)$/
@host = $1 @host = $1
@port = $2.to_i @port = $2.to_i
end end
when 'Content-Length' when 'content-length'
@content_length = value.to_i @content_length = value.to_i
# we don't want to have hundreds of threads running # we don't want to have hundreds of threads running
when 'Connection' when 'connection'
value = 'close' value = 'close'
when 'Proxy-Connection' when 'proxy-connection'
name = 'Connection' name = 'Connection'
# disable gzip, chunked, etc encodings # disable gzip, chunked, etc encodings
when 'Accept-Encoding' when 'accept-encoding'
value = 'identity' value = 'identity'
end end
......
...@@ -102,6 +102,15 @@ class Response ...@@ -102,6 +102,15 @@ class Response
@body = response.body || '' @body = response.body || ''
end end
# Convert this response object to a 302 redirect to +url+.
def redirect!(url)
@code = '302'
@status = 'Moved'
@body = nil
@headers['Location'] = url
@headers['Connection'] = 'close'
end
# Parse a single response +line+. # Parse a single response +line+.
def <<(line) def <<(line)
# we already parsed the heders, collect response body # we already parsed the heders, collect response body
......
...@@ -52,12 +52,16 @@ class StrippedObject ...@@ -52,12 +52,16 @@ class StrippedObject
# Return a normalized version of +url+. # Return a normalized version of +url+.
def self.normalize( url, schema = 'https' ) def self.normalize( url, schema = 'https' )
has_schema = url.include?('://')
# add schema if needed # add schema if needed
unless url.include?('://') if has_schema
has_slash = ( url =~ /^.+:\/\/.+\/.*$/ )
else
has_slash = ( url =~ /^.+\/.*$/ )
url = "#{schema}://#{url}" url = "#{schema}://#{url}"
end end
# add path if needed # add slash if needed
unless url.end_with?('/') unless has_slash
url = "#{url}/" url = "#{url}/"
end end
url url
......
...@@ -67,7 +67,7 @@ class Streamer ...@@ -67,7 +67,7 @@ class Streamer
response = r response = r
end end
if response.textual? or request.method == 'DELETE' if @ctx.options.proxies.no_http_logs == false and ( response.textual? or request.method == 'DELETE' )
StreamLogger.log_http( request, response ) StreamLogger.log_http( request, response )
else else
Logger.debug "[#{request.client}] -> #{request.to_url} [#{response.code}]" Logger.debug "[#{request.client}] -> #{request.to_url} [#{response.code}]"
......
...@@ -27,6 +27,8 @@ module TCP ...@@ -27,6 +27,8 @@ module TCP
# end # end
# end # end
class Module < BetterCap::Pluggable class Module < BetterCap::Pluggable
def on_options( opts ); end
def check_opts; end
# This callback is called when the target is sending data to the upstream server. # This callback is called when the target is sending data to the upstream server.
# +event+ is an instance of the BetterCap::Proxy::TCP::Event class. # +event+ is an instance of the BetterCap::Proxy::TCP::Event class.
def on_data( event ); end def on_data( event ); end
...@@ -41,6 +43,15 @@ class Module < BetterCap::Pluggable ...@@ -41,6 +43,15 @@ class Module < BetterCap::Pluggable
@@loaded = {} @@loaded = {}
class << self class << self
# Register custom options for each available module.
def register_options(opts)
@@loaded.each do |name,mod|
if mod.respond_to?(:on_options)
mod.on_options(opts)
end
end
end
# Called when a class inherits this base class. # Called when a class inherits this base class.
def inherited(subclass) def inherited(subclass)
name = subclass.name.upcase name = subclass.name.upcase
...@@ -48,7 +59,7 @@ class Module < BetterCap::Pluggable ...@@ -48,7 +59,7 @@ class Module < BetterCap::Pluggable
end end
# Load +file+ as a proxy module. # Load +file+ as a proxy module.
def load( file ) def load( file, opts )
begin begin
require file require file
rescue LoadError => e rescue LoadError => e
...@@ -58,6 +69,8 @@ class Module < BetterCap::Pluggable ...@@ -58,6 +69,8 @@ class Module < BetterCap::Pluggable
@@loaded.each do |name,mod| @@loaded.each do |name,mod|
@@loaded[name] = mod.new @@loaded[name] = mod.new
end end
self.register_options(opts)
end end
# Execute method +even_name+ for each loaded module instance using +event+ # Execute method +even_name+ for each loaded module instance using +event+
......
...@@ -18,6 +18,7 @@ module Shell ...@@ -18,6 +18,7 @@ module Shell
# Execute +command+ and return its output. # Execute +command+ and return its output.
# Raise +BetterCap::Error+ if the return code is not 0. # Raise +BetterCap::Error+ if the return code is not 0.
def execute(command) def execute(command)
Logger.debug command
r = '' r = ''