bettercap 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1fb03c684e4f5c63193e20fa8d8cd226f619059
4
- data.tar.gz: 02c684a36e92f414fb1ba06095e6690309320d60
3
+ metadata.gz: 182a02bf76fdd35026f7807807c63c0f27f2dae2
4
+ data.tar.gz: 231c55ddf54d61c4fd50bc7aa66e39e825f99fd8
5
5
  SHA512:
6
- metadata.gz: 9f0b04d7769d9d27a02c53bdf7af45024a417bd67fae18a3fa67b7b47af51454a8246e222df3d38657ab519af3dfa7d3e947406916ac963d6735f5514e7bc831
7
- data.tar.gz: 9a145c4ecb97d929433b5411657d551145f3e1e4c642a42c54c5d6deba673556e53aae746647e1c82d713f0d7bb7bb049aec3648bd3ab00dcf85d5f5ae227ee5
6
+ metadata.gz: 17d11b9a0f810509cde47dc5e236ea22e0d18abd8fb5ec74fec50abab8e0b65f2ee6d2a91490d1d4e861cfb0e52d6f4febc383a30f4e89bd98a0f4280443b6de
7
+ data.tar.gz: 0c465a3405d311a59323be9b976951c6e670353af20ccb57e6a7189de89d463162a56b0f9d5c5dc6283b467830ff4e4677b4ee1d4db6fd0749bb699ea8747ed8
@@ -17,10 +17,10 @@ module BetterCap
17
17
  class Context
18
18
  # Instance of BetterCap::Options class.
19
19
  attr_accessor :options
20
- # A dictionary containing information about the selected network interface.
21
- attr_accessor :ifconfig
22
20
  # Instance of the current BetterCap::Firewalls class.
23
21
  attr_accessor :firewall
22
+ # Local interface ( as an instance of BetterCap::Network::Target ).
23
+ attr_accessor :iface
24
24
  # Network gateway ( as an instance of BetterCap::Network::Target ).
25
25
  attr_accessor :gateway
26
26
  # A list of BetterCap::Target objects which is periodically updated.
@@ -63,8 +63,10 @@ class Context
63
63
  @running = true
64
64
  @timeout = 5
65
65
  @options = Options.new iface
66
- @ifconfig = nil
67
- @firewall = nil
66
+ @discovery = Discovery::Thread.new self
67
+ @firewall = Firewalls::Base.get
68
+ @memory = Memory.new
69
+ @iface = nil
68
70
  @gateway = nil
69
71
  @targets = []
70
72
  @spoofer = nil
@@ -72,10 +74,7 @@ class Context
72
74
  @dnsd = nil
73
75
  @proxies = []
74
76
  @redirections = []
75
- @discovery = Discovery::Thread.new self
76
- @firewall = Firewalls::Base.get
77
77
  @packets = nil
78
- @memory = Memory.new
79
78
  end
80
79
 
81
80
  # Update the Context state parsing network related informations.
@@ -86,23 +85,24 @@ class Context
86
85
  'correct network configuration, this could also happen if bettercap '\
87
86
  'is launched from a virtual environment.' unless Network::Validator.is_ip?(gw)
88
87
 
89
- @gateway = Network::Target.new gw
90
- @targets = @options.core.targets unless @options.core.targets.nil?
91
- @ifconfig = PacketFu::Utils.ifconfig @options.core.iface
92
88
 
89
+ cfg = PacketFu::Utils.ifconfig @options.core.iface
93
90
  raise BetterCap::Error, "Could not determine IPv4 address of '#{@options.core.iface}', make sure this interface "\
94
- 'is active and connected.' if @ifconfig[:ip4_obj].nil?
91
+ 'is active and connected.' if cfg[:ip4_obj].nil?
92
+
93
+ @gateway = Network::Target.new gw
94
+ @targets = @options.core.targets unless @options.core.targets.nil?
95
+ @iface = Network::Target.new( cfg[:ip_saddr], cfg[:eth_saddr], cfg[:ip4_obj], cfg[:iface] )
96
+
97
+ Logger.info "[#{@iface.name.green}] #{@iface.to_s(false)}"
95
98
 
96
99
  Logger.debug '----- NETWORK INFORMATIONS -----'
97
- Logger.debug " network = #{@ifconfig[:ip4_obj]}"
100
+ Logger.debug " network = #{@iface.network} ( #{@iface.network.to_range.to_s.split('..').join( ' -> ')} )"
98
101
  Logger.debug " gateway = #{@gateway.ip}"
99
- Logger.debug " local_ip = #{@ifconfig[:ip_saddr]}\n"
100
- @ifconfig.each do |key,value|
101
- Logger.debug " ifconfig[:#{key}] = #{value}"
102
- end
102
+ Logger.debug " local_ip = #{@iface.ip}"
103
103
  Logger.debug "--------------------------------\n"
104
104
 
