bettercap 1.1.8 → 1.1.9

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.
@@ -0,0 +1,57 @@
1
+ =begin
2
+
3
+ BETTERCAP
4
+
5
+ Author : Simone 'evilsocket' Margaritelli
6
+ Email : evilsocket@gmail.com
7
+ Blog : http://www.evilsocket.net/
8
+
9
+ This project is released under the GPL 3 license.
10
+
11
+ =end
12
+ class Discovery
13
+ def initialize( ctx )
14
+ @ctx = ctx
15
+ @running = false
16
+ @thread = nil
17
+ end
18
+
19
+ def start
20
+ @running = true
21
+ @thread = Thread.new { worker }
22
+ end
23
+
24
+ def stop
25
+ @running = false
26
+ if @thread != nil
27
+ Logger.info( 'Stopping network discovery thread ...' ) unless @ctx.options.arpcache
28
+ begin
29
+ @thread.exit
30
+ rescue
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def worker
38
+ Logger.debug( 'Network discovery thread started.' ) unless @ctx.options.arpcache
39
+
40
+ while @running
41
+ empty_list = @ctx.targets.empty?
42
+
43
+ if @ctx.options.should_discover_hosts?
44
+ Logger.info 'Searching for targets ...' if empty_list
45
+ end
46
+
47
+ @ctx.targets = Network.get_alive_targets(@ctx).sort_by { |t| t.sortable_ip }
48
+
49
+ if empty_list and not @ctx.targets.empty?
50
+ Logger.info "Collected #{@ctx.targets.size} total targets."
51
+ @ctx.targets.each do |target|
52
+ Logger.info " #{target}"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -25,20 +25,20 @@ class LinuxFirewall < IFirewall
25
25
  shell.execute("echo #{enabled ? 0 : 1} > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts")
26
26
  end
27
27
 
28
- def add_port_redirection( iface, proto, from, addr, to )
28
+ def add_port_redirection( r )
29
29
  # post route
30
30
  shell.execute('iptables -t nat -I POSTROUTING -s 0/0 -j MASQUERADE')
31
31
  # accept all
32
32
  shell.execute('iptables -P FORWARD ACCEPT')
33
33
  # add redirection
34
- shell.execute("iptables -t nat -A PREROUTING -i #{iface} -p #{proto} --dport #{from} -j DNAT --to #{addr}:#{to}")
34
+ shell.execute("iptables -t nat -A PREROUTING -i #{r.interface} -p #{r.protocol} --dport #{r.src_port} -j DNAT --to #{r.dst_address}:#{r.dst_port}")
35
35
  end
36
36
 
37
- def del_port_redirection( iface, proto, from, addr, to )
37
+ def del_port_redirection( r )
38
38
  # remove post route
39
39
  shell.execute('iptables -t nat -D POSTROUTING -s 0/0 -j MASQUERADE')
40
40
  # remove redirection
41
- shell.execute("iptables -t nat -D PREROUTING -i #{iface} -p #{proto} --dport #{from} -j DNAT --to #{addr}:#{to}")
41
+ shell.execute("iptables -t nat -D PREROUTING -i #{r.interface} -p #{r.protocol} --dport #{r.src_port} -j DNAT --to #{r.dst_address}:#{r.dst_port}")
42
42
  end
43
43
 
44
44
  private
@@ -31,12 +31,12 @@ class OSXFirewall < IFirewall
31
31
  rescue; end
32
32
  end
33
33
 
34
- def add_port_redirection( iface, proto, from, addr, to )
34
+ def add_port_redirection( r )
35
35
  # create the pf config file
36
36
  config_file = "/tmp/bettercap_pf_#{Process.pid}.conf"
37
37
 
38
38
  File.open( config_file, 'a+t' ) do |f|
