bettercap 1.3.5 → 1.3.6
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/README.md +9 -10
- data/lib/bettercap.rb +28 -3
- data/lib/bettercap/context.rb +2 -2
- data/lib/bettercap/discovery/agents/icmp.rb +1 -1
- data/lib/bettercap/firewalls/base.rb +25 -0
- data/lib/bettercap/monkey/packetfu/utils.rb +70 -10
- data/lib/bettercap/network/network.rb +11 -3
- data/lib/bettercap/network/packet_queue.rb +1 -1
- data/lib/bettercap/network/protos/base.rb +97 -0
- data/lib/bettercap/network/protos/dhcp.rb +350 -0
- data/lib/bettercap/options.rb +4 -4
- data/lib/bettercap/proxy/stream_logger.rb +7 -3
- data/lib/bettercap/shell.rb +23 -1
- data/lib/bettercap/sniffer/parsers/base.rb +52 -0
- data/lib/bettercap/sniffer/parsers/cookie.rb +45 -0
- data/lib/bettercap/sniffer/parsers/custom.rb +0 -1
- data/lib/bettercap/sniffer/parsers/dhcp.rb +48 -0
- data/lib/bettercap/sniffer/parsers/dict.rb +0 -1
- data/lib/bettercap/sniffer/parsers/ftp.rb +0 -1
- data/lib/bettercap/sniffer/parsers/httpauth.rb +0 -2
- data/lib/bettercap/sniffer/parsers/https.rb +0 -2
- data/lib/bettercap/sniffer/parsers/irc.rb +0 -1
- data/lib/bettercap/sniffer/parsers/mail.rb +0 -1
- data/lib/bettercap/sniffer/parsers/mpd.rb +0 -1
- data/lib/bettercap/sniffer/parsers/nntp.rb +0 -1
- data/lib/bettercap/sniffer/parsers/ntlmss.rb +0 -2
- data/lib/bettercap/sniffer/parsers/post.rb +5 -3
- data/lib/bettercap/sniffer/parsers/redis.rb +0 -1
- data/lib/bettercap/sniffer/parsers/rlogin.rb +0 -1
- data/lib/bettercap/sniffer/parsers/snpp.rb +0 -1
- data/lib/bettercap/sniffer/parsers/url.rb +0 -2
- data/lib/bettercap/sniffer/parsers/whatsapp.rb +33 -0
- data/lib/bettercap/sniffer/sniffer.rb +6 -6
- data/lib/bettercap/spoofers/base.rb +23 -0
- data/lib/bettercap/version.rb +1 -1
- metadata +7 -5
- data/lib/bettercap/factories/firewall.rb +0 -43
- data/lib/bettercap/factories/parser.rb +0 -74
- data/lib/bettercap/factories/spoofer.rb +0 -53
data/lib/bettercap/options.rb
CHANGED
@@ -175,7 +175,7 @@ class Options
|
|
175
175
|
ctx.options.iface = v
|
176
176
|
end
|
177
177
|
|
178
|
-
opts.on( '-S', '--spoofer NAME', 'Spoofer module to use, available: ' +
|
178
|
+
opts.on( '-S', '--spoofer NAME', 'Spoofer module to use, available: ' + Spoofers::Base.available.join(', ') + ' - default: ' + ctx.options.spoofer ) do |v|
|
179
179
|
ctx.options.spoofer = v
|
180
180
|
end
|
181
181
|
|
@@ -223,9 +223,9 @@ class Options
|
|
223
223
|
ctx.options.sniffer_filter = v
|
224
224
|
end
|
225
225
|
|
226
|
-
opts.on( '-P', '--parsers PARSERS', 'Comma separated list of packet parsers to enable, "*" for all ( NOTE: Will set -X to true ), available: ' +
|
226
|
+
opts.on( '-P', '--parsers PARSERS', 'Comma separated list of packet parsers to enable, "*" for all ( NOTE: Will set -X to true ), available: ' + Parsers::Base.available.join(', ') + ' - default: *' ) do |v|
|
227
227
|
ctx.options.sniffer = true
|
228
|
-
ctx.options.parsers =
|
228
|
+
ctx.options.parsers = Parsers::Base.from_cmdline(v)
|
229
229
|
end
|
230
230
|
|
231
231
|
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|
|
@@ -492,7 +492,7 @@ class Options
|
|
492
492
|
spoofers = []
|
493
493
|
spoofer_modules_names = @spoofer.split(",")
|
494
494
|
spoofer_modules_names.each do |module_name|
|
495
|
-
spoofers <<
|
495
|
+
spoofers << Spoofers::Base.get_by_name( module_name )
|
496
496
|
end
|
497
497
|
spoofers
|
498
498
|
end
|
@@ -26,7 +26,7 @@ class StreamLogger
|
|
26
26
|
|
27
27
|
# Search for the +addr+ IP address inside the list of collected targets and return
|
28
28
|
# its compact string representation ( @see BetterCap::Target#to_s_compact ).
|
29
|
-
def self.addr2s( addr )
|
29
|
+
def self.addr2s( addr, alt = nil )
|
30
30
|
ctx = Context.get
|
31
31
|
|
32
32
|
return 'local' if addr == ctx.ifconfig[:ip_saddr]
|
@@ -34,6 +34,10 @@ class StreamLogger
|
|
34
34
|
target = ctx.find_target addr, nil
|
35
35
|
return target.to_s_compact unless target.nil?
|
36
36
|
|
37
|
+
if addr == '0.0.0.0' and !alt.nil?
|
38
|
+
return alt
|
39
|
+
end
|
40
|
+
|
37
41
|
addr
|
38
42
|
end
|
39
43
|
|
@@ -41,8 +45,8 @@ class StreamLogger
|
|
41
45
|
def self.log_raw( pkt, label, payload )
|
42
46
|
nl = if label.include?"\n" then "\n" else " " end
|
43
47
|
label = label.strip
|
44
|
-
Logger.raw( "[#{self.addr2s(pkt.ip_saddr)} > #{self.addr2s(pkt.ip_daddr)}
|
45
|
-
"[#{label.green}]#{nl}#{payload.strip
|
48
|
+
Logger.raw( "[#{self.addr2s(pkt.ip_saddr, pkt.eth2s(:src))} > #{self.addr2s(pkt.ip_daddr,pkt.eth2s(:dst))}#{pkt.respond_to?('tcp_dst') ? ':' + pkt.tcp_dst.to_s : ''}] " \
|
49
|
+
"[#{label.green}]#{nl}#{payload.strip}" )
|
46
50
|
end
|
47
51
|
|
48
52
|
# Log a HTTP ( HTTPS if +is_https+ is true ) stream performed by the +client+
|
data/lib/bettercap/shell.rb
CHANGED
@@ -35,11 +35,33 @@ module Shell
|
|
35
35
|
r
|
36
36
|
end
|
37
37
|
|
38
|
-
#
|
38
|
+
# Cross-platform way of finding an executable in the $PATH.
|
39
|
+
def which(cmd)
|
40
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
41
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
42
|
+
exts.each { |ext|
|
43
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
44
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
return nil
|
48
|
+
end
|
49
|
+
|
50
|
+
# Cross-platform way of finding an executable in the $PATH.
|
51
|
+
def available?(cmd)
|
52
|
+
!which(cmd).nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get the +iface+ network interface configuration ( using ifconfig ).
|
39
56
|
def ifconfig(iface = '')
|
40
57
|
self.execute( "LANG=en && ifconfig #{iface}" )
|
41
58
|
end
|
42
59
|
|
60
|
+
# Get the +iface+ network interface configuration ( using iproute2 ).
|
61
|
+
def ip(iface = '')
|
62
|
+
self.execute( "LANG=en && ip addr show #{iface}" )
|
63
|
+
end
|
64
|
+
|
43
65
|
# Get the ARP table cached on this computer.
|
44
66
|
def arp
|
45
67
|
self.execute( 'LANG=en && arp -a -n' )
|
@@ -14,6 +14,58 @@ module BetterCap
|
|
14
14
|
module Parsers
|
15
15
|
# Base class for BetterCap::Parsers.
|
16
16
|
class Base
|
17
|
+
# Hash of available parsers ( parser name -> class name )
|
18
|
+
@@loaded = {}
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Called when this base class is inherited from one of the parsers.
|
22
|
+
def inherited(subclass)
|
23
|
+
name = subclass.name.split('::')[2].upcase
|
24
|
+
if name != 'CUSTOM'
|
25
|
+
@@loaded[name] = subclass.name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return a list of available parsers names.
|
30
|
+
def available
|
31
|
+
@@loaded.keys
|
32
|
+
end
|
33
|
+
|
34
|
+
# Parse the +v+ command line argument and return a list of parser names.
|
35
|
+
# Will raise BetterCap::Error if one or more parser names are not valid.
|
36
|
+
def from_cmdline(v)
|
37
|
+
raise BetterCap::Error, "No parser names provided" if v.nil?
|
38
|
+
|
39
|
+
avail = available
|
40
|
+
list = v.split(',').collect(&:strip).collect(&:upcase).reject{ |c| c.empty? }
|
41
|
+
list.each do |parser|
|
42
|
+
raise BetterCap::Error, "Invalid parser name '#{parser}'." unless avail.include?(parser) or parser == '*'
|
43
|
+
end
|
44
|
+
list
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return a list of BetterCap::Parsers instances by their +parsers+ names.
|
48
|
+
def load_by_names(parsers)
|
49
|
+
loaded = []
|
50
|
+
|
51
|
+
@@loaded.each do |name,cname|
|
52
|
+
if parsers.include?(name) or parsers == ['*']
|
53
|
+
Logger.debug "Loading parser #{name} ( #{cname} ) ..."
|
54
|
+
loaded << BetterCap::Loader.load(cname).new
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
loaded
|
59
|
+
end
|
60
|
+
|
61
|
+
# Load and return an instance of the BetterCap::Parsers::Custom parser
|
62
|
+
# given the +expression+ Regex object.
|
63
|
+
def load_custom(expression)
|
64
|
+
Logger.debug "Loading custom parser: '#{expression}' ..."
|
65
|
+
[ BetterCap::Parsers::Custom.new(expression) ]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
17
69
|
# Initialize this parser.
|
18
70
|
def initialize
|
19
71
|
@filters = []
|
@@ -0,0 +1,45 @@
|
|
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
|
+
module BetterCap
|
15
|
+
module Parsers
|
16
|
+
# HTTP cookies parser.
|
17
|
+
class Cookie < Base
|
18
|
+
# Cookies to ignore.
|
19
|
+
FILTER = [ '__cfduid', '_ga', '_gat' ].freeze
|
20
|
+
|
21
|
+
def on_packet( pkt )
|
22
|
+
hostname = nil
|
23
|
+
cookies = {}
|
24
|
+
|
25
|
+
pkt.to_s.split("\n").each do |line|
|
26
|
+
if line =~ /Host:\s*([^\s]+)/i
|
27
|
+
hostname = $1
|
28
|
+
elsif line =~ /.*Cookie:\s*(.+)/i
|
29
|
+
$1.strip.split(';').each do |v|
|
30
|
+
k, v = v.split('=').map(&:strip)
|
31
|
+
next if k.nil? or v.nil?
|
32
|
+
unless k.empty? or v.empty? or FILTER.include?(k)
|
33
|
+
cookies[k] = v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
unless hostname.nil? or cookies.empty?
|
40
|
+
StreamLogger.log_raw( pkt, "COOKIE", "[#{hostname.yellow}] #{cookies.map{|k,v| "#{k.green}=#{v.yellow}"}.join('; ')}" )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,48 @@
|
|
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
|
+
# DHCP packets and authentication parser.
|
16
|
+
class DHCP < Base
|
17
|
+
def on_packet( pkt )
|
18
|
+
begin
|
19
|
+
if pkt.udp_dst == 67 or pkt.udp_dst == 68
|
20
|
+
packet = Network::Protos::DHCP::Packet.parse( pkt.payload )
|
21
|
+
unless packet.nil?
|
22
|
+
auth = packet.authentication
|
23
|
+
cid = auth.nil?? nil : packet.client_identifier
|
24
|
+
msg = "[#{packet.type.yellow}] #{'Transaction-ID'.green}=#{packet.transaction_id.yellow}"
|
25
|
+
|
26
|
+
unless cid.nil?
|
27
|
+
msg += " #{'Client-ID'.green}='#{cid.yellow}'"
|
28
|
+
end
|
29
|
+
|
30
|
+
unless auth.nil?
|
31
|
+
msg += "\n#{'AUTHENTICATION'.green}:\n\n"
|
32
|
+
auth.each do |k,v|
|
33
|
+
msg += " #{k.blue} : #{v.yellow}\n"
|
34
|
+
end
|
35
|
+
msg += "\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
StreamLogger.log_raw( pkt, 'DHCP', msg )
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue Exception => e
|
42
|
+
Logger.debug e.message
|
43
|
+
Logger.debug e.backtrace.join("\n")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -10,8 +10,6 @@ Blog : http://www.evilsocket.net/
|
|
10
10
|
This project is released under the GPL 3 license.
|
11
11
|
|
12
12
|
=end
|
13
|
-
require 'bettercap/sniffer/parsers/base'
|
14
|
-
require 'colorize'
|
15
13
|
|
16
14
|
module BetterCap
|
17
15
|
module Parsers
|
@@ -32,7 +30,11 @@ class Post < Base
|
|
32
30
|
|
33
31
|
req.body.split('&').each do |v|
|
34
32
|
name, value = v.split('=')
|
35
|
-
|
33
|
+
if name.nil? or value.nil?
|
34
|
+
msg << " #{URI.unescape(v).yellow}\n"
|
35
|
+
else
|
36
|
+
msg << " #{name.blue} : #{URI.unescape(value).yellow}\n"
|
37
|
+
end
|
36
38
|
end
|
37
39
|
|
38
40
|
StreamLogger.log_raw( pkt, "POST", req.to_url(1000) )
|
@@ -0,0 +1,33 @@
|
|
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
|
+
WhatsApp OS Parser:
|
11
|
+
Author : Gianluca Costa
|
12
|
+
Email : g.costa@xplico.org
|
13
|
+
|
14
|
+
This project is released under the GPL 3 license.
|
15
|
+
|
16
|
+
=end
|
17
|
+
|
18
|
+
module BetterCap
|
19
|
+
module Parsers
|
20
|
+
# WhatsApp traffic parser.
|
21
|
+
class Whatsapp < Base
|
22
|
+
def on_packet( pkt )
|
23
|
+
begin
|
24
|
+
if ( pkt.tcp_dst == 443 or pkt.tcp_dst == 5222 or pkt.tcp_dst == 5223 ) and pkt.payload =~ /^WA.*?([a-zA-Z\-\.0-9]+).*?([0-9]+)/
|
25
|
+
version = $1
|
26
|
+
phone = $2
|
27
|
+
StreamLogger.log_raw( pkt, 'WHATSAPP', "#{'phone'.green}=#{phone.yellow} #{'version'.green}=#{version.yellow}" )
|
28
|
+
end
|
29
|
+
rescue; end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -58,9 +58,9 @@ class Sniffer
|
|
58
58
|
if @@ctx.options.sniffer_src.nil?
|
59
59
|
@@cap.stream
|
60
60
|
else
|
61
|
-
Logger.info "Reading packets from #{@@ctx.options.sniffer_src} ..."
|
61
|
+
Logger.info "[#{'SNIFFER'.green}] Reading packets from #{@@ctx.options.sniffer_src} ..."
|
62
62
|
|
63
|
-
PcapFile.file_to_array @@ctx.options.sniffer_src
|
63
|
+
PacketFu::PcapFile.file_to_array @@ctx.options.sniffer_src
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
@@ -99,14 +99,14 @@ class Sniffer
|
|
99
99
|
@@ctx = ctx
|
100
100
|
|
101
101
|
unless @@ctx.options.sniffer_pcap.nil?
|
102
|
-
@@pcap = PcapFile.new
|
103
|
-
Logger.
|
102
|
+
@@pcap = PacketFu::PcapFile.new
|
103
|
+
Logger.info "[#{'SNIFFER'.green}] Saving packets to #{@@ctx.options.sniffer_pcap} ."
|
104
104
|
end
|
105
105
|
|
106
106
|
if @@ctx.options.custom_parser.nil?
|
107
|
-
@@parsers =
|
107
|
+
@@parsers = Parsers::Base.load_by_names @@ctx.options.parsers
|
108
108
|
else
|
109
|
-
@@parsers =
|
109
|
+
@@parsers = Parsers::Base.load_custom @@ctx.options.custom_parser
|
110
110
|
end
|
111
111
|
|
112
112
|
@@cap = Capture.new(
|