pwn 0.5.154 → 0.5.156
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_sast +1 -1
- data/lib/pwn/plugins/ip_info.rb +53 -19
- data/lib/pwn/plugins/thread_pool.rb +41 -19
- data/lib/pwn/version.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 47f160c73c391cf48bd0a847d0031d342ef79b77575e86872153105905072141
         | 
| 4 | 
            +
              data.tar.gz: 7c71dc97fd8e8e4e99584ab8bbb97363d46a2edd6786f2bb8e4ee9bd7c0b16cb
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 55c9643d11b525ef375396f51354e2356363567de4ca1b9e0d9ea9281ff2af58698bd819a38f1e8b0c7212b03012403257c5e4622ae16e7ee018a36e1d66c7e1
         | 
| 7 | 
            +
              data.tar.gz: c67b6fe7fbf5966d2630c4a0e99f254e58e6b09a6b15cad13a79897f9ab42c0f2efb7147547c70b4663e0f830af672d72859abc50e64e54b51ad999c4a33220b
         | 
    
        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.156]:001 >>> PWN.help
         | 
| 41 41 | 
             
            ```
         | 
| 42 42 |  | 
| 43 43 | 
             
            [](https://youtu.be/G7iLUY4FzsI)
         | 
| @@ -52,7 +52,7 @@ $ rvm use ruby-3.3.1@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.156]:001 >>> PWN.help
         | 
| 56 56 | 
             
            ```
         | 
| 57 57 |  | 
| 58 58 | 
             
            If you're using a multi-user install of RVM do:
         | 
| @@ -62,7 +62,7 @@ $ rvm use ruby-3.3.1@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.156]: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_sast
    CHANGED
    
    
    
        data/lib/pwn/plugins/ip_info.rb
    CHANGED
    
    | @@ -46,12 +46,30 @@ module PWN | |
| 46 46 | 
             
                    raise e
         | 
| 47 47 | 
             
                  end
         | 
| 48 48 |  | 
| 49 | 
            +
                  # Supported Method Parameters::
         | 
| 50 | 
            +
                  # is_rfc1918 = PWN::Plugins::IPInfo.check_rfc1918(
         | 
| 51 | 
            +
                  #   ip: 'required - IP to check'
         | 
| 52 | 
            +
                  # )
         | 
| 53 | 
            +
                  public_class_method def self.check_rfc1918(opts = {})
         | 
| 54 | 
            +
                    ip = opts[:ip].to_s.scrub.strip.chomp
         | 
| 55 | 
            +
                    ip_obj = IPAddress.valid?(ip) ? IPAddress.parse(ip) : nil
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    rfc1918_ranges = [
         | 
| 58 | 
            +
                      IPAddress('10.0.0.0/8'),     # 10.0.0.0 - 10.255.255.255
         | 
| 59 | 
            +
                      IPAddress('172.16.0.0/12'),  # 172.16.0.0 - 172.31.255.255
         | 
| 60 | 
            +
                      IPAddress('192.168.0.0/16')  # 192.168.0.0 - 192.168.255.255
         | 
| 61 | 
            +
                    ]
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    rfc1918_ranges.any? { |range| range.include?(ip_obj) }
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 49 66 | 
             
                  # Supported Method Parameters::
         | 
| 50 67 | 
             
                  # ip_info_struc = PWN::Plugins::IPInfo.get(
         | 
| 51 68 | 
             
                  #   target: 'required - IP or Host to lookup',
         | 
| 52 69 | 
             
                  #   proxy: 'optional - use a proxy',
         | 
| 53 70 | 
             
                  #   tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
         | 
| 54 | 
            -
                  #   skip_api: 'optional - skip the API call'
         | 
| 71 | 
            +
                  #   skip_api: 'optional - skip the API call',
         | 
| 72 | 
            +
                  #   dns_server: 'optional - DNS server to use for lookup (default: your default DNS server)'
         | 
| 55 73 | 
             
                  # )
         | 
| 56 74 |  | 
| 57 75 | 
             
                  public_class_method def self.get(opts = {})
         | 
| @@ -63,19 +81,29 @@ module PWN | |
| 63 81 | 
             
                    ip_info_resp = []
         | 
| 64 82 | 
             
                    ip_resp_hash = {}
         | 
| 65 83 | 
             
                    is_ip = IPAddress.valid?(target)
         | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
                       | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 84 | 
            +
                    hostname = '' if is_ip
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    unless is_ip
         | 
