pwn 0.5.66 → 0.5.68

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9943bae307e743e8dbb7bdb1a1ccdd1c0b61f29e8413bceb25350fc6023fd289
4
- data.tar.gz: 2f32981b889d1e6e21591094b4781162239c36e7b67f7cd0e91eab0c5c400ed1
3
+ metadata.gz: cca5ba837282d7ef87252c9d3181b4d049d5afea636969ef82bfff09511f9719
4
+ data.tar.gz: 4d83a544b2f94f45705e85ecaf23b6f50ce28bc4768621670b84a5c7add91e9d
5
5
  SHA512:
6
- metadata.gz: 58eacda5cce4074c84615d5aee751ef4f642185521b641798563bbed6eb7348cf8a6cee5fe261084784b9c646a3c6cdddca8004f8fb157173861c75ea9e3d637
7
- data.tar.gz: 958c25709cc837206df7d2890e06e2604a7d27fd61e97b30a48fe561306aacef3348d4e24f7eb23699ed371019adc28f31d688995f0451d414416b8992109226
6
+ metadata.gz: 8b227bd65016feaea205b104ea6f82021742f815802f49681a45110ff3806f5170a5d2c5d2e0d5d760494f558b522e8a0415ab76834d04a3520f0588d27c7bd9
7
+ data.tar.gz: 1428ae5ed10c526258affdbaf1a0480cd2ff737cc4e77d9f6f38d23668f9deebf0fcd6d93bccf192d671b5afdc12f3244462fddb39a1171857fef777261842cf
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-03-25 22:25:57 UTC using RuboCop version 1.62.1.
3
+ # on 2024-03-26 16:48:38 UTC using RuboCop version 1.62.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -35,6 +35,12 @@ Layout/LineLength:
35
35
  - 'lib/pwn/reports/uri_buster.rb'
36
36
  - 'lib/pwn/sast/banned_function_calls_c.rb'
37
37
 
38
+ # Offense count: 7
39
+ # Configuration parameters: AllowedMethods, AllowedPatterns.
40
+ Lint/NestedMethodDefinition:
41
+ Exclude:
42
+ - 'lib/pwn/plugins/repl.rb'
43
+
38
44
  # Offense count: 311
39
45
  # This cop supports unsafe autocorrection (--autocorrect-all).
40
46
  # Configuration parameters: AutoCorrect.
@@ -88,6 +94,14 @@ Metrics/ModuleLength:
88
94
  - 'lib/pwn/plugins/open_ai.rb'
89
95
  - 'lib/pwn/plugins/packet.rb'
90
96
 
97
+ # Offense count: 1
98
+ # This cop supports safe autocorrection (--autocorrect).
99
+ # Configuration parameters: EnforcedStyle.
100
+ # SupportedStyles: prefer_alias, prefer_alias_method
101
+ Style/Alias:
102
+ Exclude:
103
+ - 'lib/pwn/plugins/monkey_patch.rb'
104
+
91
105
  # Offense count: 160
92
106
  Style/ClassVars:
93
107
  Enabled: false
@@ -105,11 +119,10 @@ Style/ExplicitBlockArgument:
105
119
  Exclude:
106
120
  - 'lib/pwn/plugins/nmap_it.rb'
107
121
 
108
- # Offense count: 3
122
+ # Offense count: 2
109
123
  # This cop supports safe autocorrection (--autocorrect).
110
124
  Style/IfUnlessModifier:
111
125
  Exclude:
112
- - 'bin/pwn'
113
126
  - 'lib/pwn/plugins/baresip.rb'
114
127
  - 'lib/pwn/plugins/mail_agent.rb'
115
128
 
@@ -133,18 +146,3 @@ Style/RedundantStringEscape:
133
146
  # This cop supports unsafe autocorrection (--autocorrect-all).
134
147
  Style/SlicingWithRange:
135
148
  Enabled: false
136
-
137
- # Offense count: 1
138
- # This cop supports safe autocorrection (--autocorrect).
139
- # Configuration parameters: AllowModifier.
140
- Style/SoleNestedConditional:
141
- Exclude:
142
- - 'bin/pwn'
143
-
144
- # Offense count: 1
145
- # This cop supports safe autocorrection (--autocorrect).
146
- # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
147
- # SupportedStyles: single_quotes, double_quotes
148
- Style/StringLiterals:
149
- Exclude:
150
- - 'bin/pwn'
data/Gemfile CHANGED
@@ -50,7 +50,7 @@ gem 'msfrpc-client', '1.1.2'
50
50
  gem 'netaddr', '2.0.6'
51
51
  gem 'net-ldap', '0.19.0'
52
52
  gem 'net-openvpn', '0.8.7'
53
- gem 'net-smtp', '0.4.0.1'
53
+ gem 'net-smtp', '0.5.0'
54
54
  gem 'nexpose', '7.3.0'
55
55
  gem 'nokogiri', '1.16.3'
56
56
  gem 'nokogiri-diff', '0.3.0'
data/README.md CHANGED
@@ -37,7 +37,7 @@ $ cd /opt/pwn
37
37
  $ ./install.sh
38
38
  $ ./install.sh ruby-gem
39
39
  $ pwn
40
- pwn[v0.5.66]:001 >>> PWN.help
40
+ pwn[v0.5.68]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI)
@@ -52,7 +52,7 @@ $ rvm use ruby-3.3.0@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.5.66]:001 >>> PWN.help
55
+ pwn[v0.5.68]:001 >>> PWN.help
56
56
  ```
57
57
 
58
58
  If you're using a multi-user install of RVM do:
@@ -62,7 +62,7 @@ $ rvm use ruby-3.3.0@pwn
62
62
  $ rvmsudo gem uninstall --all --executables pwn
63
63
  $ rvmsudo gem install --verbose pwn
64
64
  $ pwn
65
- pwn[v0.5.66]:001 >>> PWN.help
65
+ pwn[v0.5.68]:001 >>> PWN.help
66
66
  ```
67
67
 
68
68
  PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
data/bin/pwn CHANGED
@@ -1,12 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'base64'
5
4
  require 'optparse'
6
5
  require 'pwn'
7
- require 'pry'
8
- require 'tty-prompt'
9
- require 'yaml'
10
6
 
11
7
  opts = {}
12
8
  OptionParser.new do |options|
