pwn 0.5.164 → 0.5.165

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/README.md +3 -3
  4. data/bin/pwn +1 -1
  5. data/bin/pwn_android_war_dialer +2 -2
  6. data/bin/pwn_aws_describe_resources +2 -2
  7. data/bin/pwn_bdba_groups +2 -2
  8. data/bin/pwn_bdba_scan +2 -2
  9. data/bin/pwn_burp_suite_pro_active_scan +2 -2
  10. data/bin/pwn_char_base64_encoding +2 -2
  11. data/bin/pwn_char_dec_encoding +2 -2
  12. data/bin/pwn_char_hex_escaped_encoding +2 -2
  13. data/bin/pwn_char_html_entity_encoding +2 -2
  14. data/bin/pwn_char_unicode_escaped_encoding +2 -2
  15. data/bin/pwn_char_url_encoding +2 -2
  16. data/bin/pwn_crt_sh +82 -0
  17. data/bin/pwn_defectdojo_engagement_create +2 -2
  18. data/bin/pwn_defectdojo_importscan +2 -2
  19. data/bin/pwn_defectdojo_reimportscan +2 -2
  20. data/bin/pwn_diff_csv_files_w_column_exclude +4 -4
  21. data/bin/pwn_domain_reversewhois +2 -2
  22. data/bin/pwn_fuzz_net_app_proto +3 -3
  23. data/bin/pwn_gqrx_scanner +2 -2
  24. data/bin/pwn_jenkins_create_job +2 -2
  25. data/bin/pwn_jenkins_create_view +2 -2
  26. data/bin/pwn_jenkins_install_plugin +2 -2
  27. data/bin/pwn_jenkins_thinBackup_aws_s3 +2 -2
  28. data/bin/pwn_jenkins_update_plugins +2 -2
  29. data/bin/pwn_jenkins_useradd +2 -2
  30. data/bin/pwn_mail_agent +2 -2
  31. data/bin/pwn_nessus_cloud_scan_crud +2 -2
  32. data/bin/pwn_nessus_cloud_vulnscan +2 -2
  33. data/bin/pwn_nexpose +2 -2
  34. data/bin/pwn_nmap_discover_tcp_udp +2 -2
  35. data/bin/pwn_openvas_vulnscan +2 -2
  36. data/bin/pwn_owasp_zap_active_scan +2 -2
  37. data/bin/pwn_pastebin_sample_filter +2 -2
  38. data/bin/pwn_phone +3 -3
  39. data/bin/pwn_sast +3 -3
  40. data/bin/pwn_serial_check_voicemail +2 -2
  41. data/bin/pwn_serial_msr206 +2 -2
  42. data/bin/pwn_serial_son_micro_sm132_rfid +2 -2
  43. data/bin/pwn_shodan_graphql_introspection +1 -1
  44. data/bin/pwn_shodan_search +2 -2
  45. data/bin/pwn_simple_http_server +1 -1
  46. data/bin/pwn_web_cache_deception +4 -4
  47. data/bin/pwn_www_checkip +3 -1
  48. data/bin/pwn_www_uri_buster +141 -57
  49. data/bin/pwn_xss_dom_vectors +2 -2
  50. data/lib/pwn/plugins/authentication_helper.rb +2 -2
  51. data/lib/pwn/plugins/ip_info.rb +2 -0
  52. data/lib/pwn/plugins/log.rb +3 -1
  53. data/lib/pwn/plugins/repl.rb +3 -1
  54. data/lib/pwn/plugins/thread_pool.rb +12 -42
  55. data/lib/pwn/plugins/tor.rb +51 -12
  56. data/lib/pwn/plugins/transparent_browser.rb +3 -0
  57. data/lib/pwn/plugins/voice.rb +3 -3
  58. data/lib/pwn/reports/uri_buster.rb +18 -6
  59. data/lib/pwn/version.rb +1 -1
  60. metadata +8 -6
@@ -8,7 +8,7 @@ require 'yaml'
8
8
  opts = {}
9
9
  OptionParser.new do |options|
10
10
  options.banner = "USAGE:
11
- #{$PROGRAM_NAME} [opts]
11
+ #{File.basename($PROGRAM_NAME)} [opts]
12
12
  "
13
13
 
14
14
  options.on('-cYPATH', '--yaml-config=YPATH', '<Required - YAML Config Containing Access & Secret Keys for Authentication>') do |c|
