bettercap 1.1.10 → 1.2.0

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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.md +8 -7
  3. data/bin/bettercap +8 -2
  4. data/lib/bettercap.rb +5 -4
  5. data/lib/bettercap/context.rb +54 -8
  6. data/lib/bettercap/discovery/agents/arp.rb +17 -4
  7. data/lib/bettercap/discovery/agents/base.rb +16 -52
  8. data/lib/bettercap/discovery/agents/icmp.rb +25 -17
  9. data/lib/bettercap/discovery/agents/udp.rb +9 -20
  10. data/lib/bettercap/discovery/{discovery.rb → thread.rb} +10 -9
  11. data/lib/bettercap/error.rb +1 -2
  12. data/lib/bettercap/factories/{firewall_factory.rb → firewall.rb} +11 -7
  13. data/lib/bettercap/factories/{parser_factory.rb → parser.rb} +13 -3
  14. data/lib/bettercap/factories/{spoofer_factory.rb → spoofer.rb} +10 -3
  15. data/lib/bettercap/firewalls/base.rb +76 -0
  16. data/lib/bettercap/firewalls/linux.rb +26 -16
  17. data/lib/bettercap/firewalls/osx.rb +22 -13
  18. data/lib/bettercap/firewalls/redirection.rb +15 -1
  19. data/lib/bettercap/httpd/server.rb +5 -0
  20. data/lib/bettercap/logger.rb +29 -8
  21. data/lib/bettercap/network.rb +105 -105
  22. data/lib/bettercap/options.rb +99 -41
  23. data/lib/bettercap/packet_queue.rb +92 -0
  24. data/lib/bettercap/proxy/certstore.rb +49 -43
  25. data/lib/bettercap/proxy/module.rb +4 -2
  26. data/lib/bettercap/proxy/proxy.rb +7 -2
  27. data/lib/bettercap/proxy/request.rb +28 -16
  28. data/lib/bettercap/proxy/response.rb +23 -2
  29. data/lib/bettercap/proxy/stream_logger.rb +6 -0
  30. data/lib/bettercap/proxy/streamer.rb +13 -5
  31. data/lib/bettercap/proxy/thread_pool.rb +6 -14
  32. data/lib/bettercap/shell.rb +5 -3
  33. data/lib/bettercap/sniffer/parsers/base.rb +7 -1
  34. data/lib/bettercap/sniffer/parsers/custom.rb +6 -1
  35. data/lib/bettercap/sniffer/parsers/ftp.rb +8 -5
  36. data/lib/bettercap/sniffer/parsers/httpauth.rb +4 -1
  37. data/lib/bettercap/sniffer/parsers/https.rb +4 -1
  38. data/lib/bettercap/sniffer/parsers/irc.rb +8 -5
  39. data/lib/bettercap/sniffer/parsers/mail.rb +8 -5
  40. data/lib/bettercap/sniffer/parsers/ntlmss.rb +21 -18
  41. data/lib/bettercap/sniffer/parsers/post.rb +4 -1
  42. data/lib/bettercap/sniffer/parsers/url.rb +4 -1
  43. data/lib/bettercap/sniffer/sniffer.rb +7 -3
  44. data/lib/bettercap/spoofers/arp.rb +69 -94
  45. data/lib/bettercap/spoofers/base.rb +132 -0
  46. data/lib/bettercap/spoofers/icmp.rb +200 -0
  47. data/lib/bettercap/spoofers/none.rb +8 -2
  48. data/lib/bettercap/target.rb +117 -90
  49. data/lib/bettercap/update_checker.rb +6 -0
  50. data/lib/bettercap/version.rb +3 -1
  51. metadata +24 -8
  52. data/lib/bettercap/base/ifirewall.rb +0 -46
  53. data/lib/bettercap/base/ispoofer.rb +0 -32