39
- f.write "rdr pass on #{iface} proto #{proto} from any to any port #{from} -> #{addr} port #{to}\n"
39
+ f.write "rdr pass on #{r.interface} proto #{r.protocol} from any to any port #{r.src_port} -> #{r.dst_address} port #{r.dst_port}\n"
40
40
  end
41
41
 
42
42
  # load the rule
@@ -45,7 +45,7 @@ class OSXFirewall < IFirewall
45
45
  enable true
46
46
  end
47
47
 
48
- def del_port_redirection( iface, proto, from, addr, to )
48
+ def del_port_redirection( r )
49
49
  # FIXME: This should search for multiple rules inside the
50
50
  # file and remove only this one.
51
51
 
@@ -11,43 +11,49 @@ This project is released under the GPL 3 license.
11
11
  =end
12
12
  module Logger
13
13
  class << self
14
- attr_accessor :logfile, :debug_enabled
15
-
16
- @@semaphore = Mutex.new
14
+ def init( debug, logfile )
15
+ @@debug = debug
16
+ @@logfile = logfile
17
+ @@queue = Queue.new
18
+ @@thread = Thread.new { worker }
19
+ end
17
20
 
18
21
  def error(message)
19
- write(formatted_message(message, 'E').red)
22
+ @@queue.push formatted_message(message, 'E').red
20
23
  end
21
24
 
22
25
  def info(message)
23
- write(formatted_message(message, 'I'))
26
+ @@queue.push formatted_message(message, 'I')
24
27
  end
25
28
 
26
29
  def warn(message)
27
- write(formatted_message(message, 'W').yellow)
30
+ @@queue.push formatted_message(message, 'W').yellow
28
31
  end
29
32
 
30
33
  def debug(message)
31
- if @debug_enabled
32
- write(formatted_message(message, 'D').light_black)
34
+ if @@debug
35
+ @@queue.push formatted_message(message, 'D').light_black
33
36
  end
34
37
  end
35
38
 