| 87 | 
            +
                      begin
         | 
| 88 | 
            +
                        hostname = target
         | 
| 89 | 
            +
                        dns_server = opts[:dns_server]
         | 
| 90 | 
            +
                        dns_resolver = Resolv::DNS.new(nameserver: [dns_server]) if dns_server
         | 
| 91 | 
            +
                        dns_resolver ||= Resolv::DNS.new
         | 
| 92 | 
            +
                        target = dns_resolver.getaddress(target).to_s
         | 
| 93 | 
            +
                      rescue Resolv::ResolvError
         | 
| 94 | 
            +
                        target = nil
         | 
| 95 | 
            +
                      end
         | 
| 72 96 | 
             
                    end
         | 
| 73 97 |  | 
| 74 98 | 
             
                    ip_resp_hash = ip_info_rest_call(ip: target, proxy: proxy) unless skip_api
         | 
| 99 | 
            +
                    is_rfc1918 = check_rfc1918(ip: target)
         | 
| 75 100 | 
             
                    ip_resp_hash[:ip] = target
         | 
| 101 | 
            +
                    ip_resp_hash[:is_rfc1918] = is_rfc1918
         | 
| 102 | 
            +
                    ip_resp_hash[:hostname] = hostname
         | 
| 103 | 
            +
             | 
| 76 104 | 
             
                    ip_info_resp.push(ip_resp_hash) unless target.nil?
         | 
| 77 105 |  | 
| 78 | 
            -
                    if proxy.nil? | 
| 106 | 
            +
                    if proxy.nil?
         | 
| 79 107 | 
             
                      ip_info_resp.each do |ip_resp|
         | 
| 80 108 | 
             
                        tls_port_avail = PWN::Plugins::Sock.check_port_in_use(
         | 
| 81 109 | 
             
                          server_ip: target,
         | 
| @@ -128,7 +156,7 @@ module PWN | |
| 128 156 | 
             
                  # PWN::Plugins::IPInfo.bruteforce_subdomains(
         | 
| 129 157 | 
             
                  #   parent_domain: 'required - Parent Domain to brute force',
         | 
| 130 158 | 
             
                  #   dictionary: 'required - Dictionary to use for subdomain brute force',
         | 
| 131 | 
            -
                  #   max_threads: 'optional - Maximum number of threads to use (default:  | 
| 159 | 
            +
                  #   max_threads: 'optional - Maximum number of threads to use (default: 9)',
         | 
| 132 160 | 
             
                  #   proxy: 'optional - use a proxy',
         | 
| 133 161 | 
             
                  #   tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
         | 
| 134 162 | 
             
                  #   results_file: 'optional - File to write results to (default: /tmp/parent_domain-timestamp-pwn_bruteforce_subdomains.txt)'
         | 
| @@ -141,15 +169,14 @@ module PWN | |
| 141 169 | 
             
                    dictionary = opts[:dictionary] ||= default_dictionary
         | 
| 142 170 | 
             
                    raise "ERROR: Dictionary file not found: #{dictionary}" unless File.exist?(dictionary)
         | 
| 143 171 |  | 
| 144 | 
            -
                    max_threads = opts[:max_threads] | 
| 145 | 
            -
                    max_threads = 8 unless max_threads.positive?
         | 
| 172 | 
            +
                    max_threads = opts[:max_threads]
         | 
| 146 173 |  | 
| 147 174 | 
             
                    proxy = opts[:proxy]
         | 
| 148 175 | 
             
                    tls_port = opts[:tls_port]
         | 
| 149 176 | 
             
                    timestamp = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
         | 
| 150 177 | 
             
                    results_file = opts[:results_file] ||= "/tmp/SUBS.#{parent_domain}-#{timestamp}-pwn_bruteforce_subdomains.txt"
         | 
| 151 178 |  | 
| 152 | 
            -
                    File.write(results_file,  | 
| 179 | 
            +
                    File.write(results_file, "[\n")
         | 
| 153 180 |  | 
| 154 181 | 
             
                    # Break up dictonary file into sublines and process each subline in a thread
         | 
| 155 182 | 
             
                    dict_lines = File.readlines(dictionary).shuffle
         | 
| @@ -158,17 +185,16 @@ module PWN | |
| 158 185 | 
             
                      enumerable_array: dict_lines,
         | 
| 159 186 | 
             
                      max_threads: max_threads
         | 
| 160 187 | 
             
                    ) do |subline|
         | 
| 188 | 
            +
                      print '.'
         | 
| 161 189 | 
             
                      subdomain = subline.to_s.scrub.strip.chomp
         | 
| 162 190 | 
             
                      target = parent_domain if subdomain.empty?
         | 
| 163 | 
            -
                      target = "#{subdomain}.#{parent_domain}"
         | 
| 191 | 
            +
                      target = "#{subdomain}.#{parent_domain}" unless subdomain.empty?
         | 
| 164 192 | 
             
                      ip_info_resp = get(
         | 
| 165 193 | 
             
                        target: target,
         | 
| 166 194 | 
             
                        proxy: proxy,
         | 
| 167 195 | 
             
                        tls_port: tls_port,
         | 
| 168 196 | 
             
                        skip_api: true
         | 
| 169 197 | 
             
                      )
         | 
| 170 | 
            -
                      puts "SUBD: #{target} RESP: #{ip_info_resp}" if ip_info_resp.empty?
         | 
| 171 | 
            -
                      puts "SUBD: #{target} RESP:\n#{ip_info_resp}" if ip_info_resp.any?
         | 
| 172 198 |  | 
| 173 199 | 
             
                      mutex.synchronize do
         | 
| 174 200 | 
             
                        File.open(results_file, 'a') do |file|
         | 
| @@ -185,8 +211,11 @@ module PWN | |
| 185 211 | 
             
                    raise e
         | 
| 186 212 | 
             
                  ensure
         | 
| 187 213 | 
             
                    # Strip trailing comma and close JSON array
         | 
| 188 | 
            -
                    File.readlines(results_file) | 
| 189 | 
            -
                     | 
| 214 | 
            +
                    final_results = File.readlines(results_file)
         | 
| 215 | 
            +
                    # Strip trailing comma from last line
         | 
| 216 | 
            +
                    last_line = final_results[-1][0..-2]
         | 
| 217 | 
            +
                    final_results[-1] = last_line
         | 
| 218 | 
            +
                    File.write(results_file, "#{final_results.join}\n]")
         | 
| 190 219 | 
             
                  end
         | 
| 191 220 |  | 
| 192 221 | 
             
                  # Author(s):: 0day Inc. <support@0dayinc.com>
         | 
| @@ -201,17 +230,22 @@ module PWN | |
| 201 230 |  | 
| 202 231 | 
             
                  public_class_method def self.help
         | 
| 203 232 | 
             
                    puts "USAGE:
         | 
| 233 | 
            +
                      is_rfc1918 = #{self}.check_rfc1918(
         | 
| 234 | 
            +
                        ip: 'required - IP to check'
         | 
| 235 | 
            +
                      )
         | 