@@ -29,7 +29,7 @@ OptionParser.new do |options|
29
29
  end.parse!
30
30
 
31
31
  if opts.empty?
32
- puts `#{$PROGRAM_NAME} --help`
32
+ puts `#{File.basename($PROGRAM_NAME)} --help`
33
33
  exit 1
34
34
  end
35
35
 
data/bin/pwn_nexpose CHANGED
@@ -7,7 +7,7 @@ require 'optparse'
7
7
  opts = {}
8
8
  OptionParser.new do |options|
9
9
  options.banner = "USAGE:
10
- #{$PROGRAM_NAME} [opts]
10
+ #{File.basename($PROGRAM_NAME)} [opts]
11
11
  "
12
12
 
13
13
  # Nexpose Flags
@@ -19,7 +19,7 @@ OptionParser.new do |options|
19
19
  end.parse!
20
20
 
21
21
  if opts.empty?
22
- puts `#{$PROGRAM_NAME} --help`
22
+ puts `#{File.basename($PROGRAM_NAME)} --help`
23
23
  exit 1
24
24
  end
25
25
 
@@ -9,7 +9,7 @@ require 'time'
9
9
  opts = {}
10
10
  OptionParser.new do |options|
11
11
  options.banner = "USAGE:
12
- #{$PROGRAM_NAME} [opts]
12
+ #{File.basename($PROGRAM_NAME)} [opts]
13
13
  "
14
14
 
15
15
  options.on('-fFILE', '--target-file=FILE', '<Required if --target-range excluded - File containing one supported nmap target / line e.g. foo.bar, 10.1.1.1, 192.168.1.1-20, 192.168.1.0/24, etc>') do |f|
@@ -42,7 +42,7 @@ OptionParser.new do |options|
42
42
  end.parse!
43
43
 
44
44
  if opts.empty?
45
- puts `#{$PROGRAM_NAME} --help`
45
+ puts `#{File.basename($PROGRAM_NAME)} --help`
46
46
  exit 1
47
47
  end
48
48
 
@@ -8,7 +8,7 @@ require 'yaml'
8
8
  opts = {}
9
9
  OptionParser.new do |options|
10
10
  options.banner = "USAGE:
11
- #{$PROGRAM_NAME} [opts]
11
+ #{File.basename($PROGRAM_NAME)} [opts]
12
12
  "
13
13
 
14
14
  options.on('-cYPATH', '--yaml-config=YPATH', '<Required - YAML Config Containing Username & Password for Authentication>') do |c|
@@ -29,7 +29,7 @@ OptionParser.new do |options|
29
29
  end.parse!
30
30
 
31
31
  if opts.empty?
32
- puts `#{$PROGRAM_NAME} --help`
32
+ puts `#{File.basename($PROGRAM_NAME)} --help`
33
33
  exit 1
34
34
  end
35
35
 
@@ -7,7 +7,7 @@ require 'optparse'
7
7
  opts = {}
8
8
  OptionParser.new do |options|
9
9
  options.banner = "USAGE:
10
- #{$PROGRAM_NAME} [opts]
10
+ #{File.basename($PROGRAM_NAME)} [opts]
11
11
  "
12
12
 
13
13
  options.on('-aAPIKEY', '--api_key=APIKEY', '<Required - OWASP Zap API Key (Tools>Options>API)>') do |a|
@@ -44,7 +44,7 @@ OptionParser.new do |options|
44
44
  end.parse!
45
45
 
46
46
  if opts.empty?
47
- puts `#{$PROGRAM_NAME} --help`
47
+ puts `#{File.basename($PROGRAM_NAME)} --help`
48
48
  exit 1
49
49
  end
50
50
 
@@ -7,7 +7,7 @@ require 'optparse'
7
7
  opts = {}
8
8
  OptionParser.new do |options|
9
9
  options.banner = "USAGE:
10
- #{$PROGRAM_NAME} [opts]
10
+ #{File.basename($PROGRAM_NAME)} [opts]
11
11
  "
12
12
 
13
13
  options.on('-rPATTERN', '--regex=PATTERN', "<Required - Regex Pattern for Interesting Pastes ('.*' for All)>") do |r|
@@ -20,7 +20,7 @@ OptionParser.new do |options|
20
20
  end.parse!