@@ -32,413 +28,23 @@ OptionParser.new do |options|
32
28
  end.parse!
33
29
 
34
30
  begin
35
- def yaml_config_encrypted?(opts = {})
36
- config = opts[:config]
37
-
38
- config_contents = File.read(config)
39
- config_contents.is_a?(String) && Base64.strict_encode64(Base64.decode64(config_contents)) == config_contents
40
- end
41
-
42
- def cleanup_pids(opts = {})
43
- pids_arr = opts[:pids_arr]
44
-
45
- pids_arr.each do |pid_line|
46
- pid = pid_line[2].to_i
47
- Process.kill('TERM', pid)
48
- rescue Errno::ESRCH
49
- next
50
- end
51
- end
52
-
53
- def refresh_ps1_proc(opts = {})
54
- mode = opts[:mode]
55
-
56
- proc do |_target_self, _nest_level, pi|
57
- pi.config.pwn_repl_line += 1
58
- line_pad = format(
59
- '%0.3d',
60
- pi.config.pwn_repl_line
61
- )
62
-
63
- pi.config.prompt_name = :pwn
64
- name = "\001\e[1m\002\001\e[31m\002#{pi.config.prompt_name}\001\e[0m\002"
65
- version = "\001\e[36m\002v#{PWN::VERSION}\001\e[0m\002"
66
- line_count = "\001\e[34m\002#{line_pad}\001\e[0m\002"
67
- dchars = "\001\e[32m\002>>>\001\e[0m\002"
68
- dchars = "\001\e[33m\002***\001\e[0m\002" if mode == :splat
69
-
70
- if pi.config.pwn_asm
71
- pi.config.prompt_name = 'pwn.asm'
72
- name = "\001\e[1m\002\001\e[37m\002#{pi.config.prompt_name}\001\e[0m\002"
73
- dchars = "\001\e[32m\002>>>\001\e[33m\002"
74
- dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
75
- end
76
-
77
- if pi.config.pwn_ai
78
- pi.config.prompt_name = 'pwn.ai'
79
- pi.config.prompt_name = 'pwn.ai.SPEAKING' if pi.config.pwn_ai_speak
80
- name = "\001\e[1m\002\001\e[33m\002#{pi.config.prompt_name}\001\e[0m\002"
81
- dchars = "\001\e[32m\002>>>\001\e[33m\002"
82
- dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
83
- if pi.config.pwn_ai_debug
84
- dchars = "\001\e[32m\002(DEBUG) >>>\001\e[33m\002"
85
- dchars = "\001\e[33m\002(DEBUG) ***\001\e[33m\002" if mode == :splat
86
- end
87
- end
88
-
89
- "#{name}[#{version}]:#{line_count} #{dchars} ".to_s.scrub
90
- end
91
- end
92
-
93
- # Pry Monkey Patches \_(oo)_/
94
- class Pry
95
- # Overwrite Pry::History.push method in History class to get duplicate history entries
96
- # in order to properly replay automation in this prototyping driver
97
- class History
98
- def push(line)
99
- return line if line.empty? || invalid_readline_line?(line)
100
-
101
- begin
102
- last_line = @history[-1]
103
- rescue IndexError
104
- last_line = nil
105
- end
106
-
107
- @history << line
108
- @history_line_count += 1
109
- @saver.call(line) if !should_ignore?(line) &&
110
- Pry.config.history_save
111
-
112
- line
113
- end
114
- alias << push
115
- end
116
-
117
- def handle_line(line, options)
118
- if line.nil?
119
- config.control_d_handler.call(self)
120
- return
121
- end
122
-
123
- ensure_correct_encoding!(line)
124
- Pry.history << line unless options[:generated]
125
-
126
- @suppress_output = false
127
- inject_sticky_locals!
128
- begin
129
- unless process_command_safely(line)
130
- @eval_string += "#{line.chomp}\n" if !line.empty? || !@eval_string.empty?
131
- end
132
- rescue RescuableException => e
133
- self.last_exception = e
134
- result = e
135
-
136
- Pry.critical_section do
137
- show_result(result)
138
- end
139
- return
140
- end
141
-
142
- # This hook is supposed to be executed after each line of ruby code
143
- # has been read (regardless of whether eval_string is yet a complete expression)
144
- exec_hook :after_read, eval_string, self
145
-
146
- begin
147
- complete_expr = true if config.pwn_ai || config.pwn_asm
148
- complete_expr = Pry::Code.complete_expression?(@eval_string) unless config.pwn_ai || config.pwn_asm
149
- rescue SyntaxError => e
150
- output.puts e.message.gsub(/^.*syntax error, */, "SyntaxError: ")
151
- reset_eval_string
152
- end
153
-
154
- if complete_expr
155
- @suppress_output = true if @eval_string =~ /;\Z/ ||
156
- @eval_string.empty? ||
157
- @eval_string =~ /\A *#.*\n\z/ ||
158
- config.pwn_ai ||
159
- config.pwn_asm
160
-
161
- # A bug in jruby makes java.lang.Exception not rescued by
162
- # `rescue Pry::RescuableException` clause.
163
- #
164
- # * https://github.com/pry/pry/issues/854
165
- # * https://jira.codehaus.org/browse/JRUBY-7100
166
- #
167
- # Until that gets fixed upstream, treat java.lang.Exception
168
- # as an additional exception to be rescued explicitly.
169
- #
170
- # This workaround has a side effect: java exceptions specified
171
- # in `Pry.config.unrescued_exceptions` are ignored.
172
- jruby_exceptions = []
173
- jruby_exceptions << Java::JavaLang::Exception if Helpers::Platform.jruby?
174
-
175
- begin
176
- # Reset eval string, in case we're evaluating Ruby that does something
177
- # like open a nested REPL on this instance.
178
- eval_string = @eval_string
179
- reset_eval_string
180
-
181
- result = evaluate_ruby(eval_string) unless config.pwn_ai ||
182
- config.pwn_asm
183
-
184
- result = eval_string if config.pwn_ai ||
185
- config.pwn_asm
186
- rescue RescuableException, *jruby_exceptions => e
187
- # Eliminate following warning:
188
- # warning: singleton on non-persistent Java type X
189
- # (http://wiki.jruby.org/Persistence)
190
- if Helpers::Platform.jruby? && e.class.respond_to?('__persistent__')
191
- e.class.__persistent__ = true
192
- end
193
- self.last_exception = e
194
- result = e
195
- end
196
-
197
- Pry.critical_section do
198
- show_result(result)
199
- end
200
- end
201
-
202
- throw(:breakout) if current_binding.nil?
203
- end
204
-
205
- # Ensure the return value in pwn_ai mode reflects the input
206
- def evaluate_ruby(code)
207
- # if config.pwn_ai || config.pwn_asm
208
- # result = message = code.to_s
209
- # return
210
- # end
211
- inject_sticky_locals!
212
- exec_hook :before_eval, code, self
213
-
214
- result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
215
- set_last_result(result, code)
216
- ensure
217
- update_input_history(code)
218
- exec_hook :after_eval, result, self
219
- end
220
- end
221
-
222
- # Define Custom REPL Commands
223
- Pry::Commands.create_command 'welcome-banner' do
224
- description 'Display the random welcome banner, including basic usage.'
225
-
226
- def process
227
- puts PWN::Banner.welcome
228
- end
229
- end
230
-
231
- Pry::Commands.create_command 'toggle-pager' do
232
- description 'Toggle less on returned objects surpassing the terminal.'
233
-
234
- def process
235
- pi = pry_instance
236
- pi.config.pager ? pi.config.pager = false : pi.config.pager = true
237
- end
238
- end
239
-
240
- # class PWNCompleter < Pry::InputCompleter
241
- # def call(input)
242
- # end
243
- # end
244
-
245
- Pry::Commands.create_command 'pwn-asm' do
246
- description 'Initiate pwn.asm shell.'
247
-
248
- def process
249
- pi = pry_instance
250
- pi.config.pwn_asm = true
251
- pi.custom_completions = proc do
252
- prompt = TTY::Prompt.new
253
- [pi.input.line_buffer]
254
- # prompt.select(pi.input.line_buffer)
255
- end
256
- end
257
- end
258
-
259
- Pry::Commands.create_command 'pwn-ai' do
260
- description 'Initiate pwn.ai chat interface.'
261
-
262
- def process
263
- pi = pry_instance
264
- pi.config.pwn_ai = true
265
- pi.config.color = false if pi.config.pwn_ai
266
- pi.config.color = true unless pi.config.pwn_ai
267
- end
268
- end
269
-
270
- Pry::Commands.create_command 'toggle-pwn-ai-debug' do
271
- description 'Display the response_history object while using pwn.ai'
272
-
273
- def process
274
- pi = pry_instance
275
- pi.config.pwn_ai_debug ? pi.config.pwn_ai_debug = false : pi.config.pwn_ai_debug = true
276
- end
277
- end
278
-
279
- Pry::Commands.create_command 'toggle-pwn-ai-speaks' do
280
- description 'Use speech capabilities within pwn.ai to speak answers.'
281
-
282
- def process
283
- pi = pry_instance
284
- pi.config.pwn_ai_speak ? pi.config.pwn_ai_speak = false : pi.config.pwn_ai_speak = true
285
- end
286
- end
287
-
288
- Pry::Commands.create_command 'back' do
289
- description 'Jump back to pwn REPL when in pwn-asm || pwn-ai.'
290
-
291
- def process
292
- pi = pry_instance
293
- pi.config.pwn_asm = false if pi.config.pwn_asm
294
- pi.config.pwn_ai = false if pi.config.pwn_ai
295
- pi.config.pwn_ai_debug = false if pi.config.pwn_ai_debug
296
- pi.config.pwn_ai_speak = false if pi.config.pwn_ai_speak
297
- pi.config.completer = Pry::InputCompleter
298
- end
299
- end
300
-
301
- # Define REPL Hooks
302
- # Welcome Banner Hook
303
- Pry.config.hooks.add_hook(:before_session, :welcome) do |output, _binding, _pi|
304
- output.puts PWN::Banner.welcome
305
- end
306
-
307
- # pwn.ai Hooks
308
- Pry.config.hooks.add_hook(:before_session, :init_opts) do |_output, _binding, pi|
309
- if opts[:yaml_config_path] && File.exist?(opts[:yaml_config_path])
310
- yaml_config_path = opts[:yaml_config_path]
311
- is_encrypted = yaml_config_encrypted?(config: yaml_config_path)
312
-
313
- if is_encrypted
314
- # TODO: Implement "something you know, something you have, && something you are?"
315
- decryption_file = opts[:decryption_file] ||= "#{Dir.home}/pwn.decryptor.yaml"
316
- yaml_decryptor = YAML.load_file(decryption_file, symbolize_names: true) if File.exist?(decryption_file)
317
-
318
- key = opts[:key] ||= yaml_decryptor[:key] ||= ENV.fetch('PWN_DECRYPTOR_KEY')
319
- key = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption Key') if key.nil?
320
-
321
- iv = opts[:iv] ||= yaml_decryptor[:iv] ||= ENV.fetch('PWN_DECRYPTOR_IV')
322
- iv = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption IV') if iv.nil?
323
-
324
- encrypted_config_dump = PWN::Plugins::Vault.dump(
325
- file: yaml_config_path,
326
- key: key,
327
- iv: iv
328
- )
329
- yaml_config = YAML.load(encrypted_config_dump, symbolize_names: true)
330
- else
331
- yaml_config = YAML.load_file(yaml_config_path, symbolize_names: true)
332
- end
333
-
334
- pi.config.pwn_ai_key = yaml_config[:ai_key]
335
- Pry.config.pwn_ai_key = pi.config.pwn_ai_key
336
- end
337
- end
338
-
339
- Pry.config.hooks.add_hook(:after_read, :pwn_asm_hook) do |request, pi|
340
- if pi.config.pwn_asm && !request.chomp.empty?
341
- request = pi.input.line_buffer
342
-
343
- # Analyze request to determine if it should be processed as opcodes or asm.
344
- straight_hex = /^[a-fA-F0-9\s]+$/
345
- hex_esc_strings = /\\x[\da-fA-F]{2}/
346
- hex_comma_delim_w_dbl_qt = /"(?:[0-9a-fA-F]{2})",?/
347
- hex_comma_delim_w_sng_qt = /'(?:[0-9a-fA-F]{2})',?/
348
- hex_byte_array_as_str = /^\[\s*(?:"[0-9a-fA-F]{2}",\s*)*"[0-9a-fA-F]{2}"\s*\]$/
349
-
350
- if request.match?(straight_hex) ||
351
- request.match?(hex_esc_strings) ||
352
- request.match?(hex_comma_delim_w_dbl_qt) ||
353
- request.match?(hex_comma_delim_w_sng_qt) ||
354
- request.match?(hex_byte_array_as_str)
355
-
356
- response = PWN::Plugins::Assembly.opcodes_to_asm(
357
- opcodes: request,
358
- opcodes_always_strings_obj: true
359
- )
360
- else
361
- response = PWN::Plugins::Assembly.asm_to_opcodes(asm: request)
362
- end
363
- puts "\001\e[31m\002#{response}\001\e[0m\002"
364
- end
365
- end
366
-
367
- Pry.config.hooks.add_hook(:after_read, :pwn_ai_hook) do |request, pi|
368
- if pi.config.pwn_ai && !request.chomp.empty?
369
- request = pi.input.line_buffer.to_s
370
- debug = pi.config.pwn_ai_debug
371
- ai_key = pi.config.pwn_ai_key
372
- ai_key ||= ''
373
- if ai_key.empty?
374
- ai_key = PWN::Plugins::AuthenticationHelper.mask_password(
375
- prompt: 'OpenAI API Key'
376
- )
377
- pi.config.pwn_ai_key = ai_key
378
- end
379
-
380
- response_history = pi.config.pwn_ai_response_history
381
- speak_answer = pi.config.pwn_ai_speak
382
- response = PWN::Plugins::OpenAI.chat(
383
- token: ai_key,
384
- request: request.chomp,
385
- temp: 1,
386
- response_history: response_history,
387
- speak_answer: speak_answer
388
- )
389
- last_response = response[:choices].last[:content]
390
- puts "\n\001\e[32m\002#{last_response}\001\e[0m\002\n\n"
391
-
392
- response_history = {
393
- id: response[:id],
394
- object: response[:object],
395
- model: response[:model],
396
- usage: response[:usage]
397
- }
398
- response_history[:choices] ||= response[:choices]
399
-
400
- if debug
401
- puts 'DEBUG: response_history => '
402
- pp response_history
403
- puts "\nresponse_history[:choices] Length: #{response_history[:choices].length}\n" unless response_history.nil?
404
- end
405
- pi.config.pwn_ai_response_history = response_history
406
- end
407
- end
408
-
409
- # Define PS1 Prompt
410
- Pry.config.pwn_repl_line = 0
411
- Pry.config.prompt_name = :pwn
412
- arrow_ps1_proc = refresh_ps1_proc
413
- splat_ps1_proc = refresh_ps1_proc(mode: :splat)
414
- prompt_ps1 = [arrow_ps1_proc, splat_ps1_proc]
415
- prompt = Pry::Prompt.new(
416
- :pwn,
417
- 'PWN Prototyping REPL',
418
- prompt_ps1
419
- )
420
-
421
- # Start PWN REPL
422
31
  pwn_pid = Process.pid
