xettercap 1.5.7xerob
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/xettercap +61 -0
- data/lib/bettercap/banner +2 -0
- data/lib/bettercap/context.rb +259 -0
- data/lib/bettercap/discovery/agents/arp.rb +37 -0
- data/lib/bettercap/discovery/agents/base.rb +73 -0
- data/lib/bettercap/discovery/agents/icmp.rb +44 -0
- data/lib/bettercap/discovery/agents/udp.rb +30 -0
- data/lib/bettercap/discovery/thread.rb +128 -0
- data/lib/bettercap/error.rb +16 -0
- data/lib/bettercap/firewalls/base.rb +103 -0
- data/lib/bettercap/firewalls/bsd.rb +74 -0
- data/lib/bettercap/firewalls/linux.rb +65 -0
- data/lib/bettercap/firewalls/redirection.rb +42 -0
- data/lib/bettercap/loader.rb +27 -0
- data/lib/bettercap/logger.rb +131 -0
- data/lib/bettercap/memory.rb +56 -0
- data/lib/bettercap/monkey/celluloid/actor.rb +23 -0
- data/lib/bettercap/monkey/celluloid/io/udp_socket.rb +26 -0
- data/lib/bettercap/monkey/em-proxy/proxy.rb +23 -0
- data/lib/bettercap/monkey/openssl/server.rb +35 -0
- data/lib/bettercap/monkey/packetfu/pcap.rb +51 -0
- data/lib/bettercap/monkey/packetfu/utils.rb +210 -0
- data/lib/bettercap/monkey/system.rb +25 -0
- data/lib/bettercap/network/arp_reader.rb +91 -0
- data/lib/bettercap/network/hw-prefixes +21326 -0
- data/lib/bettercap/network/network.rb +102 -0
- data/lib/bettercap/network/packet_queue.rb +129 -0
- data/lib/bettercap/network/protos/base.rb +154 -0
- data/lib/bettercap/network/protos/dhcp.rb +227 -0
- data/lib/bettercap/network/protos/mysql.rb +40 -0
- data/lib/bettercap/network/protos/ntlm.rb +97 -0
- data/lib/bettercap/network/protos/snmp.rb +49 -0
- data/lib/bettercap/network/protos/teamviewer.rb +119 -0
- data/lib/bettercap/network/servers/dnsd.rb +152 -0
- data/lib/bettercap/network/servers/httpd.rb +55 -0
- data/lib/bettercap/network/services +2182 -0
- data/lib/bettercap/network/target.rb +168 -0
- data/lib/bettercap/network/validator.rb +96 -0
- data/lib/bettercap/options/core_options.rb +197 -0
- data/lib/bettercap/options/options.rb +165 -0
- data/lib/bettercap/options/proxy_options.rb +314 -0
- data/lib/bettercap/options/server_options.rb +73 -0
- data/lib/bettercap/options/sniff_options.rb +90 -0
- data/lib/bettercap/options/spoof_options.rb +71 -0
- data/lib/bettercap/pluggable.rb +37 -0
- data/lib/bettercap/proxy/http/module.rb +105 -0
- data/lib/bettercap/proxy/http/modules/injectcss.rb +79 -0
- data/lib/bettercap/proxy/http/modules/injecthtml.rb +80 -0
- data/lib/bettercap/proxy/http/modules/injectjs.rb +79 -0
- data/lib/bettercap/proxy/http/proxy.rb +184 -0
- data/lib/bettercap/proxy/http/request.rb +192 -0
- data/lib/bettercap/proxy/http/response.rb +226 -0
- data/lib/bettercap/proxy/http/ssl/authority.rb +182 -0
- data/lib/bettercap/proxy/http/ssl/bettercap-ca.pem +49 -0
- data/lib/bettercap/proxy/http/ssl/server.rb +63 -0
- data/lib/bettercap/proxy/http/sslstrip/cookiemonitor.rb +67 -0
- data/lib/bettercap/proxy/http/sslstrip/lock.ico +0 -0
- data/lib/bettercap/proxy/http/sslstrip/strip.rb +325 -0
- data/lib/bettercap/proxy/http/streamer.rb +225 -0
- data/lib/bettercap/proxy/stream_logger.rb +181 -0
- data/lib/bettercap/proxy/tcp/module.rb +75 -0
- data/lib/bettercap/proxy/tcp/proxy.rb +123 -0
- data/lib/bettercap/proxy/thread_pool.rb +194 -0
- data/lib/bettercap/shell.rb +70 -0
- data/lib/bettercap/sniffer/parsers/base.rb +87 -0
- data/lib/bettercap/sniffer/parsers/cookie.rb +45 -0
- data/lib/bettercap/sniffer/parsers/creditcard.rb +62 -0
- data/lib/bettercap/sniffer/parsers/custom.rb +26 -0
- data/lib/bettercap/sniffer/parsers/dhcp.rb +45 -0
- data/lib/bettercap/sniffer/parsers/dict.rb +37 -0
- data/lib/bettercap/sniffer/parsers/ftp.rb +24 -0
- data/lib/bettercap/sniffer/parsers/httpauth.rb +44 -0
- data/lib/bettercap/sniffer/parsers/https.rb +42 -0
- data/lib/bettercap/sniffer/parsers/irc.rb +24 -0
- data/lib/bettercap/sniffer/parsers/mail.rb +24 -0
- data/lib/bettercap/sniffer/parsers/mpd.rb +36 -0
- data/lib/bettercap/sniffer/parsers/mysql.rb +27 -0
- data/lib/bettercap/sniffer/parsers/nntp.rb +24 -0
- data/lib/bettercap/sniffer/parsers/ntlmss.rb +34 -0
- data/lib/bettercap/sniffer/parsers/pgsql.rb +36 -0
- data/lib/bettercap/sniffer/parsers/post.rb +33 -0
- data/lib/bettercap/sniffer/parsers/redis.rb +39 -0
- data/lib/bettercap/sniffer/parsers/rlogin.rb +45 -0
- data/lib/bettercap/sniffer/parsers/snmp.rb +44 -0
- data/lib/bettercap/sniffer/parsers/snpp.rb +37 -0
- data/lib/bettercap/sniffer/parsers/teamviewer.rb +30 -0
- data/lib/bettercap/sniffer/parsers/url.rb +30 -0
- data/lib/bettercap/sniffer/parsers/whatsapp.rb +33 -0
- data/lib/bettercap/sniffer/sniffer.rb +142 -0
- data/lib/bettercap/spoofers/arp.rb +150 -0
- data/lib/bettercap/spoofers/base.rb +152 -0
- data/lib/bettercap/spoofers/icmp.rb +202 -0
- data/lib/bettercap/spoofers/none.rb +57 -0
- data/lib/bettercap/update_checker.rb +57 -0
- data/lib/bettercap/version.rb +18 -0
- data/lib/bettercap.rb +70 -0
- metadata +276 -0
@@ -0,0 +1,168 @@
|
|
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 Network
|
16
|
+
# This class represents a target, namely a single endpoint device on the
|
17
|
+
# network.
|
18
|
+
class Target
|
19
|
+
# The IP address of this device.
|
20
|
+
attr_accessor :ip
|
21
|
+
# The MAC address of the device network interface.
|
22
|
+
attr_accessor :mac
|
23
|
+
# Network object.
|
24
|
+
attr_accessor :network
|
25
|
+
# Vendor of the device network interface if available.
|
26
|
+
attr_accessor :vendor
|
27
|
+
# NetBIOS hostname of the device if available OR interface name in case of
|
28
|
+
# local address.
|
29
|
+
attr_accessor :name
|
30
|
+
# True if the IP attribute of this target needs to be updated.
|
31
|
+
attr_accessor :ip_refresh
|
32
|
+
|
33
|
+
# Timeout in seconds for the NBNS hostname resolution request.
|
34
|
+
NBNS_TIMEOUT = 30
|
35
|
+
# UDP port for the NBNS hostname resolution request.
|
36
|
+
NBNS_PORT = 137
|
37
|
+
# Buffer size for the NBNS hostname resolution request.
|
38
|
+
NBNS_BUFSIZE = 65536
|
39
|
+
# NBNS hostname resolution request buffer.
|
40
|
+
NBNS_REQUEST = "\x82\x28\x0\x0\x0\x1\x0\x0\x0\x0\x0\x0\x20\x43\x4B\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x0\x0\x21\x0\x1"
|
41
|
+
|
42
|
+
@@prefixes = nil
|
43
|
+
@@lock = Mutex.new
|
44
|
+
|
45
|
+
# Create a +Target+ object given its +ip+ and (optional) +mac+ address.
|
46
|
+
# The +ip+ argument could also be a MAC address itself, in this case the
|
47
|
+
# ip address will be parsed from the computer ARP cache and updated
|
48
|
+
# accordingly.
|
49
|
+
def initialize( ip, mac=nil, network=nil, name=nil )
|
50
|
+
if Network::Validator.is_ip?(ip)
|
51
|
+
@ip = ip
|
52
|
+
@ip_refresh = false
|
53
|
+
elsif Network::Validator.is_mac?(ip)
|
54
|
+
@ip = nil
|
55
|
+
mac = ip
|
56
|
+
@ip_refresh = true
|
57
|
+
else
|
58
|
+
raise BetterCap::Error, "'#{ip}' is not a valid IP or MAC address."
|
59
|
+
end
|
60
|
+
|
61
|
+
@mac = Target.normalized_mac(mac) unless mac.nil?
|
62
|
+
@vendor = Target.lookup_vendor(@mac) unless mac.nil?
|
63
|
+
@name = name
|
64
|
+
@network = network
|
65
|
+
@resolver = Thread.new { resolve! } unless Context.get.options.core.no_target_nbns or @ip.nil? or !@network.nil?
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return the integer representation of the +ip+ attribute which can be
|
69
|
+
# used for sorting a list of +Target+ objects+
|
70
|
+
def sortable_ip
|
71
|
+
@ip.split('.').inject(0) {|total,value| (total << 8 ) + value.to_i}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return true if both the ip and mac are not nil.
|
75
|
+
def spoofable?
|
76
|
+
( !@ip.nil? and !@mac.nil? )
|
77
|
+
end
|
78
|
+
|
79
|
+
# +mac+ attribute setter, it will normalize the +value+ and perform
|
80
|
+
# a vendor lookup.
|
81
|
+
def mac=(value)
|
82
|
+
@mac = Target.normalized_mac(value)
|
83
|
+
@vendor = Target.lookup_vendor(@mac) unless @mac.nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return a verbose string representation of this object.
|
87
|
+
def to_s(padding=true)
|
88
|
+
address = @ip.nil?? '???' : @ip
|
89
|
+
fmt = padding ? '%-15s : %-17s' : '%s : %s'
|
90
|
+
vendor = @vendor.nil?? " ( ??? )" : " ( #{@vendor} )"
|
91
|
+
|
92
|
+
s = sprintf( fmt, address, @mac )
|
93
|
+
s += " / #{@name}" unless @name.nil?
|
94
|
+
s += vendor
|
95
|
+
s
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return a compact string representation of this object.
|
99
|
+
def to_s_compact
|
100
|
+
return "#{@name}/#{@ip}" if @name
|
101
|
+
@ip
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return true if this +Target+ is equal to the specified +ip+ and +mac+,
|
105
|
+
# otherwise return false.
|
106
|
+
def equals?(ip, mac = nil)
|
107
|
+
# compare by ip if no mac
|
108
|
+
return ( @ip == ip ) if mac.nil?
|
109
|
+
# false if no mac or if it's different
|
110
|
+
return false if @mac.nil? || ( @mac != mac )
|
111
|
+
|
112
|
+
###Logger.info "Found IP #{ip} for target #{@mac}!" if @ip.nil?
|
113
|
+
@ip = ip
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.normalized_mac(v)
|
118
|
+
v.split(':').map { |e| e.size == 2 ? e.upcase : "0#{e.upcase}" }.join(':')
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# Attempt to perform a NBNS name resolution for this target.
|
124
|
+
def resolve!
|
125
|
+
resp, sock = nil, nil
|
126
|
+
begin
|
127
|
+
sock = UDPSocket.open
|
128
|
+
sock.send( NBNS_REQUEST, 0, @ip, NBNS_PORT )
|
129
|
+
resp = if select([sock], nil, nil, NBNS_TIMEOUT)
|
130
|
+
sock.recvfrom(NBNS_BUFSIZE)
|
131
|
+
end
|
132
|
+
if resp
|
133
|
+
@name = parse_nbns_response resp
|
134
|
+
###Logger.info "Found NetBIOS name '#{@name}' for address #{@ip}"
|
135
|
+
end
|
136
|
+
rescue Exception => e
|
137
|
+
Logger.debug e
|
138
|
+
ensure
|
139
|
+
sock.close if sock
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Given the +resp+ NBNS response, parse the hostname from it.
|
144
|
+
def parse_nbns_response resp
|
145
|
+
resp[0][57,15].to_s.strip
|
146
|
+
end
|
147
|
+
|
148
|
+
# Lookup the given +mac+ address in order to find its vendor.
|
149
|
+
def self.lookup_vendor( mac )
|
150
|
+
@@lock.synchronize {
|
151
|
+
if @@prefixes.nil?
|
152
|
+
Logger.debug 'Preloading hardware vendor prefixes ...'
|
153
|
+
|
154
|
+
@@prefixes = {}
|
155
|
+
filename = File.dirname(__FILE__) + '/hw-prefixes'
|
156
|
+
File.open( filename ).each do |line|
|
157
|
+
if line =~ /^([A-F0-9]{6})\s(.+)$/
|
158
|
+
@@prefixes[$1] = $2
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
}
|
163
|
+
|
164
|
+
@@prefixes[ mac.split(':')[0,3].join('').upcase ]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,96 @@
|
|
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
|
+
module BetterCap
|
14
|
+
module Network
|
15
|
+
# Simple class to perform validation of various addresses, ranges, etc.
|
16
|
+
class Validator
|
17
|
+
# Basic expression to validate an IP address.
|
18
|
+
IP_ADDRESS_REGEX = '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})'
|
19
|
+
# Quite self explainatory :)
|
20
|
+
IP_OCTECT_MAX = 255
|
21
|
+
|
22
|
+
# Return true if +ip+ is a valid IP address, otherwise false.
|
23
|
+
def self.is_ip?(ip)
|
24
|
+
if /\A#{IP_ADDRESS_REGEX}\Z/ =~ ip.to_s
|
25
|
+
return $~.captures.all? { |i| i.to_i <= IP_OCTECT_MAX }
|
26
|
+
end
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return true if +port+ is a valid port, otherwise false.
|
31
|
+
def self.is_valid_port?(port)
|
32
|
+
port ||= ""
|
33
|
+
return false if port.strip.empty?
|
34
|
+
return false unless port =~ /^[0-9]+$/
|
35
|
+
port = port.to_i
|
36
|
+
return ( port > 0 and port <= 65535 )
|
37
|
+
end
|
38
|
+
|
39
|
+
# Extract valid IP addresses from +data+ and yields each one of them.
|
40
|
+
def self.each_ip(data)
|
41
|
+
data.scan(/(#{IP_ADDRESS_REGEX})/).each do |m|
|
42
|
+
yield( m[0] ) if m[0] != '0.0.0.0'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return true if +r+ is a valid IP address range ( 192.168.1.1-93 ), otherwise false.
|
47
|
+
def self.is_range?(r)
|
48
|
+
if /\A#{IP_ADDRESS_REGEX}\-(\d{1,3})\Z/ =~ r.to_s
|
49
|
+
return $~.captures.all? { |i| i.to_i <= IP_OCTECT_MAX }
|
50
|
+
end
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Parse +r+ as an IP range and return the first and last IP.
|
55
|
+
def self.parse_range(r)
|
56
|
+
first, last_part = r.split('-')
|
57
|
+
last = first.split('.')[0..2].join('.') + ".#{last_part}"
|
58
|
+
first = IPAddr.new(first)
|
59
|
+
last = IPAddr.new(last)
|
60
|
+
|
61
|
+
[ first, last ]
|
62
|
+
end
|
63
|
+
|
64
|
+
# Parse +r+ as an IP range and yields each address in it.
|
65
|
+
def self.each_in_range(r)
|
66
|
+
first, last = self.parse_range(r)
|
67
|
+
loop do
|
68
|
+
yield first.to_s
|
69
|
+
break if first == last
|
70
|
+
first = first.succ
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Parse +m+ as a netmask and yields each address in it.
|
75
|
+
def self.each_in_netmask(m)
|
76
|
+
IPAddr.new(m).to_range.each do |o|
|
77
|
+
yield o.to_s
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Return true if +n+ is a valid IP netmask range ( 192.168.1.1/24 ), otherwise false.
|
82
|
+
def self.is_netmask?(n)
|
83
|
+
if /\A#{IP_ADDRESS_REGEX}\/(\d+)\Z/ =~ n.to_s
|
84
|
+
return $~.captures.all? { |i| i.to_i <= IP_OCTECT_MAX }
|
85
|
+
end
|
86
|
+
false
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return true if +mac+ is a valid MAC address, otherwise false.
|
90
|
+
def self.is_mac?(mac)
|
91
|
+
( /^[a-f0-9]{1,2}\:[a-f0-9]{1,2}\:[a-f0-9]{1,2}\:[a-f0-9]{1,2}\:[a-f0-9]{1,2}\:[a-f0-9]{1,2}$/i =~ mac.to_s )
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,197 @@
|
|
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
|
+
|
16
|
+
class CoreOptions
|
17
|
+
# Network interface.
|
18
|
+
attr_accessor :iface
|
19
|
+
# Gateway IP address.
|
20
|
+
attr_accessor :gateway
|
21
|
+
# Comma separated list of targets.
|
22
|
+
attr_accessor :targets
|
23
|
+
# Comma separated list of ip addresses to ignore.
|
24
|
+
attr_accessor :ignore
|
25
|
+
# If false will disable active network discovery, the program will just use
|
26
|
+
# the current ARP cache.
|
27
|
+
attr_accessor :discovery
|
28
|
+
# If true, targets NBNS hostname resolution won't be performed.
|
29
|
+
attr_accessor :no_target_nbns
|
30
|
+
# Log file name.
|
31
|
+
attr_accessor :logfile
|
32
|
+
# If true the Logger will prepend timestamps to each line.
|
33
|
+
attr_accessor :log_timestamp
|
34
|
+
# If true will suppress every log message which is not an error or a warning.
|
35
|
+
attr_accessor :silent
|
36
|
+
# If true will enable debug messages.
|
37
|
+
attr_accessor :debug
|
38
|
+
# If different than 0, this time will be used as a delay while sending packets.
|
39
|
+
attr_accessor :packet_throttle
|
40
|
+
# If true, bettercap will check for updates then exit.
|
41
|
+
attr_accessor :check_updates
|
42
|
+
# If not nil, the interface MAC address will be changed to this value.
|
43
|
+
attr_accessor :use_mac
|
44
|
+
|
45
|
+
def initialize( iface )
|
46
|
+
@iface = iface
|
47
|
+
@gateway = nil
|
48
|
+
@targets = nil
|
49
|
+
@logfile = nil
|
50
|
+
@log_timestamp = false
|
51
|
+
@silent = false
|
52
|
+
@debug = false
|
53
|
+
@ignore = nil
|
54
|
+
@discovery = true
|
55
|
+
@no_target_nbns = false
|
56
|
+
@packet_throttle = 0.0
|
57
|
+
@check_updates = false
|
58
|
+
@use_mac = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse!( ctx, opts )
|
62
|
+
opts.separator ""
|
63
|
+
opts.separator "MAIN:".bold
|
64
|
+
opts.separator ""
|
65
|
+
|
66
|
+
opts.on( '-I', '--interface IFACE', 'Network interface name - default: ' + @iface.to_s.yellow ) do |v|
|
67
|
+
@iface = v
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.on( '--use-mac ADDRESS', 'Change the interface MAC address to this value before performing the attack.' ) do |v|
|
71
|
+
@use_mac = v
|
72
|
+
raise BetterCap::Error, "Invalid MAC address specified." unless Network::Validator.is_mac?(@use_mac)
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on( '--random-mac', 'Change the interface MAC address to a random one before performing the attack.' ) do |v|
|
76
|
+
@use_mac = [format('%0.2x', rand(256) & ~1), (1..5).map { format('%0.2x', rand(256)) }].join(':')
|
77
|
+
end
|
78
|
+
|
79
|
+
opts.on( '-G', '--gateway ADDRESS', 'Manually specify the gateway address, if not specified the current gateway will be retrieved and used. ' ) do |v|
|
80
|
+
@gateway = v
|
81
|
+
raise BetterCap::Error, "The specified gateway '#{v}' is not a valid IPv4 address." unless Network::Validator.is_ip?(v)
|
82
|
+
end
|
83
|
+
|
84
|
+
opts.on( '-T', '--target ADDRESS1,ADDRESS2', 'Target IP addresses, if not specified the whole subnet will be targeted.' ) do |v|
|
85
|
+
self.targets = v
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on( '--ignore ADDRESS1,ADDRESS2', 'Ignore these addresses if found while searching for targets.' ) do |v|
|
89
|
+
self.ignore = v
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.on( '--no-discovery', "Do not actively search for hosts, just use the current ARP cache, default to #{'false'.yellow}." ) do
|
93
|
+
@discovery = false
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on( '--no-target-nbns', 'Disable target NBNS hostname resolution.' ) do
|
97
|
+
@no_target_nbns = true
|
98
|
+
end
|
99
|
+
|
100
|
+
opts.on( '--packet-throttle NUMBER', 'Number of seconds ( can be a decimal number ) to wait between each packet to be sent.' ) do |v|
|
101
|
+
@packet_throttle = v.to_f
|
102
|
+
raise BetterCap::Error, "Invalid packet throttle value specified." if @packet_throttle <= 0.0
|
103
|
+
end
|
104
|
+
|
105
|
+
opts.on( '--check-updates', 'Will check if any update is available and then exit.' ) do
|
106
|
+
@check_updates = true
|
107
|
+
end
|
108
|
+
|
109
|
+
opts.on( '-h', '--help', 'Display the available options.') do
|
110
|
+
puts opts
|
111
|
+
puts "\nFor examples & docs please visit " + "http://bettercap.org/docs/".bold
|
112
|
+
exit
|
113
|
+
end
|
114
|
+
|
115
|
+
opts.separator ""
|
116
|
+
opts.separator "LOGGING:".bold
|
117
|
+
opts.separator ""
|
118
|
+
|
119
|
+
opts.on( '-O', '--log LOG_FILE', 'Log all messages into a file, if not specified the log messages will be only print into the shell.' ) do |v|
|
120
|
+
@logfile = v
|
121
|
+
end
|
122
|
+
|
123
|
+
opts.on( '--log-timestamp', 'Enable logging with timestamps for each line, disabled by default.' ) do
|
124
|
+
@log_timestamp = true
|
125
|
+
end
|
126
|
+
|
127
|
+
opts.on( '-D', '--debug', 'Enable debug logging.' ) do
|
128
|
+
@debug = true
|
129
|
+
end
|
130
|
+
|
131
|
+
opts.on( '--silent', "Suppress every message which is not an error or a warning, default to #{'false'.yellow}." ) do
|
132
|
+
@silent = true
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
def validate!
|
138
|
+
raise BetterCap::Error, 'This software must run as root.' unless Process.uid == 0
|
139
|
+
raise BetterCap::Error, 'No default interface found, please specify one with the -I argument.' if @iface.nil?
|
140
|
+
end
|
141
|
+
|
142
|
+
# Return true if active host discovery is enabled, otherwise false.
|
143
|
+
def discovery?
|
144
|
+
( @discovery and @targets.nil? )
|
145
|
+
end
|
146
|
+
|
147
|
+
# Split specified targets and parse them ( either as IP or MAC ), will raise a
|
148
|
+
# BetterCap::Error if one or more invalid addresses are specified.
|
149
|
+
def targets=(value)
|
150
|
+
@targets = []
|
151
|
+
|
152
|
+
value.split(",").each do |t|
|
153
|
+
if Network::Validator.is_ip?(t) or Network::Validator.is_mac?(t)
|
154
|
+
@targets << Network::Target.new(t)
|
155
|
+
|
156
|
+
elsif Network::Validator.is_range?(t)
|
157
|
+
Network::Validator.each_in_range( t ) do |address|
|
158
|
+
@targets << Network::Target.new(address)
|
159
|
+
end
|
160
|
+
|
161
|
+
elsif Network::Validator.is_netmask?(t)
|
162
|
+
Network::Validator.each_in_netmask(t) do |address|
|
163
|
+
@targets << Network::Target.new(address)
|
164
|
+
end
|
165
|
+
|
166
|
+
else
|
167
|
+
raise BetterCap::Error, "Invalid target specified '#{t}', valid formats are IP addresses, "\
|
168
|
+
"MAC addresses, IP ranges ( 192.168.1.1-30 ) or netmasks ( 192.168.1.1/24 ) ."
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Setter for the #ignore attribute, will raise a BetterCap::Error if one
|
174
|
+
# or more invalid IP addresses are specified.
|
175
|
+
def ignore=(value)
|
176
|
+
@ignore = value.split(",")
|
177
|
+
valid = @ignore.select { |target| Network::Validator.is_ip?(target) }
|
178
|
+
|
179
|
+
raise BetterCap::Error, "Invalid ignore addresses specified." if valid.empty?
|
180
|
+
|
181
|
+
invalid = @ignore - valid
|
182
|
+
invalid.each do |target|
|
183
|
+
Logger.warn "Not a valid address: #{target}"
|
184
|
+
end
|
185
|
+
|
186
|
+
@ignore = valid
|
187
|
+
|
188
|
+
Logger.warn "Ignoring #{valid.join(", ")} ."
|
189
|
+
end
|
190
|
+
|
191
|
+
# Return true if the +ip+ address needs to be ignored, otherwise false.
|
192
|
+
def ignore_ip?(ip)
|
193
|
+
!@ignore.nil? and @ignore.include?(ip)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
@@ -0,0 +1,165 @@
|
|
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
|
+
# Parse command line arguments, set options and initialize +Context+
|
16
|
+
# accordingly.
|
17
|
+
class Options
|
18
|
+
# Core options
|
19
|
+
attr_reader :core
|
20
|
+
# Spoofing related options.
|
21
|
+
attr_reader :spoof
|
22
|
+
# Sniffing related options.
|
23
|
+
attr_reader :sniff
|
24
|
+
# Proxies related options.
|
25
|
+
attr_reader :proxies
|
26
|
+
# Misc servers related options.
|
27
|
+
attr_reader :servers
|
28
|
+
|
29
|
+
# Create a BetterCap::Options class instance using the specified network interface.
|
30
|
+
def initialize( iface )
|
31
|
+
@core = CoreOptions.new iface
|
32
|
+
@spoof = SpoofOptions.new
|
33
|
+
@sniff = SniffOptions.new
|
34
|
+
@proxies = ProxyOptions.new
|
35
|
+
@servers = ServerOptions.new
|
36
|
+
end
|
37
|
+
|
38
|
+
# Initialize the BetterCap::Context, parse command line arguments and update program
|
39
|
+
# state accordingly.
|
40
|
+
# Will rise a BetterCap::Error if errors occurred.
|
41
|
+
def self.parse!
|
42
|
+
ctx = Context.get
|
43
|
+
|
44
|
+
OptionParser.new do |opts|
|
45
|
+
opts.version = BetterCap::VERSION
|
46
|
+
opts.banner = "Usage: bettercap [options]"
|
47
|
+
|
48
|
+
ctx.options.core.parse!( ctx, opts )
|
49
|
+
ctx.options.spoof.parse!( ctx, opts )
|
50
|
+
ctx.options.sniff.parse!( ctx, opts )
|
51
|
+
ctx.options.proxies.parse!( ctx, opts )
|
52
|
+
ctx.options.servers.parse!( ctx, opts )
|
53
|
+
|
54
|
+
end.parse!
|
55
|
+
|
56
|
+
# Initialize logging system.
|
57
|
+
Logger.init( ctx )
|
58
|
+
|
59
|
+
if ctx.options.core.check_updates
|
60
|
+
UpdateChecker.check
|
61
|
+
exit
|
62
|
+
end
|
63
|
+
|
64
|
+
# Validate options.
|
65
|
+
ctx.options.validate!( ctx )
|
66
|
+
# Load firewall instance, network interface informations and detect the
|
67
|
+
# gateway address.
|
68
|
+
ctx.update!
|
69
|
+
|
70
|
+
ctx
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate!( ctx )
|
74
|
+
@core.validate!
|
75
|
+
@proxies.validate!( ctx )
|
76
|
+
# Print starting message.
|
77
|
+
starting_message
|
78
|
+
end
|
79
|
+
|
80
|
+
def need_gateway?
|
81
|
+
( @core.discovery? or @spoof.enabled? )
|
82
|
+
end
|
83
|
+
|
84
|
+
# Helper method to create a Firewalls::Redirection object.
|
85
|
+
def redir( address, port, to, proto = 'TCP' )
|
86
|
+
Firewalls::Redirection.new( @core.iface, proto, nil, port, address, to )
|
87
|
+
end
|
88
|
+
|
89
|
+
# Helper method to create a Firewalls::Redirection object for a single address ( +from+ ).
|
90
|
+
def redir_single( from, address, port, to, proto = 'TCP' )
|
91
|
+
Firewalls::Redirection.new( @core.iface, proto, from, port, address, to )
|
92
|
+
end
|
93
|
+
|
94
|
+
# Create a list of BetterCap::Firewalls::Redirection objects which are needed
|
95
|
+
# given the specified command line arguments.
|
96
|
+
def get_redirections iface
|
97
|
+
redirections = []
|
98
|
+
|
99
|
+
if @servers.dnsd or @proxies.sslstrip?
|
100
|
+
redirections << redir( iface.ip, 53, @servers.dnsd_port )
|
101
|
+
redirections << redir( iface.ip, 53, @servers.dnsd_port, 'UDP' )
|
102
|
+
end
|
103
|
+
|
104
|
+
if @proxies.proxy
|
105
|
+
@proxies.http_ports.each do |port|
|
106
|
+
if @proxies.proxy_upstream_address.nil?
|
107
|
+
redirections << redir( iface.ip, port, @proxies.proxy_port )
|
108
|
+
else
|
109
|
+
redirections << redir_single( @proxies.proxy_upstream_address, iface.ip, port, @proxies.proxy_port )
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if @proxies.proxy_https
|
115
|
+
@proxies.https_ports.each do |port|
|
116
|
+
if @proxies.proxy_upstream_address.nil?
|
117
|
+
redirections << redir( iface.ip, port, @proxies.proxy_https_port )
|
118
|
+
else
|
119
|
+
redirections << redir_single( @proxies.proxy_upstream_address, iface.ip, port, @proxies.proxy_port )
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if @proxies.tcp_proxy
|
125
|
+
redirections << redir_single( @proxies.tcp_proxy_upstream_address, iface.ip, @proxies.tcp_proxy_upstream_port, @proxies.tcp_proxy_port )
|
126
|
+
end
|
127
|
+
|
128
|
+
if @proxies.custom_proxy
|
129
|
+
@proxies.http_ports.each do |port|
|
130
|
+
redirections << redir( @proxies.custom_proxy, port, @proxies.custom_proxy_port )
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
if @proxies.custom_https_proxy
|
135
|
+
@proxies.https_ports.each do |port|
|
136
|
+
redirections << redir( @proxies.custom_https_proxy, port, @proxies.custom_https_proxy_port )
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
@proxies.custom_redirections.each do |r|
|
141
|
+
redirections << redir( iface.ip, r[:from], r[:to], r[:proto] )
|
142
|
+
end
|
143
|
+
|
144
|
+
redirections
|
145
|
+
end
|
146
|
+
|
147
|
+
# Print the starting status message.
|
148
|
+
def starting_message
|
149
|
+
on = '✔'.green
|
150
|
+
off = '✘'.red
|
151
|
+
status = {
|
152
|
+
'spoofing' => ( @spoof.enabled? ? on : off ),
|
153
|
+
'discovery' => ( @core.discovery? ? on : off ),
|
154
|
+
'sniffer' => ( @sniff.enabled? ? on : off ),
|
155
|
+
'tcp-proxy' => ( @proxies.tcp_proxy ? on : off ),
|
156
|
+
'http-proxy' => ( @proxies.proxy ? on : off ),
|
157
|
+
'https-proxy' => ( @proxies.proxy_https ? on : off ),
|
158
|
+
'sslstrip' => ( @proxies.sslstrip? ? on : off ),
|
159
|
+
'http-server' => ( @servers.httpd ? on : off ),
|
160
|
+
'dns-server' => ( @proxies.sslstrip? or @servers.dnsd ? on : off )
|
161
|
+
}
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|