pwn 0.5.199 → 0.5.200
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/CHANGELOG_BETWEEN_TAGS.txt +190 -176
- data/Gemfile +4 -4
- data/README.md +3 -3
- data/bin/pwn_sast +2 -0
- data/etc/pwn.yaml.EXAMPLE +3 -0
- data/lib/pwn/plugins/hunter.rb +160 -0
- data/lib/pwn/plugins/repl.rb +3 -0
- data/lib/pwn/plugins/transparent_browser.rb +138 -20
- data/lib/pwn/plugins.rb +1 -0
- data/lib/pwn/sast/local_storage.rb +145 -0
- data/lib/pwn/sast/post_message.rb +144 -0
- data/lib/pwn/sast.rb +2 -0
- data/lib/pwn/version.rb +1 -1
- data/lib/pwn.rb +3 -0
- data/spec/lib/pwn/plugins/hunter_spec.rb +15 -0
- data/spec/lib/pwn/sast/local_storage_spec.rb +25 -0
- data/spec/lib/pwn/sast/post_message_spec.rb +25 -0
- metadata +16 -10
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module PWN
|
7
|
+
module Plugins
|
8
|
+
# This plugin is used for interacting w/ Hunter's REST API using
|
9
|
+
# the 'rest' browser type of PWN::Plugins::TransparentBrowser.
|
10
|
+
# This is based on the following Hunter API Specification:
|
11
|
+
# https://hunter.how/search-api
|
12
|
+
module Hunter
|
13
|
+
@@logger = PWN::Plugins::PWNLogger.create
|
14
|
+
|
15
|
+
# Supported Method Parameters::
|
16
|
+
# hunter_rest_call(
|
17
|
+
# http_method: 'optional HTTP method (defaults to GET)
|
18
|
+
# rest_call: 'required rest call to make per the schema',
|
19
|
+
# params: 'optional params passed in the URI or HTTP Headers',
|
20
|
+
# http_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST'
|
21
|
+
# )
|
22
|
+
|
23
|
+
private_class_method def self.hunter_rest_call(opts = {})
|
24
|
+
hunter_obj = opts[:hunter_obj]
|
25
|
+
http_method = if opts[:http_method].nil?
|
26
|
+
:get
|
27
|
+
else
|
28
|
+
opts[:http_method].to_s.scrub.to_sym
|
29
|
+
end
|
30
|
+
rest_call = opts[:rest_call].to_s.scrub
|
31
|
+
params = opts[:params]
|
32
|
+
http_body = opts[:http_body].to_s.scrub
|
33
|
+
base_hunter_api_uri = 'https://api.hunter.how'
|
34
|
+
|
35
|
+
browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)
|
36
|
+
rest_client = browser_obj[:browser]::Request
|
37
|
+
|
38
|
+
case http_method
|
39
|
+
when :get
|
40
|
+
response = rest_client.execute(
|
41
|
+
method: :get,
|
42
|
+
url: "#{base_hunter_api_uri}/#{rest_call}",
|
43
|
+
headers: {
|
44
|
+
content_type: 'application/json; charset=UTF-8',
|
45
|
+
params: params
|
46
|
+
},
|
47
|
+
verify_ssl: false
|
48
|
+
)
|
49
|
+
|
50
|
+
when :post
|
51
|
+
response = rest_client.execute(
|
52
|
+
method: :post,
|
53
|
+
url: "#{base_hunter_api_uri}/#{rest_call}",
|
54
|
+
headers: {
|
55
|
+
content_type: 'application/json; charset=UTF-8',
|
56
|
+
params: params
|
57
|
+
},
|
58
|
+
payload: http_body,
|
59
|
+
verify_ssl: false
|
60
|
+
)
|
61
|
+
|
62
|
+
else
|
63
|
+
raise @@logger.error("Unsupported HTTP Method #{http_method} for #{self} Plugin")
|
64
|
+
end
|
65
|
+
JSON.parse(response.scrub, symbolize_names: true)
|
66
|
+
rescue JSON::ParserError => e
|
67
|
+
{
|
68
|
+
total: 0,
|
69
|
+
matches: [],
|
70
|
+
error: "JSON::ParserError #{e.message}",
|
71
|
+
rest_call: rest_call,
|
72
|
+
params: params
|
73
|
+
}
|
74
|
+
rescue RestClient::TooManyRequests
|
75
|
+
print 'Too many requests. Sleeping 10s...'
|
76
|
+
sleep 10
|
77
|
+
retry
|
78
|
+
rescue StandardError => e
|
79
|
+
case e.message
|
80
|
+
when '400 Bad Request', '404 Resource Not Found'
|
81
|
+
"#{e.message}: #{e.response}"
|
82
|
+
else
|
83
|
+
raise e
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Supported Method Parameters::
|
88
|
+
# search_results = PWN::Plugins::Hunter.search(
|
89
|
+
# api_key: 'required hunter api key',
|
90
|
+
# query: 'required - hunter search query',
|
91
|
+
# start_time: 'required - start date for the search (format is yyyy-mm-dd)',
|
92
|
+
# end_time: 'required - end date for the search (format is yyyy-mm-dd)',
|
93
|
+
# start_page: 'optional - starting page number for pagination (default is 1)',
|
94
|
+
# page_size: 'optional - number of results per page (default is 10)',
|
95
|
+
# fields: 'optional - comma-separated list of fields 'product,transport_protocol,protocol,banner,country,province,city,asn,org,web,updated_at' (default is nil)'
|
96
|
+
# )
|
97
|
+
|
98
|
+
public_class_method def self.search(opts = {})
|
99
|
+
api_key = opts[:api_key].to_s.scrub
|
100
|
+
raise "ERROR: #{self} requires a valid Hunter API Key" if api_key.empty?
|
101
|
+
|
102
|
+
query = opts[:query].to_s.scrub
|
103
|
+
raise "ERROR: #{self} requires a valid query" if query.empty?
|
104
|
+
|
105
|
+
start_time = opts[:start_time]
|
106
|
+
raise "ERROR: #{self} requires a valid start time" if start_time.nil?
|
107
|
+
|
108
|
+
end_time = opts[:end_time]
|
109
|
+
raise "ERROR: #{self} requires a valid end time" if end_time.nil?
|
110
|
+
|
111
|
+
start_page = opts[:start_page] ||= 1
|
112
|
+
page_size = opts[:page_size] ||= 10
|
113
|
+
fields = opts[:fields]
|
114
|
+
|
115
|
+
params = {}
|
116
|
+
params[:'api-key'] = api_key
|
117
|
+
base64_query = Base64.urlsafe_encode64(query)
|
118
|
+
params[:query] = base64_query
|
119
|
+
params[:page] = start_page
|
120
|
+
params[:page_size] = page_size
|
121
|
+
params[:start_time] = start_time
|
122
|
+
params[:end_time] = end_time
|
123
|
+
params[:fields] = fields
|
124
|
+
|
125
|
+
hunter_rest_call(
|
126
|
+
rest_call: 'search',
|
127
|
+
params: params
|
128
|
+
)
|
129
|
+
rescue StandardError => e
|
130
|
+
raise e
|
131
|
+
end
|
132
|
+
|
133
|
+
# Author(s):: 0day Inc. <support@0dayinc.com>
|
134
|
+
|
135
|
+
public_class_method def self.authors
|
136
|
+
"AUTHOR(S):
|
137
|
+
0day Inc. <support@0dayinc.com>
|
138
|
+
"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Display Usage for this Module
|
142
|
+
|
143
|
+
public_class_method def self.help
|
144
|
+
puts "USAGE:
|
145
|
+
search_results = #{self}.query(
|
146
|
+
api_key: 'required hunter api key',
|
147
|
+
query: 'required - hunter search query',
|
148
|
+
start_time: 'required - start date for the search (format is yyyy-mm-dd)',
|
149
|
+
end_time: 'required - end date for the search (format is yyyy-mm-dd)',
|
150
|
+
start_page: 'optional - starting page number for pagination (default is 1)',
|
151
|
+
page_size: 'optional - number of results per page (default is 10)',
|
152
|
+
fields: 'optional - comma-separated list of fields 'product,transport_protocol,protocol,banner,country,province,city,asn,org,web,updated_at' (default is nil)'
|
153
|
+
)
|
154
|
+
|
155
|
+
#{self}.authors
|
156
|
+
"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
data/lib/pwn/plugins/repl.rb
CHANGED
@@ -542,6 +542,9 @@ module PWN
|
|
542
542
|
pi.config.pwn_irc = pi.config.p[:irc]
|
543
543
|
Pry.config.pwn_irc = pi.config.pwn_irc
|
544
544
|
|
545
|
+
pi.config.pwn_hunter = pi.config.p[:hunter][:api_key]
|
546
|
+
Pry.config.pwn_hunter = pi.config.pwn_hunter
|
547
|
+
|
545
548
|
pi.config.pwn_shodan = pi.config.p[:shodan][:api_key]
|
546
549
|
Pry.config.pwn_shodan = pi.config.pwn_shodan
|
547
550
|
|
@@ -110,7 +110,7 @@ module PWN
|
|
110
110
|
# DevTools ToolBox Settings in Firefox about:config
|
111
111
|
this_profile['devtools.f12.enabled'] = true
|
112
112
|
this_profile['devtools.toolbox.host'] = 'right'
|
113
|
-
this_profile['devtools.toolbox.sidebar.width'] =
|
113
|
+
this_profile['devtools.toolbox.sidebar.width'] = 1700
|
114
114
|
this_profile['devtools.toolbox.splitconsoleHeight'] = 200
|
115
115
|
|
116
116
|
# DevTools Debugger Settings in Firefox about:config
|
@@ -118,8 +118,7 @@ module PWN
|
|
118
118
|
this_profile['devtools.debugger.start-panel-size'] = 200
|
119
119
|
this_profile['devtools.debugger.end-panel-size'] = 200
|
120
120
|
this_profile['devtools.debugger.auto-pretty-print'] = true
|
121
|
-
|
122
|
-
# this_profile['devtools.debugger.ui.editor-wrapping'] = true
|
121
|
+
this_profile['devtools.debugger.ui.editor-wrapping'] = true
|
123
122
|
this_profile['devtools.debugger.features.javascript-tracing'] = true
|
124
123
|
this_profile['devtools.debugger.xhr-breakpoints-visible'] = true
|
125
124
|
this_profile['devtools.debugger.expressions-visible'] = true
|
@@ -333,6 +332,14 @@ module PWN
|
|
333
332
|
browser_obj[:devtools].send_cmd('DOMSnapshot.enable')
|
334
333
|
end
|
335
334
|
|
335
|
+
firefox_browser_types = %i[firefox headless_firefox]
|
336
|
+
if firefox_browser_types.include?(browser_type)
|
337
|
+
# browser_obj[:devtools].send_cmd(
|
338
|
+
# 'EventBreakpoints.setInstrumentationBreakpoint',
|
339
|
+
# eventName: 'script'
|
340
|
+
# )
|
341
|
+
end
|
342
|
+
|
336
343
|
# Future BiDi API that's more universally supported across browsers
|
337
344
|
# browser_obj[:bidi] = driver.bidi
|
338
345
|
|
@@ -450,16 +457,22 @@ module PWN
|
|
450
457
|
|
451
458
|
browser = browser_obj[:browser]
|
452
459
|
case js
|
453
|
-
when 'debugger', 'debugger;', 'debugger()', 'debugger();'
|
454
|
-
Timeout.timeout(1) { console_resp = browser.execute_script('debugger') }
|
455
460
|
when 'clear', 'clear;', 'clear()', 'clear();'
|
456
|
-
|
461
|
+
script = 'console.clear()'
|
457
462
|
else
|
458
|
-
|
463
|
+
script = "console.log(#{js})"
|
464
|
+
end
|
465
|
+
|
466
|
+
console_resp = nil
|
467
|
+
begin
|
468
|
+
Timeout.timeout(1) { console_resp = browser.execute_script(script) }
|
469
|
+
rescue Timeout::Error, Timeout::ExitException
|
470
|
+
console_resp
|
471
|
+
rescue Selenium::WebDriver::Error::JavascriptError
|
472
|
+
script = js
|
473
|
+
retry
|
459
474
|
end
|
460
475
|
|
461
|
-
console_resp
|
462
|
-
rescue Timeout::Error, Timeout::ExitException
|
463
476
|
console_resp
|
464
477
|
rescue StandardError => e
|
465
478
|
raise e
|
@@ -637,8 +650,10 @@ module PWN
|
|
637
650
|
|
638
651
|
case action.to_s.downcase.to_sym
|
639
652
|
when :pause
|
640
|
-
|
641
|
-
|
653
|
+
devtools.send_cmd(
|
654
|
+
'EventBreakpoints.setInstrumentationBreakpoint',
|
655
|
+
eventName: 'scriptFirstStatement'
|
656
|
+
)
|
642
657
|
# devtools.send_cmd('Debugger.enable')
|
643
658
|
# devtools.send_cmd(
|
644
659
|
# 'Debugger.setInstrumentationBreakpoint',
|
@@ -664,6 +679,10 @@ module PWN
|
|
664
679
|
url
|
665
680
|
end
|
666
681
|
when :resume
|
682
|
+
devtools.send_cmd(
|
683
|
+
'EventBreakpoints.removeInstrumentationBreakpoint',
|
684
|
+
eventName: 'scriptFirstStatement'
|
685
|
+
)
|
667
686
|
devtools.send_cmd('Debugger.resume')
|
668
687
|
else
|
669
688
|
raise 'ERROR: action parameter must be :pause or :resume'
|
@@ -673,24 +692,69 @@ module PWN
|
|
673
692
|
end
|
674
693
|
|
675
694
|
# Supported Method Parameters::
|
676
|
-
# PWN::Plugins::TransparentBrowser.
|
695
|
+
# current_dom = PWN::Plugins::TransparentBrowser.dom(
|
677
696
|
# browser_obj: 'required - browser_obj returned from #open method)'
|
678
697
|
# )
|
679
698
|
|
699
|
+
public_class_method def self.dom(opts = {})
|
700
|
+
browser_obj = opts[:browser_obj]
|
701
|
+
supported = %i[chrome headless_chrome]
|
702
|
+
verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
703
|
+
|
704
|
+
devtools = browser_obj[:devtools]
|
705
|
+
computed_styles = %i[display color font-size font-family]
|
706
|
+
devtools.send_cmd(
|
707
|
+
'DOMSnapshot.captureSnapshot',
|
708
|
+
computedStyles: computed_styles
|
709
|
+
).transform_keys(&:to_sym)
|
710
|
+
rescue StandardError => e
|
711
|
+
raise e
|
712
|
+
end
|
713
|
+
|
714
|
+
# Supported Method Parameters::
|
715
|
+
# PWN::Plugins::TransparentBrowser.step_into(
|
716
|
+
# browser_obj: 'required - browser_obj returned from #open method)',
|
717
|
+
# steps: 'optional - number of steps taken (Defaults to 1)'
|
718
|
+
# )
|
719
|
+
|
680
720
|
public_class_method def self.step_into(opts = {})
|
681
721
|
browser_obj = opts[:browser_obj]
|
682
722
|
supported = %i[chrome headless_chrome]
|
683
723
|
verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
684
724
|
|
725
|
+
steps = opts[:steps].to_i
|
726
|
+
steps = 1 if steps.zero? || steps.negative?
|
727
|
+
|
728
|
+
diff_arr = []
|
685
729
|
devtools = browser_obj[:devtools]
|
686
|
-
|
730
|
+
steps.times do |s|
|
731
|
+
diff_hash = {}
|
732
|
+
step = s + 1
|
733
|
+
diff_hash[:step] = step
|
734
|
+
|
735
|
+
dom_before = dom(browser_obj: browser_obj)
|
736
|
+
diff_hash[:dom_before_step] = dom_before
|
737
|
+
|
738
|
+
devtools.send_cmd('Debugger.stepInto')
|
739
|
+
|
740
|
+
dom_after = dom(browser_obj: browser_obj)
|
741
|
+
diff_hash[:dom_after_step] = dom_after
|
742
|
+
|
743
|
+
da = dom_before.to_a - dom_after.to_a
|
744
|
+
diff_hash[:diff_dom] = da.to_h.transform_keys(&:to_sym)
|
745
|
+
|
746
|
+
diff_arr.push(diff_hash)
|
747
|
+
end
|
748
|
+
|
749
|
+
diff_arr
|
687
750
|
rescue StandardError => e
|
688
751
|
raise e
|
689
752
|
end
|
690
753
|
|
691
754
|
# Supported Method Parameters::
|
692
755
|
# PWN::Plugins::TransparentBrowser.step_out(
|
693
|
-
# browser_obj: 'required - browser_obj returned from #open method)'
|
756
|
+
# browser_obj: 'required - browser_obj returned from #open method)',
|
757
|
+
# steps: 'optional - number of steps taken (Defaults to 1)'
|
694
758
|
# )
|
695
759
|
|
696
760
|
public_class_method def self.step_out(opts = {})
|
@@ -698,15 +762,39 @@ module PWN
|
|
698
762
|
supported = %i[chrome headless_chrome]
|
699
763
|
verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
700
764
|
|
765
|
+
steps = opts[:steps].to_i
|
766
|
+
steps = 1 if steps.zero? || steps.negative?
|
767
|
+
|
768
|
+
diff_arr = []
|
701
769
|
devtools = browser_obj[:devtools]
|
702
|
-
|
770
|
+
steps.times do |s|
|
771
|
+
diff_hash = {}
|
772
|
+
step = s + 1
|
773
|
+
diff_hash[:step] = step
|
774
|
+
|
775
|
+
dom_before = dom(browser_obj: browser_obj)
|
776
|
+
diff_hash[:pre_step] = dom_before
|
777
|
+
|
778
|
+
devtools.send_cmd('Debugger.stepOut')
|
779
|
+
|
780
|
+
dom_after = dom(browser_obj: browser_obj, step_sum: step_sum)
|
781
|
+
diff_hash[:post_step] = dom_after
|
782
|
+
|
783
|
+
da = dom_before.to_a - dom_after.to_a
|
784
|
+
diff_hash[:diff] = da.to_h.transform_keys(&:to_sym)
|
785
|
+
|
786
|
+
diff_arr.push(diff_hash)
|
787
|
+
end
|
788
|
+
|
789
|
+
diff_arr
|
703
790
|
rescue StandardError => e
|
704
791
|
raise e
|
705
792
|
end
|
706
793
|
|
707
794
|
# Supported Method Parameters::
|
708
795
|
# PWN::Plugins::TransparentBrowser.step_over(
|
709
|
-
# browser_obj: 'required - browser_obj returned from #open method)'
|
796
|
+
# browser_obj: 'required - browser_obj returned from #open method)',
|
797
|
+
# steps: 'optional - number of steps taken (Defaults to 1)'
|
710
798
|
# )
|
711
799
|
|
712
800
|
public_class_method def self.step_over(opts = {})
|
@@ -714,8 +802,31 @@ module PWN
|
|
714
802
|
supported = %i[chrome headless_chrome]
|
715
803
|
verify_devtools_browser(browser_obj: browser_obj, supported: supported)
|
716
804
|
|
805
|
+
steps = opts[:steps].to_i
|
806
|
+
steps = 1 if steps.zero? || steps.negative?
|
807
|
+
|
808
|
+
diff_arr = []
|
717
809
|
devtools = browser_obj[:devtools]
|
718
|
-
|
810
|
+
steps.times do |s|
|
811
|
+
diff_hash = {}
|
812
|
+
step = s + 1
|
813
|
+
diff_hash[:step] = step
|
814
|
+
|
815
|
+
dom_before = dom(browser_obj: browser_obj)
|
816
|
+
diff_hash[:dom_before_step] = dom_before
|
817
|
+
|
818
|
+
devtools.send_cmd('Debugger.stepOver')
|
819
|
+
|
820
|
+
dom_after = dom(browser_obj: browser_obj, step_sum: step_sum)
|
821
|
+
diff_hash[:dom_after_step] = dom_after
|
822
|
+
|
823
|
+
da = dom_before.to_a - dom_after.to_a
|
824
|
+
diff_hash[:diff_dom] = da.to_h.transform_keys(&:to_sym)
|
825
|
+
|
826
|
+
diff_arr.push(diff_hash)
|
827
|
+
end
|
828
|
+
|
829
|
+
diff_arr
|
719
830
|
rescue StandardError => e
|
720
831
|
raise e
|
721
832
|
end
|
@@ -986,16 +1097,23 @@ module PWN
|
|
986
1097
|
url: 'optional - URL to navigate to after pausing debugger (Defaults to nil)'
|
987
1098
|
)
|
988
1099
|
|
989
|
-
#{self}.
|
1100
|
+
current_dom = #{self}.dom(
|
990
1101
|
browser_obj: 'required - browser_obj returned from #open method)'
|
991
1102
|
)
|
992
1103
|
|
1104
|
+
#{self}.step_into(
|
1105
|
+
browser_obj: 'required - browser_obj returned from #open method)',
|
1106
|
+
steps: 'optional - number of steps taken (Defaults to 1)'
|
1107
|
+
)
|
1108
|
+
|
993
1109
|
#{self}.step_out(
|
994
|
-
browser_obj: 'required - browser_obj returned from #open method)'
|
1110
|
+
browser_obj: 'required - browser_obj returned from #open method)',
|
1111
|
+
steps: 'optional - number of steps taken (Defaults to 1)'
|
995
1112
|
)
|
996
1113
|
|
997
1114
|
#{self}.step_over(
|
998
|
-
browser_obj: 'required - browser_obj returned from #open method)'
|
1115
|
+
browser_obj: 'required - browser_obj returned from #open method)',
|
1116
|
+
steps: 'optional - number of steps taken (Defaults to 1)'
|
999
1117
|
)
|
1000
1118
|
|
1001
1119
|
#{self}.toggle_devtools(
|
data/lib/pwn/plugins.rb
CHANGED
@@ -30,6 +30,7 @@ module PWN
|
|
30
30
|
autoload :Github, 'pwn/plugins/github'
|
31
31
|
autoload :GQRX, 'pwn/plugins/gqrx'
|
32
32
|
autoload :HackerOne, 'pwn/plugins/hacker_one'
|
33
|
+
autoload :Hunter, 'pwn/plugins/hunter'
|
33
34
|
autoload :IPInfo, 'pwn/plugins/ip_info'
|
34
35
|
autoload :IRC, 'pwn/plugins/irc'
|
35
36
|
autoload :Jenkins, 'pwn/plugins/jenkins'
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module PWN
|
6
|
+
module SAST
|
7
|
+
# SAST Module used to identify any localStorage function/method
|
8
|
+
# declarations within source code in an effort to
|
9
|
+
# determine if XSS is possible
|
10
|
+
module LocalStorage
|
11
|
+
@@logger = PWN::Plugins::PWNLogger.create
|
12
|
+
|
13
|
+
# Supported Method Parameters::
|
14
|
+
# PWN::SAST::LocalStorage.scan(
|
15
|
+
# dir_path: 'optional path to dir defaults to .'
|
16
|
+
# git_repo_root_uri: 'optional http uri of git repo scanned'
|
17
|
+
# )
|
18
|
+
|
19
|
+
public_class_method def self.scan(opts = {})
|
20
|
+
dir_path = opts[:dir_path]
|
21
|
+
git_repo_root_uri = opts[:git_repo_root_uri].to_s.scrub
|
22
|
+
result_arr = []
|
23
|
+
logger_results = ''
|
24
|
+
|
25
|
+
PWN::Plugins::FileFu.recurse_dir(dir_path: dir_path) do |entry|
|
26
|
+
if File.file?(entry) && File.basename(entry) !~ /^pwn.+(html|json|db)$/ && File.basename(entry) !~ /\.JS-BEAUTIFIED$/ && entry !~ /test/i
|
27
|
+
line_no_and_contents_arr = []
|
28
|
+
entry_beautified = false
|
29
|
+
|
30
|
+
if File.extname(entry) == '.js' && (`wc -l #{entry}`.split.first.to_i < 20 || entry.include?('.min.js') || entry.include?('-all.js'))
|
31
|
+
js_beautify = `js-beautify #{entry} > #{entry}.JS-BEAUTIFIED`.to_s.scrub
|
32
|
+
entry = "#{entry}.JS-BEAUTIFIED"
|
33
|
+
entry_beautified = true
|
34
|
+
end
|
35
|
+
|
36
|
+
test_case_filter = "
|
37
|
+
grep -n \
|
38
|
+
-e 'localStorage.getItem(' \
|
39
|
+
-e 'localStorage.setItem(' #{entry}
|
40
|
+
"
|
41
|
+
|
42
|
+
str = `#{test_case_filter}`.to_s.scrub
|
43
|
+
|
44
|
+
if str.to_s.empty?
|
45
|
+
# If str length is >= 64 KB do not include results. (Due to Mongo Document Size Restrictions)
|
46
|
+
logger_results = "#{logger_results}~" # Catching bugs is good :)
|
47
|
+
else
|
48
|
+
str = "1:Result larger than 64KB -> Size: #{str.to_s.length}. Please click the \"Path\" link for more details." if str.to_s.length >= 64_000
|
49
|
+
|
50
|
+
hash_line = {
|
51
|
+
timestamp: Time.now.strftime('%Y-%m-%d %H:%M:%S.%9N %z').to_s,
|
52
|
+
security_references: security_references,
|
53
|
+
filename: { git_repo_root_uri: git_repo_root_uri, entry: entry },
|
54
|
+
line_no_and_contents: '',
|
55
|
+
raw_content: str,
|
56
|
+
test_case_filter: test_case_filter
|
57
|
+
}
|
58
|
+
|
59
|
+
# COMMMENT: Must be a better way to implement this (regex is kinda funky)
|
60
|
+
line_contents_split = str.split(/^(\d{1,}):|\n(\d{1,}):/)[1..-1]
|
61
|
+
line_no_count = line_contents_split.length # This should always be an even number
|
62
|
+
current_count = 0
|
63
|
+
while line_no_count > current_count
|
64
|
+
line_no = line_contents_split[current_count]
|
65
|
+
contents = line_contents_split[current_count + 1]
|
66
|
+
if Dir.exist?("#{dir_path}/.git") ||
|
67
|
+
Dir.exist?('.git')
|
68
|
+
|
69
|
+
repo_root = dir_path
|
70
|
+
repo_root = '.' if Dir.exist?('.git')
|
71
|
+
|
72
|
+
author = PWN::Plugins::Git.get_author(
|
73
|
+
repo_root: repo_root,
|
74
|
+
from_line: line_no,
|
75
|
+
to_line: line_no,
|
76
|
+
target_file: entry,
|
77
|
+
entry_beautified: entry_beautified
|
78
|
+
)
|
79
|
+
else
|
80
|
+
author = 'N/A'
|
81
|
+
end
|
82
|
+
hash_line[:line_no_and_contents] = line_no_and_contents_arr.push(
|
83
|
+
line_no: line_no,
|
84
|
+
contents: contents,
|
85
|
+
author: author
|
86
|
+
)
|
87
|
+
|
88
|
+
current_count += 2
|
89
|
+
end
|
90
|
+
result_arr.push(hash_line)
|
91
|
+
logger_results = "#{logger_results}x" # Seeing progress is good :)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
logger_banner = "http://#{Socket.gethostname}:8808/doc_root/pwn-#{PWN::VERSION.to_s.scrub}/#{to_s.scrub.gsub('::', '/')}.html"
|
96
|
+
if logger_results.empty?
|
97
|
+
@@logger.info("#{logger_banner}: No files applicable to this test case.\n")
|
98
|
+
else
|
99
|
+
@@logger.info("#{logger_banner} => #{logger_results}complete.\n")
|
100
|
+
end
|
101
|
+
result_arr
|
102
|
+
rescue StandardError => e
|
103
|
+
raise e
|
104
|
+
end
|
105
|
+
|
106
|
+
# Used primarily to map NIST 800-53 Revision 4 Security Controls
|
107
|
+
# https://web.nvd.nist.gov/view/800-53/Rev4/impact?impactName=HIGH
|
108
|
+
# to PWN Exploit & Static Code Anti-Pattern Matching Modules to
|
109
|
+
# Determine the level of Testing Coverage w/ PWN.
|
110
|
+
|
111
|
+
public_class_method def self.security_references
|
112
|
+
{
|
113
|
+
sast_module: self,
|
114
|
+
section: 'MALICIOUS CODE PROTECTION',
|
115
|
+
nist_800_53_uri: 'https://csrc.nist.gov/Projects/risk-management/sp800-53-controls/release-search#/control/?version=5.1&number=SI-3',
|
116
|
+
cwe_id: '79',
|
117
|
+
cwe_uri: 'https://cwe.mitre.org/data/definitions/79.html'
|
118
|
+
}
|
119
|
+
rescue StandardError => e
|
120
|
+
raise e
|
121
|
+
end
|
122
|
+
|
123
|
+
# Author(s):: 0day Inc. <support@0dayinc.com>
|
124
|
+
|
125
|
+
public_class_method def self.authors
|
126
|
+
"AUTHOR(S):
|
127
|
+
0day Inc. <support@0dayinc.com>
|
128
|
+
"
|
129
|
+
end
|
130
|
+
|
131
|
+
# Display Usage for this Module
|
132
|
+
|
133
|
+
public_class_method def self.help
|
134
|
+
puts "USAGE:
|
135
|
+
sast_arr = #{self}.scan(
|
136
|
+
dir_path: 'optional path to dir defaults to .',
|
137
|
+
git_repo_root_uri: 'optional http uri of git repo scanned'
|
138
|
+
)
|
139
|
+
|
140
|
+
#{self}.authors
|
141
|
+
"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|