| 236 | 
            +
             | 
| 204 237 | 
             
                      ip_info_struc = #{self}.get(
         | 
| 205 238 | 
             
                        target: 'required - IP or Host to lookup',
         | 
| 206 239 | 
             
                        proxy: 'optional - use a proxy',
         | 
| 207 240 | 
             
                        tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
         | 
| 208 | 
            -
                        skip_api: 'optional - skip the API call'
         | 
| 241 | 
            +
                        skip_api: 'optional - skip the API call',
         | 
| 242 | 
            +
                        dns_server: 'optional - DNS server to use for lookup (default: your default DNS server)'
         | 
| 209 243 | 
             
                      )
         | 
| 210 244 |  | 
| 211 245 | 
             
                      #{self}.bruteforce_subdomains(
         | 
| 212 246 | 
             
                        parent_domain: 'required - Parent Domain to brute force',
         | 
| 213 247 | 
             
                        dictionary: 'required - Dictionary to use for subdomain brute force',
         | 
| 214 | 
            -
                        max_threads: 'optional - Maximum number of threads to use (default:  | 
| 248 | 
            +
                        max_threads: 'optional - Maximum number of threads to use (default: 9)',
         | 
| 215 249 | 
             
                        proxy: 'optional - use a proxy',
         | 
| 216 250 | 
             
                        tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
         | 
| 217 251 | 
             
                        results_file: 'optional - File to write results to (default: /tmp/parent_domain-timestamp-pwn_bruteforce_subdomains.txt)'
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'concurrent-ruby'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module PWN
         | 
| 4 6 | 
             
              module Plugins
         | 
| 5 7 | 
             
                # This plugin makes the creation of a thread pool much simpler.
         | 
