pwn 0.5.425 → 0.5.427

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: 30904f7cfcbec2b9dc3ae06c5cb6ef6cf1c59e711bccbc7a7302b26e21639eef
4
- data.tar.gz: 7e4c812591317c30242e558bf924d2eacd96563dbfea75ebf07161435c7c1720
3
+ metadata.gz: ac10751175e251ad205dd5e3c4cc6b1eeb12f45717b7099a018cb71b3628fa7f
4
+ data.tar.gz: 2535096c15a6baf77f987e7c79b12655a8191b895bc0a1907995fd0cae98159a
5
5
  SHA512:
6
- metadata.gz: 2fffe14cfa05fb8960ab0551de18171f02643b4162d3ee9e06ab60adb081345f71b6c8c9409555f010762d356f419445a7358e3ccf3e501b53131cac7579b94f
7
- data.tar.gz: 6795f441e69e5ec1b7bd43831bf273aec2596565ecd8734618a293c82f0fd360a16a1378399c72d1468d89bcd42c276f4b5d217a960d5d46525cfa15a80859d9
6
+ metadata.gz: 0d749319613654cbbc475657a6901e4cdad824b72a90889d4612bc89e88df188277941cdb41df44078ef500d549f5bc5de014bdacf141d37c63b82c7c1e14d65
7
+ data.tar.gz: b7538e5a12b5512f48ebf0976eb2c14009d7c97730eefc8271b77a0b7e53cbbe1b777420318c4bae032d4094e8ff495debc6a00f27ae31799fa966a213737cd3
data/Gemfile CHANGED
@@ -90,7 +90,7 @@ gem 'ruby-nmap', '1.0.3'
90
90
  gem 'ruby-saml', '1.18.1'
91
91
  gem 'rvm', '1.11.3.9'
92
92
  gem 'savon', '2.15.1'
93
- gem 'selenium-devtools', '0.139.0'
93
+ gem 'selenium-devtools', '0.140.0'
94
94
  # gem 'serialport', '1.3.2'
95
95
  # gem 'sinatra', '4.0.0'
96
96
  gem 'slack-ruby-client', '3.0.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.425]:001 >>> PWN.help
40
+ pwn[v0.5.427]: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.4.4@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.5.425]:001 >>> PWN.help
55
+ pwn[v0.5.427]: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.4.4@pwn
62
62
  $ rvmsudo gem uninstall --all --executables pwn
63
63
  $ rvmsudo gem install --verbose pwn
64
64
  $ pwn
65
- pwn[v0.5.425]:001 >>> PWN.help
65
+ pwn[v0.5.427]: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
@@ -14,16 +14,8 @@ OptionParser.new do |options|
14
14
  opts[:yaml_config_path] = p
15
15
  end
16
16
 
17
- options.on('-dPATH', '--decryptor=PATH', '<Optional - File Containing Decryption Key && IV>') do |d|
18
- opts[:decryption_file] = d
19
- end
20
-
21
- options.on('-kKEY', '--decryption-key=KEY', '<Optional - Decryption Key>') do |k|
22
- opts[:key] = k
23
- end
24
-
25
- options.on('-iIV', '--decryption-iv=PATH', '<Optional - Decryption IV>') do |i|
26
- opts[:iv] = i
17
+ options.on('-dPATH', '--yaml-decryptor=PATH', '<Optional - Out-of-Band YAML File with :key && :iv>') do |d|
18
+ opts[:yaml_decryptor_path] = d
27
19
  end
28
20
  end.parse!
29
21
 
data/lib/pwn/ai.rb CHANGED
@@ -9,7 +9,7 @@ module PWN
9
9
  autoload :Ollama, 'pwn/ai/ollama'
10
10
  autoload :OpenAI, 'pwn/ai/open_ai'
11
11
 
12
- # Display a List of Every PWN::Plugins Module
12
+ # Display a List of Every PWN::AI Module
13
13
 
14
14
  public_class_method def self.help