423
- Pry.start(
424
- self,
425
- prompt: prompt
426
- )
32
+ PWN::Plugins::REPL.start(opts)
427
33
  rescue StandardError => e
428
34
  raise e
429
35
  ensure
430
- proc_list_arr = PWN::Plugins::PS.list
36
+ ps_list_arr = PWN::Plugins::PS.list
431
37
 
432
- kid_pids_arr = proc_list_arr.select { |proc_line| proc_line[3] == pwn_pid.to_s }
38
+ kid_pids_arr = ps_list_arr.select { |ps_line| ps_line[3] == pwn_pid.to_s }
433
39
  # pp kid_pids_arr
434
40
 
435
41
  grandkid_pids_arr = []
436
42
  kid_pids_arr.each do |kid_pid|
437
- gk_arr = proc_list_arr.select { |proc_line| proc_line[3] == kid_pid[2] }
43
+ gk_arr = ps_list_arr.select { |ps_line| ps_line[3] == kid_pid[2] }
438
44
  gk_arr.each { |gk| grandkid_pids_arr.push(gk) }
439
45
  end
440
46
  # pp grandkid_pids_arr
441
47
 
442
- cleanup_pids(pids_arr: grandkid_pids_arr)
443
- cleanup_pids(pids_arr: kid_pids_arr)
48
+ PWN::Plugins::PS.cleanup_pids(pids_arr: grandkid_pids_arr)
49
+ PWN::Plugins::PS.cleanup_pids(pids_arr: kid_pids_arr)
444
50
  end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PWN