105
- @packets = Network::PacketQueue.new( @ifconfig[:iface], @options.core.packet_throttle, 4 )
105
+ @packets = Network::PacketQueue.new( @iface.name, @options.core.packet_throttle, 4 )
106
106
  # Spoofers need the context network data to be initialized.
107
107
  @spoofer = @options.spoof.parse_spoofers
108
108
  end
@@ -122,10 +122,6 @@ class Context
122
122
  def start!
123
123
  # Start targets auto discovery.
124
124
  @discovery.start
125
- # give some time to the discovery thread to spawn its workers,
126
- # this will prevent 'Too many open files' errors to delay host
127
- # discovery.
128
- sleep(1.5) if @options.core.discovery?
129
125
 
130
126
  # Start network spoofers if any.
131
127
  @spoofer.each do |spoofer|
@@ -193,7 +189,7 @@ class Context
193
189
 
194
190
  # Apply needed BetterCap::Firewalls::Redirection objects.
195
191
  def enable_port_redirection!
196
- @redirections = @options.get_redirections(@ifconfig)
192
+ @redirections = @options.get_redirections(@iface)
197
193
  @redirections.each do |r|
198
194
  Logger.debug "Redirecting #{r.protocol} traffic from #{r.src_address.nil? ? '*' : r.src_address}:#{r.src_port} to #{r.dst_address}:#{r.dst_port}"
199
195
  @firewall.add_port_redirection( r )
@@ -210,17 +206,17 @@ class Context
210
206
 
211
207
  # create HTTP proxy
212
208
  if @options.proxies.proxy
213
- @proxies << Proxy::HTTP::Proxy.new( @ifconfig[:ip_saddr], @options.proxies.proxy_port, false )
209
+ @proxies << Proxy::HTTP::Proxy.new( @iface.ip, @options.proxies.proxy_port, false )
214
210
  end
215
211
 
216
212
  # create HTTPS proxy
217
213
  if @options.proxies.proxy_https
218
- @proxies << Proxy::HTTP::Proxy.new( @ifconfig[:ip_saddr], @options.proxies.proxy_https_port, true )
214
+ @proxies << Proxy::HTTP::Proxy.new( @iface.ip, @options.proxies.proxy_https_port, true )
219
215
  end
220
216
 
221
217
  # create TCP proxy
222
218
  if @options.proxies.tcp_proxy
223
- @proxies << Proxy::TCP::Proxy.new( @ifconfig[:ip_saddr], @options.proxies.tcp_proxy_port, @options.proxies.tcp_proxy_upstream_address, @options.proxies.tcp_proxy_upstream_port )
219
+ @proxies << Proxy::TCP::Proxy.new( @iface.ip, @options.proxies.tcp_proxy_port, @options.proxies.tcp_proxy_upstream_address, @options.proxies.tcp_proxy_upstream_port )
224
220
  end
225
221
 
226
222
  @proxies.each do |proxy|
@@ -234,7 +230,7 @@ class Context
234
230
  if @options.servers.dnsd
235
231
  Logger.warn "Starting DNS server with spoofing disabled, bettercap will only reply to local DNS queries." unless @options.spoof.enabled?
236
232
 
237
- @dnsd = Network::Servers::DNSD.new( @options.servers.dnsd_file, @ifconfig[:ip_saddr], @options.servers.dnsd_port )
233
+ @dnsd = Network::Servers::DNSD.new( @options.servers.dnsd_file, @iface.ip, @options.servers.dnsd_port )
238
234
  @dnsd.start
239
235
  end
240
236
 
@@ -23,10 +23,10 @@ class Arp < Discovery::Agents::Base
23
23
  def get_probe( ip )
24
24
  pkt = PacketFu::ARPPacket.new
25
25
 
26
- pkt.eth_saddr = pkt.arp_saddr_mac = @ifconfig[:eth_saddr]
26
+ pkt.eth_saddr = pkt.arp_saddr_mac = @ctx.iface.mac
27
27
  pkt.eth_daddr = 'ff:ff:ff:ff:ff:ff'
28
28
  pkt.arp_daddr_mac = '00:00:00:00:00:00'
29
- pkt.arp_saddr_ip = @ifconfig[:ip_saddr]
29
+ pkt.arp_saddr_ip = @ctx.iface.ip
30
30
  pkt.arp_daddr_ip = ip.to_s
31
31
 
32
32
  pkt
@@ -20,13 +20,11 @@ class Base
20
20
  # Initialize the agent using the +ctx+ BetterCap::Context instance.
21
21
  # If +address+ is not nil only that ip will be probed.
22
22
  def initialize( ctx, address = nil )
23
- @ctx = ctx
24
- @ifconfig = ctx.ifconfig
25
- @local_ip = @ifconfig[:ip_saddr]
26
- @address = address
23
+ @ctx = ctx
24
+ @address = address
27
25
 
28
26
  if @address.nil?
