pwn 0.5.429 → 0.5.430
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +3 -3
 - data/bin/pwn +4 -4
 - data/bin/pwn_sast +20 -48
 - data/etc/pwn.yaml.EXAMPLE +3 -0
 - data/lib/pwn/ai/grok.rb +7 -0
 - data/lib/pwn/ai/ollama.rb +7 -0
 - data/lib/pwn/ai/open_ai.rb +7 -0
 - data/lib/pwn/config.rb +81 -0
 - data/lib/pwn/plugins/repl.rb +11 -30
 - data/lib/pwn/plugins/vault.rb +1 -84
 - data/lib/pwn/reports/sast.rb +45 -55
 - data/lib/pwn/version.rb +1 -1
 - data/lib/pwn.rb +5 -2
 - data/spec/lib/pwn/config_spec.rb +10 -0
 - metadata +3 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: eecc95f7ab850f845600203b3fed19bb77d5d3737233a7f7f98df09b84154737
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: d838e72b716e9fc50e604a95600f1c9e43db9a42772e76073adc2f4cd673e96b
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 81dea57ae53b6401b7b5fa2784d5dc64e8b2a311ad1fc89c20c18e1024739ed66502db92be54ece95695df3b7a3fb25bbacd3aa311ac69d71bf5c2f4b23496c6
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 071b3ac7ac65e4f282e3f59d223ec4c68c2f9b61b62442e613b4aa347ed6ab836cca10eafee0a148b79446c2adaf3e1a5bf60107b4871165d04d3017ef7b2fb3
         
     | 
    
        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. 
     | 
| 
      
 40 
     | 
    
         
            +
            pwn[v0.5.430]:001 >>> PWN.help
         
     | 
| 
       41 
