bettercap 1.1.8 → 1.1.9

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