29
- net = ip = @ifconfig[:ip4_obj]
27
+ net = ip = @ctx.iface.network
30
28
  # loop each ip in our subnet and push it to the queue
31
29
  while net.include?ip
32
30
  unless skip_address?(ip)
@@ -52,7 +50,7 @@ class Base
52
50
  if ip == @ctx.gateway.ip
53
51
  return !@ctx.gateway.mac.nil?
54
52
  # don't send probes to our device
55
- elsif ip == @local_ip
53
+ elsif ip == @ctx.iface.ip
56
54
  return true
57
55
  # don't stress endpoints we already discovered
58
56
  else
@@ -26,9 +26,9 @@ class Icmp
26
26
  3.times do
27
27
  pkt = PacketFu::ICMPPacket.new
28
28
 
29
- pkt.eth_saddr = ctx.ifconfig[:eth_saddr]
29
+ pkt.eth_saddr = ctx.iface.mac
30
30
  pkt.eth_daddr = 'ff:ff:ff:ff:ff:ff'
31
- pkt.ip_saddr = ctx.ifconfig[:ip_saddr]
31
+ pkt.ip_saddr = ctx.iface.ip
32
32
  pkt.ip_daddr = '255.255.255.255'
33
33
  pkt.icmp_type = 8
34
34
  pkt.icmp_code = 0
@@ -23,12 +23,16 @@ class Thread
23
23
 
24
24
  # Start the active network discovery thread.
25
25
  def start
26
- if @ctx.options.core.discovery?
27
- Logger.info "[#{'DISCOVERY'.green}] Targeting the whole subnet #{@ctx.ifconfig[:ip4_obj].to_range} ..."
28
- end
29
-
30
26
  @running = true
31
27
  @thread = ::Thread.new { worker }
28
+
29
+ if @ctx.options.core.discovery?
30
+ Logger.info "[#{'DISCOVERY'.green}] Targeting the whole subnet #{@ctx.iface.network.to_range} ..."
31
+ # give some time to the discovery thread to spawn its workers,
32
+ # this will prevent 'Too many open files' errors to delay host
33
+ # discovery.
34
+ sleep(1.5)
35
+ end
32
36
  end
33
37
 
34
38
  # Stop the active network discovery thread.
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+
4
+ BETTERCAP
5
+
6
+ Author : Simone 'evilsocket' Margaritelli
7
+ Email : evilsocket@gmail.com
8
+ Blog : http://www.evilsocket.net/
9
+
10
+ This project is released under the GPL 3 license.
11
+
12
+ =end
13
+
14
+ # Monkey patching to remove Internals::Logger.crash calls.
15
+ module Celluloid
16
+ class Actor
17
+ # Handle any exceptions that occur within a running actor
18
+ def handle_crash(exception)
19
+ shutdown ExitEvent.new(behavior_proxy, exception)
20
+ rescue => ex
21
+ end
22
+ end
23
+ end
@@ -21,7 +21,7 @@ class ArpReader
21
21
  def self.parse( ctx )
22
22
  targets = []
23
23
  self.parse_cache do |ip,mac|
24
- if ip != ctx.gateway.ip and ip != ctx.ifconfig[:ip_saddr]
24
+ if ip != ctx.gateway.ip and ip != ctx.iface.ip
25
25
  if ctx.options.core.ignore_ip?(ip)
26
26
  Logger.debug "Ignoring #{ip} ..."
27
27
  else
@@ -65,7 +65,7 @@ class ArpReader
65
65
  # Read the computer ARP cache and parse each line, it will yield each
66
66
  # ip and mac address it will be able to extract.
67
67
  def self.parse_cache
68
- iface = Context.get.ifconfig[:iface]
68
+ iface = Context.get.iface.name
69
69
  Shell.arp.split("\n").each do |line|
70
70
  m = self.parse_cache_line(iface,line)
71
71
  unless m.nil?
@@ -56,8 +56,6 @@ class << self
56
56
  def get_alive_targets( ctx )
57
57
  if ctx.options.core.discovery?
58
58
  start_agents( ctx )
59
- else
60
- Logger.debug 'Using current ARP cache.'
61
59
  end
62
60
 
63
61
  ArpReader.parse ctx
@@ -26,10 +26,19 @@ class PacketQueue
26
26
  @running = true
27
27
  @stream = PCAPRUB::Pcap.open_live( iface, 0xffff, false , 1 )
28
28
  @mutex = Mutex.new
29
- @udp = UDPSocket.new
30
29
  @queue = Queue.new
31
30
  @workers = (0...nworkers).map { ::Thread.new { worker } }
32
31
  @ctx = Context.get
32
+
33
+ begin
34
+ @udp = UDPSocket.new
35
+ rescue Errno::EMFILE
36
+ Logger.warn "It looks like another process is using a lot of UDP file descriptors" \
37
+ "and the operating system is denying resources to me, I'll try again in one second."
38
+
39
+ sleep 1.0
40
+ retry
41
+ end
33
42
  end