4
+ module Plugins
5
+ # This module provides the abilty to centralize monkey patches used in PWN
6
+ module MonkeyPatch
7
+ # Supported Method Parameters::
8
+ # PWN::Plugins::MonkeyPatch.pry
9
+
10
+ public_class_method def self.pry
11
+ # Overwrite Pry::History.push method in History class
12
+ # to get duplicate history entries in order to properly
13
+ # replay automation in this prototyping driver
14
+ Pry::History.class_eval do
15
+ def push(line)
16
+ return line if line.empty? || invalid_readline_line?(line)
17
+
18
+ begin
19
+ last_line = @history[-1]
20
+ rescue IndexError
21
+ last_line = nil
22
+ end
23
+
24
+ @history << line
25
+ @history_line_count += 1
26
+ @saver.call(line) if !should_ignore?(line) &&
27
+ Pry.config.history_save
28
+
29
+ line
30
+ end
31
+ alias << push
32
+ end
33
+
34
+ Pry.class_eval do
35
+ def handle_line(line, options)
36
+ if line.nil?
37
+ config.control_d_handler.call(self)
38
+ return
39
+ end
40
+
41
+ ensure_correct_encoding!(line)
42
+ Pry.history << line unless options[:generated]
43
+
44
+ @suppress_output = false
45
+ inject_sticky_locals!
46
+ begin
47
+ # unless process_command_safely(line)
48
+ unless process_command_safely(line) && (
49
+ line.empty? || @eval_string.empty?
50
+ )
51
+ # @eval_string += "#{line.chomp}\n" if !line.empty? || !@eval_string.empty?
52
+ @eval_string += "#{line.chomp}\n"
53
+ end
54
+ rescue RescuableException => e
55
+ self.last_exception = e
56
+ result = e
57
+
58
+ Pry.critical_section do
59
+ show_result(result)
60
+ end
61
+ return
62
+ end
63
+
64
+ # This hook is supposed to be executed after each line of ruby code
65
+ # has been read (regardless of whether eval_string is yet a complete expression)
66
+ exec_hook :after_read, eval_string, self
67
+
68
+ begin
69
+ complete_expr = true if config.pwn_ai || config.pwn_asm
70
+ complete_expr = Pry::Code.complete_expression?(@eval_string) unless config.pwn_ai || config.pwn_asm
71
+ rescue SyntaxError => e
72
+ output.puts e.message.gsub(/^.*syntax error, */, 'SyntaxError: ')
73
+ reset_eval_string
74
+ end
75
+
76
+ if complete_expr
77
+ @suppress_output = true if @eval_string =~ /;\Z/ ||
78
+ @eval_string.empty? ||
79
+ @eval_string =~ /\A *#.*\n\z/ ||
80
+ config.pwn_ai ||
81
+ config.pwn_asm
82
+
83
+ # A bug in jruby makes java.lang.Exception not rescued by
84
+ # `rescue Pry::RescuableException` clause.
85
+ #
86
+ # * https://github.com/pry/pry/issues/854
87
+ # * https://jira.codehaus.org/browse/JRUBY-7100
88
+ #
89
+ # Until that gets fixed upstream, treat java.lang.Exception
90
+ # as an additional exception to be rescued explicitly.
91
+ #
92
+ # This workaround has a side effect: java exceptions specified
93
+ # in `Pry.config.unrescued_exceptions` are ignored.
94
+ jruby_exceptions = []
95
+ jruby_exceptions << Java::JavaLang::Exception if Pry::Helpers::Platform.jruby?
96
+
97
+ begin
98
+ # Reset eval string, in case we're evaluating Ruby that does something
99
+ # like open a nested REPL on this instance.
100
+ eval_string = @eval_string
101
+ reset_eval_string
102
+
103
+ result = evaluate_ruby(eval_string) unless config.pwn_ai ||
104
+ config.pwn_asm
105
+
106
+ result = eval_string if config.pwn_ai ||
107
+ config.pwn_asm
108
+ rescue RescuableException, *jruby_exceptions => e
109
+ # Eliminate following warning:
110
+ # warning: singleton on non-persistent Java type X
111
+ # (http://wiki.jruby.org/Persistence)
112
+ e.class.__persistent__ = true if Helpers::Platform.jruby? && e.class.respond_to?('__persistent__')
113
+ self.last_exception = e
114
+ result = e
115
+ end
116
+
117
+ Pry.critical_section do
118
+ show_result(result)
119
+ end
120
+ end
121
+
122
+ throw(:breakout) if current_binding.nil?
123
+ end
124
+
125
+ # Ensure the return value in pwn_ai mode reflects the input
126
+ def evaluate_ruby(code)
127
+ # if config.pwn_ai || config.pwn_asm
128
+ # result = message = code.to_s
129
+ # return
130
+ # end
131
+ inject_sticky_locals!
132
+ exec_hook :before_eval, code, self
133
+
134
+ result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
135
+ set_last_result(result, code)
136
+ ensure
137
+ update_input_history(code)
138
+ exec_hook :after_eval, result, self
139
+ end
140
+ end
141
+ rescue StandardError => e
142
+ raise e
143
+ end
144
+
145
+ # Author(s):: 0day Inc. <request.pentest@0dayinc.com>
146
+
147
+ public_class_method def self.authors
148
+ "AUTHOR(S):
149
+ 0day Inc. <request.pentest@0dayinc.com>
150
+ "
151
+ end
152
+
153
+ # Display Usage for this Module
154
+
155
+ public_class_method def self.help
156
+ puts "USAGE:
157
+ #{self}.pry
158
+
159
+ #{self}.authors
160
+ "
161
+ end
162
+ end
163
+ end
164
+ end
@@ -47,6 +47,23 @@ module PWN
47
47
  raise e
