pwn 0.5.372 → 0.5.374

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30f6af2864825daf73f441f4514d403f6ec8285c1aca533f12478897d311517d
4
- data.tar.gz: d403f602bcf9dfc75d57d11cc96ab5a9fffc8cda9fcf81ef0dbc557c3a355ea1
3
+ metadata.gz: 5e3d39b23ca8adc50aa84b082725e3902936b071cdc77c03858a0f24927090a9
4
+ data.tar.gz: 5285148a2126bdaf0ed30f18eafcb694d58d0046e34f2d936ce0b2298bcf64e9
5
5
  SHA512:
6
- metadata.gz: 28741ba677e71a721874d623f6876e3ea68f8437ff58e102d45ba66f627b531bbcb788cd1d5997151b5f012b0e6a061fad98129a405af244b6dd0b3c4e712170
7
- data.tar.gz: 842169d61f868c45c9c68259abe9cf547e559176a314a29a5d75e5a2702921e0a99c611f84237b0b85a1ebd8edf2d14d5012a327da26fa80fa0586dd9f1f9035
6
+ metadata.gz: 0def031c11dc952a56ae71f46bfe7120784e985d6877d55c5018a7684f19d4b01faafc148c968c6fcfa72a9adae87db6510f50e8a52d054403514e0454ee5001
7
+ data.tar.gz: e4c4ced2f59e4905a4201e294e51013c141ff78d273d7ca510a6fef07e82cd89a6e4b2d11433ecccc290f13bc02a4e01e96744ba02d321f72dc8bef91a2acd63
data/.rubocop.yml CHANGED
@@ -8,19 +8,19 @@ Lint/UselessRescue:
8
8
  Metrics/AbcSize:
9
9
  Max: 537.6
10
10
  Metrics/BlockLength:
11
- Max: 220
11
+ Max: 292
12
12
  Metrics/BlockNesting:
13
- Max: 5
13
+ Max: 6
14
14
  Metrics/ClassLength:
15
15
  Max: 134
16
16
  Metrics/CyclomaticComplexity:
17
- Max: 123
17
+ Max: 157
18
18
  Metrics/MethodLength:
19
19
  Max: 485
20
20
  Metrics/ModuleLength:
21
21
  Max: 1000
22
22
  Metrics/PerceivedComplexity:
23
- Max: 122
23
+ Max: 156
24
24
  Style/HashEachMethods:
25
25
  Enabled: true
26
26
  Style/HashSyntax:
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.372]:001 >>> PWN.help
40
+ pwn[v0.5.374]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI)
@@ -52,7 +52,7 @@ $ rvm use ruby-3.4.4@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.5.372]:001 >>> PWN.help
55
+ pwn[v0.5.374]: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.372]:001 >>> PWN.help
65
+ pwn[v0.5.374]: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:
@@ -4,6 +4,7 @@ require 'base64'
4
4
  require 'json'
5
5
  require 'socket'
6
6
  require 'uri'
7
+ require 'yaml'
7
8
 
8
9
  module PWN
9
10
  module Plugins
@@ -460,9 +461,14 @@ module PWN
460
461
  debug = opts[:debug] || false
461
462
 
462
463
  # Parse the OpenAPI JSON or YAML specification file
463
- # If the opeenapi_spec is YAML, convert it to JSON
464
- openapi = JSON.parse(File.read(openapi_spec), symbolize_names: true) if openapi_spec.end_with?('.json')
465
- openapi = YAML.safe_load_file(openapi_spec, permitted_classes: [Symbol, Date, Time], aliases: true, symbolize_names: true) if openapi_spec.end_with?('.yaml', '.yml')
464
+ # If the openapi_spec is YAML, convert it to JSON
465
+ openapi = if openapi_spec.end_with?('.json')
466
+ JSON.parse(File.read(openapi_spec), symbolize_names: true)
467
+ elsif openapi_spec.end_with?('.yaml', '.yml')
468
+ YAML.safe_load_file(openapi_spec, permitted_classes: [Symbol, Date, Time], aliases: true, symbolize_names: true)
469
+ else
470
+ raise "ERROR: Unsupported file extension for #{openapi_spec}. Expected .json, .yaml, or .yml."
471
+ end
466
472
 
467
473
  # Initialize result array
468
474
  sitemap_arr = []
@@ -477,6 +483,19 @@ module PWN
477
483
  # Valid HTTP methods for validation
478
484
  valid_methods = %w[get post put patch delete head options trace connect]
479
485
 
486
+ # Helper lambda to resolve $ref in schemas
487
+ resolve_ref = lambda do |openapi, ref|
488
+ return nil unless ref&.start_with?('#/')
489
+
490
+ parts = ref.sub('#/', '').split('/')
491
+ resolved = openapi
492
+ parts.each do |part|
493
+ resolved = resolved[part.to_sym]
494
+ return nil unless resolved
495
+ end
496
+ resolved
497
+ end
498
+
480
499
  # Iterate through each server
481
500
  servers.each do |server|
482
501
  server_url = server[:url]
