Module: PWN::Plugins::Vault

Defined in:
lib/pwn/plugins/vault.rb

Overview

Used to encrypt/decrypt configuration files leveraging AES256

Class Method Summary collapse

Class Method Details

.authorsObject

Author(s)

0day Inc. <[email protected]>



327
328
329
330
331
# File 'lib/pwn/plugins/vault.rb', line 327

public_class_method def self.authors
  "AUTHOR(S):
    0day Inc. <[email protected]>
  "
end

.create(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Vault.create(

file: 'required - encrypted file to create'

)



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/pwn/plugins/vault.rb', line 43

public_class_method def self.create(opts = {})
  file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)

  cipher = OpenSSL::Cipher.new('aes-256-cbc')
  key = Base64.strict_encode64(cipher.random_key)
  iv = Base64.strict_encode64(cipher.random_iv)

  puts 'Please store the Key && IV in a secure location as they are required for decryption.'
  puts "Key: #{key}"
  puts "IV: #{iv}"

  encrypt(
    file: file,
    key: key,
    iv: iv
  )
rescue StandardError => e
  raise e
end

.decrypt(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Vault.decrypt(

file: 'required - file to decrypt',
key: 'required - key to decrypt',
iv: 'required - iv to decrypt'

)



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/pwn/plugins/vault.rb', line 70

public_class_method def self.decrypt(opts = {})
  file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)
  key = opts[:key] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'Key'
  )

  iv = opts[:iv] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'IV'
  )

  is_encrypted = file_encrypted?(file: file)
  raise 'ERROR: File is not encrypted.' unless is_encrypted

  cipher = OpenSSL::Cipher.new('aes-256-cbc')
  cipher.decrypt
  cipher.key = Base64.strict_decode64(key)
  cipher.iv = Base64.strict_decode64(iv)

  b64_decoded_file_contents = Base64.strict_decode64(File.read(file).chomp)
  plain_text = cipher.update(b64_decoded_file_contents) + cipher.final

  File.write(file, plain_text)
rescue ArgumentError
  raise 'ERROR: Incorrect Key or IV.'
rescue StandardError => e
  raise e
end

.dump(opts = {}) ⇒ Object

Supported Method Parameters

vault = PWN::Plugins::Vault.dump(

file: 'required - file to dump',
key: 'required - key to decrypt',
iv: 'required - iv to decrypt',
yaml: 'optional - dump as parsed yaml hash (default: true)'

)



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/pwn/plugins/vault.rb', line 106

def self.dump(opts = {})
  file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)
  key = opts[:key] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'Key'
  )

  iv = opts[:iv] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'IV'
  )

  yaml = opts[:yaml] ||= true

  decrypt(
    file: file,
    key: key,
    iv: iv
  )

  if yaml
    file_dump = YAML.load_file(file, symbolize_names: true)
  else
    file_dump = File.read(file)
  end

  encrypt(
    file: file,
    key: key,
    iv: iv
  )

  file_dump
rescue ArgumentError
  raise 'ERROR: Incorrect Key or IV.'
rescue StandardError => e
  raise e
end

.edit(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Vault.edit(

file: 'required - file to edit',
key: 'required - key to decrypt',
iv: 'required - iv to decrypt',
editor: 'optional - editor to use (default: "/usr/bin/vim")'

)



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/pwn/plugins/vault.rb', line 151

def self.edit(opts = {})
  file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)
  key = opts[:key] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'Key'
  )

  iv = opts[:iv] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'IV'
  )

  editor = opts[:editor] ||= '/usr/bin/vim'

  raise 'ERROR: Editor not found.' unless File.exist?(editor)

  decrypt(
    file: file,
    key: key,
    iv: iv
  )

  # Get realtive editor in case aliases are used
  relative_editor = File.basename(editor)
  system(relative_editor, file)

  # If the Pry object exists, set refresh_config to true
  Pry.config.refresh_config = true if defined?(Pry)

  encrypt(
    file: file,
    key: key,
    iv: iv
  )