48
48
  end
49
49
 
50
+ # Supported Method Parameters::
51
+ # PWN::Plugins::PS.cleanup_pids(
52
+ # pids_arr: 'required - array of pids to kill'
53
+ # )
54
+ public_class_method def self.cleanup_pids(opts = {})
55
+ pids_arr = opts[:pids_arr]
56
+
57
+ pids_arr.each do |pid_line|
58
+ pid = pid_line[2].to_i
59
+ Process.kill('TERM', pid)
60
+ rescue Errno::ESRCH
61
+ next
62
+ end
63
+ rescue StandardError => e
64
+ raise e
65
+ end
66
+
50
67
  # Author(s):: 0day Inc. <request.pentest@0dayinc.com>
51
68
 
52
69
  public_class_method def self.authors
@@ -61,6 +78,10 @@ module PWN
61
78
  puts "USAGE:
62
79
  proc_list_arr = #{self}.list
63
80
 
81
+ #{self}.cleanup_pids(
82
+ pids_arr: 'required - array of pids to kill'
83
+ )
84
+
64
85
  #{self}.authors
65
86
  "
66
87
  end
@@ -0,0 +1,317 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pry'
4
+ require 'tty-prompt'
5
+ require 'yaml'
6
+
7
+ module PWN
8
+ module Plugins
9
+ # This module contains methods related to the pwn REPL Driver.
10
+ module REPL
11
+ # Supported Method Parameters::
12
+ # PWN::Plugins::REPL.refresh_ps1_proc(
13
+ # mode: 'required - :splat or nil'
14
+ # )
15
+
16
+ public_class_method def self.refresh_ps1_proc(opts = {})
17
+ mode = opts[:mode]
18
+
19
+ proc do |_target_self, _nest_level, pi|
20
+ pi.config.pwn_repl_line += 1
21
+ line_pad = format(
22
+ '%0.3d',
23
+ pi.config.pwn_repl_line
24
+ )
25
+
26
+ pi.config.prompt_name = :pwn
27
+ name = "\001\e[1m\002\001\e[31m\002#{pi.config.prompt_name}\001\e[0m\002"
28
+ version = "\001\e[36m\002v#{PWN::VERSION}\001\e[0m\002"
29
+ line_count = "\001\e[34m\002#{line_pad}\001\e[0m\002"
30
+ dchars = "\001\e[32m\002>>>\001\e[0m\002"
31
+ dchars = "\001\e[33m\002***\001\e[0m\002" if mode == :splat
32
+
33
+ if pi.config.pwn_asm
34
+ pi.config.prompt_name = 'pwn.asm'
35
+ name = "\001\e[1m\002\001\e[37m\002#{pi.config.prompt_name}\001\e[0m\002"
36
+ dchars = "\001\e[32m\002>>>\001\e[33m\002"
37
+ dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
38
+ end
39
+
40
+ if pi.config.pwn_ai
41
+ pi.config.prompt_name = 'pwn.ai'
42
+ pi.config.prompt_name = 'pwn.ai.SPEAKING' if pi.config.pwn_ai_speak
43
+ name = "\001\e[1m\002\001\e[33m\002#{pi.config.prompt_name}\001\e[0m\002"
44
+ dchars = "\001\e[32m\002>>>\001\e[33m\002"
45
+ dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
46
+ if pi.config.pwn_ai_debug
47
+ dchars = "\001\e[32m\002(DEBUG) >>>\001\e[33m\002"
48
+ dchars = "\001\e[33m\002(DEBUG) ***\001\e[33m\002" if mode == :splat
49
+ end
50
+ end
51
+
52
+ "#{name}[#{version}]:#{line_count} #{dchars} ".to_s.scrub
53
+ end
54
+ rescue StandardError => e
55
+ raise e
56
+ end
57
+
58
+ # Supported Method Parameters::
59
+ # PWN::Plugins::REPL.add_commands
60
+
61
+ public_class_method def self.add_commands
62
+ # Define Custom REPL Commands
63
+ Pry::Commands.create_command 'welcome-banner' do
64
+ description 'Display the random welcome banner, including basic usage.'
65
+
66
+ def process
67
+ puts PWN::Banner.welcome
68
+ end
69
+ end
70
+
71
+ Pry::Commands.create_command 'toggle-pager' do
72
+ description 'Toggle less on returned objects surpassing the terminal.'
73
+
74
+ def process
75
+ pi = pry_instance
76
+ pi.config.pager ? pi.config.pager = false : pi.config.pager = true
77
+ end
78
+ end
79
+
80
+ # class PWNCompleter < Pry::InputCompleter
81
+ # def call(input)
82
+ # end
83
+ # end
84
+
85
+ Pry::Commands.create_command 'pwn-asm' do
86
+ description 'Initiate pwn.asm shell.'
87
+
88
+ def process
89
+ pi = pry_instance
90
+ pi.config.pwn_asm = true
91
+ pi.custom_completions = proc do
92
+ prompt = TTY::Prompt.new
93
+ [pi.input.line_buffer]
94
+ # prompt.select(pi.input.line_buffer)
95
+ end
96
+ end
97
+ end
98
+
99
+ Pry::Commands.create_command 'pwn-ai' do
100
+ description 'Initiate pwn.ai chat interface.'
101
+
102
+ def process
103
+ pi = pry_instance
104
+ pi.config.pwn_ai = true
105
+ pi.config.color = false if pi.config.pwn_ai
106
+ pi.config.color = true unless pi.config.pwn_ai
107
+ end
108
+ end
109
+
110
+ Pry::Commands.create_command 'toggle-pwn-ai-debug' do
111
+ description 'Display the response_history object while using pwn.ai'
112
+
113
+ def process
114
+ pi = pry_instance
115
+ pi.config.pwn_ai_debug ? pi.config.pwn_ai_debug = false : pi.config.pwn_ai_debug = true
116
+ end
117
+ end
118
+
119
+ Pry::Commands.create_command 'toggle-pwn-ai-speaks' do
120
+ description 'Use speech capabilities within pwn.ai to speak answers.'
121
+
122
+ def process
123
+ pi = pry_instance
124
+ pi.config.pwn_ai_speak ? pi.config.pwn_ai_speak = false : pi.config.pwn_ai_speak = true
125
+ end
126
+ end
127
+
128
+ Pry::Commands.create_command 'back' do
129
+ description 'Jump back to pwn REPL when in pwn-asm || pwn-ai.'
130
+
131
+ def process
132
+ pi = pry_instance
133
+ pi.config.pwn_asm = false if pi.config.pwn_asm
134
+ pi.config.pwn_ai = false if pi.config.pwn_ai
135
+ pi.config.pwn_ai_debug = false if pi.config.pwn_ai_debug
136
+ pi.config.pwn_ai_speak = false if pi.config.pwn_ai_speak
137
+ pi.config.completer = Pry::InputCompleter
138
+ end
139
+ end
140
+ rescue StandardError => e
141
+ raise e
142
+ end
143
+
144
+ # Supported Method Parameters::
145
+ # PWN::Plugins::REPL.add_hooks(
146
+ # opts: 'required - Hash object passed in via pwn OptParser'
147
+ # )
148
+
149
+ public_class_method def self.add_hooks(opts = {})
150
+ # Define REPL Hooks
151
+ # Welcome Banner Hook
152
+ Pry.config.hooks.add_hook(:before_session, :welcome) do |output, _binding, _pi|
153
+ output.puts PWN::Banner.welcome
154
+ end
155
+
156
+ # pwn.ai Hooks
157
+ Pry.config.hooks.add_hook(:before_session, :init_opts) do |_output, _binding, pi|
158
+ if opts[:yaml_config_path] && File.exist?(opts[:yaml_config_path])
159
+ yaml_config_path = opts[:yaml_config_path]
160
+ is_encrypted = PWN::Plugins::Vault.file_encrypted?(file: yaml_config_path)
161
+
162
+ if is_encrypted
163
+ # TODO: Implement "something you know, something you have, && something you are?"
164
+ decryption_file = opts[:decryption_file] ||= "#{Dir.home}/pwn.decryptor.yaml"
165
+ yaml_decryptor = YAML.load_file(decryption_file, symbolize_names: true) if File.exist?(decryption_file)
166
+
167
+ key = opts[:key] ||= yaml_decryptor[:key] ||= ENV.fetch('PWN_DECRYPTOR_KEY')
168
+ key = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption Key') if key.nil?
169
+
170
+ iv = opts[:iv] ||= yaml_decryptor[:iv] ||= ENV.fetch('PWN_DECRYPTOR_IV')
171
+ iv = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption IV') if iv.nil?
172
+
173
+ decrypted_yaml_config = PWN::Plugins::Vault.dump(
174
+ file: yaml_config_path,
175
+ key: key,
176
+ iv: iv
177
+ )
178
+ yaml_config = YAML.load(decrypted_yaml_config, symbolize_names: true)
179
+ else
180
+ yaml_config = YAML.load_file(yaml_config_path, symbolize_names: true)
181
+ end
182
+
183
+ pi.config.pwn_ai_key = yaml_config[:ai_key]
184
+ Pry.config.pwn_ai_key = pi.config.pwn_ai_key
185
+ end
186
+ end
187
+
188
+ Pry.config.hooks.add_hook(:after_read, :pwn_asm_hook) do |request, pi|
189
+ if pi.config.pwn_asm && !request.chomp.empty?
190
+ request = pi.input.line_buffer
191
+
192
+ # Analyze request to determine if it should be processed as opcodes or asm.
193
+ straight_hex = /^[a-fA-F0-9\s]+$/
194
+ hex_esc_strings = /\\x[\da-fA-F]{2}/
195
+ hex_comma_delim_w_dbl_qt = /"(?:[0-9a-fA-F]{2})",?/
196
+ hex_comma_delim_w_sng_qt = /'(?:[0-9a-fA-F]{2})',?/
197
+ hex_byte_array_as_str = /^\[\s*(?:"[0-9a-fA-F]{2}",\s*)*"[0-9a-fA-F]{2}"\s*\]$/
198
+
199
+ if request.match?(straight_hex) ||
200
+ request.match?(hex_esc_strings) ||
201
+ request.match?(hex_comma_delim_w_dbl_qt) ||
202
+ request.match?(hex_comma_delim_w_sng_qt) ||
203
+ request.match?(hex_byte_array_as_str)
204
+
205
+ response = PWN::Plugins::Assembly.opcodes_to_asm(
206
+ opcodes: request,
207
+ opcodes_always_strings_obj: true
208
+ )
209
+ else
210
+ response = PWN::Plugins::Assembly.asm_to_opcodes(asm: request)
211
+ end
212
+ puts "\001\e[31m\002#{response}\001\e[0m\002"
213
+ end
214
+ end
215
+
216
+ Pry.config.hooks.add_hook(:after_read, :pwn_ai_hook) do |request, pi|
217
+ if pi.config.pwn_ai && !request.chomp.empty?
218
+ request = pi.input.line_buffer.to_s
219
+ debug = pi.config.pwn_ai_debug
220
+ ai_key = pi.config.pwn_ai_key
221
+ ai_key ||= ''
222
+ if ai_key.empty?
223
+ ai_key = PWN::Plugins::AuthenticationHelper.mask_password(
224
+ prompt: 'OpenAI API Key'
225
+ )
226
+ pi.config.pwn_ai_key = ai_key
227
+ end
228
+
229
+ response_history = pi.config.pwn_ai_response_history
230
+ speak_answer = pi.config.pwn_ai_speak
231
+ response = PWN::Plugins::OpenAI.chat(
232
+ token: ai_key,
233
+ request: request.chomp,
234
+ temp: 1,
235
+ response_history: response_history,
236
+ speak_answer: speak_answer
237
+ )
238
+ last_response = response[:choices].last[:content]
239
+ puts "\n\001\e[32m\002#{last_response}\001\e[0m\002\n\n"
240
+
241
+ response_history = {
242
+ id: response[:id],
243
+ object: response[:object],
244
+ model: response[:model],
245
+ usage: response[:usage]
246
+ }
247
+ response_history[:choices] ||= response[:choices]
248
+
249
+ if debug
250
+ puts 'DEBUG: response_history => '
251
+ pp response_history
252
+ puts "\nresponse_history[:choices] Length: #{response_history[:choices].length}\n" unless response_history.nil?
253
+ end
254
+ pi.config.pwn_ai_response_history = response_history
255
+ end
256
+ end
257
+ rescue StandardError => e
258
+ raise e
259
+ end
260
+
261
+ # Supported Method Parameters::
262
+ # PWN::Plugins::REPL.start(
263
+ # opts: 'required - Hash object passed in via pwn OptParser'
264
+ # )
265
+
266
+ public_class_method def self.start(opts = {})
267
+ # Monkey Patch Pry, add commands, && hooks
268
+ PWN::Plugins::MonkeyPatch.pry
269
+ add_commands
270
+ add_hooks(opts)
271
+
272
+ # Define PS1 Prompt
273
+ Pry.config.pwn_repl_line = 0
274
+ Pry.config.prompt_name = :pwn
275
+ arrow_ps1_proc = refresh_ps1_proc
276
+ splat_ps1_proc = refresh_ps1_proc(mode: :splat)
277
+ ps1 = [arrow_ps1_proc, splat_ps1_proc]
278
+ prompt = Pry::Prompt.new(:pwn, 'PWN Prototyping REPL', ps1)
279
+
280
+ # Start PWN REPL
281
+ Pry.start(self, prompt: prompt)
282
+ rescue StandardError => e
283
+ raise e
284
+ end
285
+
286
+ # Author(s):: 0day Inc. <request.pentest@0dayinc.com>
287
+
288
+ public_class_method def self.authors
289
+ "AUTHOR(S):
290
+ 0day Inc. <request.pentest@0dayinc.com>
291
+ "
292
+ end
293
+
294
+ # Display Usage for this Module
295
+
296
+ public_class_method def self.help
297
+ puts "USAGE:
298
+ #{self}.refresh_ps1_proc(
299
+ mode: 'required - :splat or nil'
300
+ )
301
+
302
+ #{self}.add_commands
303
+
304
+ #{self}.add_hooks(
305
+ opts: 'required - Hash object passed in via pwn OptParser'
306
+ )
307
+
308
+ #{self}.start(
309
+ opts: 'required - Hash object passed in via pwn OptParser'
310
+ )
311
+
312
+ #{self}.authors
313
+ "
314
+ end
315
+ end
316
+ end
317
+ end
@@ -184,6 +184,21 @@ module PWN
184
184
  raise e