@@ -609,6 +628,88 @@ module PWN
609
628
  all_parameters = path_parameters + operation_parameters
610
629
  warn("[DEBUG] All parameters for #{method_str.upcase} #{full_path}: #{all_parameters.inspect}") if debug && !all_parameters.empty?
611
630
 
631
+ # Determine response code from operation[:responses].keys
632
+ fallback_response_code = 200
633
+ response_keys = operation[:responses].keys
634
+ response_key = response_keys.find { |key| key.to_s.to_i.between?(100, 599) } || fallback_response_code.to_s
635
+ response_code = response_key.to_s.to_i
636
+
637
+ # Construct response body from operation responses schema example, schema $ref example, etc.
638
+ response_obj = operation[:responses][response_key] || {}
639
+ content = response_obj[:content] || {}
640
+ content_type = content.keys.first&.to_s || 'text/plain'
641
+
642
+ response_body = ''
643
+ unless [204, 304].include?(response_code)
644
+ content_obj = content[content_type.to_sym] || {}
645
+ example = content_obj[:example]
646
+ if example.nil? && content_obj[:examples].is_a?(Hash)
647
+ ex_key = content_obj[:examples].keys.first
648
+ if ex_key
649
+ ex = content_obj[:examples][ex_key]
650
+ if ex[:$ref]
651
+ resolved_ex = resolve_ref.call(openapi, ex[:$ref])
652
+ example = resolved_ex[:value] if resolved_ex
653
+ else
654
+ example = ex[:value]
655
+ end
656
+ end
657
+ end
658
+
659
+ if example.nil?
660
+ schema = content_obj[:schema]
661
+ if schema
662
+ if schema[:$ref]
663
+ ref = schema[:$ref]
664
+ if ref.start_with?('#/')
665
+ parts = ref.sub('#/', '').split('/')
666
+ resolved = openapi
667
+ parts.each do |part|
668
+ resolved = resolved[part.to_sym]
669
+ break unless resolved
670
+ end
671
+ schema = resolved if resolved
672
+ end
673
+ end
674
+
675
+ example = schema[:example]
676
+ if example.nil? && schema[:examples].is_a?(Hash)
677
+ ex_key = schema[:examples].keys.first
678
+ if ex_key
679
+ ex = schema[:examples][ex_key]
680
+ if ex[:$ref]
681
+ resolved_ex = resolve_ref.call(openapi, ex[:$ref])
682
+ example = resolved_ex[:value] if resolved_ex
683
+ else
684
+ example = ex[:value]
685
+ end
686
+ end
687
+ end
688
+ end
689
+ end
690
+
691
+ response_body = example || response_obj[:description] || "INFO: Unable to resolve response body from #{openapi_spec} => { 'http_method': '#{method_str.upcase}', 'path': '#{full_path}', 'response_code': '#{response_code}' }"
692
+ end
693
+
694
+ # Try to extract query samples from response example if it's a links object
695
+ query_hash = nil
696
+ if response_body.is_a?(Hash) && response_body[:links]
697
+ href = response_body.dig(:links, :self, :href)
698
+ # href ||= response_body[:links].values.first&.dig(:href) rescue nil
699
+ if href.nil? && response_body[:links].is_a?(Hash) && !response_body[:links].empty?
700
+ first_value = response_body[:links].values.first
701
+ href = first_value[:href] if first_value.is_a?(Hash)
702
+ end
703
+ if href
704
+ begin
705
+ parsed_uri = URI.parse(href)
706
+ query_hash = URI.decode_www_form(parsed_uri.query).to_h if parsed_uri.path.end_with?(path_str) && parsed_uri.query
707
+ rescue URI::InvalidURIError => e
708
+ warn("[DEBUG] Invalid href in response example: #{href} - #{e.message}") if debug
709
+ end
710
+ end
711
+ end
712
+
612
713
  # Process path parameters for substitution
613
714
  request_path = full_path.dup
614
715
  query_params = []
@@ -617,21 +718,67 @@ module PWN
617
718
  next unless param.is_a?(Hash) && param[:name] && param[:in]
618
719
 
619
720
  param_name = param[:name].to_s
