bettercap 1.1.10 → 1.2.0

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