36
- def write(message)
37
- # make sure that logging is thread safe
38
- @@semaphore.synchronize {
39
- puts message
39
+ def raw(message)
40
+ @@queue.push message
41
+ end
42
+
43
+ private
40
44
 
41
- if !@logfile.nil?
42
- f = File.open( @logfile, 'a+t' )
45
+ def worker
46
+ loop do
47
+ message = @@queue.pop
48
+ puts message
49
+ unless @@logfile.nil?
50
+ f = File.open( @@logfile, 'a+t' )
43
51
  f.puts( message.gsub( /\e\[(\d+)(;\d+)*m/, '') + "\n")
44
52
  f.close
45
53
  end
46
- }
54
+ end
47
55
  end
48
56
 
49
- private
50
-
51
57
  def formatted_message(message, message_type)
52
58
  "[#{message_type}] #{message}"
53
59
  end
@@ -45,11 +45,6 @@ class Network
45
45
  end
46
46
  end
47
47
  end
48
-
49
- raise BetterCap::Error, "Could not detect the gateway address for interface #{Context.get.options.iface}, "\
50
- 'make sure you\'ve specified the correct network interface to use and to have the '\
51
- 'correct network configuration, this could also happen if bettercap '\
52
- 'is launched from a virtual environment.' unless !gw.nil? and is_ip?(gw)
53
48
  gw
54
49
  end
55
50
 
@@ -80,6 +80,187 @@ class Options
80
80
  @check_updates = false
81
81
  end
82
82
 
83
+ def self.parse!
84
+ ctx = Context.get
85
+
86
+ OptionParser.new do |opts|
87
+ opts.banner = "Usage: #{$0} [options]"
88
+ opts.version = BetterCap::VERSION
89
+
90
+ opts.on( '-G', '--gateway ADDRESS', 'Manually specify the gateway address, if not specified the current gateway will be retrieved and used. ' ) do |v|
91
+ ctx.options.gateway = v
92
+ end
93
+
94
+ opts.on( '-I', '--interface IFACE', 'Network interface name - default: ' + ctx.options.iface.to_s ) do |v|
95
+ ctx.options.iface = v
96
+ end
97
+
98
+ opts.on( '-S', '--spoofer NAME', 'Spoofer module to use, available: ' + SpooferFactory.available.join(', ') + ' - default: ' + ctx.options.spoofer ) do |v|
99
+ ctx.options.spoofer = v
100
+ end
101
+
102
+ opts.on( '-T', '--target ADDRESS1,ADDRESS2', 'Target IP addresses, if not specified the whole subnet will be targeted.' ) do |v|
103
+ ctx.options.target = v
104
+ end
105
+
106
+ opts.on( '--ignore ADDRESS1,ADDRESS2', 'Ignore these addresses if found while searching for targets.' ) do |v|
107
+ ctx.options.ignore = v
108
+ end
109
+
110
+ opts.on( '-O', '--log LOG_FILE', 'Log all messages into a file, if not specified the log messages will be only print into the shell.' ) do |v|
111
+ ctx.options.logfile = v
112
+ end
113
+
114
+ opts.on( '-D', '--debug', 'Enable debug logging.' ) do
115
+ ctx.options.debug = true
116
+ end
117
+
118
+ opts.on( '-L', '--local', 'Parse packets coming from/to the address of this computer ( NOTE: Will set -X to true ), default to false.' ) do
119
+ ctx.options.local = true
120
+ ctx.options.sniffer = true
121
+ end
122
+
123
+ opts.on( '-X', '--sniffer', 'Enable sniffer.' ) do
124
+ ctx.options.sniffer = true
125
+ end
126
+
127
+ opts.on( '--sniffer-source FILE', 'Load packets from the specified PCAP file instead of the interface ( will enable sniffer ).' ) do |v|
128
+ ctx.options.sniffer = true
129
+ ctx.options.sniffer_src = File.expand_path v
130
+ end
131
+
132
+ opts.on( '--sniffer-pcap FILE', 'Save all packets to the specified PCAP file ( will enable sniffer ).' ) do |v|
133
+ ctx.options.sniffer = true
134
+ ctx.options.sniffer_pcap = File.expand_path v
135
+ end
136
+
137
+ opts.on( '--sniffer-filter EXPRESSION', 'Configure the sniffer to use this BPF filter ( will enable sniffer ).' ) do |v|
138
+ ctx.options.sniffer = true
139
+ ctx.options.sniffer_filter = v
140
+ end
141
+
142
+ opts.on( '-P', '--parsers PARSERS', 'Comma separated list of packet parsers to enable, "*" for all ( NOTE: Will set -X to true ), available: ' + ParserFactory.available.join(', ') + ' - default: *' ) do |v|
143
+ ctx.options.sniffer = true
144
+ ctx.options.parsers = ParserFactory.from_cmdline(v)
145
+ end
146
+
147
+ opts.on( '--no-discovery', 'Do not actively search for hosts, just use the current ARP cache, default to false.' ) do
148
+ ctx.options.arpcache = true
149
+ end
150
+
151
+ opts.on( '--no-spoofing', 'Disable spoofing, alias for --spoofer NONE.' ) do
152
+ ctx.options.spoofer = 'NONE'
153
+ end
154
+
155
+ opts.on( '--half-duplex', 'Enable half-duplex MITM, this will make bettercap work in those cases when the router is not vulnerable.' ) do
156
+ ctx.options.half_duplex = true
157
+ end
158
+
159
+ opts.on( '--proxy', 'Enable HTTP proxy and redirects all HTTP requests to it, default to false.' ) do
160
+ ctx.options.proxy = true
161
+ end
162
+
163
+ opts.on( '--proxy-https', 'Enable HTTPS proxy and redirects all HTTPS requests to it, default to false.' ) do
164
+ ctx.options.proxy = true
165
+ ctx.options.proxy_https = true
166
+ end
167
+
168
+ opts.on( '--proxy-port PORT', 'Set HTTP proxy port, default to ' + ctx.options.proxy_port.to_s + ' .' ) do |v|
169
+ ctx.options.proxy = true
170
+ ctx.options.proxy_port = v.to_i
171
+ end
172
+
173
+ opts.on( '--proxy-https-port PORT', 'Set HTTPS proxy port, default to ' + ctx.options.proxy_https_port.to_s + ' .' ) do |v|
174
+ ctx.options.proxy = true
175
+ ctx.options.proxy_https = true
176
+ ctx.options.proxy_https_port = v.to_i
177
+ end
178
+
179
+ opts.on( '--proxy-pem FILE', 'Use a custom PEM certificate file for the HTTPS proxy.' ) do |v|
180
+ ctx.options.proxy = true
181
+ ctx.options.proxy_https = true
182
+ ctx.options.proxy_pem_file = File.expand_path v
183
+ end
184
+
185
+ opts.on( '--proxy-module MODULE', 'Ruby proxy module to load.' ) do |v|
186
+ ctx.options.proxy = true
187
+ ctx.options.proxy_module = File.expand_path v
188
+ end
189
+
190
+ opts.on( '--custom-proxy ADDRESS', 'Use a custom HTTP upstream proxy instead of the builtin one.' ) do |v|
191
+ ctx.options.custom_proxy = v
192
+ end
193
+
194
+ opts.on( '--custom-proxy-port PORT', 'Specify a port for the custom HTTP upstream proxy, default to ' + ctx.options.custom_proxy_port.to_s + ' .' ) do |v|
195
+ ctx.options.custom_proxy_port = v.to_i
196
+ end
197
+
198
+ opts.on( '--custom-https-proxy ADDRESS', 'Use a custom HTTPS upstream proxy instead of the builtin one.' ) do |v|
199
+ ctx.options.custom_https_proxy = v
200
+ end
201
+
202
+ opts.on( '--custom-https-proxy-port PORT', 'Specify a port for the custom HTTPS upstream proxy, default to ' + ctx.options.custom_https_proxy_port.to_s + ' .' ) do |v|
203
+ ctx.options.custom_https_proxy_port = v.to_i
204
+ end
205
+
206
+ opts.on( '--httpd', 'Enable HTTP server, default to false.' ) do
207
+ ctx.options.httpd = true
208
+ end
209
+
210
+ opts.on( '--httpd-port PORT', 'Set HTTP server port, default to ' + ctx.options.httpd_port.to_s + '.' ) do |v|
211
+ ctx.options.httpd = true
212
+ ctx.options.httpd_port = v.to_i
213
+ end
214
+
215
+ opts.on( '--httpd-path PATH', 'Set HTTP server path, default to ' + ctx.options.httpd_path + '.' ) do |v|
216
+ ctx.options.httpd = true
217
+ ctx.options.httpd_path = v
218
+ end
219
+
220
+ opts.on( '--check-updates', 'Will check if any update is available and then exit.' ) do
221
+ ctx.options.check_updates = true
222
+ end
223
+
224
+ opts.on('-h', '--help', 'Display the available options.') do
225
+ puts opts
226
+ puts "\nFor examples & docs please visit " + "http://bettercap.org/docs/".bold
227
+ exit
228
+ end
229
+ end.parse!
230
+
231
+ Logger.init( ctx.options.debug, ctx.options.logfile )
232
+
233
+ Logger.warn "You are running an unstable/beta version of this software, please" \
234
+ " update to a stable one if available." if BetterCap::VERSION =~ /[\d\.+]b/
235
+
236
+ if ctx.options.check_updates
237
+ UpdateChecker.check
238
+ exit
239
+ end
240
+
241
+ raise BetterCap::Error, 'This software must run as root.' unless Process.uid == 0
242
+ raise BetterCap::Error, 'No default interface found, please specify one with the -I argument.' if ctx.options.iface.nil?
243
+
244
+ unless ctx.options.gateway.nil?
245
+ raise BetterCap::Error, "The specified gateway '#{ctx.options.gateway}' is not a valid IPv4 address." unless Network.is_ip?(ctx.options.gateway)
246
+ ctx.gateway = ctx.options.gateway
247
+ Logger.debug("Targetting manually specified gateway #{ctx.gateway}")
248
+ end
249
+
250
+ unless ctx.options.target.nil?
251
+ ctx.targets = ctx.options.to_targets
252
+ end
253
+
254
+ # Load firewall instance, network interface informations and detect the
255
+ # gateway address.
256
+ ctx.update!
257
+
258
+ # Spoofers need the context network data to be initialized.
259
+ ctx.spoofer = ctx.options.to_spoofers
260
+
261
+ ctx
262
+ end
263
+
83
264
  def should_discover_hosts?
84
265
  !@arpcache
85
266
  end
@@ -89,7 +270,7 @@ class Options
89
270
  end
90
271
 
91
272
  def has_spoofer?
92
- @spoofer == 'NONE' or @spoofer == 'none'
273
+ @spoofer != 'NONE' and @spoofer != 'none'
93
274
  end
94
275
 
95
276
  def has_http_sniffer_enabled?
@@ -40,7 +40,7 @@ class StreamLogger
40
40
  response_s += " [#{response.code}]"
41
41
  end
42
42
 
43
- Logger.write "[#{self.addr2s(client)}] #{request.verb.light_blue} #{request_s} #{response_s}"
43
+ Logger.raw "[#{self.addr2s(client)}] #{request.verb.light_blue} #{request_s} #{response_s}"
44
44
  end
45
45
  end
46
46
  end
@@ -12,13 +12,22 @@ module Shell
12
12
 
13
13
  #return the output of command
14
14
  def execute(command)
15
- r=%x(#{command})
16
- if $? != 0
17
- raise BetterCap::Error, "Error, executing #{command}"
15
+ r = ''
16
+ 10.times do
17
+ begin
18
+ r=%x(#{command})
19
+ if $? != 0
20
+ raise BetterCap::Error, "Error, executing #{command}"
21
+ end
22
+ break
23
+ rescue Errno::EMFILE => e
24
+ Logger.debug "Retrying command '#{command}' due to Errno::EMFILE error ..."
25
+ sleep 1
26
+ end
18
27
  end
19
- return r
28
+ r
20
29
  end
21
-
30
+
22
31
  def ifconfig(iface = '')
23
32
  self.execute( "LANG=en && ifconfig #{iface}" )
24
33
  end
@@ -28,7 +28,7 @@ class BaseParser
28
28
  s = pkt.to_s
29
29
  @filters.each do |filter|
30
30
  if s =~ filter
31
- Logger.write "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
31
+ Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
32
32
  "[#{@name}] ".green +
33
33
  pkt.payload.strip.yellow
34
34
  end
@@ -31,11 +31,11 @@ class HttpauthParser < BaseParser
31
31
  decoded = Base64.decode64(encoded)
32
32
  user, pass = decoded.split(':')
33
33
 
34
- Logger.write "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
34
+ Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
35
35
  '[HTTP BASIC AUTH]'.green + " http://#{hostname}#{path} - username=#{user} password=#{pass}".yellow
36
36
 
37
37
  elsif line =~ /Authorization:\s*Digest\s+(.+)/i
38
- Logger.write "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
38
+ Logger.raw "[#{addr2s(pkt.ip_saddr)}:#{pkt.tcp_src} > #{addr2s(pkt.ip_daddr)}:#{pkt.tcp_dst} #{pkt.proto.last}] " +
39
39
  '[HTTP DIGEST AUTH]'.green + " http://#{hostname}#{path}\n#{$1}".yellow
40
40
 
41
41
  end