34
43
 
35
44
  # Push a packet to the queue.
@@ -78,22 +78,26 @@ class DNSD
78
78
  :server_class => DnsWrapper
79
79
  }
80
80
 
81
- RubyDNS::run_server( options ) do
82
- # Suppress RubyDNS logging.
83
- @logger.level = ::Logger::ERROR
84
- @upstream ||= RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
85
-
86
- # Default DNS handler
87
- otherwise do |transaction|
88
- Logger.debug "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}' -> upstream DNS"
89
- transaction.passthrough!(@upstream)
81
+ begin
82
+ RubyDNS::run_server( options ) do
83
+ # Suppress RubyDNS logging.
84
+ @logger.level = ::Logger::ERROR
85
+ @upstream ||= RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
86
+
87
+ # Default DNS handler
88
+ otherwise do |transaction|
89
+ Logger.debug "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}' -> upstream DNS"
90
+ transaction.passthrough!(@upstream)
91
+ end
90
92
  end
91
- end
92
93
 
93
- unless @hosts.nil?
94
- DNSD.parse_hosts( @hosts ).each do |exp,addr|
95
- add_rule!( exp, addr )
94
+ unless @hosts.nil?
95
+ DNSD.parse_hosts( @hosts ).each do |exp,addr|
96
+ add_rule!( exp, addr )
97
+ end
96
98
  end
99
+ rescue Errno::EADDRINUSE
100
+ raise BetterCap::Error, "[DNS] It looks like there's another process listening on #{@address}:#{@port}, please chose a different port."
97
101
  end
98
102
  end
99
103
 
@@ -120,7 +124,7 @@ class DNSD
120
124
  expression = $2
121
125
 
122
126
  if address == 'local'
123
- address = Context.get.ifconfig[:ip_saddr].to_s
127
+ address = Context.get.iface.ip.to_s
124
128
  end
125
129
 
126
130
  raise BetterCap::Error, "Invalid IPv4 address '#{address}' on line #{lineno + 1} of '#{filename}'." \
@@ -22,12 +22,16 @@ class HTTPD
22
22
  def initialize( port = 8081, path = './' )
23
23
  @port = port
24
24
  @path = path
25
- @server = WEBrick::HTTPServer.new(
26
- Port: @port,
27
- DocumentRoot: @path,
28
- Logger: WEBrick::Log.new("/dev/null"),
29
- AccessLog: []
30
- )
25
+ begin
26
+ @server = WEBrick::HTTPServer.new(
27
+ Port: @port,
28
+ DocumentRoot: @path,
29
+ Logger: WEBrick::Log.new("/dev/null"),
30
+ AccessLog: []
31
+ )
32
+ rescue Errno::EADDRINUSE
33
+ raise BetterCap::Error, "[HTTPD] It looks like there's another process listening on port #{@port}, please chose a different port."
34
+ end
31
35
  end
32
36
 
33
37
  # Start the server.
@@ -20,10 +20,13 @@ class Target
20
20
  attr_accessor :ip
21
21
  # The MAC address of the device network interface.
22
22
  attr_accessor :mac
23
+ # Network object.
24
+ attr_accessor :network
23
25
  # Vendor of the device network interface if available.
24
26
  attr_accessor :vendor
25
- # NetBIOS hostname of the device if available.
26
- attr_accessor :hostname
27
+ # NetBIOS hostname of the device if available OR interface name in case of
28
+ # local address.
29
+ attr_accessor :name
27
30
  # True if the IP attribute of this target needs to be updated.
28
31
  attr_accessor :ip_refresh
29
32
 
@@ -43,7 +46,7 @@ class Target
43
46
  # The +ip+ argument could also be a MAC address itself, in this case the
44
47
  # ip address will be parsed from the computer ARP cache and updated
45
48
  # accordingly.
46
- def initialize( ip, mac=nil )
49
+ def initialize( ip, mac=nil, network=nil, name=nil )
47
50
  if Network::Validator.is_ip?(ip)
48
51
  @ip = ip
49
52
  @ip_refresh = false
@@ -57,8 +60,9 @@ class Target
57
60
 
58
61
  @mac = Target.normalized_mac(mac) unless mac.nil?
59
62
  @vendor = Target.lookup_vendor(@mac) unless mac.nil?
60
- @hostname = nil
61
- @resolver = Thread.new { resolve! } unless Context.get.options.core.no_target_nbns or @ip.nil?
63
+ @name = name
64
+ @network = network
65
+ @resolver = Thread.new { resolve! } unless Context.get.options.core.no_target_nbns or @ip.nil? or !@network.nil?
62
66
  end
63
67
 
64
68
  # Return the integer representation of the +ip+ attribute which can be