21
21
 
22
22
  if opts.empty?
23
- puts `#{$PROGRAM_NAME} --help`
23
+ puts `#{File.basename($PROGRAM_NAME)} --help`
24
24
  exit 1
25
25
  end
26
26
 
data/bin/pwn_phone CHANGED
@@ -11,7 +11,7 @@ require 'waveform'
11
11
  opts = {}
12
12
  OptionParser.new do |options|
13
13
  options.banner = "USAGE:
14
- #{$PROGRAM_NAME} [opts]
14
+ #{File.basename($PROGRAM_NAME)} [opts]
15
15
  "
16
16
 
17
17
  options.on('-tPATH', '--target-file=PATH', '<Required - File Containing List of Targets to Dial>') do |t|
@@ -64,7 +64,7 @@ OptionParser.new do |options|
64
64
  end.parse!
65
65
 
66
66
  if opts.empty?
67
- puts `#{$PROGRAM_NAME} --help`
67
+ puts `#{File.basename($PROGRAM_NAME)} --help`
68
68
  exit 1
69
69
  end
70
70
 
@@ -105,7 +105,7 @@ begin
105
105
  )
106
106
 
107
107
  # Generate HTML Report
108
- print "#{$PROGRAM_NAME} Generating Report..."
108
+ print "#{File.basename($PROGRAM_NAME)} Generating Report..."
109
109
  PWN::Reports::Phone.generate(
110
110
  dir_path: session_root,
111
111
  results_hash: results_hash
data/bin/pwn_sast CHANGED
@@ -8,7 +8,7 @@ require 'htmlentities'
8
8
  opts = {}
9
9
  OptionParser.new do |options|
10
10
  options.banner = "USAGE:
11
- #{$PROGRAM_NAME} [opts]
11
+ #{File.basename($PROGRAM_NAME)} [opts]
12
12
  "
13
13
 
14
14
  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|
@@ -41,7 +41,7 @@ OptionParser.new do |options|
41
41
  end.parse!
42
42
 
43
43
  if opts.empty?
44
- puts `#{$PROGRAM_NAME} --help`
44
+ puts `#{File.basename($PROGRAM_NAME)} --help`
45
45
  exit 1
46
46
  end
47
47
 
@@ -155,7 +155,7 @@ begin
155
155
  end
156
156
 
157
157
  # Generate HTML Report
158
- print "#{$PROGRAM_NAME} Generating Report..."
158
+ print "#{File.basename($PROGRAM_NAME)} Generating Report..."
159
159
  PWN::Reports::SAST.generate(
160
160
  dir_path: dir_path,
161
161
  results_hash: results_hash
@@ -7,7 +7,7 @@ require 'optparse'
7
7
  opts = {}
8
8
  OptionParser.new do |options|
9
9
  options.banner = "USAGE:
10
- #{$PROGRAM_NAME} [opts]
10
+ #{File.basename($PROGRAM_NAME)} [opts]
11
11
  "
12
12
 
13
13
  options.on('-vNUM', '--voicemail_num=num', '<Required - Number to Voicemail Service>') do |num|
@@ -19,7 +19,7 @@ OptionParser.new do |options|
19
19
  end.parse!
20
20
 
21
21
  if opts.empty?
22
- puts `#{$PROGRAM_NAME} --help`
22
+ puts `#{File.basename($PROGRAM_NAME)} --help`
23
23
  exit 1
24
24
  end
25
25
 
@@ -8,7 +8,7 @@ require 'json'
8
8
  opts = {}
9
9
  OptionParser.new do |options|
10
10
  options.banner = "USAGE:
11
- #{$PROGRAM_NAME} [opts]
11
+ #{File.basename($PROGRAM_NAME)} [opts]
12
12
  "
13
13
 
14
14
  options.on('-dDEV', '--block-dev=DEV', '<Optional - MSR206 block device path (defaults to /dev/ttyUSB0)>') do |d|
@@ -37,7 +37,7 @@ OptionParser.new do |options|
37
37
  end.parse!
38
38
 
39
39
  if opts.empty?
40
- puts `#{$PROGRAM_NAME} --help`
40
+ puts `#{File.basename($PROGRAM_NAME)} --help`
41
41
  exit 1
42
42
  end
43
43
 
@@ -8,7 +8,7 @@ require 'json'
8
8
  opts = {}
9
9
  OptionParser.new do |options|
10
10
  options.banner = "USAGE:
11
- #{$PROGRAM_NAME} [opts]
11
+ #{File.basename($PROGRAM_NAME)} [opts]
12
12
  "
13
13
 
14
14
  options.on('-dDEV', '--block-dev=DEV', '<Optional - SonMicroRFID block device path (defaults to /dev/ttyUSB0)>') do |d|
@@ -37,7 +37,7 @@ OptionParser.new do |options|
37
37
  end.parse!
38
38
 
39
39
  if opts.empty?
40
- puts `#{$PROGRAM_NAME} --help`
40
+ puts `#{File.basename($PROGRAM_NAME)} --help`
41
41
  exit 1
42
42
  end
43
43
 
@@ -14,7 +14,7 @@ OptionParser.new do |options|
14
14
  end.parse!
15
15
 
16
16
  if opts.empty?
17
- puts `#{$PROGRAM_NAME} --help`
17
+ puts `#{File.basename($PROGRAM_NAME)} --help`
18
18
  exit 1
19
19
  end
20
20
 
@@ -9,7 +9,7 @@ require 'json'
9
9
  opts = {}
10
10
  OptionParser.new do |options|
11
11
  options.banner = "USAGE:
12
- #{$PROGRAM_NAME} [opts]
12
+ #{File.basename($PROGRAM_NAME)} [opts]
13
13
  "
14
14
 
15
15
  options.on('-cYAML', '--config-yaml=YAML', '<Required - YAML config containing api_key from Shodan.io>') do |y|
@@ -30,7 +30,7 @@ OptionParser.new do |options|
30
30
  end.parse!
31
31
 
32
32
  if opts.empty?
33
- puts `#{$PROGRAM_NAME} --help`
33
+ puts `#{File.basename($PROGRAM_NAME)} --help`
34
34
  exit 1
35
35
  end
36
36
 
@@ -6,7 +6,7 @@ require 'optparse'
6
6
  opts = {}
7
7
  OptionParser.new do |options|
8
8
  options.banner = "USAGE:
9
- #{$PROGRAM_NAME} [opts]
9
+ #{File.basename($PROGRAM_NAME)} [opts]
10
10
  "
11
11
  options.on('-iIP', '--bind-ip=IP', '<Optional - HTTP Bind IP to Listen> (Defaults to Localhost)') do |i|
12
12
  opts[:bind_ip] = i
@@ -7,7 +7,7 @@ require 'optparse'
7
7
  opts = {}
8
8
  OptionParser.new do |options|
9
9
  options.banner = "USAGE:
10
- #{$PROGRAM_NAME} [opts]
10
+ #{File.basename($PROGRAM_NAME)} [opts]
11
11
  "
12
12
 
13
13
  options.on('-tURL', '--target-url=URL', '<Required - URL to Target>') do |t|
@@ -44,7 +44,7 @@ OptionParser.new do |options|
44
44
  end.parse!
45
45
 
46
46
  if opts.empty?
47
- puts `#{$PROGRAM_NAME} --help`
47
+ puts `#{File.basename($PROGRAM_NAME)} --help`
48
48
  exit 1
49
49
  end
50
50
 
@@ -142,12 +142,12 @@ begin
142
142
  pwn_www_mod = PWN::WWW.const_get(pwn_www_mod_str)
143
143
  unless pwn_www_mod.respond_to?('open') && pwn_www_mod.respond_to?('login') && pwn_www_mod.respond_to?('logout')
144
144
  puts "#{@dark_red}Module PWN::WWW::#{pwn_www_mod_str} Missing #open #login and/or #logout Method(s)#{@end_of_color}"
145
- puts `#{$PROGRAM_NAME} -l`
145
+ puts `#{File.basename($PROGRAM_NAME)} -l`
146
146
  exit
147
147
  end
148
148
  rescue NAME_ERROR => e
149
149
  puts "#{@dark_red}Invalid module PWN::WWW::#{pwn_www_mod_str}#{@end_of_color}"
150
- puts `#{$PROGRAM_NAME} -l`
150
+ puts `#{File.basename($PROGRAM_NAME)} -l`
151
151
  exit
152
152
  end
153
153
 
data/bin/pwn_www_checkip CHANGED
@@ -8,7 +8,7 @@ require 'optparse'
8
8
  opts = {}
9
9
  OptionParser.new do |options|
10
10
  options.banner = "USAGE:
11
- #{$PROGRAM_NAME} [opts]
11
+ #{File.basename($PROGRAM_NAME)} [opts]
12
12
  "
13
13
  options.on('-tIP_HOST', '--target=IP_HOST', '<Optional - IP or Host to Check (Default - Your Public IP)>') do |t|
14
14
  opts[:target] = t
@@ -64,6 +64,8 @@ begin
64
64
  )
65
65
  puts JSON.pretty_generate(ip_info_obj)
66
66
  end
67
+ rescue Interrupt
68
+ puts "\n#{File.basename($PROGRAM_NAME)} => Goodbye."
67
69
  rescue StandardError => e
68
70
  raise e
69
71
  ensure
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: false
3
3
 
4
+ require 'time'
4
5
  require 'pwn'
5
6
  require 'optparse'
6
7
  require 'uri'
@@ -12,7 +13,7 @@ require 'json'
12
13
  opts = {}
13
14
  OptionParser.new do |options|
14
15
  options.banner = "USAGE:
15
- #{$PROGRAM_NAME} [opts]
16
+ #{File.basename($PROGRAM_NAME)} [opts]
16
17
  "
17
18
 
18
19
  options.on('-uURL', '--target-url=URL', '<Required - Target URL)>') do |t|
@@ -27,6 +28,14 @@ OptionParser.new do |options|
27
28
  opts[:append_pattern] = a
28
29
  end
29
30
 
31
+ options.on('-TFLOAT', '--max-timeout=FLOAT', '<Optional - HTTP Request Timeout (Default 6.0 Seconds)>') do |t|
32
+ opts[:max_timeout] = t
33
+ end
34
+
35
+ options.on('-TFLOAT', '--max-retries=FLOAT', '<Optional - HTTP Request Retries (Default 3)>') do |r|
36
+ opts[:max_retries] = r
37
+ end
38
+
30
39
  options.on('-pPROXY', '--proxy=PROXY', '<Optional - Proxy SCHEME://ADDRESS:PORT || tor>') do |p|
31
40
  opts[:proxy] = p
32
41
  end
@@ -61,7 +70,7 @@ OptionParser.new do |options|
61
70
  end.parse!
62
71
 
63
72
  if opts.empty?
64
- puts `#{$PROGRAM_NAME} --help`
73
+ puts `#{File.basename($PROGRAM_NAME)} --help`
65
74
  exit 1
66
75
  end
67
76
 
@@ -71,43 +80,60 @@ def request_path(opts = {})
71
80
  http_request_headers = opts[:http_request_headers]
72
81
  wordlist_line = opts[:wordlist_line]
73
82
  http_method = opts[:http_method]
83
+ max_timeout = opts[:max_timeout]
84
+ timeout = opts[:max_timeout] ||= max_timeout / 21.0
85
+ max_retries = opts[:max_retries]
74
86
 
75
87
  rest_client_resp_hash = {}
76
- begin
77
- print '.'
78
- http_uri = "#{target_url}/#{wordlist_line}"
79
- if proxy
80
- browser_obj = PWN::Plugins::TransparentBrowser.open(
81
- browser_type: :rest,
82
- proxy: proxy
83
- )
84
- else
85
- browser_obj = PWN::Plugins::TransparentBrowser.open(
86
- browser_type: :rest
87
- )
88
- end
89
- rest_client = browser_obj[:browser]::Request
90
88
 
91
- headers = {
92
- user_agent: Faker::Internet.user_agent
93
- }
89
+ # request_count && timeout values to begin with
90
+ request_count = 1
94
91
 
95
- if http_request_headers
96
- headers = JSON.parse(
97
- http_request_headers,
98
- symbolize_names: true
99
- )
100
- end
92
+ if proxy
93
+ browser_obj = PWN::Plugins::TransparentBrowser.open(
94
+ browser_type: :rest,
95
+ proxy: proxy
96
+ )
97
+ else
98
+ browser_obj = PWN::Plugins::TransparentBrowser.open(
99
+ browser_type: :rest
100
+ )
101
+ end
102
+ rest_client = browser_obj[:browser]::Request
103
+
104
+ headers = {
105
+ user_agent: Faker::Internet.user_agent
106
+ }
107
+
108
+ http_uri = "#{target_url}/#{wordlist_line}"
109
+
110
+ if http_request_headers
111
+ headers = JSON.parse(
112
+ http_request_headers,
113
+ symbolize_names: true
114
+ )
115
+ end
116
+
117
+ timestamp_fmt = '%Y-%m-%d %H:%M:%S.%L'
101
118
 
119
+ begin
120
+ request_timestamp = Time.now.strftime(timestamp_fmt)
102
121
  response = rest_client.execute(
103
122
  method: http_method,
104
123
  url: http_uri,
105
124
  headers: headers,
106
- verify_ssl: false
125
+ verify_ssl: false,
126
+ timeout: timeout
107
127
  )
128
+ response_timestamp = Time.now.strftime(timestamp_fmt)
129
+
130
+ # duration in milliseconds
131
+ duration = Time.parse(response_timestamp) - Time.parse(request_timestamp)
108
132
 
109
133
  rest_client_resp_hash = {
110
- request_timestamp: Time.now.strftime('%Y-%m-%d_%H-%M-%S'),
134
+ request_timestamp: request_timestamp,
135
+ response_timestamp: response_timestamp,
136
+ duration: duration,
111
137
  http_uri: http_uri,
112
138
  http_method: http_method,
113
139
  http_resp_code: response.code,
@@ -115,9 +141,8 @@ def request_path(opts = {})
115
141
  http_resp_headers: JSON.pretty_generate(response.headers),
116
142
  http_resp: "#{response.body[0..300]}..."
117
143
  }
118
- rescue Errno::ECONNREFUSED
119
- raise 'ERROR: Connection(s) Refused. Try lowering the --max-threads value.'
120
- rescue Errno::ECONNRESET,
144
+ rescue Errno::ECONNREFUSED,
145
+ Errno::ECONNRESET,
121
146
  NoMethodError,
122
147
  OpenSSL::SSL::SSLError,
123
148
  RestClient::Exceptions::ReadTimeout,
@@ -125,9 +150,16 @@ def request_path(opts = {})
125
150
  RestClient::ServerBrokeConnection,
126
151
  SOCKSError => e
127
152
 
153
+ timeout += 0.1
154
+ retry if timeout < max_timeout
155
+
128
156
  # May be best to switch Tor channel if SOCKSError is rescued
157
+ response_timestamp = Time.now.strftime(timestamp_fmt)
158
+ duration = Time.parse(response_timestamp) - Time.parse(request_timestamp)
129
159
  rest_client_resp_hash = {
130
- request_timestamp: Time.now.strftime('%Y-%m-%d_%H-%M-%S'),
160
+ request_timestamp: request_timestamp,
161
+ response_timestamp: response_timestamp,
162
+ duration: duration,
131
163
  http_uri: http_uri,
132
164
  http_method: http_method,
133
165
  http_resp_code: e.class,
@@ -136,9 +168,14 @@ def request_path(opts = {})
136
168
  http_resp: "ERROR: #{e.message}"
137
169
  }
138
170
  rescue RestClient::ExceptionWithResponse => e
171
+ response_timestamp = Time.now.strftime(timestamp_fmt)
172
+ duration = Time.parse(response_timestamp) - Time.parse(request_timestamp)
173
+
139
174
  if e.respond_to?(:response)
140
175
  rest_client_resp_hash = {
141
- request_timestamp: Time.now.strftime('%Y-%m-%d_%H-%M-%S'),
176
+ request_timestamp: request_timestamp,
177
+ response_timestamp: response_timestamp,
178
+ duration: duration,
142
179
  http_uri: http_uri,
143
180
  http_method: http_method,
144
181
  http_resp_code: e.response.code,
@@ -148,7 +185,9 @@ def request_path(opts = {})
148
185
  }
149
186
  else
150
187
  resp_client_resp_hash = {
151
- request_timestamp: Time.now.strftime('%Y-%m-%d_%H-%M-%S'),
188
+ request_timestamp: request_timestamp,
189
+ response_timestamp: response_timestamp,
190
+ duration: duration,
152
191
  http_uri: http_uri,
153
192
  http_method: http_method,
154
193
  http_resp_code: 'N/A',
@@ -163,17 +202,20 @@ def request_path(opts = {})
163
202
  url_encoded_wordlist_arr.push(CGI.escape(path))
164
203
  end
165
204
  wordlist_line = url_encoded_wordlist_arr.join('/')
166
-
167
- retry
168
205
  rescue RestClient::TooManyRequests
206
+ request_count += 1
169
207
  sleep 60
170
- ensure
171
- browser_obj = PWN::Plugins::TransparentBrowser.close(
172
- browser_obj: browser_obj
173
- )
208
+
209
+ retry if request_count < max_retries
210
+ rescue SystemExit, Interrupt
211
+ puts "\n#{File.basename($PROGRAM_NAME)}.#{__method__} Goodbye."
174
212
  end
175
213
 
176
214
  rest_client_resp_hash
215
+ ensure
216
+ browser_obj = PWN::Plugins::TransparentBrowser.close(
217
+ browser_obj: browser_obj
218
+ )
177
219
  end
178
220
 
179
221
  begin
@@ -189,18 +231,31 @@ begin
189
231
  raise "ERROR: #{wordlist} Does Not Exist." unless File.exist?(wordlist)
190
232
 
191
233
  append_pattern = opts[:append_pattern]
192
- proxy = opts[:proxy]
234
+ max_timeout = opts[:max_timeout] ||= 6
235
+ if max_timeout
236
+ max_timeout = max_timeout.to_f
237
+ raise 'ERROR: --max-timeout must be a positive float.' unless max_timeout.positive?
238
+ end
193
239
 
194
- max_threads = opts[:max_threads]
195
- max_threads ||= 25
240
+ max_retries = opts[:max_retries] ||= 3
241
+ if max_retries
242
+ max_retries = max_retries.to_i
243
+ raise 'ERROR: --max-retries must be a positive integer.' unless max_retries.positive?
244
+ end
196
245
 
197
- http_request_headers = opts[:http_request_headers]
246
+ proxy = opts[:proxy]
247
+ max_threads = opts[:max_threads] ||= 25
248
+ if max_threads
249
+ max_threads = max_threads.to_i
250
+ raise 'ERROR: --max-threads must be a positive integer.' unless max_threads.positive?
251
+ end
198
252
 
253
+ http_request_headers = opts[:http_request_headers]
199
254
  include_http_response_codes = opts[:include_http_response_codes]
200
- include_http_response_codes = include_http_response_codes.delete("\s").split(',') if include_http_response_codes
255
+ include_http_response_codes = include_http_response_codes.to_s.delete("\s").split(',') if include_http_response_codes
201
256
 
202
257
  exclude_http_response_codes = opts[:exclude_http_response_codes]
203
- exclude_http_response_codes = exclude_http_response_codes.delete("\s").split(',') if exclude_http_response_codes
258
+ exclude_http_response_codes = exclude_http_response_codes.to_s.delete("\s").split(',') if exclude_http_response_codes
204
259
 
205
260
  raise 'ERROR: Flags --include-response-codes and --exclude-response-codes cannot be used together.' if include_http_response_codes && exclude_http_response_codes
206
261
 
@@ -230,33 +285,60 @@ begin
230
285
 
231
286
  next if wordlist_line.match?(/^#/)
232
287
 
233
- http_methods = %i[DELETE GET HEAD OPTIONS PATCH POST PUT TRACE].shuffle
288
+ http_methods = %i[
289
+ DELETE
290
+ GET
291
+ HEAD
292
+ OPTIONS
293
+ PATCH
294
+ POST
295
+ PUT
296
+ TRACE
297
+ ].shuffle
298
+
299
+ timeout = nil
234
300
  http_methods.each do |http_method|
235
301
  # TODO: Implement HTTP response timeout
302
+ prev_rest_client_resp_hash = results_hash[:data].last
236
303
  rest_client_resp_hash = request_path(
237
304
  target_url: target_url,
238
305
  proxy: proxy,
239
306
  http_request_headers: http_request_headers,
240
307
  wordlist_line: "#{wordlist_line}#{append_pattern}",
241
- http_method: http_method
308
+ http_method: http_method,
309
+ max_timeout: max_timeout,
310
+ timeout: timeout,
311
+ max_retries: max_retries
242
312
  )
243
313
 
244
314
  mutex.synchronize do
245
- if include_http_response_codes
246
- ret_http_resp_code = rest_client_resp_hash[:http_resp_code].to_s
247
- results_hash[:data].push(rest_client_resp_hash) if include_http_response_codes.include?(ret_http_resp_code)
248
- elsif exclude_http_response_codes
249
- ret_http_resp_code = rest_client_resp_hash[:http_resp_code].to_s
250
- results_hash[:data].push(rest_client_resp_hash) unless exclude_http_response_codes.include?(ret_http_resp_code)
315
+ ret_http_resp_code = rest_client_resp_hash[:http_resp_code].to_s
316
+ # Better waY to implement this?
317
+ if include_http_response_codes.is_a?(Array)
318
+ # If include_http_response_codes is an array, only include
319
+ # the response into the results_hash if the response code
320
+ # is in the include_http_response_codes array
321
+ results_hash[:data].push(rest_client_resp_hash) if include_http_response_codes.include?(ret_http_resp_code) &&
322
+ prev_rest_client_resp_hash != rest_client_resp_hash &&
323
+ rest_client_resp_hash.any?
251
324
  else
252
- results_hash[:data].push(rest_client_resp_hash)
325
+ # If exclude_http_response_codes is an array, only include
326
+ # the response into the results_hash if the response code
327
+ # is not in the exclude_http_response_codes array
328
+ results_hash[:data].push(rest_client_resp_hash) unless exclude_http_response_codes.is_a?(Array) &&
329
+ exclude_http_response_codes.include?(ret_http_resp_code) &&
330
+ prev_rest_client_resp_hash != rest_client_resp_hash &&
331
+ rest_client_resp_hash.any?
253
332
  end
333
+ # Finally puts the last line of the results_hash to STDOUT
334
+ puts results_hash[:data].last if results_hash[:data].last.any?
335
+ timeout = results_hash[:data].last[:duration].to_f if results_hash[:data].last.any?
254
336
  end
255
337
  end
256
338
  end
257
339
 
258
340
  # Generate HTML Report
259
- print "#{$PROGRAM_NAME} Generating Report..."
341
+ print "#{File.basename($PROGRAM_NAME)} Generating Report..."
260
342
  PWN::Reports::URIBuster.generate(
261
343
  dir_path: dir_path,
262
344
  results_hash: results_hash
@@ -283,8 +365,10 @@ begin
283
365
  listen_port
284
366
  )
285
367
  end
286
- rescue SystemExit, Interrupt
287
- puts "\nGoodbye."
368
+ # rescue SystemExit, Interrupt
369
+ # puts "\nGoodbye."
370
+ rescue Interrupt
371
+ puts "\n#{File.basename($PROGRAM_NAME)} Goodbye."
288
372
  rescue StandardError => e
289
373
  puts e.backtrace
290
374
  raise e
@@ -7,7 +7,7 @@ require 'optparse'
7
7
  opts = {}
8
8
  OptionParser.new do |options|
9
9
  options.banner = "USAGE:
10
- #{$PROGRAM_NAME} [opts]
10
+ #{File.basename($PROGRAM_NAME)} [opts]
11
11
  "
12
12
 
13
13
  options.on('-tFQDN', '--target-fqdn=FQDN', '<Required - FQDN to Target>') do |t|
@@ -36,7 +36,7 @@ OptionParser.new do |options|
36
36
  end.parse!
37
37
 
38
38
  if opts.empty?
39
- puts `#{$PROGRAM_NAME} --help`
39
+ puts `#{File.basename($PROGRAM_NAME)} --help`
40
40
  exit 1
41
41
  end
42
42
 
@@ -30,7 +30,7 @@ module PWN
30
30
  pass = TTY::Prompt.new.mask("#{prompt}: ")
31
31
  pass.to_s.strip.chomp.scrub
32
32
  rescue Interrupt
33
- puts 'CTRL+C detected...goodbye.'
33
+ puts "#{self}.#{__method__} => Goodbye."
34
34
  rescue StandardError => e
35
35
  raise e
36
36
  end
@@ -46,7 +46,7 @@ module PWN
46
46
  mfa = TTY::Prompt.new.ask("#{prompt}: ")
47
47
  mfa.to_s.strip.chomp.scrub
48
48
  rescue Interrupt
49
- puts 'CTRL+C detected...goodbye.'
49
+ puts "#{self}.#{__method__} => Goodbye."
50
50
  rescue StandardError => e
51
51
  raise e
52
52
  end