bettercap 1.5.8 → 1.5.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/bettercap +3 -0
- data/lib/bettercap/context.rb +20 -3
- data/lib/bettercap/discovery/agents/base.rb +7 -22
- data/lib/bettercap/discovery/thread.rb +0 -1
- data/lib/bettercap/logger.rb +1 -1
- data/lib/bettercap/network/packet_queue.rb +0 -1
- data/lib/bettercap/options/options.rb +1 -4
- data/lib/bettercap/options/proxy_options.rb +8 -1
- data/lib/bettercap/options/sniff_options.rb +5 -0
- data/lib/bettercap/proxy/http/modules/injecthtml.rb +19 -3
- data/lib/bettercap/proxy/http/modules/redirect.rb +59 -0
- data/lib/bettercap/proxy/http/request.rb +8 -8
- data/lib/bettercap/proxy/http/response.rb +9 -0
- data/lib/bettercap/proxy/http/sslstrip/strip.rb +7 -3
- data/lib/bettercap/proxy/http/streamer.rb +1 -1
- data/lib/bettercap/proxy/tcp/module.rb +14 -1
- data/lib/bettercap/shell.rb +1 -0
- data/lib/bettercap/sniffer/parsers/base.rb +33 -3
- data/lib/bettercap/sniffer/parsers/ftp.rb +1 -0
- data/lib/bettercap/sniffer/parsers/mail.rb +2 -1
- data/lib/bettercap/spoofers/arp.rb +7 -4
- data/lib/bettercap/version.rb +1 -1
- data/lib/bettercap.rb +16 -27
- metadata +35 -35
- data/lib/bettercap/sniffer/parsers/creditcard.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 689778ce78e7e19dfdde9cf0c1d880741e8cb481
|
4
|
+
data.tar.gz: ab97d62ec1d8a1272025a8a9e8ff02cb4f3bbfe1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a817c0cccd0fc50cc963acb07841129c790b1e8cbf97b05347ecdfe8bf18dccf716a30c1f465575af2e0ae8592a8592be584b4011fda34ba09788ce6080cd37
|
7
|
+
data.tar.gz: 1a4169ecc87ada74613b0a464eb432b12a73de5a636adeea5b1421330928547e28283b75342e110776c48c8fabc271099f5e5044deee5129d8a14e5cf7c0e3ac
|
data/bin/bettercap
CHANGED
data/lib/bettercap/context.rb
CHANGED
@@ -40,8 +40,8 @@ class Context
|
|
40
40
|
attr_reader :timeout
|
41
41
|
# Instance of BetterCap::PacketQueue.
|
42
42
|
attr_reader :packets
|
43
|
-
#
|
44
|
-
attr_reader :
|
43
|
+
# Precomputed list of possible addresses on the current network.
|
44
|
+
attr_reader :endpoints
|
45
45
|
|
46
46
|
@@instance = nil
|
47
47
|
|
@@ -65,7 +65,6 @@ class Context
|
|
65
65
|
@options = Options.new iface
|
66
66
|
@discovery = Discovery::Thread.new self
|
67
67
|
@firewall = Firewalls::Base.get
|
68
|
-
@memory = Memory.new
|
69
68
|
@iface = nil
|
70
69
|
@original_mac = nil
|
71
70
|
@gateway = nil
|
@@ -76,6 +75,7 @@ class Context
|
|
76
75
|
@proxies = []
|
77
76
|
@redirections = []
|
78
77
|
@packets = nil
|
78
|
+
@endpoints = []
|
79
79
|
end
|
80
80
|
|
81
81
|
# Update the Context state parsing network related informations.
|
@@ -105,6 +105,8 @@ class Context
|
|
105
105
|
@gateway = Network::Target.new gw
|
106
106
|
@targets = @options.core.targets unless @options.core.targets.nil?
|
107
107
|
@iface = Network::Target.new( cfg[:ip_saddr], cfg[:eth_saddr], cfg[:ip4_obj], cfg[:iface] )
|
108
|
+
raise BetterCap::Error, "Could not determine MAC address of '#{@options.core.iface}', make sure this interface "\
|
109
|
+
'is active and connected.' unless Network::Validator::is_mac?(@iface.mac)
|
108
110
|
|
109
111
|
Logger.info "[#{@iface.name.green}] #{@iface.to_s(false)}"
|
110
112
|
|
@@ -117,6 +119,21 @@ class Context
|
|
117
119
|
@packets = Network::PacketQueue.new( @iface.name, @options.core.packet_throttle, 4 )
|
118
120
|
# Spoofers need the context network data to be initialized.
|
119
121
|
@spoofer = @options.spoof.parse_spoofers
|
122
|
+
|
123
|
+
if @options.core.discovery?
|
124
|
+
tstart = Time.now
|
125
|
+
Logger.info "[#{'DISCOVERY'.green}] Precomputing list of possible endpoints, this could take a while depending on your subnet ..."
|
126
|
+
net = ip = @iface.network
|
127
|
+
# loop each ip in our subnet and push it to the queue
|
128
|
+
while net.include?ip
|
129
|
+
if ip != @gateway.ip and ip != @iface.ip
|
130
|
+
@endpoints << ip
|
131
|
+
end
|
132
|
+
ip = ip.succ
|
133
|
+
end
|
134
|
+
tend = Time.now
|
135
|
+
Logger.info "[#{'DISCOVERY'.green}] Done in #{(tend - tstart) * 1000.0} ms"
|
136
|
+
end
|
120
137
|
end
|
121
138
|
|
122
139
|
# Find a target given its +ip+ and +mac+ addresses inside the #targets
|
@@ -24,13 +24,8 @@ class Base
|
|
24
24
|
@address = address
|
25
25
|
|
26
26
|
if @address.nil?
|
27
|
-
|
28
|
-
|
29
|
-
while net.include?ip
|
30
|
-
unless skip_address?(ip)
|
31
|
-
@ctx.packets.push( get_probe(ip) )
|
32
|
-
end
|
33
|
-
ip = ip.succ
|
27
|
+
@ctx.endpoints.each do |ip|
|
28
|
+
@ctx.packets.push( get_probe(ip) )
|
34
29
|
end
|
35
30
|
else
|
36
31
|
if skip_address?(@address)
|
@@ -46,21 +41,11 @@ class Base
|
|
46
41
|
|
47
42
|
# Return true if +ip+ must be skipped during discovery, otherwise false.
|
48
43
|
def skip_address?(ip)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
#
|
53
|
-
|
54
|
-
return true
|
55
|
-
# don't stress endpoints we already discovered
|
56
|
-
else
|
57
|
-
target = @ctx.find_target( ip.to_s, nil )
|
58
|
-
# known target?
|
59
|
-
return false if target.nil?
|
60
|
-
# do we still need to get the mac for this target?
|
61
|
-
return ( target.mac.nil?? false : true )
|
62
|
-
end
|
63
|
-
|
44
|
+
target = @ctx.find_target( ip.to_s, nil )
|
45
|
+
# known target?
|
46
|
+
return false if target.nil?
|
47
|
+
# do we still need to get the mac for this target?
|
48
|
+
return ( target.mac.nil?? false : true )
|
64
49
|
end
|
65
50
|
|
66
51
|
# Each Discovery::Agent::Base derived class should implement this method.
|
data/lib/bettercap/logger.rb
CHANGED
@@ -157,7 +157,7 @@ class Options
|
|
157
157
|
'https-proxy' => ( @proxies.proxy_https ? on : off ),
|
158
158
|
'sslstrip' => ( @proxies.sslstrip? ? on : off ),
|
159
159
|
'http-server' => ( @servers.httpd ? on : off ),
|
160
|
-
'dns-server' => ( @proxies.sslstrip? or @servers.dnsd ? on : off )
|
160
|
+
'dns-server' => ( (@proxies.sslstrip? or @servers.dnsd) ? on : off )
|
161
161
|
}
|
162
162
|
|
163
163
|
msg = "Starting [ "
|
@@ -167,9 +167,6 @@ class Options
|
|
167
167
|
msg += "] ...\n\n"
|
168
168
|
|
169
169
|
Logger.info msg
|
170
|
-
|
171
|
-
Logger.warn "You are running an unstable/beta version of this software, please" \
|
172
|
-
" update to a stable one if available." if BetterCap::VERSION =~ /[\d\.+]b/
|
173
170
|
end
|
174
171
|
end
|
175
172
|
end
|
@@ -38,6 +38,8 @@ class ProxyOptions
|
|
38
38
|
attr_accessor :allow_local_connections
|
39
39
|
# If true, log HTTP responses too.
|
40
40
|
attr_accessor :log_response
|
41
|
+
# If true, suppress HTTP requests logs.
|
42
|
+
attr_accessor :no_http_logs
|
41
43
|
# If true, TCP proxy will be enabled.
|
42
44
|
attr_accessor :tcp_proxy
|
43
45
|
# TCP proxy local port.
|
@@ -72,6 +74,7 @@ class ProxyOptions
|
|
72
74
|
@sslstrip = true
|
73
75
|
@allow_local_connections = false
|
74
76
|
@log_response = false
|
77
|
+
@no_http_logs = false
|
75
78
|
|
76
79
|
@tcp_proxy = false
|
77
80
|
@tcp_proxy_port = 2222
|
@@ -103,7 +106,7 @@ class ProxyOptions
|
|
103
106
|
|
104
107
|
opts.on( '--tcp-proxy-module MODULE', "Ruby TCP proxy module to load." ) do |v|
|
105
108
|
@tcp_proxy_module = File.expand_path(v)
|
106
|
-
Proxy::TCP::Module.load( @tcp_proxy_module )
|
109
|
+
Proxy::TCP::Module.load( @tcp_proxy_module, opts )
|
107
110
|
end
|
108
111
|
|
109
112
|
opts.on( '--tcp-proxy-port PORT', "Set local TCP proxy port, default to #{@tcp_proxy_port.to_s.yellow} ." ) do |v|
|
@@ -167,6 +170,10 @@ class ProxyOptions
|
|
167
170
|
@log_response = true
|
168
171
|
end
|
169
172
|
|
173
|
+
opts.on( '--no-http-logs', 'Suppress HTTP requests and responses logs.' ) do
|
174
|
+
@no_http_logs = true
|
175
|
+
end
|
176
|
+
|
170
177
|
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|
|
171
178
|
Proxy::HTTP::Module.load(ctx, opts, v)
|
172
179
|
@proxy = true
|
@@ -74,6 +74,11 @@ class SniffOptions
|
|
74
74
|
@parsers = Parsers::Base.from_cmdline(v)
|
75
75
|
end
|
76
76
|
|
77
|
+
opts.on( '--disable-parsers PARSERS', "Comma separated list of packet parsers to disable ( NOTE: Will set -X to true )" ) do |v|
|
78
|
+
@enabled = true
|
79
|
+
@parsers = Parsers::Base.from_exclusion_list(v)
|
80
|
+
end
|
81
|
+
|
77
82
|
opts.on( '--custom-parser EXPRESSION', 'Use a custom regular expression in order to capture and show sniffed data ( NOTE: Will set -X to true ).' ) do |v|
|
78
83
|
@enabled = true
|
79
84
|
@parsers = ['CUSTOM']
|
@@ -25,6 +25,8 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module
|
|
25
25
|
@@iframe = nil
|
26
26
|
# HTML data to be injected.
|
27
27
|
@@data = nil
|
28
|
+
# Position of the injection, 0 = just after <body>, 1 = before </body>
|
29
|
+
@@position = 0
|
28
30
|
|
29
31
|
# Add custom command line arguments to the +opts+ OptionParser instance.
|
30
32
|
def self.on_options(opts)
|
@@ -45,6 +47,16 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module
|
|
45
47
|
opts.on( '--html-iframe-url URL', 'URL of the iframe that will be injected, if this option is specified an "iframe" tag will be injected.' ) do |v|
|
46
48
|
@@iframe = v
|
47
49
|
end
|
50
|
+
|
51
|
+
opts.on( '--html-position POSITION', 'Position of the injection, valid values are START for injecting after the <body> tag and END to inject just before </body>.' ) do |v|
|
52
|
+
if v == 'START'
|
53
|
+
@@position = 0
|
54
|
+
elsif v == 'END'
|
55
|
+
@@position = 1
|
56
|
+
else
|
57
|
+
raise BetterCap::Error, "#{v} invalid position, only START or END values are accepted."
|
58
|
+
end
|
59
|
+
end
|
48
60
|
end
|
49
61
|
|
50
62
|
# Create an instance of this module and raise a BetterCap::Error if command
|
@@ -61,12 +73,16 @@ class InjectHTML < BetterCap::Proxy::HTTP::Module
|
|
61
73
|
BetterCap::Logger.info "[#{'INJECTHTML'.green}] Injecting HTML code into #{request.to_url}"
|
62
74
|
|
63
75
|
if @@data.nil?
|
64
|
-
|
76
|
+
replacement = "<iframe src=\"#{@@iframe}\" frameborder=\"0\" height=\"0\" width=\"0\"></iframe>"
|
65
77
|
else
|
66
|
-
|
78
|
+
replacement = "#{@@data}"
|
67
79
|
end
|
68
80
|
|
69
|
-
|
81
|
+
if @@position == 0
|
82
|
+
response.body.sub!( /<body([^>]*)>/i ) { "<body#{$1}>#{replacement}" }
|
83
|
+
else
|
84
|
+
response.body.sub!( /<\/body>/i ) { "#{replacement}</body>" }
|
85
|
+
end
|
70
86
|
end
|
71
87
|
end
|
72
88
|
end
|
@@ -0,0 +1,59 @@
|
|
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
|
+
# This proxy module will redirect to a custom URL.
|
15
|
+
class Redirect < BetterCap::Proxy::HTTP::Module
|
16
|
+
meta(
|
17
|
+
'Name' => 'Redirect',
|
18
|
+
'Description' => 'This proxy module will redirect the target(s) to a custom URL.',
|
19
|
+
'Version' => '1.0.0',
|
20
|
+
'Author' => "Simone 'evilsocket' Margaritelli",
|
21
|
+
'License' => 'GPL3'
|
22
|
+
)
|
23
|
+
|
24
|
+
# URL to redirect the target(s) to.
|
25
|
+
@@url = nil
|
26
|
+
# Optional regex filter for redirections.
|
27
|
+
@@filter = nil
|
28
|
+
|
29
|
+
# Add custom command line arguments to the +opts+ OptionParser instance.
|
30
|
+
def self.on_options(opts)
|
31
|
+
opts.separator ""
|
32
|
+
opts.separator "Redirect Proxy Module Options:"
|
33
|
+
opts.separator ""
|
34
|
+
|
35
|
+
opts.on( '--redirect-url URL', 'URL to redirect the target(s) to.' ) do |v|
|
36
|
+
@@url = v
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on( '--redirect-filter EXPRESSION', 'Optional regex filter for redirections.' ) do |v|
|
40
|
+
@@filter = Regexp.new(v)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Create an instance of this module and raise a BetterCap::Error if command
|
45
|
+
# line arguments weren't correctly specified.
|
46
|
+
def initialize
|
47
|
+
raise BetterCap::Error, "No --redirect-url option specified for the proxy module." if @@url.nil?
|
48
|
+
raise BetterCap::Error, "Invalid URL specified." unless @@url =~ /\A#{URI::regexp}\z/
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_request( request, response )
|
52
|
+
if response.content_type =~ /^text\/html.*/ and !@@url.include?(request.host)
|
53
|
+
if @@filter.nil? or @@filter.match(request.to_url)
|
54
|
+
BetterCap::Logger.info "[#{'REDIRECT'.green}] Redirecting #{request.to_url} to #{@@url} ..."
|
55
|
+
response.redirect!(@@url)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -110,22 +110,22 @@ class Request
|
|
110
110
|
name = $1
|
111
111
|
value = $2
|
112
112
|
|
113
|
-
case name
|
114
|
-
when '
|
113
|
+
case name.downcase
|
114
|
+
when 'host'
|
115
115
|
@host = value
|
116
116
|
if @host =~ /([^:]*):([0-9]*)$/
|
117
117
|
@host = $1
|
118
118
|
@port = $2.to_i
|
119
119
|
end
|
120
|
-
when '
|
120
|
+
when 'content-length'
|
121
121
|
@content_length = value.to_i
|
122
|
-
|
123
|
-
when '
|
122
|
+
# we don't want to have hundreds of threads running
|
123
|
+
when 'connection'
|
124
124
|
value = 'close'
|
125
|
-
when '
|
125
|
+
when 'proxy-connection'
|
126
126
|
name = 'Connection'
|
127
|
-
|
128
|
-
when '
|
127
|
+
# disable gzip, chunked, etc encodings
|
128
|
+
when 'accept-encoding'
|
129
129
|
value = 'identity'
|
130
130
|
end
|
131
131
|
|
@@ -102,6 +102,15 @@ class Response
|
|
102
102
|
@body = response.body || ''
|
103
103
|
end
|
104
104
|
|
105
|
+
# Convert this response object to a 302 redirect to +url+.
|
106
|
+
def redirect!(url)
|
107
|
+
@code = '302'
|
108
|
+
@status = 'Moved'
|
109
|
+
@body = nil
|
110
|
+
@headers['Location'] = url
|
111
|
+
@headers['Connection'] = 'close'
|
112
|
+
end
|
113
|
+
|
105
114
|
# Parse a single response +line+.
|
106
115
|
def <<(line)
|
107
116
|
# we already parsed the heders, collect response body
|
@@ -52,12 +52,16 @@ class StrippedObject
|
|
52
52
|
|
53
53
|
# Return a normalized version of +url+.
|
54
54
|
def self.normalize( url, schema = 'https' )
|
55
|
+
has_schema = url.include?('://')
|
55
56
|
# add schema if needed
|
56
|
-
|
57
|
+
if has_schema
|
58
|
+
has_slash = ( url =~ /^.+:\/\/.+\/.*$/ )
|
59
|
+
else
|
60
|
+
has_slash = ( url =~ /^.+\/.*$/ )
|
57
61
|
url = "#{schema}://#{url}"
|
58
62
|
end
|
59
|
-
# add
|
60
|
-
unless
|
63
|
+
# add slash if needed
|
64
|
+
unless has_slash
|
61
65
|
url = "#{url}/"
|
62
66
|
end
|
63
67
|
url
|
@@ -67,7 +67,7 @@ class Streamer
|
|
67
67
|
response = r
|
68
68
|
end
|
69
69
|
|
70
|
-
if response.textual? or request.method == 'DELETE'
|
70
|
+
if @ctx.options.proxies.no_http_logs == false and ( response.textual? or request.method == 'DELETE' )
|
71
71
|
StreamLogger.log_http( request, response )
|
72
72
|
else
|
73
73
|
Logger.debug "[#{request.client}] -> #{request.to_url} [#{response.code}]"
|
@@ -27,6 +27,8 @@ module TCP
|
|
27
27
|
# end
|
28
28
|
# end
|
29
29
|
class Module < BetterCap::Pluggable
|
30
|
+
def on_options( opts ); end
|
31
|
+
def check_opts; end
|
30
32
|
# This callback is called when the target is sending data to the upstream server.
|
31
33
|
# +event+ is an instance of the BetterCap::Proxy::TCP::Event class.
|
32
34
|
def on_data( event ); end
|
@@ -41,6 +43,15 @@ class Module < BetterCap::Pluggable
|
|
41
43
|
@@loaded = {}
|
42
44
|
|
43
45
|
class << self
|
46
|
+
# Register custom options for each available module.
|
47
|
+
def register_options(opts)
|
48
|
+
@@loaded.each do |name,mod|
|
49
|
+
if mod.respond_to?(:on_options)
|
50
|
+
mod.on_options(opts)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
44
55
|
# Called when a class inherits this base class.
|
45
56
|
def inherited(subclass)
|
46
57
|
name = subclass.name.upcase
|
@@ -48,7 +59,7 @@ class Module < BetterCap::Pluggable
|
|
48
59
|
end
|
49
60
|
|
50
61
|
# Load +file+ as a proxy module.
|
51
|
-
def load( file )
|
62
|
+
def load( file, opts )
|
52
63
|
begin
|
53
64
|
require file
|
54
65
|
rescue LoadError => e
|
@@ -58,6 +69,8 @@ class Module < BetterCap::Pluggable
|
|
58
69
|
@@loaded.each do |name,mod|
|
59
70
|
@@loaded[name] = mod.new
|
60
71
|
end
|
72
|
+
|
73
|
+
self.register_options(opts)
|
61
74
|
end
|
62
75
|
|
63
76
|
# Execute method +even_name+ for each loaded module instance using +event+
|
data/lib/bettercap/shell.rb
CHANGED
@@ -44,6 +44,21 @@ class Base
|
|
44
44
|
list
|
45
45
|
end
|
46
46
|
|
47
|
+
# Parse the +v+ command line argument and return a list of parser names
|
48
|
+
# disabling the ones specified.
|
49
|
+
# Will raise BetterCap::Error if one or more parser names are not valid.
|
50
|
+
def from_exclusion_list(v)
|
51
|
+
raise BetterCap::Error, "No parser names provided" if v.nil?
|
52
|
+
|
53
|
+
avail = available
|
54
|
+
list = v.split(',').collect(&:strip).collect(&:upcase).reject{ |c| c.empty? }
|
55
|
+
list.each do |parser|
|
56
|
+
raise BetterCap::Error, "Invalid parser name '#{parser}'." unless avail.include?(parser)
|
57
|
+
end
|
58
|
+
|
59
|
+
avail - list
|
60
|
+
end
|
61
|
+
|
47
62
|
# Return a list of BetterCap::Parsers instances by their +parsers+ names.
|
48
63
|
def load_by_names(parsers)
|
49
64
|
loaded = []
|
@@ -70,15 +85,30 @@ class Base
|
|
70
85
|
def initialize
|
71
86
|
@filters = []
|
72
87
|
@name = 'BASE'
|
88
|
+
@port = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def match_port?( pkt )
|
92
|
+
return true unless !@port.nil?
|
93
|
+
begin
|
94
|
+
if pkt.respond_to?(:tcp_dst) and pkt.tcp_dst == @port
|
95
|
+
return true
|
96
|
+
elsif pkt.respond_to?(:udp_dst) and pkt.udp_dst == @port
|
97
|
+
return true
|
98
|
+
end
|
99
|
+
rescue; end
|
100
|
+
false
|
73
101
|
end
|
74
102
|
|
75
103
|
# This method will be called from the BetterCap::Sniffer for each
|
76
104
|
# incoming packet ( +pkt ) and will apply the parser filter to it.
|
77
105
|
def on_packet( pkt )
|
78
106
|
s = pkt.to_s
|
79
|
-
|
80
|
-
|
81
|
-
|
107
|
+
if match_port?(pkt)
|
108
|
+
@filters.each do |filter|
|
109
|
+
if s =~ filter
|
110
|
+
StreamLogger.log_raw( pkt, @name, pkt.payload )
|
111
|
+
end
|
82
112
|
end
|
83
113
|
end
|
84
114
|
end
|
@@ -128,10 +128,13 @@ class Arp < Base
|
|
128
128
|
# Return true if the +pkt+ packet is an ARP 'who-has' query coming
|
129
129
|
# from some network endpoint.
|
130
130
|
def is_arp_query?(pkt)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
131
|
+
begin
|
132
|
+
# we're only interested in 'who-has' packets
|
133
|
+
return ( pkt.arp_opcode == 1 and \
|
134
|
+
pkt.arp_dst_mac.to_s == '00:00:00:00:00:00' and \
|
135
|
+
pkt.arp_src_ip.to_s != @ctx.iface.ip )
|
136
|
+
rescue; end
|
137
|
+
false
|
135
138
|
end
|
136
139
|
|
137
140
|
# Will watch for incoming ARP requests and spoof the source address.
|
data/lib/bettercap/version.rb
CHANGED
@@ -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.
|
15
|
+
VERSION = '1.5.9'
|
16
16
|
# Program banner.
|
17
17
|
BANNER = File.read( File.dirname(__FILE__) + '/banner' ).gsub( '#VERSION#', "v#{VERSION}")
|
18
18
|
end
|
data/lib/bettercap.rb
CHANGED
@@ -17,52 +17,41 @@
|
|
17
17
|
Encoding.default_external = Encoding::UTF_8
|
18
18
|
Encoding.default_internal = Encoding::UTF_8
|
19
19
|
|
20
|
-
require '
|
20
|
+
require 'packetfu'
|
21
|
+
require 'em-proxy'
|
22
|
+
require 'webrick'
|
23
|
+
require 'rubydns'
|
21
24
|
require 'colorize'
|
22
|
-
require 'digest'
|
23
|
-
require 'ipaddr'
|
24
25
|
require 'json'
|
25
26
|
require 'net/dns'
|
26
27
|
require 'net/http'
|
27
|
-
require 'openssl'
|
28
28
|
require 'optparse'
|
29
|
-
require 'packetfu'
|
30
|
-
require 'pcaprub'
|
31
|
-
require 'resolv'
|
32
|
-
require 'rubydns'
|
33
|
-
require 'socket'
|
34
|
-
require 'stringio'
|
35
|
-
require 'thread'
|
36
|
-
require 'uri'
|
37
|
-
require 'webrick'
|
38
|
-
require 'zlib'
|
39
|
-
require 'em-proxy'
|
40
29
|
|
41
30
|
Object.send :remove_const, :Config rescue nil
|
42
31
|
Config = RbConfig
|
43
32
|
|
44
|
-
def bettercap_autoload(
|
33
|
+
def bettercap_autoload(path = '')
|
45
34
|
dir = File.dirname(__FILE__) + "/bettercap/#{path}"
|
46
35
|
deps = []
|
47
36
|
files = []
|
48
37
|
monkey = []
|
49
38
|
|
50
|
-
Dir[dir+
|
51
|
-
filename = filename.gsub(
|
39
|
+
Dir[dir + '**/*.rb'].each do |filename|
|
40
|
+
filename = filename.gsub(dir, '').gsub('.rb', '')
|
52
41
|
filename = "bettercap/#{path}#{filename}"
|
42
|
+
|
43
|
+
next if filename.include?('proxy/http/modules')
|
53
44
|
# Proxy modules must be loaded at runtime.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
files << filename
|
61
|
-
end
|
45
|
+
if filename.end_with?('/base') || filename.include?('pluggable')
|
46
|
+
deps << filename
|
47
|
+
elsif filename.include?('monkey')
|
48
|
+
monkey << filename
|
49
|
+
else
|
50
|
+
files << filename
|
62
51
|
end
|
63
52
|
end
|
64
53
|
|
65
|
-
(
|
54
|
+
(deps + files + monkey).each do |file|
|
66
55
|
require file
|
67
56
|
end
|
68
57
|
end
|
metadata
CHANGED
@@ -1,147 +1,147 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bettercap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.9
|
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-
|
11
|
+
date: 2016-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 0.8.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.8.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: packetfu
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.1'
|
34
|
-
- -
|
34
|
+
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: 1.1.10
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- - ~>
|
41
|
+
- - "~>"
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '1.1'
|
44
|
-
- -
|
44
|
+
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: 1.1.10
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: pcaprub
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - ~>
|
51
|
+
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0.12'
|
54
|
-
- -
|
54
|
+
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: 0.12.0
|
57
57
|
type: :runtime
|
58
58
|
prerelease: false
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- - ~>
|
61
|
+
- - "~>"
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0.12'
|
64
|
-
- -
|
64
|
+
- - ">="
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: 0.12.0
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: network_interface
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
70
70
|
requirements:
|
71
|
-
- - ~>
|
71
|
+
- - "~>"
|
72
72
|
- !ruby/object:Gem::Version
|
73
73
|
version: '0.0'
|
74
|
-
- -
|
74
|
+
- - ">="
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: 0.0.1
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - ~>
|
81
|
+
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '0.0'
|
84
|
-
- -
|
84
|
+
- - ">="
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: 0.0.1
|
87
87
|
- !ruby/object:Gem::Dependency
|
88
88
|
name: net-dns
|
89
89
|
requirement: !ruby/object:Gem::Requirement
|
90
90
|
requirements:
|
91
|
-
- - ~>
|
91
|
+
- - "~>"
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0.8'
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.8.0
|
97
97
|
type: :runtime
|
98
98
|
prerelease: false
|
99
99
|
version_requirements: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - ~>
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '0.8'
|
104
|
-
- -
|
104
|
+
- - ">="
|
105
105
|
- !ruby/object:Gem::Version
|
106
106
|
version: 0.8.0
|
107
107
|
- !ruby/object:Gem::Dependency
|
108
108
|
name: rubydns
|
109
109
|
requirement: !ruby/object:Gem::Requirement
|
110
110
|
requirements:
|
111
|
-
- - ~>
|
111
|
+
- - "~>"
|
112
112
|
- !ruby/object:Gem::Version
|
113
113
|
version: '1.0'
|
114
|
-
- -
|
114
|
+
- - ">="
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: 1.0.3
|
117
117
|
type: :runtime
|
118
118
|
prerelease: false
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
|
-
- - ~>
|
121
|
+
- - "~>"
|
122
122
|
- !ruby/object:Gem::Version
|
123
123
|
version: '1.0'
|
124
|
-
- -
|
124
|
+
- - ">="
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: 1.0.3
|
127
127
|
- !ruby/object:Gem::Dependency
|
128
128
|
name: em-proxy
|
129
129
|
requirement: !ruby/object:Gem::Requirement
|
130
130
|
requirements:
|
131
|
-
- - ~>
|
131
|
+
- - "~>"
|
132
132
|
- !ruby/object:Gem::Version
|
133
133
|
version: '0.1'
|
134
|
-
- -
|
134
|
+
- - ">="
|
135
135
|
- !ruby/object:Gem::Version
|
136
136
|
version: 0.1.8
|
137
137
|
type: :runtime
|
138
138
|
prerelease: false
|
139
139
|
version_requirements: !ruby/object:Gem::Requirement
|
140
140
|
requirements:
|
141
|
-
- - ~>
|
141
|
+
- - "~>"
|
142
142
|
- !ruby/object:Gem::Version
|
143
143
|
version: '0.1'
|
144
|
-
- -
|
144
|
+
- - ">="
|
145
145
|
- !ruby/object:Gem::Version
|
146
146
|
version: 0.1.8
|
147
147
|
description: BetterCap is the state of the art, modular, portable and easily extensible
|
@@ -155,6 +155,8 @@ extra_rdoc_files: []
|
|
155
155
|
files:
|
156
156
|
- LICENSE.md
|
157
157
|
- README.md
|
158
|
+
- bin/bettercap
|
159
|
+
- lib/bettercap.rb
|
158
160
|
- lib/bettercap/banner
|
159
161
|
- lib/bettercap/context.rb
|
160
162
|
- lib/bettercap/discovery/agents/arp.rb
|
@@ -203,6 +205,7 @@ files:
|
|
203
205
|
- lib/bettercap/proxy/http/modules/injectcss.rb
|
204
206
|
- lib/bettercap/proxy/http/modules/injecthtml.rb
|
205
207
|
- lib/bettercap/proxy/http/modules/injectjs.rb
|
208
|
+
- lib/bettercap/proxy/http/modules/redirect.rb
|
206
209
|
- lib/bettercap/proxy/http/proxy.rb
|
207
210
|
- lib/bettercap/proxy/http/request.rb
|
208
211
|
- lib/bettercap/proxy/http/response.rb
|
@@ -220,7 +223,6 @@ files:
|
|
220
223
|
- lib/bettercap/shell.rb
|
221
224
|
- lib/bettercap/sniffer/parsers/base.rb
|
222
225
|
- lib/bettercap/sniffer/parsers/cookie.rb
|
223
|
-
- lib/bettercap/sniffer/parsers/creditcard.rb
|
224
226
|
- lib/bettercap/sniffer/parsers/custom.rb
|
225
227
|
- lib/bettercap/sniffer/parsers/dhcp.rb
|
226
228
|
- lib/bettercap/sniffer/parsers/dict.rb
|
@@ -249,30 +251,28 @@ files:
|
|
249
251
|
- lib/bettercap/spoofers/none.rb
|
250
252
|
- lib/bettercap/update_checker.rb
|
251
253
|
- lib/bettercap/version.rb
|
252
|
-
- lib/bettercap.rb
|
253
|
-
- bin/bettercap
|
254
254
|
homepage: http://github.com/evilsocket/bettercap
|
255
255
|
licenses:
|
256
256
|
- GPL-3.0
|
257
257
|
metadata: {}
|
258
258
|
post_install_message:
|
259
259
|
rdoc_options:
|
260
|
-
- --charset=UTF-8
|
260
|
+
- "--charset=UTF-8"
|
261
261
|
require_paths:
|
262
262
|
- lib
|
263
263
|
required_ruby_version: !ruby/object:Gem::Requirement
|
264
264
|
requirements:
|
265
|
-
- -
|
265
|
+
- - ">="
|
266
266
|
- !ruby/object:Gem::Version
|
267
267
|
version: '1.9'
|
268
268
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
269
269
|
requirements:
|
270
|
-
- -
|
270
|
+
- - ">="
|
271
271
|
- !ruby/object:Gem::Version
|
272
272
|
version: '0'
|
273
273
|
requirements: []
|
274
274
|
rubyforge_project:
|
275
|
-
rubygems_version: 2.
|
275
|
+
rubygems_version: 2.5.1
|
276
276
|
signing_key:
|
277
277
|
specification_version: 4
|
278
278
|
summary: A complete, modular, portable and easily extensible MITM framework.
|
@@ -1,62 +0,0 @@
|
|
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
|
-
|
13
|
-
module BetterCap
|
14
|
-
module Parsers
|
15
|
-
# CC parser.
|
16
|
-
class CreditCard < Base
|
17
|
-
PARSERS = [
|
18
|
-
# All major cards.
|
19
|
-
/(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})/m,
|
20
|
-
# American Express
|
21
|
-
/(3[47][0-9]{13})/m,
|
22
|
-
# Diners Club
|
23
|
-
/(3(?:0[0-5]|[68][0-9])[0-9]{11})/m,
|
24
|
-
# Discover
|
25
|
-
/(6011[0-9]{12})/m,
|
26
|
-
# MasterCard
|
27
|
-
/(5[1-5][0-9]{14})/m,
|
28
|
-
# Visa
|
29
|
-
/(4[0-9]{12}(?:[0-9]{3})?)/m
|
30
|
-
].freeze
|
31
|
-
|
32
|
-
def on_packet( pkt )
|
33
|
-
begin
|
34
|
-
payload = pkt.to_s
|
35
|
-
PARSERS.each do |expr|
|
36
|
-
matches = payload.scan( expr )
|
37
|
-
matches.each do |m|
|
38
|
-
StreamLogger.log_raw( pkt, 'CREDITCARD', m ) if luhn?(m)
|
39
|
-
end
|
40
|
-
break unless matches.empty?
|
41
|
-
end
|
42
|
-
rescue; end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Validate +cc+ with Lughn algorithm.
|
46
|
-
def luhn?(cc)
|
47
|
-
digits = cc.split(//).map(&:to_i)
|
48
|
-
last = digits.pop
|
49
|
-
|
50
|
-
products = digits.reverse.map.with_index do |n,i|
|
51
|
-
i.even? ? n*2 : n*1
|
52
|
-
end.reverse
|
53
|
-
sum = products.inject(0) { |t,p| t + p.to_s.split(//).map(&:to_i).inject(:+) }
|
54
|
-
checksum = 10 - (sum % 10)
|
55
|
-
checksum == 10 ? 0 : checksum
|
56
|
-
|
57
|
-
( last == checksum )
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|