@@ -86,15 +90,15 @@ class Target
86
90
  vendor = @vendor.nil?? " ( ??? )" : " ( #{@vendor} )"
87
91
 
88
92
  s = sprintf( fmt, address, @mac )
89
- s += " / #{@hostname}" unless @hostname.nil?
93
+ s += " / #{@name}" unless @name.nil?
90
94
  s += vendor
91
95
  s
92
96
  end
93
97
 
94
98
  # Return a compact string representation of this object.
95
99
  def to_s_compact
96
- if @hostname
97
- "#{@hostname}/#{@ip}"
100
+ if @name
101
+ "#{@name}/#{@ip}"
98
102
  else
99
103
  @ip
100
104
  end
@@ -131,8 +135,8 @@ private
131
135
  sock.recvfrom(NBNS_BUFSIZE)
132
136
  end
133
137
  if resp
134
- @hostname = parse_nbns_response resp
135
- Logger.info "Found NetBIOS name '#{@hostname}' for address #{@ip}"
138
+ @name = parse_nbns_response resp
139
+ Logger.info "Found NetBIOS name '#{@name}' for address #{@ip}"
136
140
  end
137
141
  rescue Exception => e
138
142
  Logger.debug e
@@ -22,9 +22,9 @@ class CoreOptions
22
22
  attr_accessor :targets
23
23
  # Comma separated list of ip addresses to ignore.
24
24
  attr_accessor :ignore
25
- # If true will disable active network discovery, the program will just use
25
+ # If false will disable active network discovery, the program will just use
26
26
  # the current ARP cache.
27
- attr_accessor :arpcache
27
+ attr_accessor :discovery
28
28
  # If true, targets NBNS hostname resolution won't be performed.
29
29
  attr_accessor :no_target_nbns
30
30
  # Log file name.
@@ -49,7 +49,7 @@ class CoreOptions
49
49
  @silent = false
50
50
  @debug = false
51
51
  @ignore = nil
52
- @arpcache = false
52
+ @discovery = true
53
53
  @no_target_nbns = false
54
54
  @packet_throttle = 0.0
55
55
  @check_updates = false
@@ -78,7 +78,7 @@ class CoreOptions
78
78
  end
79
79
 
80
80
  opts.on( '--no-discovery', "Do not actively search for hosts, just use the current ARP cache, default to #{'false'.yellow}." ) do
81
- @arpcache = true
81
+ @discovery = false
82
82
  end
83
83
 
84
84
  opts.on( '--no-target-nbns', 'Disable target NBNS hostname resolution.' ) do
@@ -129,7 +129,7 @@ class CoreOptions
129
129
 
130
130
  # Return true if active host discovery is enabled, otherwise false.
131
131
  def discovery?
132
- ( @targets.nil? and !@arpcache )
132
+ ( @discovery and @targets.nil? )
133
133
  end
134
134
 
135
135
  # Split specified targets and parse them ( either as IP or MAC ), will raise a
@@ -93,28 +93,28 @@ class Options
93
93
 
94
94
  # Create a list of BetterCap::Firewalls::Redirection objects which are needed
95
95
  # given the specified command line arguments.
96
- def get_redirections ifconfig
96
+ def get_redirections iface
97
97
  redirections = []
98
98
 
99
99
  if @servers.dnsd or @proxies.sslstrip?
100
- redirections << redir( ifconfig[:ip_saddr], 53, @servers.dnsd_port )
101
- redirections << redir( ifconfig[:ip_saddr], 53, @servers.dnsd_port, 'UDP' )
100
+ redirections << redir( iface.ip, 53, @servers.dnsd_port )
101
+ redirections << redir( iface.ip, 53, @servers.dnsd_port, 'UDP' )
102
102
  end
103
103
 
104
104
  if @proxies.proxy
105
105
  @proxies.http_ports.each do |port|
106
- redirections << redir( ifconfig[:ip_saddr], port, @proxies.proxy_port )
106
+ redirections << redir( iface.ip, port, @proxies.proxy_port )
107
107
  end
108
108
  end
109
109
 
110
110
  if @proxies.proxy_https
111
111
  @proxies.https_ports.each do |port|
112
- redirections << redir( ifconfig[:ip_saddr], port, @proxies.proxy_https_port )
112
+ redirections << redir( iface.ip, port, @proxies.proxy_https_port )
113
113
  end
114
114
  end
115
115
 
116
116
  if @proxies.tcp_proxy
117
- redirections << redir_single( @proxies.tcp_proxy_upstream_address, ifconfig[:ip_saddr], @proxies.tcp_proxy_upstream_port, @proxies.tcp_proxy_port )
117
+ redirections << redir_single( @proxies.tcp_proxy_upstream_address, iface.ip, @proxies.tcp_proxy_upstream_port, @proxies.tcp_proxy_port )
118
118
  end
119
119
 
120
120
  if @proxies.custom_proxy