721
+
722
+ # Get param_value with precedence: param.examples > param.example > schema.examples > schema.example > 'FUZZ'
723
+ param_value = if param[:examples].is_a?(Hash) && !param[:examples].empty?
724
+ first_ex = param[:examples].values.first
725
+ if first_ex.is_a?(Hash)
726
+ if first_ex[:$ref]
727
+ # Resolve $ref for example if present
728
+ resolved_ex = resolve_ref.call(openapi, first_ex[:$ref])
729
+ resolved_ex[:value] if resolved_ex
730
+ else
731
+ first_ex[:value]
732
+ end
733
+ else
734
+ first_ex
735
+ end || 'FUZZ'
736
+ elsif param.key?(:example)
737
+ param[:example]
738
+ else
739
+ schema = param[:schema]
740
+ if schema
741
+ if schema[:$ref]
742
+ resolved_schema = resolve_ref.call(openapi, schema[:$ref])
743
+ schema = resolved_schema if resolved_schema
744
+ end
745
+ if schema[:examples].is_a?(Hash) && !schema[:examples].empty?
746
+ first_ex = schema[:examples].values.first
747
+ if first_ex.is_a?(Hash)
748
+ if first_ex[:$ref]
749
+ resolved_ex = resolve_ref.call(openapi, first_ex[:$ref])
750
+ resolved_ex[:value] if resolved_ex
751
+ else
752
+ first_ex[:value]
753
+ end
754
+ else
755
+ first_ex
756
+ end || 'FUZZ'
757
+ elsif schema.key?(:example)
758
+ schema[:example]
759
+ else
760
+ 'FUZZ'
761
+ end
762
+ else
763
+ 'FUZZ'
764
+ end
765
+ end
766
+
767
+ # If still 'FUZZ' and it's a query param, try to get from response example query_hash
768
+ param_value = query_hash[param_name] if param_value == 'FUZZ' && param[:in] == 'query' && query_hash&.key?(param_name)
769
+
620
770
  case param[:in]
621
771
  when 'header'
622
772
  # Aggregate remaining HTTP header names from spec,
623
773
  # reference as keys, and assign their respective
624
774
  # values to the request_headers hash
625
775
  param_key = param_name.downcase
626
- param_value = param[:schema]&.dig(:example) || 'FUZZ'
627
776
  request_headers[param_key] = param_value.to_s
628
777
  when 'path'
629
- # Substitute path parameter with a default value (e.g., 'FUZZ')
630
- param_value = param[:schema]&.dig(:example) || 'FUZZ'
778
+ # Substitute path parameter with the resolved value
631
779
  request_path.gsub!("{#{param_name}}", param_value.to_s)
632
780
  when 'query'
633
781
  # Collect query parameters
634
- param_value = param[:schema]&.dig(:example) || 'FUZZ'
635
782
  query_params.push("#{URI.encode_www_form_component(param_name)}=#{URI.encode_www_form_component(param_value.to_s)}")
636
783
  end
637
784
  end
@@ -653,12 +800,6 @@ module PWN
653
800
  request = request_lines.join("\r\n")
654
801
  encoded_request = Base64.strict_encode64(request)
655
802
 
656
- # Determine response code from operation[:responses].keys
657
- fallback_response_code = 200
658
- response_keys = operation[:responses].keys
659
- response_key = response_keys.find { |key| key.to_s.to_i.between?(100, 599) } || fallback_response_code.to_s
660
- response_code = response_key.to_s.to_i
661
-
662
803
  response_status = case response_code
663
804
  when 200 then '200 OK'
664
805
  when 201 then '201 Created'
@@ -680,52 +821,11 @@ module PWN
680
821
  else "#{fallback_response_code} OK"
681
822
  end
682
823
 
683
- # Construct response body from operation responses schema example, schema $ref example, etc.
684
- response_obj = operation[:responses][response_key] || {}
685
- content = response_obj[:content] || {}
686
- content_type = content.keys.first&.to_s || 'text/plain'
687
-
688
- response_body = ''
689
- unless [204, 304].include?(response_code)
690
- content_obj = content[content_type.to_sym] || {}
691
- example = content_obj[:example]
692
- if example.nil? && content_obj[:examples].is_a?(Hash)
693
- ex_key = content_obj[:examples].keys.first
694
- example = content_obj[:examples][ex_key][:value] if ex_key
695
- end
696
-
697
- if example.nil?
698
- schema = content_obj[:schema]
699
- if schema
700
- if schema[:$ref]
701
- ref = schema[:$ref]
702
- if ref.start_with?('#/')
703
- parts = ref.sub('#/', '').split('/')
704
- resolved = openapi
705
- parts.each do |part|
706
- resolved = resolved[part.to_sym]
707
- break unless resolved
708
- end
709
- schema = resolved if resolved
710
- end
711
- end
712
-
713
- example = schema[:example]
714
- if example.nil? && schema[:examples].is_a?(Hash)
715
- ex_key = schema[:examples].keys.first
716
- example = schema[:examples][ex_key][:value] if ex_key
717
- end
718
- end
719
- end
720
-
721
- response_body = example || response_obj[:description] || "INFO: Unable to resolve response body from #{openapi_spec} => { 'http_method': '#{method_str.upcase}', 'path': '#{request_path}', 'response_code': '#{response_code}' }"
722
-
723
- # Serialize based on content_type
724
- if content_type =~ /json/i && (response_body.is_a?(Hash) || response_body.is_a?(Array))
725
- response_body = JSON.generate(response_body)
726
- else
727
- response_body = response_body.to_s
728
- end
824
+ # Serialize response_body based on content_type
825
+ if content_type =~ /json/i && (response_body.is_a?(Hash) || response_body.is_a?(Array))
826
+ response_body = JSON.generate(response_body)
827
+ else
828
+ response_body = response_body.to_s
729
829
  end
730
830
 
731
831
  response_lines = [
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.372'
4
+ VERSION = '0.5.374'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.372
4
+ version: 0.5.374
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.