@@ -0,0 +1,92 @@
1
+ =begin
2
+ BETTERCAP
3
+ Author : Simone 'evilsocket' Margaritelli
4
+ Email : evilsocket@gmail.com
5
+ Blog : http://www.evilsocket.net/
6
+ This project is released under the GPL 3 license.
7
+ =end
8
+ require 'bettercap/error'
9
+
10
+ module BetterCap
11
+ # This class is responsible for sending various network packets.
12
+ class PacketQueue
13
+ # Initialize the PacketQueue, it will spawn +nworkers+ thread and
14
+ # will send packets to the +iface+ network interface.
15
+ def initialize( iface, nworkers = 4 )
16
+ @iface = iface
17
+ @nworkers = nworkers
18
+ @running = true
19
+ @injector = PacketFu::Inject.new(:iface => iface)
20
+ @queue = Queue.new
21
+ @workers = (0...nworkers).map {
22
+ ::Thread.new {
23
+ Logger.debug "PacketQueue worker started."
24
+
25
+ while @running
26
+ begin
27
+ packet = @queue.pop
28
+ # nil packet pushed to signal stopping
29
+ if packet.nil?
30
+ Logger.debug "Got nil packet, PacketQueue stopping ..."
31
+ break
32
+
33
+ # [ ip, port, data ] pushed by Discovery::Agents::Udp
34
+ elsif packet.is_a?(Array)
35
+ ip, port, data = packet
36
+ Logger.debug "Sending UDP data packet to #{ip}:#{port} ..."
37
+
38
+ # TODO: Maybe just create one globally?
39
+ sd = UDPSocket.new
40
+ sd.send( data, 0, ip, port )
41
+ sd = nil
42
+
43
+ # PacketFu packet
44
+ else
45
+ Logger.debug "Sending #{packet.class.name} packet ..."
46
+
47
+ # Use a global PacketFu::Inject object.
48
+ @injector.array = [packet.headers[0].to_s]
49
+ @injector.inject
50
+ end
51
+ rescue Exception => e
52
+ Logger.debug "#{self.class.name} ( #{packet.class.name} ) : #{e.message}"
53
+
54
+ # If we've got an error message such as:
55
+ # (cannot open BPF device) /dev/bpf0: Too many open files
56
+ # We want to retry to probe this ip in a while.
57
+ if e.message.include? 'Too many open files'
58
+ Logger.debug "Repushing #{self.class.name} to the packet queue ..."
59
+ push(packet)
60
+ end
61
+ end
62
+ end
63
+
64
+ Logger.debug "PacketQueue worker stopped."
65
+ }
66
+ }
67
+ end
68
+
69
+ # Push a packet to the queue.
70
+ def push(packet)
71
+ @queue.push(packet)
72
+ end
73
+
74
+ # Wait for the packet queue to be empty.
75
+ def wait_empty( timeout )
76
+ begin
77
+ Timeout::timeout(timeout) {
78
+ while !@queue.empty?
79
+ sleep 0.5
80
+ end
81
+ }
82
+ rescue; end
83
+ end
84
+
85
+ # Notify the queue to stop and wait for every worker to finish.
86
+ def stop
87
+ @running = false
88
+ @nworkers.times { push(nil) }
89
+ @workers.map(&:join)
90
+ end
91
+ end
92
+ end
@@ -14,55 +14,61 @@ require 'openssl'
14
14
 
15
15
  module BetterCap
16
16
  module Proxy
17
- class CertStore
18
- @@selfsigned = {}
19
- @@frompems = {}
17
+ # Class responsible of handling digital certificate loading or on the fly
18
+ # creation.
19
+ class CertStore
20
+ @@selfsigned = {}
21
+ @@frompems = {}
20
22
 
21
- def self.from_file( filename )
22
- unless @@frompems.has_key? filename
23
- Logger.info "Loading self signed HTTPS certificate from '#{filename}' ..."
23
+ # Load a certificate from the +filename+ file and return an
24
+ # OpenSSL::X509::Certificate instance for it.
25
+ def self.from_file( filename )
26
+ unless @@frompems.has_key? filename
27
+ Logger.info "Loading self signed HTTPS certificate from '#{filename}' ..."
24
28
 
25
- pem = File.read filename
29
+ pem = File.read filename
26
30
 
27
- @@frompems[filename] = { :cert => OpenSSL::X509::Certificate.new(pem), :key => OpenSSL::PKey::RSA.new(pem) }
28
- end
29
-
30
- @@frompems[filename]
31
+ @@frompems[filename] = { :cert => OpenSSL::X509::Certificate.new(pem), :key => OpenSSL::PKey::RSA.new(pem) }
31
32
  end
32
33
 