15
15
  constants.sort
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'rest-client'
5
+ require 'tty-spinner'
6
+
7
+ module PWN
8
+ module Blockchain
9
+ # This plugin interacts with BitCoin's Blockchain API.
10
+ module BTC
11
+ # Supported Method Parameters::
12
+ # btc_rest_call(
13
+ # http_method: 'optional HTTP method (defaults to GET)
14
+ # rest_call: 'required rest call to make per the schema',
15
+ # params: 'optional params passed in the URI or HTTP Headers',
16
+ # http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',
17
+ # timeout: 'optional timeout in seconds (defaults to 180)',
18
+ # spinner: 'optional - display spinner (defaults to false)'
19
+ # )
20
+
21
+ private_class_method def self.btc_rest_call(opts = {})
22
+ http_method = if opts[:http_method].nil?
23
+ :get
24
+ else
25
+ opts[:http_method].to_s.scrub.to_sym
26
+ end
27
+
28
+ base_uri = 'https://api.blockcypher.com/v1/btc/'
29
+ rest_call = opts[:rest_call].to_s.scrub
30
+ params = opts[:params]
31
+ headers = { content_type: 'application/json; charset=UTF-8' }
32
+
33
+ http_body = opts[:http_body]
34
+ http_body ||= {}
35
+
36
+ timeout = opts[:timeout]
37
+ timeout ||= 180
38
+
39
+ spinner = opts[:spinner] || false
40
+
41
+ browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)
42
+ rest_client = browser_obj[:browser]::Request
43
+
44
+ if spinner
45
+ spin = TTY::Spinner.new(format: :dots)
46
+ spin.auto_spin
47
+ end
48
+
49
+ retries = 0
50
+ case http_method
51
+ when :delete, :get
52
+ headers[:params] = params
53
+ response = rest_client.execute(
54
+ method: http_method,
55
+ url: "#{base_uri}#{rest_call}",
56
+ headers: headers,
57
+ verify_ssl: false,
58
+ timeout: timeout
59
+ )
60
+
61
+ when :post
62
+ if http_body.key?(:multipart)
63
+ headers[:content_type] = 'multipart/form-data'
64
+
65
+ response = rest_client.execute(
66
+ method: http_method,
67
+ url: "#{base_uri}#{rest_call}",
68
+ headers: headers,
69
+ payload: http_body,
70
+ verify_ssl: false,
71
+ timeout: timeout
72
+ )
73
+ else
74
+ response = rest_client.execute(
75
+ method: http_method,
76
+ url: "#{base_uri}#{rest_call}",
77
+ headers: headers,
78
+ payload: http_body.to_json,
79
+ verify_ssl: false,
80
+ timeout: timeout
81
+ )
82
+ end
83
+
84
+ else
85
+ raise "Unsupported HTTP Method #{http_method} for #{self} Plugin"
86
+ end
87
+
88
+ response
89
+ rescue RestClient::ExceptionWithResponse => e
90
+ case e.http_code
91
+ when 400, 404
92
+ "#{e.http_code} #{e.message}: #{e.response.body}"
93
+ else
94
+ raise e
95
+ end
96
+ rescue StandardError => e
97
+ raise e
98
+ ensure
99
+ spin.stop if spinner
100
+ end
101
+
102
+ # Supported Method Parameters::
103
+ # latest_block = PWN::Blockchain::BTC.get_latest_block(
104
+ # token: 'optional - API token for higher rate limits'
105
+ # )
106
+
107
+ public_class_method def self.get_latest_block(opts = {})
108
+ params = {}
109
+ params[:token] = opts[:token] if opts[:token]
110
+
111
+ rest_call = 'main'
112
+ response = btc_rest_call(rest_call: rest_call, params: params)
113
+
114
+ JSON.parse(response.body, symbolize_names: true)
115
+ rescue StandardError => e
116
+ raise e
117
+ end
118
+
119
+ # Supported Method Parameters::
120
+ # PWN::Blockchain::BTC.get_block_details(
121
+ # height: 'required - block height number',
122
+ # token: 'optional - API token for higher rate limits'
123
+ # )
124
+ public_class_method def self.get_block_details(opts = {})
125
+ height = opts[:height]
126
+ params = {}
127
+ params[:token] = opts[:token] if opts[:token]
128
+
129
+ rest_call = "main/blocks/#{height}"
130
+ response = btc_rest_call(rest_call: rest_call, params: params)
131
+
132
+ JSON.parse(response.body, symbolize_names: true)
133
+ rescue StandardError => e
134
+ raise e
135
+ end
136
+
137
+ # Author(s):: 0day Inc. <support@0dayinc.com>
138
+
139
+ public_class_method def self.authors
140
+ "AUTHOR(S):
141
+ 0day Inc. <support@0dayinc.com>
142
+ "
143
+ end
144
+
145
+ # Display Usage for this Module
146
+
147
+ public_class_method def self.help
148
+ puts "USAGE:
149
+ latest_block = #{self}.get_latest_block
150
+
151
+ block_details = #{self}.get_block_details(
152
+ height: 'required - block height number'
153
+ )
154
+
155
+ #{self}.authors
156
+ "
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'rest-client'
5
+ require 'tty-spinner'
6
+
7
+ module PWN
8
+ module Blockchain
9
+ # This plugin interacts with BitCoin's Blockchain API.
10
+ module ETH
11
+ # Supported Method Parameters::
12
+ # eth_rest_call(
13
+ # http_method: 'optional HTTP method (defaults to GET)
14
+ # rest_call: 'required rest call to make per the schema',
15
+ # params: 'optional params passed in the URI or HTTP Headers',
16
+ # http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST',
17
+ # timeout: 'optional timeout in seconds (defaults to 180)',
18
+ # spinner: 'optional - display spinner (defaults to false)'
19
+ # )
20
+
21
+ private_class_method def self.eth_rest_call(opts = {})
22
+ http_method = if opts[:http_method].nil?
23
+ :get
24
+ else
25
+ opts[:http_method].to_s.scrub.to_sym
26
+ end
27
+
28
+ base_uri = 'https://api.blockcypher.com/v1/eth/'
29
+ rest_call = opts[:rest_call].to_s.scrub
30
+ params = opts[:params]
31
+ headers = { content_type: 'application/json; charset=UTF-8' }
32
+
33
+ http_body = opts[:http_body]
34
+ http_body ||= {}
35
+
36
+ timeout = opts[:timeout]
37
+ timeout ||= 180
38
+
39
+ spinner = opts[:spinner] || false
40
+
41
+ browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)
42
+ rest_client = browser_obj[:browser]::Request
43
+
44
+ if spinner
45
+ spin = TTY::Spinner.new(format: :dots)
46
+ spin.auto_spin
47
+ end
48
+
49
+ retries = 0
50
+ case http_method
51
+ when :delete, :get
52
+ headers[:params] = params
53
+ response = rest_client.execute(
54
+ method: http_method,
55
+ url: "#{base_uri}#{rest_call}",
56
+ headers: headers,
57
+ verify_ssl: false,
58
+ timeout: timeout
59
+ )
60
+
61
+ when :post
62
+ if http_body.key?(:multipart)
63
+ headers[:content_type] = 'multipart/form-data'
64
+
65
+ response = rest_client.execute(
66
+ method: http_method,
67
+ url: "#{base_uri}#{rest_call}",
68
+ headers: headers,
69
+ payload: http_body,
70
+ verify_ssl: false,
71
+ timeout: timeout
72
+ )
73
+ else
74
+ response = rest_client.execute(
75
+ method: http_method,
76
+ url: "#{base_uri}#{rest_call}",
77
+ headers: headers,
78
+ payload: http_body.to_json,
79
+ verify_ssl: false,
80
+ timeout: timeout
81
+ )
82
+ end
83
+
84
+ else
85
+ raise "Unsupported HTTP Method #{http_method} for #{self} Plugin"
86
+ end
87
+
88
+ response
89
+ rescue RestClient::ExceptionWithResponse => e
90
+ case e.http_code
91
+ when 400, 404
92
+ "#{e.http_code} #{e.message}: #{e.response.body}"
93
+ else
94
+ raise e
95
+ end
96
+ rescue StandardError => e
97
+ raise e
98
+ ensure
99
+ spin.stop if spinner
100
+ end
101
+
102
+ # Supported Method Parameters::
103
+ # latest_block = PWN::Blockchain::ETH.get_latest_block(
104
+ # token: 'optional - API token for higher rate limits'
105
+ # )
106
+
107
+ public_class_method def self.get_latest_block(opts = {})
108
+ params = {}
109
+ params[:token] = opts[:token] if opts[:token]
110
+
111
+ rest_call = 'main'
112
+ response = eth_rest_call(rest_call: rest_call, params: params)
113
+
114
+ JSON.parse(response.body, symbolize_names: true)
115
+ rescue StandardError => e
116
+ raise e
117
+ end
118
+
119
+ # Supported Method Parameters::
120
+ # PWN::Blockchain::ETH.get_block_details(
121
+ # height: 'required - block height number',
122
+ # token: 'optional - API token for higher rate limits'
123
+ # )
124
+ public_class_method def self.get_block_details(opts = {})
125
+ height = opts[:height]
126
+ params = {}
127
+ params[:token] = opts[:token] if opts[:token]
128
+
129
+ rest_call = "main/blocks/#{height}"
130
+ response = eth_rest_call(rest_call: rest_call, params: params)
131
+
132
+ JSON.parse(response.body, symbolize_names: true)
133
+ rescue StandardError => e
134
+ raise e
135
+ end
136
+
137
+ # Author(s):: 0day Inc. <support@0dayinc.com>
138
+
139
+ public_class_method def self.authors
140
+ "AUTHOR(S):
141
+ 0day Inc. <support@0dayinc.com>
142
+ "
143
+ end
144
+
145
+ # Display Usage for this Module
146
+
147
+ public_class_method def self.help
148
+ puts "USAGE:
149
+ latest_block = #{self}.get_latest_block
150
+
151
+ block_details = #{self}.get_block_details(
152
+ height: 'required - block height number'
153
+ )
154
+
155
+ #{self}.authors
156
+ "
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PWN
4
+ # This file, using the autoload directive loads SP plugins
5
+ # into memory only when they're needed. For more information, see:
6
+ # http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
7
+ module Blockchain
8
+ autoload :BTC, 'pwn/blockchain/btc'
9
+ autoload :ETH, 'pwn/blockchain/eth'
10
+
11
+ # Display a List of Every PWN::Blockchain Module
12
+
13
+ public_class_method def self.help
14
+ constants.sort
15
+ end
16
+ end
17
+ end
@@ -18,7 +18,11 @@ module PWN
18
18
  mode = opts[:mode]