185
185
  end
186
186
 
187
+ # Supported Method Parameters::
188
+ # PWN::Plugins::Vault.file_encrypted?(
189
+ # file: 'required - file to check if encrypted'
190
+ # )
191
+ public_class_method def self.file_encrypted?(opts = {})
192
+ file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)
193
+
194
+ raise 'ERROR: File does not exist.' unless File.exist?(file)
195
+
196
+ file_contents = File.read(file)
197
+ file_contents.is_a?(String) && Base64.strict_encode64(Base64.strict_decode64(file_contents)) == file_contents
198
+ rescue StandardError => e
199
+ raise e
200
+ end
201
+
187
202
  # Author(s):: 0day Inc. <request.pentest@0dayinc.com>
188
203
 
189
204
  public_class_method def self.authors
@@ -231,6 +246,10 @@ module PWN
231
246
  iv: 'required - iv to decrypt'
232
247
  )
233
248
 
249
+ #{self}.file_encrypted?(
250
+ file: 'required - file to check if encrypted'
251
+ )
252
+
234
253
  #{self}.authors
235
254
  "
236
255
  end
data/lib/pwn/plugins.rb CHANGED
@@ -37,6 +37,7 @@ module PWN
37
37
  autoload :Log, 'pwn/plugins/log'
38
38
  autoload :MailAgent, 'pwn/plugins/mail_agent'