33
- def self.get_selfsigned( subject = '/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com' )
34
- unless @@selfsigned.has_key? subject
35
- Logger.info "Generating self signed HTTPS certificate for subject '#{subject}' ..."
36
-
37
- key = OpenSSL::PKey::RSA.new(2048)
38
- public_key = key.public_key
39
-
40
- cert = OpenSSL::X509::Certificate.new
41
- cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
42
- cert.not_before = Time.now
43
- cert.not_after = Time.now + 365 * 24 * 60 * 60
44
- cert.public_key = public_key
45
- cert.serial = 0x0
46
- cert.version = 2
47
-
48
- ef = OpenSSL::X509::ExtensionFactory.new
49
- ef.subject_certificate = cert
50
- ef.issuer_certificate = cert
51
- cert.extensions = [
52
- ef.create_extension("basicConstraints","CA:TRUE", true),
53
- ef.create_extension("subjectKeyIdentifier", "hash"),
54
- ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
55
- ]
56
- cert.add_extension ef.create_extension("authorityKeyIdentifier",
57
- "keyid:always,issuer:always")
58
-
59
- cert.sign key, OpenSSL::Digest::SHA256.new
60
-
61
- @@selfsigned[subject] = { :cert => cert, :key => key }
62
- end
63
-
64
- @@selfsigned[subject]
34
+ @@frompems[filename]
35
+ end
36
+
37
+ # Create a self signed digital certificate using the specified +subject+ string.
38
+ # Will return a OpenSSL::X509::Certificate instance.
39
+ def self.get_selfsigned( subject = '/C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com' )
40
+ unless @@selfsigned.has_key? subject
41
+ Logger.info "Generating self signed HTTPS certificate for subject '#{subject}' ..."
42
+
43
+ key = OpenSSL::PKey::RSA.new(2048)
44
+ public_key = key.public_key
45
+
46
+ cert = OpenSSL::X509::Certificate.new
47
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
48
+ cert.not_before = Time.now
49
+ cert.not_after = Time.now + 365 * 24 * 60 * 60
50
+ cert.public_key = public_key
51
+ cert.serial = 0x0
52
+ cert.version = 2
53
+
54
+ ef = OpenSSL::X509::ExtensionFactory.new
55
+ ef.subject_certificate = cert
56
+ ef.issuer_certificate = cert
57
+ cert.extensions = [
58
+ ef.create_extension("basicConstraints","CA:TRUE", true),
59
+ ef.create_extension("subjectKeyIdentifier", "hash"),
60
+ ef.create_extension("keyUsage", "cRLSign,keyCertSign", true),
61
+ ]
62
+ cert.add_extension ef.create_extension("authorityKeyIdentifier",
63
+ "keyid:always,issuer:always")
64
+
65
+ cert.sign key, OpenSSL::Digest::SHA256.new
66
+
67
+ @@selfsigned[subject] = { :cert => cert, :key => key }
65
68
  end
69
+
70
+ @@selfsigned[subject]
66
71
  end
67
72
  end
68
73
  end
74
+ end
@@ -13,18 +13,20 @@ require 'bettercap/logger'
13
13
 
14
14
  module BetterCap
15
15
  module Proxy
16
+ # Base class for transparent proxy modules.
16
17
  class Module
17
18
  @@modules = []
18
-
19
+ # Return a list of registered modules.
19
20
  def self.modules
20
21
  @@modules
21
22
  end
22
23
 
23
- # we're enabled by default, yo!
24
+ # Return true if the module is enabled, otherwise false.
24
25
  def enabled?
25
26
  true
26
27
  end
27
28
 
29
+ # Register available proxy modules into the system.
28
30
  def self.register_modules
29
31
  Object.constants.each do |klass|
30
32
  const = Kernel.const_get(klass)
@@ -18,8 +18,11 @@ require 'bettercap/network'
18
18
 
19
19
  module BetterCap
20
20
  module Proxy
21
-
21
+ # Transparent proxy class.
22
22
  class Proxy
23
+ # Initialize the transparent proxy, making it listen on +address+:+port+ and
24
+ # use the specified +processor+ routine for each request.
25
+ # If +is_https+ is true a HTTPS proxy will be created, otherwise a HTTP one.
23
26
  def initialize( address, port, is_https, processor )
24
27
  @socket = nil
25
28
  @address = address
@@ -49,6 +52,7 @@ class Proxy
49
52
  end
50
53
  end
51
54
 
55
+ # Start this proxy instance.
52
56
  def start
53
57
  begin
54
58
  @server = @socket = TCPServer.new( @address, @port )
@@ -70,6 +74,7 @@ class Proxy
70
74
  end
71
75
  end
72
76
 
77
+ # Stop this proxy instance.
73
78
  def stop
74
79
  begin
75
80
  Logger.info "Stopping #{@type} proxy ..."
@@ -77,7 +82,7 @@ class Proxy
77
82
  if @socket and @running
78
83
  @running = false
79
84
  @socket.close
80
- @pool.shutdown
85
+ @pool.shutdown false
81
86
  end