41 
     | 
    
         
             
            ```
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
            [](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. 
     | 
| 
      
 55 
     | 
    
         
            +
            pwn[v0.5.430]: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. 
     | 
| 
      
 65 
     | 
    
         
            +
            pwn[v0.5.430]: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
    
    | 
         @@ -10,12 +10,12 @@ OptionParser.new do |options| 
     | 
|
| 
       10 
10 
     | 
    
         
             
                #{File.basename($PROGRAM_NAME)} [opts]
         
     | 
| 
       11 
11 
     | 
    
         
             
              "
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
              options.on('- 
     | 
| 
       14 
     | 
    
         
            -
                opts[: 
     | 
| 
      
 13 
     | 
    
         
            +
              options.on('-YPATH', '--pwn-config=PATH', '<Optional - PWN YAML File>') do |p|
         
     | 
| 
      
 14 
     | 
    
         
            +
                opts[:pwn_config_path] = p
         
     | 
| 
       15 
15 
     | 
    
         
             
              end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
              options.on('- 
     | 
| 
       18 
     | 
    
         
            -
                opts[: 
     | 
| 
      
 17 
     | 
    
         
            +
              options.on('-ZPATH', '--pwn-decryptor=PATH', '<Optional - Out-of-Band YAML File with :key && :iv>') do |d|
         
     | 
| 
      
 18 
     | 
    
         
            +
                opts[:pwn_decryptor_path] = d
         
     | 
| 
       19 
19 
     | 
    
         
             
              end
         
     | 
| 
       20 
20 
     | 
    
         
             
            end.parse!
         
     | 
| 
       21 
21 
     | 
    
         | 
    
        data/bin/pwn_sast
    CHANGED
    
    | 
         @@ -11,6 +11,15 @@ OptionParser.new do |options| 
     | 
|
| 
       11 
11 
     | 
    
         
             
                #{File.basename($PROGRAM_NAME)} [opts]
         
     | 
| 
       12 
12 
     | 
    
         
             
              "
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
              # TODO: HOW TO LOAD THE pwn.yaml CONFIGURATION FILE FOR EVERYTHING UNLESS OVERRIDDEN???
         
     | 
| 
      
 15 
     | 
    
         
            +
              options.on('-YPATH', '--pwn-config=PATH', '<Optional - PWN YAML File>') do |p|
         
     | 
| 
      
 16 
     | 
    
         
            +
                opts[:pwn_config_path] = p
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              options.on('-ZPATH', '--pwn-decryptor=PATH', '<Optional - Out-of-Band YAML File with :key && :iv>') do |d|
         
     | 
| 
      
 20 
     | 
    
         
            +
                opts[:pwn_decryptor_path] = d
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       14 
23 
     | 
    
         
             
              options.on('-uGITURI', '--uri-source-root=GITURI', '<Required - HTTP URI of Git Repo Scanned e.g. https://github.com/0dayInc/pwn/tree/master>') do |u|
         
     | 
| 
       15 
24 
     | 
    
         
             
                opts[:uri_source_root] = u
         
     | 
| 
       16 
25 
     | 
    
         
             
              end
         
     | 
| 
         @@ -35,30 +44,6 @@ OptionParser.new do |options| 
     | 
|
| 
       35 
44 
     | 
    
         
             
                opts[:report_name] = n
         
     | 
| 
       36 
45 
     | 
    
         
             
              end
         
     | 
| 
       37 
46 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
              options.on('-aENGINE', '--ai-engine=ENGINE', '<Optional AI Engine to Analyze Results (grok, ollama, openai) [WARNING: CAN DRAMATICALLY INCREASE AI USAGE COSTS AND/OR SCAN DURATIONS]>') do |a|
         
     | 
| 
       39 
     | 
    
         
            -
                opts[:ai_engine] = a
         
     | 
| 
       40 
     | 
    
         
            -
              end
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
              options.on('-bURI', '--ai-base-uri=URI', '<Optional AI Base API URI (Only Required for "ollama" AI Engine.  Supported by other LLMs when hosted privately)>') do |b|
         
     | 
| 
       43 
     | 
    
         
            -
                opts[:ai_base_uri] = b
         
     | 
| 
       44 
     | 
    
         
            -
              end
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
              options.on('-mMODEL', '--ai-model=MODEL', '<Optional AI Model to Use for Respective AI Engine (e.g., grok-4-0709, grok-3-mini-fast, gpt5-chat-latest, chargpt-4o-latest, llama-3.1, etc.)>') do |m|
         
     | 
| 
       47 
     | 
    
         
            -
                opts[:ai_model] = m
         
     | 
| 
       48 
     | 
    
         
            -
              end
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
              options.on('-kTOKEN', '--ai-key=TOKEN', '<Optional AI Key/Token for Respective AI Engine>') do |k|
         
     | 
| 
       51 
     | 
    
         
            -
                opts[:ai_key] = k
         
     | 
| 
       52 
     | 
    
         
            -
              end
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
              options.on('-SCONTENT', '--ai-system-content=CONTENT', '<Optional AI System Role Content for Respective AI Engine (Defaults to, "Confidence score of 0-10 this is vulnerable (0 being not vulnerable, moving upwards in confidence of exploitation). Provide additional context to assist penetration tester assessment.")>') do |s|
         
     | 
| 
       55 
     | 
    
         
            -
                opts[:ai_system_role_content] = s
         
     | 
| 
       56 
     | 
    
         
            -
              end
         
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
              options.on('-FTEMP', '--ai-temp=TEMP', '<Optional AI Temperature for Respective AI Engine (Default 0.9)>') do |t|
         
     | 
| 
       59 
     | 
    
         
            -
                opts[:ai_temp] = t
         
     | 
| 
       60 
     | 
    
         
            -
              end
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
47 
     | 
    
         
             
              options.on('-s', '--[no-]start-reporting-server', '<Optional - Start Simple HTTP Server for Reporting>') do |s|
         
     | 
| 
       63 
48 
     | 
    
         
             
                opts[:start_reporting_server] = s
         
     | 
| 
       64 
49 
     | 
    
         
             
              end
         
     | 
| 
         @@ -71,6 +56,16 @@ end 
     | 
|
| 
       71 
56 
     | 
    
         | 
| 
       72 
57 
     | 
    
         
             
            begin
         
     | 
| 
       73 
58 
     | 
    
         
             
              timestamp = Time.now.strftime('%Y-%m-%d_%H:%M:%S%z')
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              pwn_config_path = opts[:pwn_config_path]
         
     | 
| 
      
 61 
     | 
    
         
            +
              pwn_decryptor_path = opts[:pwn_decryptor_path]
         
     | 
| 
      
 62 
     | 
    
         
            +
              if pwn_config_path
         
     | 
| 
      
 63 
     | 
    
         
            +
                PWN::Config.refresh(
         
     | 
| 
      
 64 
     | 
    
         
            +
                  pwn_config_path: pwn_config_path,
         
     | 
| 
      
 65 
     | 
    
         
            +
                  pwn_decryptor_path: pwn_decryptor_path
         
     | 
| 
      
 66 
     | 
    
         
            +
                )
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
       74 
69 
     | 
    
         
             
              previous_dir = Dir.pwd
         
     | 
| 
       75 
70 
     | 
    
         | 
| 
       76 
71 
     | 
    
         
             
              pwn_provider = 'ruby-gem'
         
     | 
| 
         @@ -95,23 +90,6 @@ begin 
     | 
|
| 
       95 
90 
     | 
    
         
             
              report_name ||= "#{File.basename(Dir.pwd)}-#{timestamp}" if dir_path == '.'
         
     | 
| 
       96 
91 
     | 
    
         
             
              report_name ||= "#{File.basename(dir_path)}-#{timestamp}" unless dir_path == '.'
         
     | 
| 
       97 
92 
     | 
    
         | 
| 
       98 
     | 
    
         
            -
              ai_engine = opts[:ai_engine]
         
     | 
| 
       99 
     | 
    
         
            -
              if ai_engine
         
     | 
| 
       100 
     | 
    
         
            -
                ai_engine = ai_engine.to_s.to_sym
         
     | 
| 
       101 
     | 
    
         
            -
                valid_ai_engines = %i[grok ollama openai]
         
     | 
| 
       102 
     | 
    
         
            -
                raise "ERROR: Invalid AI Engine. Valid options are: #{valid_ai_engines.join(', ')}" unless valid_ai_engines.include?(ai_engine)
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                ai_base_uri = opts[:ai_base_uri]
         
     | 
| 
       105 
     | 
    
         
            -
                raise 'ERROR: --base-uri Parameter for Ollama AI engine is required.' if ai_engine == :ollama && ai_base_uri.nil?
         
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
                ai_model = opts[:ai_model]
         
     | 
| 
       108 
     | 
    
         
            -
                raise 'ERROR: AI Model is required for AI engine ollama.' if ai_engine == :ollama && ai_model.nil?
         
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
                ai_key = opts[:ai_key] ||= PWN::Plugins::AuthenticationHelper.mask_password(prompt: "#{ai_engine} Token")
         
     | 
| 
       111 
     | 
    
         
            -
                ai_system_role_content = opts[:ai_system_role_content]
         
     | 
| 
       112 
     | 
    
         
            -
                ai_temp = opts[:ai_temp]
         
     | 
| 
       113 
     | 
    
         
            -
              end
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
93 
     | 
    
         
             
              start_reporting_server = opts[:start_reporting_server]
         
     | 
| 
       116 
94 
     | 
    
         | 
| 
       117 
95 
     | 
    
         
             
              # Define Test Cases to Run & Start Thread Pool
         
     | 
| 
         @@ -202,13 +180,7 @@ begin 
     | 
|
| 
       202 
180 
     | 
    
         
             
              PWN::Reports::SAST.generate(
         
     | 
| 
       203 
181 
     | 
    
         
             
                dir_path: dir_path,
         
     | 
| 
       204 
182 
     | 
    
         
             
                results_hash: results_hash,
         
     | 
| 
       205 
     | 
    
         
            -
                report_name: report_name 
     | 
| 
       206 
     | 
    
         
            -
                ai_engine: ai_engine,
         
     | 
| 
       207 
     | 
    
         
            -
                ai_model: ai_model,
         
     | 
| 
       208 
     | 
    
         
            -
                ai_key: ai_key,
         
     | 
| 
       209 
     | 
    
         
            -
                ai_base_uri: ai_base_uri,
         
     | 
| 
       210 
     | 
    
         
            -
                ai_system_role_content: ai_system_role_content,
         
     | 
| 
       211 
     | 
    
         
            -
                ai_temp: ai_temp
         
     | 
| 
      
 183 
     | 
    
         
            +
                report_name: report_name
         
     | 
| 
       212 
184 
     | 
    
         
             
              )
         
     | 
| 
       213 
185 
     | 
    
         
             
              puts 'complete.'
         
     | 
| 
       214 
186 
     | 
    
         | 
    
        data/etc/pwn.yaml.EXAMPLE
    CHANGED
    
    | 
         @@ -2,6 +2,9 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            # Use PWN::Plugins::Vault.create(file: 'pwn.yaml') to encrypt this file
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            # ai_engine: 'openai' || 'ollama'
         
     | 
| 
      
 5 
     | 
    
         
            +
            # WARNING::
         
     | 
| 
      
 6 
     | 
    
         
            +
            # If PWN::CONFIG[:ai][:instrospection] = true, the active ai agent will be used
         
     | 
| 
      
 7 
     | 
    
         
            +
            # wherever possible throughout pwn. Proceeds with caution, as this may incur additional costs
         
     | 
| 
       5 
8 
     | 
    
         
             
            ai:
         
     | 
| 
       6 
9 
     | 
    
         
             
              active: 'grok'
         
     | 
| 
       7 
10 
     | 
    
         
             
              introspection: false
         
     | 
    
        data/lib/pwn/ai/grok.rb
    CHANGED
    
    | 
         @@ -25,6 +25,13 @@ module PWN 
     | 
|
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                  private_class_method def self.grok_rest_call(opts = {})
         
     | 
| 
       27 
27 
     | 
    
         
             
                    token = opts[:token]
         
     | 
| 
      
 28 
     | 
    
         
            +
                    if token.nil?
         
     | 
| 
      
 29 
     | 
    
         
            +
                      PWN::Plugins::AuthenticationHelper.mask_password(
         
     | 
| 
      
 30 
     | 
    
         
            +
                        prompt: 'Grok Key'
         
     | 
| 
      
 31 
     | 
    
         
            +
                      )
         
     | 
| 
      
 32 
     | 
    
         
            +
                      PWN::CONFIG[:ai][:grok][:key] = token
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       28 
35 
     | 
    
         
             
                    http_method = if opts[:http_method].nil?
         
     | 
| 
       29 
36 
     | 
    
         
             
                                    :get
         
     | 
| 
       30 
37 
     | 
    
         
             
                                  else
         
     | 
    
        data/lib/pwn/ai/ollama.rb
    CHANGED
    
    | 
         @@ -27,6 +27,13 @@ module PWN 
     | 
|
| 
       27 
27 
     | 
    
         
             
                  private_class_method def self.ollama_rest_call(opts = {})
         
     | 
| 
       28 
28 
     | 
    
         
             
                    base_uri = opts[:base_uri]
         
     | 
| 
       29 
29 
     | 
    
         
             
                    token = opts[:token]
         
     | 
| 
      
 30 
     | 
    
         
            +
                    if token.nil?
         
     | 
| 
      
 31 
     | 
    
         
            +
                      PWN::Plugins::AuthenticationHelper.mask_password(
         
     | 
| 
      
 32 
     | 
    
         
            +
                        prompt: 'Ollama Key'
         
     | 
| 
      
 33 
     | 
    
         
            +
                      )
         
     | 
| 
      
 34 
     | 
    
         
            +
                      PWN::CONFIG[:ai][:ollama][:key] = token
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
       30 
37 
     | 
    
         
             
                    http_method = if opts[:http_method].nil?
         
     | 
| 
       31 
38 
     | 
    
         
             
                                    :get
         
     | 
| 
       32 
39 
     | 
    
         
             
                                  else
         
     | 
    
        data/lib/pwn/ai/open_ai.rb
    CHANGED
    
    | 
         @@ -26,6 +26,13 @@ module PWN 
     | 
|
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                  private_class_method def self.open_ai_rest_call(opts = {})
         
     | 
| 
       28 
28 
     | 
    
         
             
                    token = opts[:token]
         
     | 
| 
      
 29 
     | 
    
         
            +
                    if token.nil?
         
     | 
| 
      
 30 
     | 
    
         
            +
                      PWN::Plugins::AuthenticationHelper.mask_password(
         
     | 
| 
      
 31 
     | 
    
         
            +
                        prompt: 'OpenAI Key'
         
     | 
| 
      
 32 
     | 
    
         
            +
                      )
         
     | 
| 
      
 33 
     | 
    
         
            +
                      PWN::CONFIG[:ai][:openai][:key] = token
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
       29 
36 
     | 
    
         
             
                    http_method = if opts[:http_method].nil?
         
     | 
| 
       30 
37 
     | 
    
         
             
                                    :get
         
     | 
| 
       31 
38 
     | 
    
         
             
                                  else
         
     | 
    
        data/lib/pwn/config.rb
    ADDED
    
    | 
         @@ -0,0 +1,81 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module PWN
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Used to manage PWN configuration settings within PWN drivers.
         
     | 
| 
      
 8 
     | 
    
         
            +
              module Config
         
     | 
| 
      
 9 
     | 
    
         
            +
                # Supported Method Parameters::
         
     | 
| 
      
 10 
     | 
    
         
            +
                # PWN::Config.refresh(
         
     | 
| 
      
 11 
     | 
    
         
            +
                #   pwn_config_path: 'optional - Path to pwn.yaml file.  Defaults to ~/.pwn/pwn.yaml',
         
     | 
| 
      
 12 
     | 
    
         
            +
                #   pwn_decryptor_path: 'optional - Path to pwn.decryptor.yaml file.  Defaults to ~/.pwn/pwn.decryptor.yaml'
         
     | 
| 
      
 13 
     | 
    
         
            +
                # )
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                public_class_method def self.refresh(opts = {})
         
     | 
| 
      
 16 
     | 
    
         
            +
                  pwn_config_root = "#{Dir.home}/.pwn"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  FileUtils.mkdir_p(pwn_config_root)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  pwn_config_path = opts[:pwn_config_path] ||= "#{pwn_config_root}/pwn.yaml"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  raise "PWN Config (#{pwn_config_path}) does not exist!" unless File.exist?(pwn_config_path)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  is_encrypted = PWN::Plugins::Vault.file_encrypted?(file: pwn_config_path)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  if is_encrypted
         
     | 
| 
      
 25 
     | 
    
         
            +
                    pwn_decryptor_path = opts[:pwn_decryptor_path] ||= "#{pwn_config_root}/pwn.decryptor.yaml"
         
     | 
| 
      
 26 
     | 
    
         
            +
                    raise "PWN Decryptor (#{pwn_decryptor_path}) does not exist!" unless File.exist?(pwn_decryptor_path)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    pwn_decryptor = YAML.load_file(pwn_decryptor_path, symbolize_names: true)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    key = opts[:key] ||= pwn_decryptor[:key] ||= ENV.fetch('PWN_DECRYPTOR_KEY')
         
     | 
| 
      
 31 
     | 
    
         
            +
                    key = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption Key') if key.nil?
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    iv = opts[:iv] ||= pwn_decryptor[:iv] ||= ENV.fetch('PWN_DECRYPTOR_IV')
         
     | 
| 
      
 34 
     | 
    
         
            +
                    iv = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption IV') if iv.nil?
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                    config = PWN::Plugins::Vault.dump(
         
     | 
| 
      
 37 
     | 
    
         
            +
                      file: pwn_config_path,
         
     | 
| 
      
 38 
     | 
    
         
            +
                      key: key,
         
     | 
| 
      
 39 
     | 
    
         
            +
                      iv: iv
         
     | 
| 
      
 40 
     | 
    
         
            +
                    )
         
     | 
| 
      
 41 
     | 
    
         
            +
                  else
         
     | 
| 
      
 42 
     | 
    
         
            +
                    config = YAML.load_file(pwn_config_path, symbolize_names: true)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  valid_ai_engines = %i[
         
     | 
| 
      
 46 
     | 
    
         
            +
                    grok
         
     | 
| 
      
 47 
     | 
    
         
            +
                    openai
         
     | 
| 
      
 48 
     | 
    
         
            +
                    ollama
         
     | 
| 
      
 49 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  engine = config[:ai][:active].to_s.downcase.to_sym
         
     | 
| 
      
 52 
     | 
    
         
            +
                  raise "ERROR: Unsupported AI Engine: #{engine} in #{pwn_config_path}.  Supported AI Engines:\n#{valid_ai_engines.inspect}" unless valid_ai_engines.include?(engine)
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  model = config[:ai][engine][:model]
         
     | 
| 
      
 55 
     | 
    
         
            +
                  system_role_content = config[:ai][engine][:system_role_content]
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  # Reset the ai response history on config refresh
         
     | 
| 
      
 58 
     | 
    
         
            +
                  config[:ai][engine][:response_history] = {
         
     | 
| 
      
 59 
     | 
    
         
            +
                    id: '',
         
     | 
| 
      
 60 
     | 
    
         
            +
                    object: '',
         
     | 
| 
      
 61 
     | 
    
         
            +
                    model: model,
         
     | 
| 
      
 62 
     | 
    
         
            +
                    usage: {},
         
     | 
| 
      
 63 
     | 
    
         
            +
                    choices: [
         
     | 
| 
      
 64 
     | 
    
         
            +
                      {
         
     | 
| 
      
 65 
     | 
    
         
            +
                        role: 'system',
         
     | 
| 
      
 66 
     | 
    
         
            +
                        content: system_role_content
         
     | 
| 
      
 67 
     | 
    
         
            +
                      }
         
     | 
| 
      
 68 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 69 
     | 
    
         
            +
                  }
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  # These two lines should be immutable for the session
         
     | 
| 
      
 72 
     | 
    
         
            +
                  config[:pwn_config_path] = pwn_config_path
         
     | 
| 
      
 73 
     | 
    
         
            +
                  config[:pwn_decryptor_path] = pwn_decryptor_path if is_encrypted
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  Pry.config.refresh = false if defined?(Pry)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  PWN.send(:remove_const, :CONFIG) if PWN.const_defined?(:CONFIG)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  PWN.const_set(:CONFIG, config)
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/pwn/plugins/repl.rb
    CHANGED
    
    | 
         @@ -18,11 +18,7 @@ module PWN 
     | 
|
| 
       18 
18 
     | 
    
         
             
                    mode = opts[:mode]
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                    proc do |_target_self, _nest_level, pi|
         
     | 
| 
       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(opts)
         
     | 
| 
       25 
     | 
    
         
            -
                      end
         
     | 
| 
      
 21 
     | 
    
         
            +
                      pi.config.pwn = PWN::Config.refresh(opts) if Pry.config.refresh
         
     | 
| 
       26 
22 
     | 
    
         | 
| 
       27 
23 
     | 
    
         
             
                      pi.config.pwn_repl_line += 1
         
     | 
| 
       28 
24 
     | 
    
         
             
                      line_pad = format(
         
     | 
| 
         @@ -464,24 +460,24 @@ module PWN 
     | 
|
| 
       464 
460 
     | 
    
         | 
| 
       465 
461 
     | 
    
         
             
                      def process
         
     | 
| 
       466 
462 
     | 
    
         
             
                        pi = pry_instance
         
     | 
| 
       467 
     | 
    
         
            -
                         
     | 
| 
       468 
     | 
    
         
            -
                        unless File.exist?( 
     | 
| 
       469 
     | 
    
         
            -
                          puts "ERROR: pwn.yaml not found: #{ 
     | 
| 
      
 463 
     | 
    
         
            +
                        pwn_config_path = pi.config.pwn_config_path ||= "#{Dir.home}/.pwn/pwn.yaml"
         
     | 
| 
      
 464 
     | 
    
         
            +
                        unless File.exist?(pwn_config_path)
         
     | 
| 
      
 465 
     | 
    
         
            +
                          puts "ERROR: pwn.yaml not found: #{pwn_config_path}"
         
     | 
| 
       470 
466 
     | 
    
         
             
                          return
         
     | 
| 
       471 
467 
     | 
    
         
             
                        end
         
     | 
| 
       472 
468 
     | 
    
         | 
| 
       473 
     | 
    
         
            -
                         
     | 
| 
       474 
     | 
    
         
            -
                        unless File.exist?( 
     | 
| 
       475 
     | 
    
         
            -
                          puts "ERROR: pwn.decryptor.yaml not found: #{ 
     | 
| 
      
 469 
     | 
    
         
            +
                        pwn_decryptor_path = pi.config.pwn_decryptor_path ||= "#{Dir.home}/.pwn/pwn.decryptor.yaml"
         
     | 
| 
      
 470 
     | 
    
         
            +
                        unless File.exist?(pwn_decryptor_path)
         
     | 
| 
      
 471 
     | 
    
         
            +
                          puts "ERROR: pwn.decryptor.yaml not found: #{pwn_decryptor_path}"
         
     | 
| 
       476 
472 
     | 
    
         
             
                          return
         
     | 
| 
       477 
473 
     | 
    
         
             
                        end
         
     | 
| 
       478 
474 
     | 
    
         | 
| 
       479 
     | 
    
         
            -
                        decryptor = YAML.load_file( 
     | 
| 
      
 475 
     | 
    
         
            +
                        decryptor = YAML.load_file(pwn_decryptor_path, symbolize_names: true)
         
     | 
| 
       480 
476 
     | 
    
         
             
                        key = decryptor[:key]
         
     | 
| 
       481 
477 
     | 
    
         
             
                        iv = decryptor[:iv]
         
     | 
| 
       482 
478 
     | 
    
         | 
| 
       483 
479 
     | 
    
         
             
                        PWN::Plugins::Vault.edit(
         
     | 
| 
       484 
     | 
    
         
            -
                          file:  
     | 
| 
      
 480 
     | 
    
         
            +
                          file: pwn_config_path,
         
     | 
| 
       485 
481 
     | 
    
         
             
                          key: key,
         
     | 
| 
       486 
482 
     | 
    
         
             
                          iv: iv
         
     | 
| 
       487 
483 
     | 
    
         
             
                        )
         
     | 
| 
         @@ -539,9 +535,7 @@ module PWN 
     | 
|
| 
       539 
535 
     | 
    
         | 
| 
       540 
536 
     | 
    
         
             
                    # Initialize pwn.yaml Configuration using :before_session Hook
         
     | 
| 
       541 
537 
     | 
    
         
             
                    Pry.config.hooks.add_hook(:before_session, :init_opts) do |_output, _binding, pi|
         
     | 
| 
       542 
     | 
    
         
            -
                       
     | 
| 
       543 
     | 
    
         
            -
                      opts[:pi] = pi
         
     | 
| 
       544 
     | 
    
         
            -
                      PWN::Plugins::Vault.refresh_config(opts)
         
     | 
| 
      
 538 
     | 
    
         
            +
                      pi.config.pwn = PWN::Config.refresh(opts)
         
     | 
| 
       545 
539 
     | 
    
         
             
                    end
         
     | 
| 
       546 
540 
     | 
    
         | 
| 
       547 
541 
     | 
    
         
             
                    Pry.config.hooks.add_hook(:after_read, :pwn_asm_hook) do |request, pi|
         
     | 
| 
         @@ -588,13 +582,6 @@ module PWN 
     | 
|
| 
       588 
582 
     | 
    
         
             
                        engine = pi.config.pwn[:ai][:active].to_s.downcase.to_sym
         
     | 
| 
       589 
583 
     | 
    
         
             
                        base_uri = pi.config.pwn[:ai][engine][:base_uri]
         
     | 
| 
       590 
584 
     | 
    
         
             
                        key = pi.config.pwn[:ai][engine][:key] ||= ''
         
     | 
| 
       591 
     | 
    
         
            -
                        if key.empty?
         
     | 
| 
       592 
     | 
    
         
            -
                          key = PWN::Plugins::AuthenticationHelper.mask_password(
         
     | 
| 
       593 
     | 
    
         
            -
                            prompt: 'pwn-ai Key'
         
     | 
| 
       594 
     | 
    
         
            -
                          )
         
     | 
| 
       595 
     | 
    
         
            -
                          pi.config.pwn[:ai][engine][:key] = key
         
     | 
| 
       596 
     | 
    
         
            -
                        end
         
     | 
| 
       597 
     | 
    
         
            -
             
     | 
| 
       598 
585 
     | 
    
         
             
                        response_history = pi.config.pwn[:ai][engine][:response_history]
         
     | 
| 
       599 
586 
     | 
    
         
             
                        speak_answer = pi.config.pwn_ai_speak
         
     | 
| 
       600 
587 
     | 
    
         
             
                        model = pi.config.pwn[:ai][engine][:model]
         
     | 
| 
         @@ -683,16 +670,10 @@ module PWN 
     | 
|
| 
       683 
670 
     | 
    
         
             
                  public_class_method def self.start(opts = {})
         
     | 
| 
       684 
671 
     | 
    
         
             
                    # Monkey Patch Pry, add commands, && hooks
         
     | 
| 
       685 
672 
     | 
    
         
             
                    PWN::Plugins::MonkeyPatch.pry
         
     | 
| 
       686 
     | 
    
         
            -
                    add_commands
         
     | 
| 
       687 
     | 
    
         
            -
             
     | 
| 
       688 
673 
     | 
    
         
             
                    pwn_config_root = "#{Dir.home}/.pwn"
         
     | 
| 
       689 
     | 
    
         
            -
                    FileUtils.mkdir_p(pwn_config_root)
         
     | 
| 
       690 
     | 
    
         
            -
             
     | 
| 
       691 
674 
     | 
    
         
             
                    Pry.config.history_file = "#{pwn_config_root}/pwn_history"
         
     | 
| 
       692 
675 
     | 
    
         | 
| 
       693 
     | 
    
         
            -
                     
     | 
| 
       694 
     | 
    
         
            -
                    opts[:yaml_decryptor_path] ||= "#{pwn_config_root}/pwn.decryptor.yaml"
         
     | 
| 
       695 
     | 
    
         
            -
             
     | 
| 
      
 676 
     | 
    
         
            +
                    add_commands
         
     | 
| 
       696 
677 
     | 
    
         
             
                    add_hooks(opts)
         
     | 
| 
       697 
678 
     | 
    
         | 
| 
       698 
679 
     | 
    
         
             
                    # Define PS1 Prompt
         
     | 
    
        data/lib/pwn/plugins/vault.rb
    CHANGED
    
    | 
         @@ -15,7 +15,7 @@ module PWN 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  #   iv: 'required - iv to decrypt'
         
     | 
| 
       16 
16 
     | 
    
         
             
                  # )
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                  def self.refresh_encryption_secrets(opts = {})
         
     | 
| 
      
 18 
     | 
    
         
            +
                  public_class_method def self.refresh_encryption_secrets(opts = {})
         
     | 
| 
       19 
19 
     | 
    
         
             
                    file = opts[:file].to_s.scrub if File.exist?(opts[:file].to_s.scrub)
         
     | 
| 
       20 
20 
     | 
    
         
             
                    key = opts[:key]
         
     | 
| 
       21 
21 
     | 
    
         
             
                    iv = opts[:iv]
         
     | 
| 
         @@ -234,83 +234,6 @@ module PWN 
     | 
|
| 
       234 
234 
     | 
    
         
             
                    raise e
         
     | 
| 
       235 
235 
     | 
    
         
             
                  end
         
     | 
| 
       236 
236 
     | 
    
         | 
| 
       237 
     | 
    
         
            -
                  # Supported Method Parameters::
         
     | 
| 
       238 
     | 
    
         
            -
                  # PWN::Plugins::Vault.refresh_config(
         
     | 
| 
       239 
     | 
    
         
            -
                  #    yaml_config_path: 'required - full path to pwn.yaml file',
         
     | 
| 
       240 
     | 
    
         
            -
                  #    pi: 'optional - Pry instance (default: Pry)',
         
     | 
| 
       241 
     | 
    
         
            -
                  #    yaml_decryptor_path: 'optional - full path to decryption YAML file'
         
     | 
| 
       242 
     | 
    
         
            -
                  #  )
         
     | 
| 
       243 
     | 
    
         
            -
                  public_class_method def self.refresh_config(opts = {})
         
     | 
| 
       244 
     | 
    
         
            -
                    yaml_config_path = opts[:yaml_config_path]
         
     | 
| 
       245 
     | 
    
         
            -
             
     | 
| 
       246 
     | 
    
         
            -
                    return false unless File.exist?(yaml_config_path)
         
     | 
| 
       247 
     | 
    
         
            -
             
     | 
| 
       248 
     | 
    
         
            -
                    pi = opts[:pi]
         
     | 
| 
       249 
     | 
    
         
            -
                    raise 'ERROR: Pry instance is required.' if pi.nil?
         
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
                    is_encrypted = PWN::Plugins::Vault.file_encrypted?(file: yaml_config_path)
         
     | 
| 
       252 
     | 
    
         
            -
             
     | 
| 
       253 
     | 
    
         
            -
                    if is_encrypted
         
     | 
| 
       254 
     | 
    
         
            -
                      # TODO: Implement "something you know, something you have, && something you are?"
         
     | 
| 
       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)
         
     | 
| 
       257 
     | 
    
         
            -
             
     | 
| 
       258 
     | 
    
         
            -
                      yaml_decryptor = YAML.load_file(yaml_decryptor_path, symbolize_names: true)
         
     | 
| 
       259 
     | 
    
         
            -
             
     | 
| 
       260 
     | 
    
         
            -
                      key = opts[:key] ||= yaml_decryptor[:key] ||= ENV.fetch('PWN_DECRYPTOR_KEY')
         
     | 
| 
       261 
     | 
    
         
            -
                      key = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption Key') if key.nil?
         
     | 
| 
       262 
     | 
    
         
            -
             
     | 
| 
       263 
     | 
    
         
            -
                      iv = opts[:iv] ||= yaml_decryptor[:iv] ||= ENV.fetch('PWN_DECRYPTOR_IV')
         
     | 
| 
       264 
     | 
    
         
            -
                      iv = PWN::Plugins::AuthenticationHelper.mask_password(prompt: 'Decryption IV') if iv.nil?
         
     | 
| 
       265 
     | 
    
         
            -
             
     | 
| 
       266 
     | 
    
         
            -
                      yaml_config = PWN::Plugins::Vault.dump(
         
     | 
| 
       267 
     | 
    
         
            -
                        file: yaml_config_path,
         
     | 
| 
       268 
     | 
    
         
            -
                        key: key,
         
     | 
| 
       269 
     | 
    
         
            -
                        iv: iv
         
     | 
| 
       270 
     | 
    
         
            -
                      )
         
     | 
| 
       271 
     | 
    
         
            -
                    else
         
     | 
| 
       272 
     | 
    
         
            -
                      yaml_config = YAML.load_file(yaml_config_path, symbolize_names: true)
         
     | 
| 
       273 
     | 
    
         
            -
                    end
         
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
       275 
     | 
    
         
            -
                    valid_ai_engines = %i[
         
     | 
| 
       276 
     | 
    
         
            -
                      grok
         
     | 
| 
       277 
     | 
    
         
            -
                      openai
         
     | 
| 
       278 
     | 
    
         
            -
                      ollama
         
     | 
| 
       279 
     | 
    
         
            -
                    ]
         
     | 
| 
       280 
     | 
    
         
            -
             
     | 
| 
       281 
     | 
    
         
            -
                    # Convert ai_engine to symbol and downcase to ensure stability
         
     | 
| 
       282 
     | 
    
         
            -
                    pi.config.pwn = yaml_config
         
     | 
| 
       283 
     | 
    
         
            -
                    engine = pi.config.pwn[:ai][:active].to_s.downcase.to_sym
         
     | 
| 
       284 
     | 
    
         
            -
                    raise "ERROR: Unsupported AI Engine: #{engine} in #{yaml_config_path}.  Supported AI Engines:\n#{valid_ai_engines.inspect}" unless valid_ai_engines.include?(engine)
         
     | 
| 
       285 
     | 
    
         
            -
             
     | 
| 
       286 
     | 
    
         
            -
                    model = pi.config.pwn[:ai][engine][:model]
         
     | 
| 
       287 
     | 
    
         
            -
                    system_role_content = pi.config.pwn[:ai][engine][:system_role_content]
         
     | 
| 
       288 
     | 
    
         
            -
             
     | 
| 
       289 
     | 
    
         
            -
                    # Reset the ai response history on config refresh
         
     | 
| 
       290 
     | 
    
         
            -
                    pi.config.pwn[:ai][engine][:response_history] = {
         
     | 
| 
       291 
     | 
    
         
            -
                      id: '',
         
     | 
| 
       292 
     | 
    
         
            -
                      object: '',
         
     | 
| 
       293 
     | 
    
         
            -
                      model: model,
         
     | 
| 
       294 
     | 
    
         
            -
                      usage: {},
         
     | 
| 
       295 
     | 
    
         
            -
                      choices: [
         
     | 
| 
       296 
     | 
    
         
            -
                        {
         
     | 
| 
       297 
     | 
    
         
            -
                          role: 'system',
         
     | 
| 
       298 
     | 
    
         
            -
                          content: system_role_content
         
     | 
| 
       299 
     | 
    
         
            -
                        }
         
     | 
| 
       300 
     | 
    
         
            -
                      ]
         
     | 
| 
       301 
     | 
    
         
            -
                    }
         
     | 
| 
       302 
     | 
    
         
            -
             
     | 
| 
       303 
     | 
    
         
            -
                    # These two lines should be immutable for the session
         
     | 
| 
       304 
     | 
    
         
            -
                    pi.config.pwn[:yaml_config_path] = yaml_config_path
         
     | 
| 
       305 
     | 
    
         
            -
                    pi.config.pwn[:yaml_decryptor_path] = yaml_decryptor_path if is_encrypted
         
     | 
| 
       306 
     | 
    
         
            -
             
     | 
| 
       307 
     | 
    
         
            -
                    Pry.config.refresh = false
         
     | 
| 
       308 
     | 
    
         
            -
             
     | 
| 
       309 
     | 
    
         
            -
                    true
         
     | 
| 
       310 
     | 
    
         
            -
                  rescue StandardError => e
         
     | 
| 
       311 
     | 
    
         
            -
                    raise e
         
     | 
| 
       312 
     | 
    
         
            -
                  end
         
     | 
| 
       313 
     | 
    
         
            -
             
     | 
| 
       314 
237 
     | 
    
         
             
                  # Author(s):: 0day Inc. <support@0dayinc.com>
         
     | 
| 
       315 
238 
     | 
    
         | 
| 
       316 
239 
     | 
    
         
             
                  public_class_method def self.authors
         
     | 
| 
         @@ -363,12 +286,6 @@ module PWN 
     | 
|
| 
       363 
286 
     | 
    
         
             
                        file: 'required - file to check if encrypted'
         
     | 
| 
       364 
287 
     | 
    
         
             
                      )
         
     | 
| 
       365 
288 
     | 
    
         | 
| 
       366 
     | 
    
         
            -
                      #{self}.refresh_config(
         
     | 
| 
       367 
     | 
    
         
            -
                        yaml_config_path: 'required - full path to pwn.yaml file',
         
     | 
| 
       368 
     | 
    
         
            -
                        pi: 'optional - Pry instance (default: Pry)',
         
     | 
| 
       369 
     | 
    
         
            -
                        yaml_decryptor_path: 'optional - full path to decryption YAML file'
         
     | 
| 
       370 
     | 
    
         
            -
                      )
         
     | 
| 
       371 
     | 
    
         
            -
             
     | 
| 
       372 
289 
     | 
    
         
             
                      #{self}.authors
         
     | 
| 
       373 
290 
     | 
    
         
             
                    "
         
     | 
| 
       374 
291 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/pwn/reports/sast.rb
    CHANGED
    
    | 
         @@ -14,13 +14,7 @@ module PWN 
     | 
|
| 
       14 
14 
     | 
    
         
             
                  # PWN::Reports::SAST.generate(
         
     | 
| 
       15 
15 
     | 
    
         
             
                  #   dir_path: 'optional - Directory path to save the report (defaults to .)',
         
     | 
| 
       16 
16 
     | 
    
         
             
                  #   results_hash: 'optional - Hash containing the results of the SAST analysis (defaults to empty hash structure)',
         
     | 
| 
       17 
     | 
    
         
            -
                  #   report_name: 'optional - Name of the report file (defaults to current directory name)' 
     | 
| 
       18 
     | 
    
         
            -
                  #   ai_engine: 'optional - AI engine to use for analysis (:grok, :ollama, or :openai)',
         
     | 
| 
       19 
     | 
    
         
            -
                  #   ai_model: 'optionnal - AI Model to Use for Respective AI Engine (e.g., grok-4i-0709, chargpt-4o-latest, llama-3.1, etc.)',
         
     | 
| 
       20 
     | 
    
         
            -
                  #   ai_key: 'optional -  AI Key/Token for Respective AI Engine',
         
     | 
| 
       21 
     | 
    
         
            -
                  #   ai_base_uri: 'optional -  AI FQDN (Only Required for "ollama" AI Engine)',
         
     | 
| 
       22 
     | 
    
         
            -
                  #   ai_system_role_content: 'optional - AI System Role Content (Defaults to "Confidence score of 0-10 this is vulnerable (0 being not vulnerable, moving upwards in confidence of exploitation). Provide additional context to assist penetration tester assessment.")',
         
     | 
| 
       23 
     | 
    
         
            -
                  #   ai_temp: 'optional - AI Temperature (Defaults to 0.1)'
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   report_name: 'optional - Name of the report file (defaults to current directory name)'
         
     | 
| 
       24 
18 
     | 
    
         
             
                  # )
         
     | 
| 
       25 
19 
     | 
    
         | 
| 
       26 
20 
     | 
    
         
             
                  public_class_method def self.generate(opts = {})
         
     | 
| 
         @@ -31,28 +25,21 @@ module PWN 
     | 
|
| 
       31 
25 
     | 
    
         
             
                    }
         
     | 
| 
       32 
26 
     | 
    
         
             
                    report_name = opts[:report_name] ||= File.basename(Dir.pwd)
         
     | 
| 
       33 
27 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
                     
     | 
| 
       35 
     | 
    
         
            -
                    if  
     | 
| 
       36 
     | 
    
         
            -
                       
     | 
| 
       37 
     | 
    
         
            -
                       
     | 
| 
       38 
     | 
    
         
            -
                       
     | 
| 
      
 28 
     | 
    
         
            +
                    ai_instrospection = PWN::CONFIG[:ai][:introspection]
         
     | 
| 
      
 29 
     | 
    
         
            +
                    if ai_instrospection
         
     | 
| 
      
 30 
     | 
    
         
            +
                      engine = PWN::CONFIG[:ai][:active].to_s.downcase.to_sym
         
     | 
| 
      
 31 
     | 
    
         
            +
                      base_uri = PWN::CONFIG[:ai][engine][:base_uri]
         
     | 
| 
      
 32 
     | 
    
         
            +
                      model = PWN::CONFIG[:ai][engine][:model]
         
     | 
| 
      
 33 
     | 
    
         
            +
                      key = PWN::CONFIG[:ai][engine][:key]
         
     | 
| 
      
 34 
     | 
    
         
            +
                      system_role_content = PWN::CONFIG[:ai][engine][:system_role_content]
         
     | 
| 
      
 35 
     | 
    
         
            +
                      temp = PWN::CONFIG[:ai][engine][:temp]
         
     | 
| 
       39 
36 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
                       
     | 
| 
       41 
     | 
    
         
            -
                      raise 'ERROR: FQDN for Ollama AI engine is required.' if ai_engine == :ollama && ai_base_uri.nil?
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                      ai_model = opts[:ai_model]
         
     | 
| 
       44 
     | 
    
         
            -
                      raise 'ERROR: AI Model is required for AI engine ollama.' if ai_engine == :ollama && ai_model.nil?
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                      ai_key = opts[:ai_key] ||= PWN::Plugins::AuthenticationHelper.mask_password(prompt: "#{ai_engine} Token")
         
     | 
| 
       47 
     | 
    
         
            -
                      ai_system_role_content = opts[:ai_system_role_content] ||= 'Your sole purpose is to analyze source code snippets and generate an Exploit Prediction Scoring System (EPSS) score between 0% - 100%.  Just generate a score unless score is higher than 75% in which a code fic should also be included.'
         
     | 
| 
       48 
     | 
    
         
            -
                      ai_temp = opts[:ai_temp] ||= 0.1
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                      puts "Analyzing source code using AI engine: #{ai_engine}\nModel: #{ai_model}\nSystem Role Content: #{ai_system_role_content}\nTemperature: #{ai_temp}"
         
     | 
| 
      
 37 
     | 
    
         
            +
                      puts "Analyzing source code using AI engine: #{engine}\nModel: #{model}\nSystem Role Content: #{system_role_content}\nTemperature: #{temp}"
         
     | 
| 
       51 
38 
     | 
    
         
             
                    end
         
     | 
| 
       52 
39 
     | 
    
         | 
| 
       53 
40 
     | 
    
         
             
                    # Calculate percentage of AI analysis based on the number of entries
         
     | 
| 
       54 
41 
     | 
    
         
             
                    total_entries = results_hash[:data].sum { |entry| entry[:line_no_and_contents].size }
         
     | 
| 
       55 
     | 
    
         
            -
                    puts "Total entries to analyze: #{total_entries}" if  
     | 
| 
      
 42 
     | 
    
         
            +
                    puts "Total entries to analyze: #{total_entries}" if engine
         
     | 
| 
       56 
43 
     | 
    
         | 
| 
       57 
44 
     | 
    
         
             
                    percent_complete = 0.0
         
     | 
| 
       58 
45 
     | 
    
         
             
                    entry_count = 0
         
     | 
| 
         @@ -79,37 +66,39 @@ module PWN 
     | 
|
| 
       79 
66 
     | 
    
         
             
                        response = nil
         
     | 
| 
       80 
67 
     | 
    
         
             
                        author = src_detail[:author].to_s.scrub.chomp.strip
         
     | 
| 
       81 
68 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
                         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                           
     | 
| 
       85 
     | 
    
         
            -
                             
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
                           
     | 
| 
       95 
     | 
    
         
            -
                             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
                           
     | 
| 
       105 
     | 
    
         
            -
                             
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
      
 69 
     | 
    
         
            +
                        if ai_instrospection
         
     | 
| 
      
 70 
     | 
    
         
            +
                          case engine
         
     | 
| 
      
 71 
     | 
    
         
            +
                          when :grok
         
     | 
| 
      
 72 
     | 
    
         
            +
                            response = PWN::AI::Grok.chat(
         
     | 
| 
      
 73 
     | 
    
         
            +
                              base_uri: base_uri,
         
     | 
| 
      
 74 
     | 
    
         
            +
                              token: key,
         
     | 
| 
      
 75 
     | 
    
         
            +
                              model: model,
         
     | 
| 
      
 76 
     | 
    
         
            +
                              system_role_content: system_role_content,
         
     | 
| 
      
 77 
     | 
    
         
            +
                              temp: temp,
         
     | 
| 
      
 78 
     | 
    
         
            +
                              request: request.chomp,
         
     | 
| 
      
 79 
     | 
    
         
            +
                              spinner: false
         
     | 
| 
      
 80 
     | 
    
         
            +
                            )
         
     | 
| 
      
 81 
     | 
    
         
            +
                          when :ollama
         
     | 
| 
      
 82 
     | 
    
         
            +
                            response = PWN::AI::Ollama.chat(
         
     | 
| 
      
 83 
     | 
    
         
            +
                              base_uri: base_uri,
         
     | 
| 
      
 84 
     | 
    
         
            +
                              token: key,
         
     | 
| 
      
 85 
     | 
    
         
            +
                              model: model,
         
     | 
| 
      
 86 
     | 
    
         
            +
                              system_role_content: system_role_content,
         
     | 
| 
      
 87 
     | 
    
         
            +
                              temp: temp,
         
     | 
| 
      
 88 
     | 
    
         
            +
                              request: request.chomp,
         
     | 
| 
      
 89 
     | 
    
         
            +
                              spinner: false
         
     | 
| 
      
 90 
     | 
    
         
            +
                            )
         
     | 
| 
      
 91 
     | 
    
         
            +
                          when :openai
         
     | 
| 
      
 92 
     | 
    
         
            +
                            response = PWN::AI::OpenAI.chat(
         
     | 
| 
      
 93 
     | 
    
         
            +
                              base_uri: base_uri,
         
     | 
| 
      
 94 
     | 
    
         
            +
                              token: key,
         
     | 
| 
      
 95 
     | 
    
         
            +
                              model: model,
         
     | 
| 
      
 96 
     | 
    
         
            +
                              system_role_content: system_role_content,
         
     | 
| 
      
 97 
     | 
    
         
            +
                              temp: temp,
         
     | 
| 
      
 98 
     | 
    
         
            +
                              request: request.chomp,
         
     | 
| 
      
 99 
     | 
    
         
            +
                              spinner: false
         
     | 
| 
      
 100 
     | 
    
         
            +
                            )
         
     | 
| 
      
 101 
     | 
    
         
            +
                          end
         
     | 
| 
       113 
102 
     | 
    
         
             
                        end
         
     | 
| 
       114 
103 
     | 
    
         | 
| 
       115 
104 
     | 
    
         
             
                        ai_analysis = nil
         
     | 
| 
         @@ -118,6 +107,7 @@ module PWN 
     | 
|
| 
       118 
107 
     | 
    
         
             
                          ai_analysis = response[:choices].last[:content] if response[:choices].last.keys.include?(:content)
         
     | 
| 
       119 
108 
     | 
    
         
             
                          # puts "AI Analysis Progress: #{percent_complete}% Line: #{line_no} | Author: #{author} | AI Analysis: #{ai_analysis}\n\n\n" if ai_analysis
         
     | 
| 
       120 
109 
     | 
    
         
             
                        end
         
     | 
| 
      
 110 
     | 
    
         
            +
                        # TODO: Make results prettier in the HTML report
         
     | 
| 
       121 
111 
     | 
    
         
             
                        src_detail[:ai_analysis] = ai_analysis.to_s.scrub.chomp.strip
         
     | 
| 
       122 
112 
     | 
    
         | 
| 
       123 
113 
     | 
    
         
             
                        spin.update(
         
     | 
    
        data/lib/pwn/version.rb
    CHANGED
    
    
    
        data/lib/pwn.rb
    CHANGED
    
    | 
         @@ -12,19 +12,22 @@ module PWN 
     | 
|
| 
       12 
12 
     | 
    
         
             
              autoload :AWS, 'pwn/aws'
         
     | 
| 
       13 
13 
     | 
    
         
             
              autoload :Banner, 'pwn/banner'
         
     | 
| 
       14 
14 
     | 
    
         
             
              autoload :Blockchain, 'pwn/blockchain'
         
     | 
| 
      
 15 
     | 
    
         
            +
              autoload :Config, 'pwn/config'
         
     | 
| 
       15 
16 
     | 
    
         
             
              autoload :FFI, 'pwn/ffi'
         
     | 
| 
       16 
17 
     | 
    
         
             
              autoload :Plugins, 'pwn/plugins'
         
     | 
| 
       17 
18 
     | 
    
         
             
              autoload :Reports, 'pwn/reports'
         
     | 
| 
       18 
19 
     | 
    
         
             
              autoload :SAST, 'pwn/sast'
         
     | 
| 
       19 
20 
     | 
    
         
             
              autoload :WWW, 'pwn/www'
         
     | 
| 
       20 
     | 
    
         
            -
              # TODO: If pwn.yaml is present, attempt to decrypt it, and initialize the YAML config
         
     | 
| 
       21 
     | 
    
         
            -
              # Do this within PWN::Plugins::AuthenticationHelper?
         
     | 
| 
       22 
21 
     | 
    
         | 
| 
       23 
22 
     | 
    
         
             
              # Display a List of Every PWN Module
         
     | 
| 
       24 
23 
     | 
    
         | 
| 
       25 
24 
     | 
    
         
             
              public_class_method def self.help
         
     | 
| 
       26 
25 
     | 
    
         
             
                constants.sort
         
     | 
| 
       27 
26 
     | 
    
         
             
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              # Initialize PWN configuration file
         
     | 
| 
      
 29 
     | 
    
         
            +
              # PWN::CONFIG is the constant that stores the configuration data
         
     | 
| 
      
 30 
     | 
    
         
            +
              PWN::Config.refresh
         
     | 
| 
       28 
31 
     | 
    
         
             
            rescue StandardError => e
         
     | 
| 
       29 
32 
     | 
    
         
             
              puts e.backtrace
         
     | 
| 
       30 
33 
     | 
    
         
             
              raise e
         
     | 
    
        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. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.5.430
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - 0day Inc.
         
     | 
| 
         @@ -1832,6 +1832,7 @@ files: 
     | 
|
| 
       1832 
1832 
     | 
    
         
             
            - lib/pwn/blockchain.rb
         
     | 
| 
       1833 
1833 
     | 
    
         
             
            - lib/pwn/blockchain/btc.rb
         
     | 
| 
       1834 
1834 
     | 
    
         
             
            - lib/pwn/blockchain/eth.rb
         
     | 
| 
      
 1835 
     | 
    
         
            +
            - lib/pwn/config.rb
         
     | 
| 
       1835 
1836 
     | 
    
         
             
            - lib/pwn/ffi.rb
         
     | 
| 
       1836 
1837 
     | 
    
         
             
            - lib/pwn/ffi/stdio.rb
         
     | 
| 
       1837 
1838 
     | 
    
         
             
            - lib/pwn/plugins.rb
         
     | 
| 
         @@ -2180,6 +2181,7 @@ files: 
     | 
|
| 
       2180 
2181 
     | 
    
         
             
            - spec/lib/pwn/blockchain/btc_spec.rb
         
     | 
| 
       2181 
2182 
     | 
    
         
             
            - spec/lib/pwn/blockchain/eth_spec.rb
         
     | 
| 
       2182 
2183 
     | 
    
         
             
            - spec/lib/pwn/blockchain_spec.rb
         
     | 
| 
      
 2184 
     | 
    
         
            +
            - spec/lib/pwn/config_spec.rb
         
     | 
| 
       2183 
2185 
     | 
    
         
             
            - spec/lib/pwn/ffi/stdio_spec.rb
         
     | 
| 
       2184 
2186 
     | 
    
         
             
            - spec/lib/pwn/ffi_spec.rb
         
     | 
| 
       2185 
2187 
     | 
    
         
             
            - spec/lib/pwn/plugins/android_spec.rb
         
     |