bettercap 1.5.1 → 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
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