@@ -130,7 +130,7 @@ class Options
130
130
  end
131
131
 
132
132
  @proxies.custom_redirections.each do |r|
133
- redirections << redir( ifconfig[:ip_saddr], r[:from], r[:to], r[:proto] )
133
+ redirections << redir( iface.ip, r[:from], r[:to], r[:proto] )
134
134
  end
135
135
 
136
136
  redirections
@@ -145,15 +145,18 @@ class ProxyOptions
145
145
  end
146
146
 
147
147
  opts.on( '--no-sslstrip', 'Disable SSLStrip.' ) do
148
+ @proxy = true
148
149
  @sslstrip = false
149
150
  end
150
151
 
151
152
  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|
152
153
  Proxy::HTTP::Module.load(ctx, opts, v)
154
+ @proxy = true
153
155
  end
154
156
 
155
157
  opts.on( '--http-ports PORT1,PORT2', "Comma separated list of HTTP ports to redirect to the proxy, default to #{@http_ports.map{|x| x.to_s.yellow }.join(', ')}." ) do |v|
156
- @http_ports = self.parse_ports( v )
158
+ @http_ports = ProxyOptions.parse_ports( v )
159
+ @proxy = true
157
160
  end
158
161
 
159
162
  opts.separator ""
@@ -176,7 +179,8 @@ class ProxyOptions
176
179
  end
177
180
 
178
181
  opts.on( '--https-ports PORT1,PORT2', "Comma separated list of HTTPS ports to redirect to the proxy, default to #{@https_ports.map{|x| x.to_s.yellow }.join(', ')}." ) do |v|
179
- @https_ports = self.parse_ports( v )
182
+ @https_ports = ProxyOptions.parse_ports( v )
183
+ @proxy_https = true
180
184
  end
181
185
 
182
186
  opts.separator ""
@@ -213,7 +217,7 @@ class ProxyOptions
213
217
  raise BetterCap::Error, "No TCP proxy upstream server port specified ( --tcp-proxy-upstream-port PORT )." if @tcp_proxy_upstream_port.nil?
214
218
  end
215
219
 
216
- if @sslstrip and ctx.options.servers.dnsd
220
+ if @proxy and @sslstrip and ctx.options.servers.dnsd
217
221
  raise BetterCap::Error, "SSL Stripping and builtin DNS server are mutually exclusive features, " \
218
222
  "either use the --no-sslstrip option or remove the --dns option."
219
223
  end
@@ -0,0 +1,37 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+
4
+ BETTERCAP
5
+
6
+ Author : Simone 'evilsocket' Margaritelli
7
+ Email : evilsocket@gmail.com
8
+ Blog : http://www.evilsocket.net/
9
+
10
+ This project is released under the GPL 3 license.
11
+
12
+ =end
13
+
14
+ module BetterCap
15
+ # Simple base class for modules and plugins of various types.
16
+ class Pluggable
17
+ @@metadata = {
18
+ 'Name' => '',
19
+ 'Version' => '',
20
+ 'Author' => "Simone 'evilsocket' Margaritelli",
21
+ 'License' => 'GPL3',
22
+ 'Description' => ''
23
+ }
24
+
25
+ # Define the metadata for this module.
26
+ def self.meta(meta = {})
27
+ meta.each do |key,value|
28
+ @@metadata[key] = value
29
+ end
30
+ end
31
+
32
+ # Get the metadata of this module.
33
+ def self.metadata
34
+ @@metadata
35
+ end
36
+ end
37
+ end
@@ -16,7 +16,7 @@ module Proxy
16
16
  module HTTP
17
17
 
18
18
  # Base class for transparent proxy modules.
19
- class Module
19
+ class Module < BetterCap::Pluggable
20
20
  @@path = File.dirname(__FILE__) + '/modules/'
21
21
  @@modules = []
22
22
 
@@ -13,6 +13,14 @@ This project is released under the GPL 3 license.
13
13
 
14
14
  # This proxy module will take care of CSS code injection.
15
15
  class InjectCSS < BetterCap::Proxy::HTTP::Module
16
+ meta(
17
+ 'Name' => 'InjectCSS',
18
+ 'Description' => 'This proxy module will take care of CSS code injection.',
19
+ 'Version' => '1.0.0',
20
+ 'Author' => "Simone 'evilsocket' Margaritelli",
21
+ 'License' => 'GPL3'
22
+ )
23
+
16
24
  # CSS data to be injected.
17
25
  @@cssdata = nil
18
26
  # CSS file URL to be injected.
@@ -13,6 +13,14 @@ This project is released under the GPL 3 license.
13
13
 
14
14
  # This proxy module will take care of HTML code injection.
15
15
  class InjectHTML < BetterCap::Proxy::HTTP::Module