rescue ArgumentError
  raise 'ERROR: Incorrect Key or IV.'
rescue StandardError => e
  raise e
end

.encrypt(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Vault.encrypt(

file: 'required - file to encrypt',
key: 'required - key to decrypt',
iv: 'required - iv to decrypt'

)



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/pwn/plugins/vault.rb', line 196

public_class_method def self.encrypt(opts = {})
  file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)
  key = opts[:key] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'Key'
  )

  iv = opts[:iv] ||= PWN::Plugins::AuthenticationHelper.mask_password(
    prompt: 'IV'
  )

  cipher = OpenSSL::Cipher.new('aes-256-cbc')
  cipher.encrypt
  cipher.key = Base64.strict_decode64(key)
  cipher.iv = Base64.strict_decode64(iv)

  data = File.read(file)
  encrypted = cipher.update(data) + cipher.final
  encrypted_string = Base64.strict_encode64(encrypted)

  File.write(file, "#{encrypted_string}\n")
rescue StandardError => e
  raise e
end

.file_encrypted?(opts = {}) ⇒ Boolean

Supported Method Parameters

PWN::Plugins::Vault.file_encrypted?(

file: 'required - file to check if encrypted'

)



224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/pwn/plugins/vault.rb', line 224

public_class_method def self.file_encrypted?(opts = {})
  file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)

  raise 'ERROR: File does not exist.' unless File.exist?(file)

  file_contents = File.read(file).chomp
  file_contents.is_a?(String) && Base64.strict_encode64(Base64.strict_decode64(file_contents)) == file_contents
rescue ArgumentError
  false
rescue StandardError => e
  raise e
end

.helpObject

Display Usage for this Module



335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/pwn/plugins/vault.rb', line 335

public_class_method def self.help
  puts "USAGE:
    #{self}.refresh_encryption_secrets(
      file: 'required - file to encrypt with new key and iv',
      key: 'required - key to decrypt',
      iv: 'required - iv to decrypt'
    )

    #{self}.create(
      file: 'required - file to encrypt'
    )

    #{self}.decrypt(
      file: 'required - file to decrypt',
      key: 'required - key to decrypt',
      iv: 'required - iv to decrypt'
    )

    #{self}.dump(
      file: 'required - file to dump',
      key: 'required - key to decrypt',
      iv: 'required - iv to decrypt',
  #   search: 'optional - search for a specific string'
    )

    #{self}.edit(
      file: 'required - file to edit',
      key: 'required - key to decrypt',
      iv: 'required - iv to decrypt',
      editor: 'optional - editor to use (default: \"/usr/bin/vim\")'
    )

    #{self}.encrypt(
      file: 'required - file to encrypt',
      key: 'required - key to decrypt',
      iv: 'required - iv to decrypt'
    )

    #{self}.file_encrypted?(
      file: 'required - file to check if encrypted'
    )

    #{self}.refresh_config_for_repl(
      yaml_config_path: 'required - full path to pwn.yaml file',
      pi: 'optional - Pry instance (default: Pry)',
      decryption_file: 'optional - full path to decryption YAML file'
    )

    #{self}.authors
  "
end