19
19
 
20
20
  proc do |_target_self, _nest_level, pi|
21
- PWN::Plugins::Vault.refresh_config_for_repl(opts) if Pry.config.refresh_config
21
+ if Pry.config.refresh
22
+ # puts "Refreshing PWN env via #{opts[:yaml_config_path]}"
23
+ opts[:pi] = pi
24
+ PWN::Plugins::Vault.refresh_config_for_repl(opts)
25
+ end
22
26
 
23
27
  pi.config.pwn_repl_line += 1
24
28
  line_pad = format(
@@ -34,8 +38,8 @@ module PWN
34
38
  dchars = "\001\e[33m\002***\001\e[0m\002" if mode == :splat
35
39
 
36
40
  if pi.config.pwn_asm
37
- arch = pi.config.pwn_asm_arch ||= PWN::Plugins::DetectOS.arch
38
- endian = pi.config.pwn_asm_endian ||= PWN::Plugins::DetectOS.endian
41
+ arch = pi.config.pwn[:asm][:arch] ||= PWN::Plugins::DetectOS.arch
42
+ endian = pi.config.pwn[:asm][:endian] ||= PWN::Plugins::DetectOS.endian
39
43
 
40
44
  pi.config.prompt_name = "pwn.asm:#{arch}/#{endian}"
41
45
  name = "\001\e[1m\002\001\e[37m\002#{pi.config.prompt_name}\001\e[0m\002"
@@ -44,10 +48,10 @@ module PWN
44
48
  end
45
49
 
46
50
  if pi.config.pwn_ai
47
- ai_engine = pi.config.pwn_ai_engine
48
- model = pi.config.pwn_ai_model
49
- system_role_content = pi.config.pwn_ai_system_role_content
50
- temp = pi.config.pwn_ai_temp
51
+ ai_engine = pi.config.pwn[:ai_engine]
52
+ model = pi.config.pwn[ai_engine][:model]
53
+ system_role_content = pi.config.pwn[ai_engine][:system_role_content]
54
+ temp = pi.config.pwn[ai_engine][:temp]
51
55
  pname = "pwn.ai:#{ai_engine}"
52
56
  pname = "pwn.ai:#{ai_engine}/#{model}" if model
53
57
  pname = "pwn.ai:#{ai_engine}/#{model}.SPEAK" if pi.config.pwn_ai_speak
@@ -173,10 +177,10 @@ module PWN
173
177
 
174
178
  reply = nil
175
179
  response_history = nil
176
- shared_chan = pi.config.pwn_irc[:shared_chan]
180
+ shared_chan = pi.config.pwn[:irc][:shared_chan]
177
181
  mem_chan = '#mem'
178
- ai_agents = pi.config.pwn_irc[:ai_agent_nicks]
179
- ai_agents_arr = pi.config.pwn_irc[:ai_agent_nicks].keys
182
+ ai_agents = pi.config.pwn[:irc][:ai_agent_nicks]
183
+ ai_agents_arr = pi.config.pwn[:irc][:ai_agent_nicks].keys
180
184
  total_ai_agents = ai_agents_arr.length
181
185
  mutex = Mutex.new
182
186
  PWN::Plugins::ThreadPool.fill(
@@ -302,15 +306,12 @@ module PWN
302
306
  next unless dm_agent == nick
303
307
 
304
308
  response_history = ai_agents[dm_agent.to_sym][:response_history]
305
- ai_engine = pi.config.pwn_ai_engine
306
- ai_base_uri = pi.config.pwn_ai_base_uri
307
- ai_key = pi.config.pwn_ai_key
308
- ai_key ||= ''
309
- ai_temp = pi.config.pwn_ai_temp
310
-
311
- model = pi.config.pwn_ai_model
312
- system_role_content = pi.config.pwn_ai_system_role_content
313
- temp = pi.config.pwn_ai_temp
309
+ ai_engine = pi.config.pwn[:ai_engine]
310
+ base_uri = pi.config.pwn[ai_engine][:base_uri]
311
+ key = pi.config.pwn[ai_engine][:key] ||= ''
312
+ temp = pi.config.pwn[ai_engine][:temp]
313
+ model = pi.config.pwn[ai_engine][:model]
314
+ system_role_content = pi.config.pwn[ai_engine][:system_role_content]
314
315
 
315
316
  users_in_chan = PWN::Plugins::IRC.names(
316
317
  irc_obj: irc_obj,
@@ -344,10 +345,10 @@ module PWN
344
345
  case ai_engine
345
346
  when :grok
346
347
  response = PWN::AI::Grok.chat(
347
- base_uri: ai_base_uri,
348
- token: ai_key,
348
+ base_uri: base_uri,
349
+ token: key,
349
350
  model: model,
350
- temp: ai_temp,
351
+ temp: temp,
351
352
  system_role_content: system_role_content,
352
353
  request: request,
353
354
  response_history: response_history,
@@ -355,10 +356,10 @@ module PWN
355
356
  )
356
357
  when :ollama
357
358
  response = PWN::AI::Ollama.chat(
358
- base_uri: ai_base_uri,
359
- token: ai_key,
359
+ base_uri: base_uri,
360
+ token: key,
360
361
  model: model,
361
- temp: ai_temp,
362
+ temp: temp,
362
363
  system_role_content: system_role_content,
363
364
  request: request,
364
365
  response_history: response_history,
@@ -366,10 +367,10 @@ module PWN
366
367
  )
367
368
  when :openai
368
369
  response = PWN::AI::OpenAI.chat(
369
- base_uri: ai_base_uri,
370
- token: ai_key,
370
+ base_uri: base_uri,
371
+ token: key,
371
372
  model: model,
372
- temp: ai_temp,
373
+ temp: temp,
373
374
  system_role_content: system_role_content,
374
375
  request: request,
375
376
  response_history: response_history,
@@ -434,7 +435,7 @@ module PWN
434
435
 
435
436
  # TODO: Use TLS for IRC Connections
436
437
  # Use an IRC nCurses CLI Client
437
- ui_nick = pi.config.pwn_irc[:ui_nick]
438
+ ui_nick = pi.config.pwn[:irc][:ui_nick]
438
439
  join_channels = ai_agents_arr.map { |ai_chan| "##{ai_chan}" }.join(',')
439
440
 
440
441
  cmd0 = "/server add pwn #{host}/#{port} -notls"
@@ -469,12 +470,13 @@ module PWN
469
470
  return
470
471
  end
471
472
 
472
- decryption_file = pi.config.decryption_file ||= "#{Dir.home}/.pwn/pwn.decryptor.yaml"
473
- unless File.exist?(decryption_file)
474
- puts "ERROR: pwn.decryptor.yaml not found: #{decryption_file}"
473
+ yaml_decryptor_path = pi.config.yaml_decryptor_path ||= "#{Dir.home}/.pwn/pwn.decryptor.yaml"
474
+ unless File.exist?(yaml_decryptor_path)
475
+ puts "ERROR: pwn.decryptor.yaml not found: #{yaml_decryptor_path}"
475
476
  return
476
477
  end
477
- decryptor = YAML.load_file(decryption_file, symbolize_names: true)
478
+
479
+ decryptor = YAML.load_file(yaml_decryptor_path, symbolize_names: true)
478
480
  key = decryptor[:key]
479
481
  iv = decryptor[:iv]
480
482
 
@@ -537,10 +539,8 @@ module PWN
537
539
 
538
540
  # Initialize pwn.yaml Configuration using :before_session Hook
539
541
  Pry.config.hooks.add_hook(:before_session, :init_opts) do |_output, _binding, pi|
542
+ # puts "Refreshing PWN env via #{opts[:yaml_config_path]}"
540
543
  opts[:pi] = pi
541
- Pry.config.yaml_config_path = opts[:yaml_config_path]
542
- Pry.config.decryption_file = opts[:decryption_file]
543
-
544
544
  PWN::Plugins::Vault.refresh_config_for_repl(opts)
545
545
  end
546
546
 
@@ -548,8 +548,8 @@ module PWN
548
548
  if pi.config.pwn_asm && !request.chomp.empty?
549
549
  request = pi.input.line_buffer
550
550
 
551
- arch = pi.config.pwn_asm_arch
552
- endian = pi.config.pwn_asm_endian
551
+ arch = pi.config.pwn[:asm][:arch]
552
+ endian = pi.config.pwn[:asm][:endian]
553
553
 
554
554
  # Analyze request to determine if it should be processed as opcodes or asm.
555
555
  straight_hex = /^[a-fA-F0-9\s]+$/
@@ -585,29 +585,27 @@ module PWN
585
585
  if pi.config.pwn_ai && !request.chomp.empty?
586
586
  request = pi.input.line_buffer.to_s
587
587
  debug = pi.config.pwn_ai_debug
588
- ai_engine = pi.config.pwn_ai_engine.to_s.to_sym
589
- ai_key = pi.config.pwn_ai_key
590
- ai_key ||= ''
591
- if ai_key.empty?
592
- ai_key = PWN::Plugins::AuthenticationHelper.mask_password(
588
+ ai_engine = pi.config.pwn[:ai_engine]
589
+ base_uri = pi.config.pwn[ai_engine][:base_uri]
590
+ key = pi.config.pwn[ai_engine][:key] ||= ''
591
+ if key.empty?
592
+ key = PWN::Plugins::AuthenticationHelper.mask_password(
593
593
  prompt: 'pwn-ai Key'
594
594
  )
595
- pi.config.pwn_ai_key = ai_key
595
+ pi.config.pwn[ai_engine][:key] = key
596
596
  end
597
597
 
598
598
  response_history = pi.config.pwn_ai_response_history
599
599
  speak_answer = pi.config.pwn_ai_speak
600
- model = pi.config.pwn_ai_model
601
- system_role_content = pi.config.pwn_ai_system_role_content
602
- temp = pi.config.pwn_ai_temp
603
-
604
- ai_base_uri = pi.config.pwn_ai_base_uri
600
+ model = pi.config.pwn[ai_engine][:model]
601
+ system_role_content = pi.config.pwn[ai_engine][:system_role_content]
602
+ temp = pi.config.pwn[ai_engine][:temp]
605
603
 
606
604
  case ai_engine
607
605
  when :grok
608
606
  response = PWN::AI::Grok.chat(
609
- base_uri: ai_base_uri,
610
- token: ai_key,
607
+ base_uri: base_uri,
608
+ token: key,
611
609
  model: model,
612
610
  system_role_content: system_role_content,
613
611
  temp: temp,
@@ -618,8 +616,8 @@ module PWN
618
616
  )
619
617
  when :ollama
620
618
  response = PWN::AI::Ollama.chat(
621
- base_uri: ai_base_uri,
622
- token: ai_key,
619
+ base_uri: base_uri,
620
+ token: key,
623
621
  model: model,
624
622
  system_role_content: system_role_content,
625
623
  temp: temp,
@@ -630,8 +628,8 @@ module PWN
630
628
  )
631
629
  when :openai
632
630
  response = PWN::AI::OpenAI.chat(
633
- base_uri: ai_base_uri,
634
- token: ai_key,
631
+ base_uri: base_uri,
632
+ token: key,
635
633
  model: model,
636
634
  system_role_content: system_role_content,
637
635
  temp: temp,
@@ -690,7 +688,7 @@ module PWN
690
688
  pwn_config_root = "#{Dir.home}/.pwn"
691
689
  FileUtils.mkdir_p(pwn_config_root)
692
690
  opts[:yaml_config_path] ||= "#{pwn_config_root}/pwn.yaml"
693
- opts[:decryption_file] ||= "#{pwn_config_root}/pwn.decryptor.yaml"
691
+ opts[:yaml_decryptor_path] ||= "#{pwn_config_root}/pwn.decryptor.yaml"
694
692
 
695
693
  add_hooks(opts)
696
694
 
@@ -173,7 +173,7 @@ module PWN
173
173
  system(relative_editor, file)
174
174
 
175
175
  # If the Pry object exists, set refresh_config to true
176
- Pry.config.refresh_config = true if defined?(Pry)
176
+ Pry.config.refresh = true if defined?(Pry)
177
177
 
178
178
  encrypt(
179
179
  file: file,
@@ -238,23 +238,24 @@ module PWN
238
238
  # PWN::Plugins::Vault.refresh_config_for_repl(
239
239
  # yaml_config_path: 'required - full path to pwn.yaml file',
240
240
  # pi: 'optional - Pry instance (default: Pry)',
241
- # decryption_file: 'optional - full path to decryption YAML file'
241
+ # yaml_decryptor_path: 'optional - full path to decryption YAML file'
242
242
  # )
243
243
  public_class_method def self.refresh_config_for_repl(opts = {})
244
244
  yaml_config_path = opts[:yaml_config_path]
245
245
 
246
246
  return false unless File.exist?(yaml_config_path)
247
247
 
248
- pi = opts[:pi] ||= Pry
248
+ pi = opts[:pi]
249
+ raise 'ERROR: Pry instance is required.' if pi.nil?
249
250
 
250
251
  is_encrypted = PWN::Plugins::Vault.file_encrypted?(file: yaml_config_path)
251
252
 
252
253
  if is_encrypted
253
254
  # TODO: Implement "something you know, something you have, && something you are?"
254
- decryption_file = opts[:decryption_file] ||= "#{Dir.home}/pwn.decryptor.yaml"
255
- raise "ERROR: #{decryption_file} does not exist." unless File.exist?(decryption_file)
255
+ yaml_decryptor_path = opts[:yaml_decryptor_path] ||= "#{Dir.home}/.pwn/pwn.decryptor.yaml"
256
+ raise "ERROR: #{yaml_decryptor_path} does not exist." unless File.exist?(yaml_decryptor_path)
256
257
 
257
- yaml_decryptor = YAML.load_file(decryption_file, symbolize_names: true)
258
+ yaml_decryptor = YAML.load_file(yaml_decryptor_path, symbolize_names: true)
258
259
 
259
260
  key = opts[:key] ||= yaml_decryptor[:key] ||= ENV.fetch('PWN_DECRYPTOR_KEY')
260
261
  key = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption Key') if key.nil?
@@ -270,52 +271,41 @@ module PWN
270
271
  else
271
272
  yaml_config = YAML.load_file(yaml_config_path, symbolize_names: true)
272
273
  end
273
- pi.config.p = yaml_config
274
- Pry.config.p = yaml_config
275
274
 
276
275
  valid_ai_engines = %i[
277
276
  grok
278
277
  openai
279
278
  ollama
280
279
  ]
281
- ai_engine = yaml_config[:ai_engine].to_s.downcase.to_sym
282
280
 
281
+ # Convert ai_engine to symbol and downcase to ensure stability
282
+ yaml_config[:ai_engine] = yaml_config[:ai_engine].to_s.downcase.to_sym
283
+ pi.config.pwn = yaml_config
284
+ ai_engine = pi.config.pwn[:ai_engine]
283
285
  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)
284
286
 
285
- pi.config.pwn_ai_engine = ai_engine
286
- Pry.config.pwn_ai_engine = ai_engine
287
-
288
- pi.config.pwn_ai_base_uri = pi.config.p[ai_engine][:base_uri]
289
- Pry.config.pwn_ai_base_uri = pi.config.pwn_ai_base_uri
290
-
291
- pi.config.pwn_ai_key = pi.config.p[ai_engine][:key]
292
- Pry.config.pwn_ai_key = pi.config.pwn_ai_key
293
-
294
- pi.config.pwn_ai_model = pi.config.p[ai_engine][:model]
295
- Pry.config.pwn_ai_model = pi.config.pwn_ai_model
296
-
297
- pi.config.pwn_ai_system_role_content = pi.config.p[ai_engine][:system_role_content]
298
- Pry.config.pwn_ai_system_role_content = pi.config.pwn_ai_system_role_content
299
-
300
- pi.config.pwn_ai_temp = pi.config.p[ai_engine][:temp]
301
- Pry.config.pwn_ai_temp = pi.config.pwn_ai_temp
302
-
303
- pi.config.pwn_asm_arch = pi.config.p[:asm][:arch]
304
- Pry.config.pwn_asm_arch = pi.config.pwn_asm_arch
305
-
306
- pi.config.pwn_asm_endian = pi.config.p[:asm][:endian]
307
- Pry.config.pwn_asm_endian = pi.config.pwn_asm_endian
308
-
309
- pi.config.pwn_irc = pi.config.p[:irc]
310
- Pry.config.pwn_irc = pi.config.pwn_irc
311
-
312
- pi.config.pwn_hunter = pi.config.p[:hunter][:api_key]
313
- Pry.config.pwn_hunter = pi.config.pwn_hunter
314
-
315
- pi.config.pwn_shodan = pi.config.p[:shodan][:api_key]
316
- Pry.config.pwn_shodan = pi.config.pwn_shodan
317
-
318
- Pry.config.refresh_config = false
287
+ model = pi.config.pwn[ai_engine][:model]
288
+ system_role_content = pi.config.pwn[ai_engine][:system_role_content]
289
+
290
+ # Reset the ai response history for new configurations
291
+ pi.config.pwn_ai_response_history = {
292
+ id: '',
293
+ object: '',
294
+ model: model,
295
+ usage: {},
296
+ choices: [
297
+ {
298
+ role: 'system',
299
+ content: system_role_content
300
+ }
301
+ ]
302
+ }
303
+
304
+ # These two lines should be immutable for the session
305
+ pi.config.pwn[:yaml_config_path] = yaml_config_path
306
+ pi.config.pwn[:yaml_decryptor_path] = yaml_decryptor_path if is_encrypted
307
+
308
+ Pry.config.refresh = false
319
309
 
320
310
  true
321
311
  rescue StandardError => e
@@ -377,7 +367,7 @@ module PWN
377
367
  #{self}.refresh_config_for_repl(
378
368
  yaml_config_path: 'required - full path to pwn.yaml file',
379
369
  pi: 'optional - Pry instance (default: Pry)',
380
- decryption_file: 'optional - full path to decryption YAML file'
370
+ yaml_decryptor_path: 'optional - full path to decryption YAML file'
381
371
  )
382
372
 
383
373
  #{self}.authors
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.425'
4
+ VERSION = '0.5.427'
5
5
  end
data/lib/pwn.rb CHANGED
@@ -11,6 +11,7 @@ module PWN
11
11
  autoload :AI, 'pwn/ai'
12
12
  autoload :AWS, 'pwn/aws'
13
13
  autoload :Banner, 'pwn/banner'
14
+ autoload :Blockchain, 'pwn/blockchain'
14
15
  autoload :FFI, 'pwn/ffi'
15
16
  autoload :Plugins, 'pwn/plugins'
16
17
  autoload :Reports, 'pwn/reports'
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::Blockchain::BTC do
6
+ it 'should display information for authors' do
7
+ authors_response = PWN::Blockchain::BTC
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::Blockchain::BTC
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::Blockchain::ETH do
6
+ it 'should display information for authors' do
7
+ authors_response = PWN::Blockchain::ETH
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::Blockchain::ETH
13
+ expect(help_response).to respond_to :help
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe PWN::Blockchain do
6
+ it 'should return data for help method' do
7
+ help_response = PWN::Blockchain.help
8
+ expect(help_response).not_to be_nil
9
+ end
10
+ 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.425
4
+ version: 0.5.427
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -1051,14 +1051,14 @@ dependencies:
1051
1051
  requirements:
1052
1052
  - - '='
1053
1053
  - !ruby/object:Gem::Version
1054
- version: 0.139.0
1054
+ version: 0.140.0
1055
1055
  type: :runtime
1056
1056
  prerelease: false
1057
1057
  version_requirements: !ruby/object:Gem::Requirement
1058
1058
  requirements:
1059
1059
  - - '='
1060
1060
  - !ruby/object:Gem::Version
1061
- version: 0.139.0
1061
+ version: 0.140.0
1062
1062
  - !ruby/object:Gem::Dependency
1063
1063
  name: slack-ruby-client
1064
1064
  requirement: !ruby/object:Gem::Requirement
@@ -1829,6 +1829,9 @@ files:
1829
1829
  - lib/pwn/banner/radare2.rb
1830
1830
  - lib/pwn/banner/radare2_ai.rb
1831
1831
  - lib/pwn/banner/white_rabbit.rb
1832
+ - lib/pwn/blockchain.rb
1833
+ - lib/pwn/blockchain/btc.rb
1834
+ - lib/pwn/blockchain/eth.rb
1832
1835
  - lib/pwn/ffi.rb
1833
1836
  - lib/pwn/ffi/stdio.rb
1834
1837
  - lib/pwn/plugins.rb
@@ -2174,6 +2177,9 @@ files:
2174
2177
  - spec/lib/pwn/banner/radare2_spec.rb
2175
2178
  - spec/lib/pwn/banner/white_rabbit_spec.rb
2176
2179
  - spec/lib/pwn/banner_spec.rb
2180
+ - spec/lib/pwn/blockchain/btc_spec.rb
2181
+ - spec/lib/pwn/blockchain/eth_spec.rb
2182
+ - spec/lib/pwn/blockchain_spec.rb
2177
2183
  - spec/lib/pwn/ffi/stdio_spec.rb
2178
2184
  - spec/lib/pwn/ffi_spec.rb
2179
2185
  - spec/lib/pwn/plugins/android_spec.rb