16
+ meta(
17
+ 'Name' => 'InjectHTML',
18
+ 'Description' => 'This proxy module will take care of HTML code injection.',
19
+ 'Version' => '1.0.0',
20
+ 'Author' => "Simone 'evilsocket' Margaritelli",
21
+ 'License' => 'GPL3'
22
+ )
23
+
16
24
  # URL of the iframe if --html-iframe-url was specified.
17
25
  @@iframe = nil
18
26
  # HTML data to be injected.
@@ -13,6 +13,14 @@ This project is released under the GPL 3 license.
13
13
 
14
14
  # This proxy module will take care of Javascript code injection.
15
15
  class InjectJS < BetterCap::Proxy::HTTP::Module
16
+ meta(
17
+ 'Name' => 'InjectJS',
18
+ 'Description' => 'This proxy module will take care of Javascript code injection.',
19
+ 'Version' => '1.0.0',
20
+ 'Author' => "Simone 'evilsocket' Margaritelli",
21
+ 'License' => 'GPL3'
22
+ )
23
+
16
24
  # JS data to be injected.
17
25
  @@jsdata = nil
18
26
  # JS file URL to be injected.
@@ -69,9 +69,10 @@ class Proxy
69
69
  end
70
70
 
71
71
  @main_thread = Thread.new &method(:server_thread)
72
+ rescue Errno::EADDRINUSE
73
+ raise BetterCap::Error, "[#{@type} PROXY] It looks like there's another process listening on #{@address}:#{@port}, please chose a different port."
72
74
  rescue Exception => e
73
75
  Logger.error "Error starting #{@type} proxy: #{e.inspect}"
74
- @socket.close unless @socket.nil?
75
76
  end
76
77
  end
77
78
 
@@ -106,7 +106,7 @@ class Strip
106
106
  @stripped = []
107
107
  @cookies = CookieMonitor.new
108
108
  @favicon = Response.from_file( File.dirname(__FILE__) + '/lock.ico', 'image/x-icon' )
109
- @resolver = BetterCap::Network::Servers::DNSD.new( nil, ctx.ifconfig[:ip_saddr], ctx.options.servers.dnsd_port )
109
+ @resolver = BetterCap::Network::Servers::DNSD.new( nil, ctx.iface.ip, ctx.options.servers.dnsd_port )
110
110
 
111
111
  @resolver.start
112
112
  end
@@ -203,6 +203,13 @@ class Streamer
203
203
  end
204
204
  end
205
205
 
206
+ # Handle a PUT request, +req+ is the request object and +res+ the response.
207
+ def do_PUT(req, res)
208
+ perform_proxy_request(req, res) do |http, path, header|
209
+ http.put(path, req.body || "", header)
210
+ end
211
+ end
212
+
206
213
  end
207
214
  end
208
215
  end
@@ -31,7 +31,7 @@ class StreamLogger
31
31
  def self.addr2s( addr, alt = nil )
32
32
  ctx = Context.get
33
33
  # check for the local address
34
- return 'local' if addr == ctx.ifconfig[:ip_saddr]
34
+ return 'local' if addr == ctx.iface.ip
35
35
  # is it a known target?
36
36
  target = ctx.find_target addr, nil
37
37
  return target.to_s_compact unless target.nil?
@@ -26,7 +26,7 @@ module TCP
26
26
  # event.data = 'bbb'
27
27
  # end
28
28
  # end
29
- class Module
29
+ class Module < BetterCap::Pluggable
30
30
  # This callback is called when the target is sending data to the upstream server.
31
31
  # +event+ is an instance of the BetterCap::Proxy::TCP::Event class.
32
32
  def on_data( event ); end
@@ -52,7 +52,7 @@ class Proxy
52
52
  # address is discovered and put in the ARP cache before we even start the proxy,
53
53
  # otherwise internal connections won't be spoofed and the proxy will be useless
54
54
  # until some random event will fill the ARP cache for the server.
55
- if @ctx.ifconfig[:ip4_obj].include?( @upstream_address )
55
+ if @ctx.iface.network.include?( @upstream_address )
56
56
  Logger.debug "[#{'TCP PROXY'.green}] Sending probe to upstream server address ..."
57
57
  BetterCap::Network.get_hw_address( @ctx, @upstream_address )
58
58
  # wait for the system to acknowledge the ARP cache changes.
@@ -88,7 +88,7 @@ class Sniffer
88
88
  return true unless pkt.is_ip?
89
89
  # skip if local packet and --local|-L was not specified.
90
90
  unless @@ctx.options.sniff.local
91
- return ( pkt.ip_saddr == @@ctx.ifconfig[:ip_saddr] or pkt.ip_daddr == @@ctx.ifconfig[:ip_saddr] )
91
+ return ( pkt.ip_saddr == @@ctx.iface.ip or pkt.ip_daddr == @@ctx.iface.ip )
92
92
  end
93
93
  rescue; end
94
94
  false
@@ -104,13 +104,13 @@ class Arp < Base
104
104
  end
105
105
  else