.refresh_config_for_repl(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Vault.refresh_config_for_repl(

  yaml_config_path: 'required - full path to pwn.yaml file',
  pi: 'optional - Pry instance (default: Pry)',
  decryption_file: 'optional - full path to decryption YAML file'
)


243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/pwn/plugins/vault.rb', line 243

public_class_method def self.refresh_config_for_repl(opts = {})
  yaml_config_path = opts[:yaml_config_path]

  return false unless File.exist?(yaml_config_path)

  pi = opts[:pi] ||= Pry

  is_encrypted = PWN::Plugins::Vault.file_encrypted?(file: yaml_config_path)

  if is_encrypted
    # TODO: Implement "something you know, something you have, && something you are?"
    decryption_file = opts[:decryption_file] ||= "#{Dir.home}/pwn.decryptor.yaml"
    raise "ERROR: #{decryption_file} does not exist." unless File.exist?(decryption_file)

    yaml_decryptor = YAML.load_file(decryption_file, symbolize_names: true)

    key = opts[:key] ||= yaml_decryptor[:key] ||= ENV.fetch('PWN_DECRYPTOR_KEY')
    key = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption Key') if key.nil?

    iv = opts[:iv] ||= yaml_decryptor[:iv] ||= ENV.fetch('PWN_DECRYPTOR_IV')
    iv = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption IV') if iv.nil?

    yaml_config = PWN::Plugins::Vault.dump(
      file: yaml_config_path,
      key: key,
      iv: iv
    )
  else
    yaml_config = YAML.load_file(yaml_config_path, symbolize_names: true)
  end
  pi.config.p = yaml_config
  Pry.config.p = yaml_config

  valid_ai_engines = %i[
    grok
    openai
    ollama
  ]
  ai_engine = yaml_config[:ai_engine].to_s.downcase.to_sym

  raise "ERROR: Unsupported AI Engine: #{ai_engine} in #{yaml_config_path}.  Supported AI Engines:\n#{valid_ai_engines.inspect}" unless valid_ai_engines.include?(ai_engine)

  pi.config.pwn_ai_engine = ai_engine
  Pry.config.pwn_ai_engine = ai_engine

  pi.config.pwn_ai_base_uri = pi.config.p[ai_engine][:base_uri]
  Pry.config.pwn_ai_base_uri = pi.config.pwn_ai_base_uri

  pi.config.pwn_ai_key = pi.config.p[ai_engine][:key]
  Pry.config.pwn_ai_key = pi.config.pwn_ai_key

  pi.config.pwn_ai_model = pi.config.p[ai_engine][:model]
  Pry.config.pwn_ai_model = pi.config.pwn_ai_model

  pi.config.pwn_ai_system_role_content = pi.config.p[ai_engine][:system_role_content]
  Pry.config.pwn_ai_system_role_content = pi.config.pwn_ai_system_role_content

  pi.config.pwn_ai_temp = pi.config.p[ai_engine][:temp]
  Pry.config.pwn_ai_temp = pi.config.pwn_ai_temp

  pi.config.pwn_asm_arch = pi.config.p[:asm][:arch]
  Pry.config.pwn_asm_arch = pi.config.pwn_asm_arch

  pi.config.pwn_asm_endian = pi.config.p[:asm][:endian]
  Pry.config.pwn_asm_endian = pi.config.pwn_asm_endian

  pi.config.pwn_irc = pi.config.p[:irc]
  Pry.config.pwn_irc = pi.config.pwn_irc

  pi.config.pwn_hunter = pi.config.p[:hunter][:api_key]
  Pry.config.pwn_hunter = pi.config.pwn_hunter

  pi.config.pwn_shodan = pi.config.p[:shodan][:api_key]
  Pry.config.pwn_shodan = pi.config.pwn_shodan

  Pry.config.refresh_config = false

  true
rescue StandardError => e
  raise e
end

.refresh_encryption_secrets(opts = {}) ⇒ Object

Supported Method Parameters

PWN::Plugins::Vault.refresh_encryption_secrets(

file: 'required - file to encrypt with new key and iv',
key: 'required - key to decrypt',
iv: 'required - iv to decrypt'

)



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/pwn/plugins/vault.rb', line 18

def self.refresh_encryption_secrets(opts = {})
  file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)
  key = opts[:key]
  iv = opts[:iv]

  decrypt(
    file: file,
    key: key,
    iv: iv
  )

  create(
    file: file
  )
rescue ArgumentError
  raise 'ERROR: Incorrect Key or IV.'
rescue StandardError => e
  raise e
end