82
87
  rescue
83
88
  end
@@ -12,9 +12,22 @@ This project is released under the GPL 3 license.
12
12
 
13
13
  module BetterCap
14
14
  module Proxy
15
+ # HTTP request parser.
15
16
  class Request
16
- attr_reader :lines, :verb, :url, :host, :port, :content_length
17
-
17
+ # Patched request lines.
18
+ attr_reader :lines
19
+ # HTTP verb.
20
+ attr_reader :verb
21
+ # Request URL.
22
+ attr_reader :url
23
+ # Hostname.
24
+ attr_reader :host
25
+ # Request port.
26
+ attr_reader :port
27
+ # Content length.
28
+ attr_reader :content_length
29
+
30
+ # Initialize this object setting #port to +default_port+.
18
31
  def initialize( default_port = 80 )
19
32
  @lines = []
20
33
  @verb = nil
@@ -24,6 +37,8 @@ class Request
24
37
  @content_length = 0
25
38
  end
26
39
 
40
+ # Read lines from the +sock+ socket and parse them.
41
+ # Will raise an exception if the #hostname can not be parsed.
27
42
  def read(sock)
28
43
  # read the first line
29
44
  self << sock.readline
@@ -40,6 +55,7 @@ class Request
40
55
  raise "Couldn't extract host from the request." unless @host
41
56
  end
42
57
 
58
+ # Parse a single request line, patch it if needed and append it to #lines.
43
59
  def <<(line)
44
60
  line = line.chomp
45
61
 
@@ -57,7 +73,6 @@ class Request
57
73
  end
58
74
 
59
75
  line = "#{@verb} #{@url} HTTP/1.1"
60
-
61
76
  # get the host header value
62
77
  elsif line =~ /^Host:\s*(.*)$/
63
78
  @host = $1
@@ -65,34 +80,31 @@ class Request
65
80
  @host = $1
66
81
  @port = $2.to_i
67
82
  end
68
-
69
83
  # parse content length, this will speed up data streaming
70
84
  elsif line =~ /^Content-Length:\s+(\d+)\s*$/i
71
85
  @content_length = $1.to_i
72
-
73
86
  # we don't want to have hundreds of threads running
74
87
  elsif line =~ /^Connection: keep-alive/i
75
88
  line = 'Connection: close'
76
-
77
89
  elsif line =~ /^Proxy-Connection: (.+)/i
78
90
  line = "Connection: #{$1}"
79
-
80
91
  # disable gzip, chunked, etc encodings
81
92
  elsif line =~ /^Accept-Encoding:.*/i
82
93
  line = 'Accept-Encoding: identity'
83
-
84
94
  end
85
95
 
86
- @lines << line
87
- end
96
+ @lines << line
97
+ end
88
98
 
89
- def post?
90
- @verb == 'POST'
91
- end
99
+ # Return true if this is a POST request, otherwise false.
100
+ def post?
101
+ @verb == 'POST'
102
+ end
92
103
 
93
- def to_s
94
- @lines.join("\n") + "\n"
95
- end
104
+ # Return a string representation of the HTTP request.
105
+ def to_s
106
+ @lines.join("\n") + "\n"
96
107
  end
97
108
  end
98
109
  end
110
+ end
@@ -12,11 +12,26 @@ This project is released under the GPL 3 license.
12
12
 
13
13
  module BetterCap
14
14
  module Proxy
15
-
15
+ # HTTP response parser.
16
16
  class Response
17
- attr_reader :content_type, :charset, :content_length, :chunked, :headers, :code, :headers_done
17
+ # Response content type.
18
+ attr_reader :content_type
19
+ # Response charset, default to UTF-8.
20
+ attr_reader :charset
21
+ # Response content length.
22
+ attr_reader :content_length
23
+ # True if this is a chunked encoded response, otherwise false.
24
+ attr_reader :chunked
25
+ # A list of response headers.
26
+ attr_reader :headers
27
+ # Response status code.
28
+ attr_reader :code
29
+ # True if the parser finished to parse the headers, otherwise false.
30
+ attr_reader :headers_done
31
+ # Response body.
18
32
  attr_accessor :body
19
33
 
34
+ # Initialize this response object state.
20
35
  def initialize
21
36
  @content_type = nil
22
37
  @charset = 'UTF-8'
@@ -28,6 +43,8 @@ class Response
28
43
  @chunked = false
29
44
  end
30
45
 
46
+ # Read lines from the +sock+ socket until all headers are correctly parsed
47
+ # and return a BetterCap::Proxy::Response instance.
31
48
  def self.from_socket(sock)
