pwn 0.4.886 → 0.4.887
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 +2 -2
- data/bin/pwn_graphql_introspection_detector +311 -0
- data/lib/pwn/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eba0652465ac3cb7e848a84e78412952875ab79a2c47dbcda39068a65e9f5c5b
|
4
|
+
data.tar.gz: 2c03fbba16c08c1820d483e744e5e1dd70812081aff632a9f71f0227f582d2a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9912c9c682b57c711c1a57e09e7ffad70c7018cf8e2689ecf7ff3a0f8fe3e5de9638588fd85c3475f58aaeb51a4892061e9cd4ceca6d1d3a40e5a2be8e88d31
|
7
|
+
data.tar.gz: c26189e41c966c8d3ca0e3988d9d901461b420a85e6d49123814b7e9331e8bcb6a7fe2dd2b3fa0d058f8b2b4f3948e3b5131a4ecb21854dc0a018e03f42d9b58
|
data/README.md
CHANGED
@@ -37,7 +37,7 @@ $ rvm use ruby-3.2.2@pwn
|
|
37
37
|
$ rvm list gemsets
|
38
38
|
$ gem install --verbose pwn
|
39
39
|
$ pwn
|
40
|
-
pwn[v0.4.
|
40
|
+
pwn[v0.4.887]:001 >>> PWN.help
|
41
41
|
```
|
42
42
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.2.2@pwn
|
|
52
52
|
$ gem uninstall --all --executables pwn
|
53
53
|
$ gem install --verbose pwn
|
54
54
|
$ pwn
|
55
|
-
pwn[v0.4.
|
55
|
+
pwn[v0.4.887]:001 >>> PWN.help
|
56
56
|
```
|
57
57
|
|
58
58
|
|
@@ -0,0 +1,311 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'faker'
|
5
|
+
require 'json'
|
6
|
+
require 'optparse'
|
7
|
+
require 'pwn'
|
8
|
+
|
9
|
+
opts = {}
|
10
|
+
OptionParser.new do |options|
|
11
|
+
options.on('-jFILE', '--json-results=FILE', 'Required - JSON results file from pwn_shodan_search driver') do |j|
|
12
|
+
opts[:json_results] = j
|
13
|
+
end
|
14
|
+
end.parse!
|
15
|
+
|
16
|
+
if opts.empty?
|
17
|
+
puts `#{$PROGRAM_NAME} --help`
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
21
|
+
json_results_path = opts[:json_results]
|
22
|
+
raise "ERROR: Shodan JSON Results File #{json_results_path} Does Not Exist." unless File.exist?(json_results_path)
|
23
|
+
|
24
|
+
search_results = JSON.parse(
|
25
|
+
File.read(json_results_path),
|
26
|
+
symbolize_names: true
|
27
|
+
)
|
28
|
+
|
29
|
+
print 'Extracting URIs from Shodan search results...'
|
30
|
+
uri_arr = PWN::Plugins::Shodan.get_uris(search_results: search_results)
|
31
|
+
puts 'complete.'
|
32
|
+
|
33
|
+
print "Extracting GraphQL URIs from #{uri_arr.count} URIs..."
|
34
|
+
graphql_uris = uri_arr.uniq.select do |uri|
|
35
|
+
uri if uri =~ %r{.+/graphql/?$} ||
|
36
|
+
uri =~ %r{.+/graphiql/?$} ||
|
37
|
+
uri =~ %r{.+/playground/?$} ||
|
38
|
+
uri =~ %r{.+/console/?$} ||
|
39
|
+
uri =~ %r{.+/query/?$} ||
|
40
|
+
uri =~ %r{.+/gql/?$} ||
|
41
|
+
uri =~ %r{.+/index.php?graphql$}
|
42
|
+
end
|
43
|
+
puts 'complete.'
|
44
|
+
|
45
|
+
browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)
|
46
|
+
rest_client = browser_obj[:browser]::Request
|
47
|
+
|
48
|
+
graphql_payloads_arr = []
|
49
|
+
|
50
|
+
graphql_schema_payload = '{ "query": "{ __schema { types { name } } }" }'
|
51
|
+
graphql_payloads_arr.push(graphql_schema_payload)
|
52
|
+
|
53
|
+
graphql_introspection_payload1 = <<-END_OF_PAYLOAD
|
54
|
+
{
|
55
|
+
query IntrospectionQuery {
|
56
|
+
__schema {
|
57
|
+
|
58
|
+
queryType { name }
|
59
|
+
mutationType { name }
|
60
|
+
subscriptionType { name }
|
61
|
+
types { ...FullType }
|
62
|
+
directives {
|
63
|
+
name
|
64
|
+
description
|
65
|
+
locations
|
66
|
+
args { ...InputValue }
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
fragment FullType on __Type {
|
72
|
+
kind
|
73
|
+
name
|
74
|
+
description
|
75
|
+
fields(includeDeprecated: true) {
|
76
|
+
name
|
77
|
+
description
|
78
|
+
args { ...InputValue }
|
79
|
+
type { ...TypeRef }
|
80
|
+
isDeprecated
|
81
|
+
deprecationReason
|
82
|
+
}
|
83
|
+
inputFields {
|
84
|
+
...InputValue
|
85
|
+
}
|
86
|
+
interfaces {
|
87
|
+
...TypeRef
|
88
|
+
}
|
89
|
+
enumValues(includeDeprecated: true) {
|
90
|
+
name
|
91
|
+
description
|
92
|
+
isDeprecated
|
93
|
+
deprecationReason
|
94
|
+
}
|
95
|
+
possibleTypes {
|
96
|
+
...TypeRef
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
fragment InputValue on __InputValue {
|
101
|
+
name
|
102
|
+
description
|
103
|
+
type { ...TypeRef }
|
104
|
+
defaultValue
|
105
|
+
}
|
106
|
+
|
107
|
+
fragment TypeRef on __Type {
|
108
|
+
kind
|
109
|
+
name
|
110
|
+
ofType {
|
111
|
+
kind
|
112
|
+
name
|
113
|
+
ofType {
|
114
|
+
kind
|
115
|
+
name
|
116
|
+
ofType {
|
117
|
+
kind
|
118
|
+
name
|
119
|
+
ofType {
|
120
|
+
kind
|
121
|
+
name
|
122
|
+
ofType {
|
123
|
+
kind
|
124
|
+
name
|
125
|
+
ofType {
|
126
|
+
kind
|
127
|
+
name
|
128
|
+
ofType {
|
129
|
+
kind
|
130
|
+
name
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
}
|
140
|
+
END_OF_PAYLOAD
|
141
|
+
graphql_payloads_arr.push(graphql_introspection_payload1)
|
142
|
+
|
143
|
+
graphql_introspection_payload2 = <<-END_OF_PAYLOAD
|
144
|
+
{
|
145
|
+
"operationName":"IntrospectionQuery",
|
146
|
+
"variables":{},
|
147
|
+
"query":"
|
148
|
+
fragment FullType on __Type {
|
149
|
+
kind
|
150
|
+
name
|
151
|
+
description
|
152
|
+
fields(includeDeprecated: true) {
|
153
|
+
name
|
154
|
+
description
|
155
|
+
args { ...InputValue }
|
156
|
+
type { ...TypeRef }
|
157
|
+
isDeprecated deprecationReason
|
158
|
+
}
|
159
|
+
inputFields { ...InputValue }
|
160
|
+
interfaces { ...TypeRef }
|
161
|
+
enumValues(includeDeprecated: true) {
|
162
|
+
name
|
163
|
+
description
|
164
|
+
isDeprecated
|
165
|
+
deprecationReason
|
166
|
+
}
|
167
|
+
possibleTypes { ...TypeRef }
|
168
|
+
}
|
169
|
+
|
170
|
+
fragment InputValue on __InputValue {
|
171
|
+
name
|
172
|
+
description
|
173
|
+
type { ...TypeRef }
|
174
|
+
defaultValue
|
175
|
+
}
|
176
|
+
|
177
|
+
fragment TypeRef on __Type {
|
178
|
+
kind
|
179
|
+
name
|
180
|
+
ofType {
|
181
|
+
kind
|
182
|
+
name
|
183
|
+
ofType {
|
184
|
+
kind
|
185
|
+
name
|
186
|
+
ofType {
|
187
|
+
kind
|
188
|
+
name
|
189
|
+
ofType {
|
190
|
+
kind
|
191
|
+
name
|
192
|
+
ofType {
|
193
|
+
kind
|
194
|
+
name
|
195
|
+
ofType {
|
196
|
+
kind
|
197
|
+
name
|
198
|
+
ofType {
|
199
|
+
kind
|
200
|
+
name
|
201
|
+
}
|
202
|
+
}
|
203
|
+
}
|
204
|
+
}
|
205
|
+
}
|
206
|
+
}
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
query IntrospectionQuery {
|
211
|
+
__schema {
|
212
|
+
queryType { name }
|
213
|
+
mutationType { name }
|
214
|
+
types { ...FullType }
|
215
|
+
directives {
|
216
|
+
name
|
217
|
+
description
|
218
|
+
locations
|
219
|
+
args { ...InputValue }
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}"
|
223
|
+
}
|
224
|
+
END_OF_PAYLOAD
|
225
|
+
graphql_payloads_arr.push(graphql_introspection_payload2)
|
226
|
+
|
227
|
+
# Math Part
|
228
|
+
graphql_uri_tot = graphql_uris.length
|
229
|
+
payload_tot = graphql_payloads_arr.length
|
230
|
+
request_tot = payload_tot * graphql_uri_tot
|
231
|
+
|
232
|
+
puts "Sending #{payload_tot} payloads to #{graphql_uris.count} GraphQL URIs..."
|
233
|
+
puts "Total HTTP Requests: #{request_tot}"
|
234
|
+
|
235
|
+
user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36'
|
236
|
+
introspection_arr_of_hashes = []
|
237
|
+
graphql_uris.each do |this_uri|
|
238
|
+
puts "IN PROGRESS: #{this_uri}..."
|
239
|
+
graphql_payloads_arr.each_with_index do |this_payload, payload_index|
|
240
|
+
# user_agent = Faker::Internet.user_agent
|
241
|
+
timestamp = Time.now.strftime('%Y-%m-%d_%H:%M:%S')
|
242
|
+
introspection_hash = {
|
243
|
+
time: timestamp,
|
244
|
+
uri: this_uri,
|
245
|
+
payload_index: payload_index,
|
246
|
+
response_code: nil,
|
247
|
+
response_headers: nil,
|
248
|
+
response: nil
|
249
|
+
}
|
250
|
+
resp = rest_client.execute(
|
251
|
+
method: :post,
|
252
|
+
url: this_uri,
|
253
|
+
headers: {
|
254
|
+
content_type: 'application/json; charset=UTF-8',
|
255
|
+
user_agent: user_agent
|
256
|
+
},
|
257
|
+
payload: this_payload,
|
258
|
+
verify_ssl: false,
|
259
|
+
timeout: 3.0,
|
260
|
+
max_redirects: 3
|
261
|
+
)
|
262
|
+
introspection_hash[:response_code] = resp.code
|
263
|
+
introspection_hash[:response_headers] = resp.headers
|
264
|
+
json_resp = JSON.parse(
|
265
|
+
resp.body,
|
266
|
+
symbolize_names: true
|
267
|
+
)
|
268
|
+
|
269
|
+
introspection_hash[:response] = json_resp
|
270
|
+
rescue Errno::ECONNREFUSED,
|
271
|
+
Errno::ENETUNREACH,
|
272
|
+
JSON::ParserError,
|
273
|
+
OpenSSL::SSL::SSLError,
|
274
|
+
SocketError => e
|
275
|
+
|
276
|
+
introspection_hash[:response_code] = "#{e.class}: #{e.message}"
|
277
|
+
|
278
|
+
next
|
279
|
+
rescue RestClient::ExceptionWithResponse => e
|
280
|
+
introspection_hash[:response_code] = "#{e.class}: #{e.message}" if e.response.nil?
|
281
|
+
|
282
|
+
if e.response
|
283
|
+
introspection_hash[:response_code] = e.response.code
|
284
|
+
introspection_hash[:response_headers] = e.response.headers
|
285
|
+
introspection_hash[:response] = e.response.body
|
286
|
+
end
|
287
|
+
|
288
|
+
next
|
289
|
+
ensure
|
290
|
+
introspection_arr_of_hashes.push(introspection_hash)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
puts 'complete.'
|
294
|
+
|
295
|
+
timestamp = Time.now.strftime('%Y-%m-%d_%H:%M:%S')
|
296
|
+
print 'Saving Introspection Results...'
|
297
|
+
File.write(
|
298
|
+
"graphql_results-#{timestamp}-ALL.json",
|
299
|
+
JSON.pretty_generate(introspection_arr_of_hashes)
|
300
|
+
)
|
301
|
+
|
302
|
+
vulnerable_uris = introspection_arr_of_hashes.select do |h|
|
303
|
+
h if (h[:response].is_a?(Hash) && h[:response].keys.first.to_sym == :data) ||
|
304
|
+
(h[:response].is_a?(Integer) && h[:response_code] == 415) ||
|
305
|
+
(h[:response].is_a?(Integer) && h[:response_code] >= 500)
|
306
|
+
end
|
307
|
+
File.write(
|
308
|
+
"graphql_results-#{timestamp}-VULNERABLE.json",
|
309
|
+
JSON.pretty_generate(vulnerable_uris)
|
310
|
+
)
|
311
|
+
puts 'complete.'
|
data/lib/pwn/version.rb
CHANGED
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.4.
|
4
|
+
version: 0.4.887
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 0day Inc.
|
@@ -1167,6 +1167,7 @@ executables:
|
|
1167
1167
|
- pwn_diff_csv_files_w_column_exclude
|
1168
1168
|
- pwn_domain_reversewhois
|
1169
1169
|
- pwn_fuzz_net_app_proto
|
1170
|
+
- pwn_graphql_introspection_detector
|
1170
1171
|
- pwn_ibm_appscan_enterprise
|
1171
1172
|
- pwn_jenkins_create_job
|
1172
1173
|
- pwn_jenkins_create_view
|
@@ -1233,6 +1234,7 @@ files:
|
|
1233
1234
|
- bin/pwn_diff_csv_files_w_column_exclude
|
1234
1235
|
- bin/pwn_domain_reversewhois
|
1235
1236
|
- bin/pwn_fuzz_net_app_proto
|
1237
|
+
- bin/pwn_graphql_introspection_detector
|
1236
1238
|
- bin/pwn_ibm_appscan_enterprise
|
1237
1239
|
- bin/pwn_jenkins_create_job
|
1238
1240
|
- bin/pwn_jenkins_create_view
|