| @@ -27,34 +29,54 @@ module PWN | |
| 27 29 | 
             
                    detach = opts[:detach] ||= false
         | 
| 28 30 |  | 
| 29 31 | 
             
                    puts "Initiating Thread Pool of #{max_threads} Worker Threads...."
         | 
| 30 | 
            -
                     | 
| 31 | 
            -
                    threads = Array.new(max_threads) do
         | 
| 32 | 
            -
                      Thread.new do
         | 
| 33 | 
            -
                        until (this_thread = queue.pop) == :POOL_EXHAUSTED
         | 
| 34 | 
            -
                          yield this_thread
         | 
| 35 | 
            -
                        end
         | 
| 36 | 
            -
                      end
         | 
| 37 | 
            -
                    end
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                    enumerable_array.uniq.sort.each do |this_thread|
         | 
| 40 | 
            -
                      queue << this_thread
         | 
| 41 | 
            -
                    end
         | 
| 32 | 
            +
                    pool = Concurrent::FixedThreadPool.new(max_threads)
         | 
| 42 33 |  | 
| 43 | 
            -
                     | 
| 44 | 
            -
                       | 
| 34 | 
            +
                    enumerable_array.each do |this_thread|
         | 
| 35 | 
            +
                      pool.post do
         | 
| 36 | 
            +
                        yield this_thread
         | 
| 37 | 
            +
                      end
         | 
| 45 38 | 
             
                    end
         | 
| 46 39 |  | 
| 47 | 
            -
                     | 
| 48 | 
            -
             | 
| 49 | 
            -
                    else
         | 
| 50 | 
            -
                      threads.each(&:join)
         | 
| 51 | 
            -
                    end
         | 
| 40 | 
            +
                    pool.shutdown
         | 
| 41 | 
            +
                    pool.wait_for_termination unless detach
         | 
| 52 42 | 
             
                  rescue Interrupt
         | 
| 53 43 | 
             
                    puts "\nGoodbye."
         | 
| 54 44 | 
             
                  rescue StandardError => e
         | 
| 45 | 
            +
                    puts e.backtrace
         | 
| 55 46 | 
             
                    raise e
         | 
| 56 47 | 
             
                  end
         | 
| 57 48 |  | 
| 49 | 
            +
                  # public_class_method def self.fill(opts = {})
         | 
| 50 | 
            +
                  #   enumerable_array = opts[:enumerable_array]
         | 
| 51 | 
            +
                  #   max_threads = opts[:max_threads].to_i
         | 
| 52 | 
            +
                  #   max_threads = 9 if max_threads.zero?
         | 
| 53 | 
            +
                  #   detach = opts[:detach] ||= false
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  #   puts "Initiating Thread Pool of #{max_threads} Worker Threads...."
         | 
| 56 | 
            +
                  #   queue = SizedQueue.new(max_threads)
         | 
| 57 | 
            +
                  #   threads = Array.new(max_threads) do
         | 
| 58 | 
            +
                  #     Thread.new do
         | 
| 59 | 
            +
                  #       until (this_thread = queue.pop) == :POOL_EXHAUSTED
         | 
| 60 | 
            +
                  #         yield this_thread
         | 
| 61 | 
            +
                  #       end
         | 
| 62 | 
            +
                  #     end
         | 
| 63 | 
            +
                  #   end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  #   enumerable_array.uniq.each do |this_thread|
         | 
| 66 | 
            +
                  #     queue << this_thread
         | 
| 67 | 
            +
                  #   end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  #   max_threads.times do
         | 
| 70 | 
            +
                  #     queue << :POOL_EXHAUSTED
         | 
| 71 | 
            +
                  #   end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  #   threads.each(&:join) unless detach
         | 
| 74 | 
            +
                  # rescue Interrupt
         | 
| 75 | 
            +
                  #   puts "\nGoodbye."
         | 
| 76 | 
            +
                  # rescue StandardError => e
         | 
| 77 | 
            +
                  #   raise e
         | 
| 78 | 
            +
                  # end
         | 
| 79 | 
            +
             | 
| 58 80 | 
             
                  # Author(s):: 0day Inc. <support@0dayinc.com>
         | 
| 59 81 |  | 
| 60 82 | 
             
                  public_class_method def self.authors
         | 
    
        data/lib/pwn/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 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.156
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - 0day Inc.
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024-06- | 
| 11 | 
            +
            date: 2024-06-05 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activesupport
         |