32
49
  response = Response.new
33
50
 
@@ -43,6 +60,7 @@ class Response
43
60
  response
44
61
  end
45
62
 
63
+ # Parse a single response +line+.
46
64
  def <<(line)
47
65
  # we already parsed the heders, collect response body
48
66
  if @headers_done
@@ -80,10 +98,13 @@ class Response
80
98
  end
81
99
  end
82
100
 
101
+ # Return true if the response content type is textual, otherwise false.
83
102
  def textual?
84
103
  @content_type and ( @content_type =~ /^text\/.+/ or @content_type =~ /^application\/.+/ )
85
104
  end
86
105
 
106
+ # Return a string representation of this response object, patching the
107
+ # Content-Length header if the #body was modified.
87
108
  def to_s
88
109
  if textual?
89
110
  @headers.map! do |header|
@@ -12,6 +12,7 @@ This project is released under the GPL 3 license.
12
12
  require 'bettercap/logger'
13
13
 
14
14
  module BetterCap
15
+ # Raw or http streams pretty logging.
15
16
  class StreamLogger
16
17
  @@MAX_REQ_SIZE = 50
17
18
 
@@ -22,6 +23,8 @@ class StreamLogger
22
23
  '5' => :red
23
24
  }
24
25
 
26
+ # Search for the +addr+ IP address inside the list of collected targets and return
27
+ # its compact string representation ( @see BetterCap::Target#to_s_compact ).
25
28
  def self.addr2s( addr )
26
29
  ctx = Context.get
27
30
 
@@ -33,6 +36,7 @@ class StreamLogger
33
36
  addr
34
37
  end
35
38
 
39
+ # Log a raw packet ( +pkt+ ) data +payload+ using the specified +label+.
36
40
  def self.log_raw( pkt, label, payload )
37
41
  nl = if label.include?"\n" then "\n" else " " end
38
42
  label = label.strip
@@ -40,6 +44,8 @@ class StreamLogger
40
44
  "[#{label.green}]#{nl}#{payload.strip.yellow}" )
41
45
  end
42
46
 
47
+ # Log a HTTP ( HTTPS if +is_https+ is true ) stream performed by the +client+
48
+ # with the +request+ and +response+ most important informations.
43
49
  def self.log_http( is_https, client, request, response )
44
50
  request_s = "#{is_https ? 'https' : 'http'}://#{request.host}#{request.url}"
45
51
  response_s = "( #{response.content_type} )"
@@ -13,16 +13,25 @@ require 'bettercap/logger'
13
13
 
14
14
  module BetterCap
15
15
  module Proxy
16
+ # Handle data streaming between clients and servers for the BetterCap::Proxy::Proxy.
16
17
  class Streamer
18
+ # Default buffer size for data streaming.
19
+ BUFSIZE = 1024 * 16
20
+
21
+ # Initialize the class with the given +processor+ routine.
17
22
  def initialize( processor )
18
23
  @processor = processor
19
24
  end
20
25
 
26
+ # Redirect the +client+ to a funny video.
21
27
  def rickroll( client )
22
28
  client.write "HTTP/1.1 302 Found\n"
23
29
  client.write "Location: https://www.youtube.com/watch?v=dQw4w9WgXcQ\n\n"
24
30
  end
25
31
 
32
+ # Perform HTML streaming for the given +request+, applying the #processor
33
+ # to the +response+.
34
+ # +from+ and +to+ are the two TCP endpoints.
26
35
  def html( request, response, from, to )
27
36
  buff = ''
28
37
 
@@ -79,12 +88,13 @@ class Streamer
79
88
  to.write response.to_s
80
89
  end
81
90
 
82
-
91
+ # Perform binary streaming using the +opts+ dictionary.
92
+ # If response|request object is available inside +opts+ and a content length
93
+ # as well use it to speed up data streaming with precise data size
94
+ # +from+ and +to+ are the two TCP endpoints.
83
95
  def binary( from, to, opts = {} )
84
96
  total_size = 0
85
97
 
86
- # if response|request object is available and a content length as well
87
- # use it to speed up data streaming with precise data size
88
98
  if not opts[:response].nil?
89
99
  to.write opts[:response].to_s
90
100
 
@@ -127,8 +137,6 @@ class Streamer
127
137
 
128
138
  private
129
139
 
130
- BUFSIZE = 1024 * 16
131
-
132
140
  def consume_stream io, size
133
141
  read_timeout = 60.0
134
142
  dest = ''