pwn 0.5.404 → 0.5.405
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/zaproxy.rb +230 -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: a4b31134e90c20f44f69f6b1fbf8b6e4c8e7911bbec857597c729c0801578d5f
|
4
|
+
data.tar.gz: 8774dc902ef4eadf6d32303cac8efe5fea2a909997a4d7d2c0fdb2e8c137e58a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a583c93c1288496cb703e5b4e48a9a70f258c955b05d0dbbf9ffa27b1be25c0d1c2568537379ebe65ae63a423bf31cc022221074e3d8cdbfe100c59739d25099
|
7
|
+
data.tar.gz: b9b3af817aa7ccd08a05cbc6cbe1107c5760db93b6ec9343a01b74d3b7d992d6f72abe4c11ef5d156f96649268a51b3b1177daa97fff2b04d7c759c781a69884
|
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.405]: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.405]: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.405]: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/lib/pwn/plugins/zaproxy.rb
CHANGED
@@ -204,6 +204,192 @@ module PWN
|
|
204
204
|
raise e
|
205
205
|
end
|
206
206
|
|
207
|
+
# Supported Method Parameters::
|
208
|
+
# PWN::Plugins::Zaproxy.add_requester_tab(
|
209
|
+
# zap_obj: 'required - zap_obj returned from #open method',
|
210
|
+
# request: 'required - base64 encoded HTTP request (e.g. from #get_sitemap method)'
|
211
|
+
# )
|
212
|
+
|
213
|
+
public_class_method def self.add_requester_tab(opts = {})
|
214
|
+
zap_obj = opts[:zap_obj]
|
215
|
+
api_key = zap_obj[:api_key].to_s.scrub
|
216
|
+
request = opts[:request]
|
217
|
+
|
218
|
+
dec_request = Base64.strict_decode64(request).force_encoding('ASCII-8BIT')
|
219
|
+
|
220
|
+
# Parse the full request string
|
221
|
+
parts = dec_request.split("\r\n\r\n", 2)
|
222
|
+
headers_part = parts[0]
|
223
|
+
body = parts[1] || ''
|
224
|
+
|
225
|
+
header_lines = headers_part.split("\r\n")
|
226
|
+
first_line = header_lines.shift
|
227
|
+
method, full_url, http_version = first_line.split
|
228
|
+
|
229
|
+
headers = []
|
230
|
+
header_lines.each do |line|
|
231
|
+
name, value = line.split(': ', 2)
|
232
|
+
headers << { name: name, value: value }
|
233
|
+
end
|
234
|
+
|
235
|
+
# Parse URL for queryString and adjust url
|
236
|
+
uri = URI.parse(full_url)
|
237
|
+
query_string = []
|
238
|
+
if uri.query
|
239
|
+
URI.decode_www_form(uri.query).each do |name, value|
|
240
|
+
query_string << { name: name, value: value }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
url = "#{uri.scheme}://#{uri.host}"
|
244
|
+
url += ":#{uri.port}" if uri.port && uri.port != (uri.scheme == 'https' ? 443 : 80)
|
245
|
+
url += uri.path
|
246
|
+
|
247
|
+
# Determine content-type
|
248
|
+
content_type_header = headers.find { |h| h[:name].downcase == 'content-type' }
|
249
|
+
mime_type = content_type_header ? content_type_header[:value] : 'application/octet-stream'
|
250
|
+
|
251
|
+
# Handle postData
|
252
|
+
post_data = nil
|
253
|
+
methods_with_body = %w[POST PUT PATCH]
|
254
|
+
if methods_with_body.include?(method) && !body.empty?
|
255
|
+
post_data = {
|
256
|
+
mimeType: mime_type,
|
257
|
+
params: [],
|
258
|
+
text: body
|
259
|
+
}
|
260
|
+
|
261
|
+
temp_body = body.dup.force_encoding('UTF-8')
|
262
|
+
if temp_body.valid_encoding?
|
263
|
+
if mime_type.include?('application/x-www-form-urlencoded')
|
264
|
+
URI.decode_www_form(temp_body).each do |name, value|
|
265
|
+
post_data[:params] << { name: name, value: value }
|
266
|
+
end
|
267
|
+
end
|
268
|
+
else
|
269
|
+
post_data[:text] = Base64.encode64(body)
|
270
|
+
post_data[:encoding] = 'base64'
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# Construct HAR request
|
275
|
+
har_request = {
|
276
|
+
method: method,
|
277
|
+
url: url,
|
278
|
+
httpVersion: http_version,
|
279
|
+
cookies: [],
|
280
|
+
headers: headers,
|
281
|
+
queryString: query_string,
|
282
|
+
headersSize: -1,
|
283
|
+
bodySize: -1
|
284
|
+
}
|
285
|
+
har_request[:postData] = post_data if post_data
|
286
|
+
|
287
|
+
har_json = JSON.generate(har_request)
|
288
|
+
|
289
|
+
params = {
|
290
|
+
apikey: api_key,
|
291
|
+
request: har_json,
|
292
|
+
followRedirects: 'true'
|
293
|
+
}
|
294
|
+
|
295
|
+
response = zap_rest_call(
|
296
|
+
zap_obj: zap_obj,
|
297
|
+
rest_call: 'OTHER/core/other/sendHarRequest/',
|
298
|
+
params: params
|
299
|
+
)
|
300
|
+
|
301
|
+
JSON.parse(response.body, symbolize_names: true)
|
302
|
+
rescue StandardError, SystemExit, Interrupt => e
|
303
|
+
stop(zap_obj: zap_obj) unless zap_obj.nil?
|
304
|
+
raise e
|
305
|
+
end
|
306
|
+
|
307
|
+
# Supported Method Parameters::
|
308
|
+
# json_sitemap = PWN::Plugins::Zaproxy.get_sitemap(
|
309
|
+
# zap_obj: 'required - zap_obj returned from #open method'
|
310
|
+
# )
|
311
|
+
|
312
|
+
public_class_method def self.get_sitemap(opts = {})
|
313
|
+
zap_obj = opts[:zap_obj]
|
314
|
+
api_key = zap_obj[:api_key].to_s.scrub
|
315
|
+
|
316
|
+
entries = []
|
317
|
+
start = 0
|
318
|
+
count = 1000
|
319
|
+
|
320
|
+
loop do
|
321
|
+
params = { apikey: api_key, start: start, count: count }
|
322
|
+
|
323
|
+
response = zap_rest_call(
|
324
|
+
zap_obj: zap_obj,
|
325
|
+
rest_call: 'OTHER/exim/other/exportHar/',
|
326
|
+
params: params
|
327
|
+
)
|
328
|
+
|
329
|
+
har_data = JSON.parse(response.body, symbolize_names: true)
|
330
|
+
new_entries = har_data[:log][:entries]
|
331
|
+
break if new_entries.empty?
|
332
|
+
|
333
|
+
entries += new_entries
|
334
|
+
start += count
|
335
|
+
end
|
336
|
+
|
337
|
+
# Deduplicate entries based on method + url
|
338
|
+
seen = Set.new
|
339
|
+
converted_messages = []
|
340
|
+
|
341
|
+
entries.each do |entry|
|
342
|
+
req = entry[:request]
|
343
|
+
key = [req[:method], req[:url]]
|
344
|
+
next if seen.include?(key)
|
345
|
+
|
346
|
+
seen.add(key)
|
347
|
+
|
348
|
+
# Build full request string
|
349
|
+
req_line = "#{req[:method]} #{req[:url]} #{req[:httpVersion]}\r\n"
|
350
|
+
req_headers = req[:headers].map { |h| "#{h[:name]}: #{h[:value]}\r\n" }.join
|
351
|
+
req_body = ''
|
352
|
+
if req[:postData] && req[:postData][:text]
|
353
|
+
req_body = req[:postData][:text]
|
354
|
+
req_body = Base64.decode64(req_body) if req[:postData][:encoding] == 'base64'
|
355
|
+
end
|
356
|
+
full_req = "#{req_line}#{req_headers}\r\n#{req_body}".force_encoding('ASCII-8BIT')
|
357
|
+
encoded_req = Base64.strict_encode64(full_req)
|
358
|
+
|
359
|
+
# Build full response string
|
360
|
+
res = entry[:response]
|
361
|
+
res_line = "#{res[:httpVersion]} #{res[:status]} #{res[:statusText]}\r\n"
|
362
|
+
res_headers = res[:headers].map { |h| "#{h[:name]}: #{h[:value]}\r\n" }.join
|
363
|
+
res_body = ''
|
364
|
+
if res[:content] && res[:content][:text]
|
365
|
+
res_body = res[:content][:text]
|
366
|
+
res_body = Base64.decode64(res_body) if res[:content][:encoding] == 'base64'
|
367
|
+
end
|
368
|
+
full_res = "#{res_line}#{res_headers}\r\n#{res_body}".force_encoding('ASCII-8BIT')
|
369
|
+
encoded_res = Base64.strict_encode64(full_res)
|
370
|
+
|
371
|
+
# Extract http_service
|
372
|
+
uri = URI.parse(req[:url])
|
373
|
+
http_service = {
|
374
|
+
host: uri.host,
|
375
|
+
port: uri.port,
|
376
|
+
protocol: uri.scheme
|
377
|
+
}
|
378
|
+
|
379
|
+
# Add to array
|
380
|
+
converted_messages << {
|
381
|
+
request: encoded_req,
|
382
|
+
response: encoded_res,
|
383
|
+
http_service: http_service
|
384
|
+
}
|
385
|
+
end
|
386
|
+
|
387
|
+
converted_messages
|
388
|
+
rescue StandardError, SystemExit, Interrupt => e
|
389
|
+
stop(zap_obj: zap_obj) unless zap_obj.nil?
|
390
|
+
raise e
|
391
|
+
end
|
392
|
+
|
207
393
|
# Supported Method Parameters::
|
208
394
|
# PWN::Plugins::Zaproxy.spider(
|
209
395
|
# zap_obj: 'required - zap_obj returned from #open method',
|
@@ -273,14 +459,41 @@ module PWN
|
|
273
459
|
headers = opts[:headers] ||= {}
|
274
460
|
raise 'ERROR: headers must be provided' if headers.empty? || !headers.is_a?(Hash)
|
275
461
|
|
462
|
+
# Check if replacer rule already exists
|
463
|
+
params = { apikey: api_key }
|
464
|
+
response = zap_rest_call(
|
465
|
+
zap_obj: zap_obj,
|
466
|
+
rest_call: 'JSON/replacer/view/rules/',
|
467
|
+
params: params
|
468
|
+
)
|
469
|
+
replacer = JSON.parse(response.body, symbolize_names: true)
|
470
|
+
|
276
471
|
replacer_resp_arr = []
|
277
472
|
headers.each_key do |header_key|
|
473
|
+
this_header = { header: header_key }
|
474
|
+
rule_exists = replacer[:rules].any? { |rule| rule[:description] == header_key.to_s && rule[:url] == target_regex }
|
475
|
+
|
476
|
+
if rule_exists
|
477
|
+
# Remove existing rule first
|
478
|
+
puts "Removing existing replacer rule for header key: #{header_key}..."
|
479
|
+
params = {
|
480
|
+
apikey: api_key,
|
481
|
+
description: header_key
|
482
|
+
}
|
483
|
+
zap_rest_call(
|
484
|
+
zap_obj: zap_obj,
|
485
|
+
rest_call: 'JSON/replacer/action/removeRule/',
|
486
|
+
params: params
|
487
|
+
)
|
488
|
+
end
|
489
|
+
|
490
|
+
puts "Adding replacer rule for header key: #{header_key}..."
|
278
491
|
params = {
|
279
492
|
apikey: api_key,
|
280
493
|
description: header_key,
|
281
|
-
enabled: true,
|
494
|
+
enabled: 'true',
|
282
495
|
matchType: 'REQ_HEADER',
|
283
|
-
matchRegex: false,
|
496
|
+
matchRegex: 'false',
|
284
497
|
matchString: header_key,
|
285
498
|
replacement: headers[header_key],
|
286
499
|
initiators: '',
|
@@ -294,7 +507,8 @@ module PWN
|
|
294
507
|
)
|
295
508
|
|
296
509
|
json_resp = JSON.parse(response.body, symbolize_names: true)
|
297
|
-
|
510
|
+
this_header[:Result] = json_resp[:Result]
|
511
|
+
replacer_resp_arr.push(this_header)
|
298
512
|
end
|
299
513
|
|
300
514
|
replacer_resp_arr
|
@@ -577,6 +791,19 @@ module PWN
|
|
577
791
|
context_name: 'optional - context name to add target_regex to (defaults to Default Context)'
|
578
792
|
)
|
579
793
|
|
794
|
+
#{self}.add_requester_tab(
|
795
|
+
zap_obj: 'required - zap_obj returned from #open method',
|
796
|
+
request: 'required - base64 encoded HTTP request (e.g. from #get_sitemap method)'
|
797
|
+
)
|
798
|
+
|
799
|
+
#{self}.get_sitemap(
|
800
|
+
zap_obj: 'required - zap_obj returned from #open method'
|
801
|
+
)
|
802
|
+
|
803
|
+
json_sitemap = #{self}.spider(
|
804
|
+
zap_obj: 'required - zap_obj returned from #open method'
|
805
|
+
)
|
806
|
+
|
580
807
|
#{self}.inject_additional_http_headers(
|
581
808
|
zap_obj: 'required - zap_obj returned from #open method',
|
582
809
|
target_regex: 'required - url regex to inject headers into (e.g. https://test.domain.local.*)',
|
data/lib/pwn/version.rb
CHANGED