106
106
  # tell the target we're the gateway
107
- send_spoofed_packet( @ctx.gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
107
+ send_spoofed_packet( @ctx.gateway.ip, @ctx.iface.mac, target.ip, target.mac )
108
108
  # tell the gateway we're the target
109
- send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @ctx.gateway.ip, @ctx.gateway.mac ) unless @ctx.options.spoof.half_duplex
109
+ send_spoofed_packet( target.ip, @ctx.iface.mac, @ctx.gateway.ip, @ctx.gateway.mac ) unless @ctx.options.spoof.half_duplex
110
110
  # tell the target we're everybody else in the network :D
111
111
  @ctx.targets.each do |e|
112
112
  if e.spoofable? and e.ip != target.ip and e.ip != @ctx.gateway.ip
113
- send_spoofed_packet( e.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
113
+ send_spoofed_packet( e.ip, @ctx.iface.mac, target.ip, target.mac )
114
114
  end
115
115
  end
116
116
  end
@@ -131,7 +131,7 @@ class Arp < Base
131
131
  # we're only interested in 'who-has' packets
132
132
  pkt.arp_opcode == 1 and \
133
133
  pkt.arp_dst_mac.to_s == '00:00:00:00:00:00' and \
134
- pkt.arp_src_ip.to_s != @ctx.ifconfig[:ip_saddr]
134
+ pkt.arp_src_ip.to_s != @ctx.iface.ip
135
135
  end
136
136
 
137
137
  # Will watch for incoming ARP requests and spoof the source address.
@@ -141,7 +141,7 @@ class Arp < Base
141
141
  sniff_packets('arp') { |pkt|
142
142
  if is_arp_query?(pkt)
143
143
  Logger.info "[#{'ARP'.green}] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
144
- send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.ifconfig[:eth_saddr], pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
144
+ send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.iface.mac, pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
145
145
  end
146
146
  }
147
147
  end
@@ -79,7 +79,6 @@ class Icmp < Base
79
79
  def initialize
80
80
  @ctx = Context.get
81
81
  @forwarding = @ctx.firewall.forwarding_enabled?
82
- @local = @ctx.ifconfig[:ip_saddr]
83
82
  @spoof_thread = nil
84
83
  @watch_thread = nil
85
84
  @running = false
@@ -97,7 +96,7 @@ class Icmp < Base
97
96
  Logger.debug "Sending ICMP Redirect to #{target.to_s_compact} redirecting #{address} to us ..."
98
97
 
99
98
  pkt = ICMPRedirectPacket.new
100
- pkt.update!( @ctx.gateway, target, @local, address )
99
+ pkt.update!( @ctx.gateway, target, @ctx.iface.ip, address )
101
100
  @ctx.packets.push(pkt)
102
101
  rescue Exception => e
103
102
  Logger.debug "#{self.class.name} : #{e.message}"
@@ -147,7 +146,7 @@ class Icmp < Base
147
146
 
148
147
  # Return true if the +pkt+ packet comes from one of our targets.
149
148
  def is_interesting_packet?(pkt)
150
- return false if pkt.ip_saddr == @local
149
+ return false if pkt.ip_saddr == @ctx.iface.ip
151
150
  @ctx.targets.each do |target|
152
151
  if target.ip and ( target.ip == pkt.ip_saddr or target.ip == pkt.ip_daddr )
153
152
  return true
@@ -12,7 +12,7 @@ This project is released under the GPL 3 license.
12
12
  =end
13
13
  module BetterCap
14
14
  # Current version of bettercap.
15
- VERSION = '1.5.1'
15
+ VERSION = '1.5.2'
16
16
  # Program banner.
17
17
  BANNER = File.read( File.dirname(__FILE__) + '/banner' ).gsub( '#VERSION#', "v#{VERSION}")
18
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bettercap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simone Margaritelli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-18 00:00:00.000000000 Z
11
+ date: 2016-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -178,6 +178,7 @@ files:
178
178
  - lib/bettercap/loader.rb
179
179
  - lib/bettercap/logger.rb
180
180
  - lib/bettercap/memory.rb
181
+ - lib/bettercap/monkey/celluloid/actor.rb
181
182
  - lib/bettercap/monkey/celluloid/io/udp_socket.rb
182
183
  - lib/bettercap/monkey/em-proxy/proxy.rb
183
184
  - lib/bettercap/monkey/openssl/server.rb
@@ -204,6 +205,7 @@ files:
204
205
  - lib/bettercap/options/server_options.rb
205
206
  - lib/bettercap/options/sniff_options.rb
206
207
  - lib/bettercap/options/spoof_options.rb
208
+ - lib/bettercap/pluggable.rb
207
209
  - lib/bettercap/proxy/http/module.rb
208
210
  - lib/bettercap/proxy/http/modules/injectcss.rb
209
211
  - lib/bettercap/proxy/http/modules/injecthtml.rb