pwn 0.5.404 → 0.5.406
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/lib/pwn/plugins/burp_suite.rb +26 -1
- data/lib/pwn/plugins/zaproxy.rb +220 -3
- data/lib/pwn/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a4875ea87dd3f0605bc2ac889621fc124d43016086b2ef914a71dd8104879ff
|
4
|
+
data.tar.gz: 68addc891a15eea06b39b9e575fd98ce3b9e5a4d3e490c20bdfbe138699b8f2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b9211d610152046dec2ddcb6c96687085ae3004d104713064831c511e7ce1aa490c5f4e69c12066bc6d11e4f81b47dd90e6b289d5df88a6a5d65abb67d24dd7
|
7
|
+
data.tar.gz: e43570d37f21c2a3dc4008da1b42ee0aa379f4aebe5880dd47bb1b4c881a767db74a85b4121a125fbcdb67a4fc7526e239dfb595fd2327dbc0742c67e76027c7
|
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.406]: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.406]: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.406]: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:
|
@@ -450,7 +450,7 @@ module PWN
|
|
450
450
|
raise 'ERROR: burp_obj parameter is required' unless burp_obj.is_a?(Hash)
|
451
451
|
|
452
452
|
openapi_spec = opts[:openapi_spec]
|
453
|
-
raise 'ERROR: openapi_spec parameter
|
453
|
+
raise 'ERROR: openapi_spec parameter is required' if openapi_spec.nil?
|
454
454
|
|
455
455
|
additional_http_headers = opts[:additional_http_headers] ||= {}
|
456
456
|
raise 'ERROR: additional_http_headers must be a Hash' unless additional_http_headers.is_a?(Hash)
|
@@ -1013,6 +1013,31 @@ module PWN
|
|
1013
1013
|
raise e
|
1014
1014
|
end
|
1015
1015
|
|
1016
|
+
# Supported Method Parameters::
|
1017
|
+
# repeater_id = PWN::Plugins::BurpSuite.find_sitemap_entries(
|
1018
|
+
# burp_obj: 'required - burp_obj returned by #start method',
|
1019
|
+
# search_string: 'required - string to search for in the sitemap entries'
|
1020
|
+
# )
|
1021
|
+
|
1022
|
+
public_class_method def self.find_sitemap_entries(opts = {})
|
1023
|
+
burp_obj = opts[:burp_obj]
|
1024
|
+
raise 'ERROR: burp_obj parameter is required' unless burp_obj.is_a?(Hash)
|
1025
|
+
|
1026
|
+
search_string = opts[:search_string]
|
1027
|
+
raise 'ERROR: search_string parameter is required' if search_string.nil?
|
1028
|
+
|
1029
|
+
rest_browser = burp_obj[:rest_browser]
|
1030
|
+
mitm_rest_api = burp_obj[:mitm_rest_api]
|
1031
|
+
|
1032
|
+
json_sitemap = get_sitemap(burp_obj: burp_obj)
|
1033
|
+
matching_entries = json_sitemap.select do |entry|
|
1034
|
+
decoded_request = Base64.strict_decode64(entry[:request])
|
1035
|
+
decoded_request.include?(search_string)
|
1036
|
+
end
|
1037
|
+
rescue StandardError => e
|
1038
|
+
raise e
|
1039
|
+
end
|
1040
|
+
|
1016
1041
|
# Supported Method Parameters::
|
1017
1042
|
# repeater_id = PWN::Plugins::BurpSuite.add_repeater_tab(
|
1018
1043
|
# burp_obj: 'required - burp_obj returned by #start method',
|
data/lib/pwn/plugins/zaproxy.rb
CHANGED
@@ -171,6 +171,95 @@ module PWN
|
|
171
171
|
raise e
|
172
172
|
end
|
173
173
|
|
174
|
+
# Supported Method Parameters::
|
175
|
+
# json_sitemap = PWN::Plugins::Zaproxy.get_sitemap(
|
176
|
+
# zap_obj: 'required - zap_obj returned from #open method',
|
177
|
+
# return_as: 'optional - :base64 or :har (defaults to :base64)'
|
178
|
+
# )
|
179
|
+
|
180
|
+
public_class_method def self.get_sitemap(opts = {})
|
181
|
+
zap_obj = opts[:zap_obj]
|
182
|
+
api_key = zap_obj[:api_key].to_s.scrub
|
183
|
+
return_as = opts[:return_as] ||= :base64
|
184
|
+
|
185
|
+
entries = []
|
186
|
+
start = 0
|
187
|
+
count = 1000
|
188
|
+
|
189
|
+
loop do
|
190
|
+
params = { apikey: api_key, start: start, count: count }
|
191
|
+
|
192
|
+
response = zap_rest_call(
|
193
|
+
zap_obj: zap_obj,
|
194
|
+
rest_call: 'OTHER/exim/other/exportHar/',
|
195
|
+
params: params
|
196
|
+
)
|
197
|
+
|
198
|
+
har_data = JSON.parse(response.body, symbolize_names: true)
|
199
|
+
new_entries = har_data[:log][:entries]
|
200
|
+
break if new_entries.empty?
|
201
|
+
|
202
|
+
entries += new_entries
|
203
|
+
start += count
|
204
|
+
end
|
205
|
+
return entries if return_as == :har
|
206
|
+
|
207
|
+
# Deduplicate entries based on method + url
|
208
|
+
seen = Set.new
|
209
|
+
converted_messages = []
|
210
|
+
|
211
|
+
entries.each do |entry|
|
212
|
+
req = entry[:request]
|
213
|
+
key = [req[:method], req[:url]]
|
214
|
+
next if seen.include?(key)
|
215
|
+
|
216
|
+
seen.add(key)
|
217
|
+
|
218
|
+
# Build full request string
|
219
|
+
req_line = "#{req[:method]} #{req[:url]} #{req[:httpVersion]}\r\n"
|
220
|
+
req_headers = req[:headers].map { |h| "#{h[:name]}: #{h[:value]}\r\n" }.join
|
221
|
+
req_body = ''
|
222
|
+
if req[:postData] && req[:postData][:text]
|
223
|
+
req_body = req[:postData][:text]
|
224
|
+
req_body = Base64.decode64(req_body) if req[:postData][:encoding] == 'base64'
|
225
|
+
end
|
226
|
+
full_req = "#{req_line}#{req_headers}\r\n#{req_body}".force_encoding('ASCII-8BIT')
|
227
|
+
encoded_req = Base64.strict_encode64(full_req)
|
228
|
+
|
229
|
+
# Build full response string
|
230
|
+
res = entry[:response]
|
231
|
+
res_line = "#{res[:httpVersion]} #{res[:status]} #{res[:statusText]}\r\n"
|
232
|
+
res_headers = res[:headers].map { |h| "#{h[:name]}: #{h[:value]}\r\n" }.join
|
233
|
+
res_body = ''
|
234
|
+
if res[:content] && res[:content][:text]
|
235
|
+
res_body = res[:content][:text]
|
236
|
+
res_body = Base64.decode64(res_body) if res[:content][:encoding] == 'base64'
|
237
|
+
end
|
238
|
+
full_res = "#{res_line}#{res_headers}\r\n#{res_body}".force_encoding('ASCII-8BIT')
|
239
|
+
encoded_res = Base64.strict_encode64(full_res)
|
240
|
+
|
241
|
+
# Extract http_service
|
242
|
+
uri = URI.parse(req[:url])
|
243
|
+
http_service = {
|
244
|
+
host: uri.host,
|
245
|
+
port: uri.port,
|
246
|
+
protocol: uri.scheme
|
247
|
+
}
|
248
|
+
|
249
|
+
# Add to array
|
250
|
+
converted_messages << {
|
251
|
+
request: encoded_req,
|
252
|
+
response: encoded_res,
|
253
|
+
http_service: http_service
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
converted_messages
|
258
|
+
rescue StandardError, SystemExit, Interrupt => e
|
259
|
+
stop(zap_obj: zap_obj) unless zap_obj.nil?
|
260
|
+
raise e
|
261
|
+
end
|
262
|
+
|
174
263
|
# Supported Method Parameters::
|
175
264
|
# PWN::Plugins::Zaproxy.add_to_scope(
|
176
265
|
# zap_obj: 'required - zap_obj returned from #open method',
|
@@ -204,6 +293,86 @@ module PWN
|
|
204
293
|
raise e
|
205
294
|
end
|
206
295
|
|
296
|
+
# Supported Method Parameters::
|
297
|
+
# PWN::Plugins::Zaproxy.find_har_entries(
|
298
|
+
# zap_obj: 'required - zap_obj returned from #open method',
|
299
|
+
# request: 'required - base64 encoded request or HAR entry :request (e.g. from #get_sitemap method)'
|
300
|
+
# )
|
301
|
+
|
302
|
+
public_class_method def self.find_har_entries(opts = {})
|
303
|
+
zap_obj = opts[:zap_obj]
|
304
|
+
api_key = zap_obj[:api_key].to_s.scrub
|
305
|
+
request = opts[:request]
|
306
|
+
raise 'ERROR: request must be provided' if request.nil?
|
307
|
+
|
308
|
+
har_sitemap = get_sitemap(
|
309
|
+
zap_obj: zap_obj,
|
310
|
+
return_as: :har
|
311
|
+
)
|
312
|
+
|
313
|
+
# HAR entry
|
314
|
+
if request.is_a?(Hash) && request.key?(:method) && request.key?(:url) && request.key?(:httpVersion)
|
315
|
+
har_entries = har_sitemap.select { |entry| entry[:request] == request }
|
316
|
+
else
|
317
|
+
# Base64 encoded string
|
318
|
+
dec_request = Base64.strict_decode64(request).force_encoding('ASCII-8BIT') unless dec_request.is_a?(Hash)
|
319
|
+
|
320
|
+
# Find the har request for the given base64 decoded dec_request value
|
321
|
+
har_entries = har_sitemap.select do |entry|
|
322
|
+
req = entry[:request]
|
323
|
+
req_line = "#{req[:method]} #{req[:url]} #{req[:httpVersion]}\r\n"
|
324
|
+
req_headers = req[:headers].map { |h| "#{h[:name]}: #{h[:value]}\r\n" }.join
|
325
|
+
req_body = ''
|
326
|
+
if req[:postData] && req[:postData][:text]
|
327
|
+
req_body = req[:postData][:text]
|
328
|
+
req_body = Base64.decode64(req_body) if req[:postData][:encoding] == 'base64'
|
329
|
+
end
|
330
|
+
full_req = "#{req_line}#{req_headers}\r\n#{req_body}".force_encoding('ASCII-8BIT')
|
331
|
+
full_req == dec_request
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
har_entries
|
336
|
+
rescue StandardError, SystemExit, Interrupt => e
|
337
|
+
stop(zap_obj: zap_obj) unless zap_obj.nil?
|
338
|
+
raise e
|
339
|
+
end
|
340
|
+
|
341
|
+
# Supported Method Parameters::
|
342
|
+
# PWN::Plugins::Zaproxy.requester(
|
343
|
+
# zap_obj: 'required - zap_obj returned from #open method',
|
344
|
+
# har_entry: 'required - har entry (e.g. from #get_sitemap method or #find_har_entries method)',
|
345
|
+
# redirect: 'optional - follow redirects if set to true (defaults to false)'
|
346
|
+
# )
|
347
|
+
|
348
|
+
public_class_method def self.requester(opts = {})
|
349
|
+
zap_obj = opts[:zap_obj]
|
350
|
+
api_key = zap_obj[:api_key].to_s.scrub
|
351
|
+
har_entry = opts[:har_entry]
|
352
|
+
raise 'ERROR: har_entry must be provided and be a valid HAR entry' unless har_entry.is_a?(Hash) && har_entry.key?(:request) && har_entry.key?(:response)
|
353
|
+
|
354
|
+
redirect = opts[:redirect] || false
|
355
|
+
raise 'ERROR: redirect must be a boolean' unless redirect.is_a?(TrueClass) || redirect.is_a?(FalseClass)
|
356
|
+
|
357
|
+
har_json = har_entry.to_json
|
358
|
+
params = {
|
359
|
+
apikey: api_key,
|
360
|
+
request: har_json,
|
361
|
+
followRedirects: redirect.to_s
|
362
|
+
}
|
363
|
+
|
364
|
+
response = zap_rest_call(
|
365
|
+
zap_obj: zap_obj,
|
366
|
+
rest_call: 'OTHER/exim/other/sendHarRequest/',
|
367
|
+
params: params
|
368
|
+
)
|
369
|
+
|
370
|
+
JSON.parse(response.body, symbolize_names: true)
|
371
|
+
rescue StandardError, SystemExit, Interrupt => e
|
372
|
+
stop(zap_obj: zap_obj) unless zap_obj.nil?
|
373
|
+
raise e
|
374
|
+
end
|
375
|
+
|
207
376
|
# Supported Method Parameters::
|
208
377
|
# PWN::Plugins::Zaproxy.spider(
|
209
378
|
# zap_obj: 'required - zap_obj returned from #open method',
|
@@ -273,14 +442,41 @@ module PWN
|
|
273
442
|
headers = opts[:headers] ||= {}
|
274
443
|
raise 'ERROR: headers must be provided' if headers.empty? || !headers.is_a?(Hash)
|
275
444
|
|
445
|
+
# Check if replacer rule already exists
|
446
|
+
params = { apikey: api_key }
|
447
|
+
response = zap_rest_call(
|
448
|
+
zap_obj: zap_obj,
|
449
|
+
rest_call: 'JSON/replacer/view/rules/',
|
450
|
+
params: params
|
451
|
+
)
|
452
|
+
replacer = JSON.parse(response.body, symbolize_names: true)
|
453
|
+
|
276
454
|
replacer_resp_arr = []
|
277
455
|
headers.each_key do |header_key|
|
456
|
+
this_header = { header: header_key }
|
457
|
+
rule_exists = replacer[:rules].any? { |rule| rule[:description] == header_key.to_s && rule[:url] == target_regex }
|
458
|
+
|
459
|
+
if rule_exists
|
460
|
+
# Remove existing rule first
|
461
|
+
puts "Removing existing replacer rule for header key: #{header_key}..."
|
462
|
+
params = {
|
463
|
+
apikey: api_key,
|
464
|
+
description: header_key
|
465
|
+
}
|
466
|
+
zap_rest_call(
|
467
|
+
zap_obj: zap_obj,
|
468
|
+
rest_call: 'JSON/replacer/action/removeRule/',
|
469
|
+
params: params
|
470
|
+
)
|
471
|
+
end
|
472
|
+
|
473
|
+
puts "Adding replacer rule for header key: #{header_key}..."
|
278
474
|
params = {
|
279
475
|
apikey: api_key,
|
280
476
|
description: header_key,
|
281
|
-
enabled: true,
|
477
|
+
enabled: 'true',
|
282
478
|
matchType: 'REQ_HEADER',
|
283
|
-
matchRegex: false,
|
479
|
+
matchRegex: 'false',
|
284
480
|
matchString: header_key,
|
285
481
|
replacement: headers[header_key],
|
286
482
|
initiators: '',
|
@@ -294,7 +490,8 @@ module PWN
|
|
294
490
|
)
|
295
491
|
|
296
492
|
json_resp = JSON.parse(response.body, symbolize_names: true)
|
297
|
-
|
493
|
+
this_header[:Result] = json_resp[:Result]
|
494
|
+
replacer_resp_arr.push(this_header)
|
298
495
|
end
|
299
496
|
|
300
497
|
replacer_resp_arr
|
@@ -571,12 +768,32 @@ module PWN
|
|
571
768
|
openapi_spec: 'required - path to OpenAPI JSON or YAML spec file'
|
572
769
|
)
|
573
770
|
|
771
|
+
#{self}.get_sitemap(
|
772
|
+
zap_obj: 'required - zap_obj returned from #open method',
|
773
|
+
return_as: 'optional - :base64 or :har (defaults to :base64)'
|
774
|
+
)
|
775
|
+
|
574
776
|
#{self}.add_to_scope(
|
575
777
|
zap_obj: 'required - zap_obj returned from #open method',
|
576
778
|
target_regex: 'required - url regex to add to scope (e.g. https://test.domain.local.*)',
|
577
779
|
context_name: 'optional - context name to add target_regex to (defaults to Default Context)'
|
578
780
|
)
|
579
781
|
|
782
|
+
#{self}.find_har_entries(
|
783
|
+
zap_obj: 'required - zap_obj returned from #open method',
|
784
|
+
request: 'required - base64 encoded request or HAR entry :request (e.g. from #get_sitemap method)'
|
785
|
+
)
|
786
|
+
|
787
|
+
#{self}.requester(
|
788
|
+
zap_obj: 'required - zap_obj returned from #open method',
|
789
|
+
har_entry: 'required - har entry (e.g. from #get_sitemap method or #find_har_entries method)',
|
790
|
+
redirect: 'optional - follow redirects if set to true (defaults to true)'
|
791
|
+
)
|
792
|
+
|
793
|
+
json_sitemap = #{self}.spider(
|
794
|
+
zap_obj: 'required - zap_obj returned from #open method'
|
795
|
+
)
|
796
|
+
|
580
797
|
#{self}.inject_additional_http_headers(
|
581
798
|
zap_obj: 'required - zap_obj returned from #open method',
|
582
799
|
target_regex: 'required - url regex to inject headers into (e.g. https://test.domain.local.*)',
|
data/lib/pwn/version.rb
CHANGED