39
39
  autoload :Metasploit, 'pwn/plugins/metasploit'
40
+ autoload :MonkeyPatch, 'pwn/plugins/monkey_patch'
40
41
  autoload :MSR206, 'pwn/plugins/msr206'
41
42
  autoload :NessusCloud, 'pwn/plugins/nessus_cloud'
42
43
  autoload :NexposeVulnScan, 'pwn/plugins/nexpose_vuln_scan'
@@ -52,6 +53,7 @@ module PWN
52
53
  autoload :Pony, 'pwn/plugins/pony'
53
54
  autoload :PS, 'pwn/plugins/ps'
54
55
  autoload :RabbitMQ, 'pwn/plugins/rabbit_mq'
56
+ autoload :REPL, 'pwn/plugins/repl'
55
57
  autoload :RFIDler, 'pwn/plugins/rfidler'
56
58
  autoload :ScannableCodes, 'pwn/plugins/scannable_codes'
57
59
  autoload :Serial, 'pwn/plugins/serial'
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.66'
4
+ VERSION = '0.5.68'
5
5
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::Plugins::MonkeyPatch do
6
+ it 'should display information for authors' do
7
+ authors_response = PWN::Plugins::MonkeyPatch
8
+ expect(authors_response).to respond_to :authors
9
+ end
10
+
11
+ it 'should display information for existing help method' do
12
+ help_response = PWN::Plugins::MonkeyPatch
13
+ expect(help_response).to respond_to :help
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::Plugins::REPL do
6
+ it 'should display information for authors' do
7
+ authors_response = PWN::Plugins::REPL
8
+ expect(authors_response).to respond_to :authors
9
+ end
10
+
11
+ it 'should display information for existing help method' do
12
+ help_response = PWN::Plugins::REPL
13
+ expect(help_response).to respond_to :help
14
+ end
15
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.66
4
+ version: 0.5.68
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -534,14 +534,14 @@ dependencies:
534
534
  requirements:
535
535
  - - '='
536
536
  - !ruby/object:Gem::Version
537
- version: 0.4.0.1
537
+ version: 0.5.0
538
538
  type: :runtime
539
539
  prerelease: false
540
540
  version_requirements: !ruby/object:Gem::Requirement
541
541
  requirements:
542
542
  - - '='
543
543
  - !ruby/object:Gem::Version
544
- version: 0.4.0.1
544
+ version: 0.5.0
545
545
  - !ruby/object:Gem::Dependency
546
546
  name: nexpose
547
547
  requirement: !ruby/object:Gem::Requirement
@@ -1767,6 +1767,7 @@ files:
1767
1767
  - lib/pwn/plugins/log.rb
1768
1768
  - lib/pwn/plugins/mail_agent.rb
1769
1769
  - lib/pwn/plugins/metasploit.rb
1770
+ - lib/pwn/plugins/monkey_patch.rb
1770
1771
  - lib/pwn/plugins/msr206.rb
1771
1772
  - lib/pwn/plugins/nessus_cloud.rb
1772
1773
  - lib/pwn/plugins/nexpose_vuln_scan.rb
@@ -1783,6 +1784,7 @@ files:
1783
1784
  - lib/pwn/plugins/ps.rb
1784
1785
  - lib/pwn/plugins/pwn_logger.rb
1785
1786
  - lib/pwn/plugins/rabbit_mq.rb
1787
+ - lib/pwn/plugins/repl.rb
1786
1788
  - lib/pwn/plugins/rfidler.rb
1787
1789
  - lib/pwn/plugins/scannable_codes.rb
1788
1790
  - lib/pwn/plugins/serial.rb
@@ -2094,6 +2096,7 @@ files:
2094
2096
  - spec/lib/pwn/plugins/log_spec.rb
2095
2097
  - spec/lib/pwn/plugins/mail_agent_spec.rb
2096
2098
  - spec/lib/pwn/plugins/metasploit_spec.rb
2099
+ - spec/lib/pwn/plugins/monkey_patch_spec.rb
2097
2100
  - spec/lib/pwn/plugins/msr206_spec.rb
2098
2101
  - spec/lib/pwn/plugins/nessus_cloud_spec.rb
2099
2102
  - spec/lib/pwn/plugins/nexpose_vuln_scan_spec.rb
@@ -2110,6 +2113,7 @@ files:
2110
2113
  - spec/lib/pwn/plugins/ps_spec.rb
2111
2114
  - spec/lib/pwn/plugins/pwn_logger_spec.rb
2112
2115
  - spec/lib/pwn/plugins/rabbit_mq_spec.rb
2116
+ - spec/lib/pwn/plugins/repl_spec.rb
2113
2117
  - spec/lib/pwn/plugins/rfidler_spec.rb
2114
2118
  - spec/lib/pwn/plugins/scannable_codes_spec.rb
2115
2119
  - spec/lib/pwn/plugins/serial_spec.rb