From 708852250a27c85fc196ee13710dee01c2923f50 Mon Sep 17 00:00:00 2001 From: dolevf Date: Sat, 27 Aug 2022 15:46:41 -0400 Subject: [PATCH 1/2] improved error handling, operation name additions, split tests --- graphql-cop.py | 4 +++- lib/tests/dos_alias_overloading.py | 6 ++--- lib/tests/dos_batch.py | 2 +- lib/tests/dos_circular_introspection.py | 2 +- lib/tests/dos_directive_overloading.py | 4 ++-- lib/tests/dos_field_duplication.py | 4 ++-- lib/tests/info_field_suggestions.py | 3 +-- lib/tests/info_get_based_mutation.py | 3 +-- lib/tests/info_get_method_support.py | 2 +- lib/tests/info_introspect.py | 2 +- lib/tests/info_post_based_csrf.py | 2 +- lib/tests/info_trace_mode.py | 8 +++---- lib/tests/info_unhandled_error.py | 29 +++++++++++++++++++++++++ lib/utils.py | 13 +++++------ version.py | 2 +- 15 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 lib/tests/info_unhandled_error.py diff --git a/graphql-cop.py b/graphql-cop.py index 708b78b..6b08df0 100644 --- a/graphql-cop.py +++ b/graphql-cop.py @@ -19,6 +19,7 @@ from lib.tests.dos_circular_introspection import circular_query_introspection from lib.tests.info_get_based_mutation import get_based_mutation from lib.tests.info_post_based_csrf import post_based_csrf +from lib.tests.info_unhandled_error import unhandled_error_detection from lib.utils import is_graphql, draw_art @@ -71,7 +72,8 @@ tests = [field_suggestions, introspection, detect_graphiql, get_method_support, alias_overloading, batch_query, field_duplication, trace_mode, directive_overloading, - circular_query_introspection, get_based_mutation, post_based_csrf] + circular_query_introspection, get_based_mutation, post_based_csrf, + unhandled_error_detection] json_output = [] diff --git a/lib/tests/dos_alias_overloading.py b/lib/tests/dos_alias_overloading.py index 2e576a0..c28aeb9 100644 --- a/lib/tests/dos_alias_overloading.py +++ b/lib/tests/dos_alias_overloading.py @@ -17,10 +17,10 @@ def alias_overloading(url, proxy, headers): for i in range(0, 101): aliases += 'alias{}:__typename \n'.format(i) - gql_response = graph_query(url, proxies=proxy, headers=headers, payload='query { ' + aliases + ' }') - + gql_response = graph_query(url, proxies=proxy, headers=headers, payload='query cop { ' + aliases + ' }') + res['curl_verify'] = curlify(gql_response) - + try: if gql_response.json()['data']['alias100']: res['result'] = True diff --git a/lib/tests/dos_batch.py b/lib/tests/dos_batch.py index d4ea18b..938e4ed 100644 --- a/lib/tests/dos_batch.py +++ b/lib/tests/dos_batch.py @@ -13,7 +13,7 @@ def batch_query(url, proxy, headers): 'curl_verify':'' } - gql_response = graph_query(url, proxies=proxy, headers=headers, payload='query { __typename }', batch=True) + gql_response = graph_query(url, proxies=proxy, headers=headers, payload='query cop { __typename }', batch=True) res['curl_verify'] = curlify(gql_response) diff --git a/lib/tests/dos_circular_introspection.py b/lib/tests/dos_circular_introspection.py index 50750c7..c935c76 100644 --- a/lib/tests/dos_circular_introspection.py +++ b/lib/tests/dos_circular_introspection.py @@ -12,7 +12,7 @@ def circular_query_introspection(url, proxy, headers): 'curl_verify':'' } - q = 'query { __schema { types { fields { type { fields { type { fields { type { fields { type { name } } } } } } } } } } }' + q = 'query cop { __schema { types { fields { type { fields { type { fields { type { fields { type { name } } } } } } } } } } }' gql_response = graph_query(url, proxies=proxy, headers=headers, payload=q) res['curl_verify'] = curlify(gql_response) diff --git a/lib/tests/dos_directive_overloading.py b/lib/tests/dos_directive_overloading.py index 8b1eb35..1a5ac75 100644 --- a/lib/tests/dos_directive_overloading.py +++ b/lib/tests/dos_directive_overloading.py @@ -13,10 +13,10 @@ def directive_overloading(url, proxy, headers): 'curl_verify':'' } - q = 'query { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }' + q = 'query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }' gql_response = graph_query(url, proxies=proxy, headers=headers, payload=q) res['curl_verify'] = curlify(gql_response) - + try: if len(gql_response.json()['errors']) == 10: res['result'] = True diff --git a/lib/tests/dos_field_duplication.py b/lib/tests/dos_field_duplication.py index 587a719..7a12bae 100644 --- a/lib/tests/dos_field_duplication.py +++ b/lib/tests/dos_field_duplication.py @@ -14,10 +14,10 @@ def field_duplication(url, proxy, headers): } duplicated_string = '__typename \n' * 500 - q = 'query { ' + duplicated_string + '} ' + q = 'query cop { ' + duplicated_string + '} ' gql_response = graph_query(url, proxies=proxy, headers=headers, payload=q) res['curl_verify'] = curlify(gql_response) - + try: if gql_response.json()['data']['__typename']: res['result'] = True diff --git a/lib/tests/info_field_suggestions.py b/lib/tests/info_field_suggestions.py index 03c11d6..27b50b0 100644 --- a/lib/tests/info_field_suggestions.py +++ b/lib/tests/info_field_suggestions.py @@ -13,10 +13,9 @@ def field_suggestions(url, proxy, headers): 'curl_verify':'' } - q = 'query { __schema { directive } }' + q = 'query cop { __schema { directive } }' gql_response = graph_query(url, proxies=proxy, headers=headers, payload=q) res['curl_verify'] = curlify(gql_response) - try: if 'Did you mean' in get_error(gql_response.json()): diff --git a/lib/tests/info_get_based_mutation.py b/lib/tests/info_get_based_mutation.py index 24e2561..b74d0ad 100644 --- a/lib/tests/info_get_based_mutation.py +++ b/lib/tests/info_get_based_mutation.py @@ -12,11 +12,10 @@ def get_based_mutation(url, proxies, headers): 'curl_verify':'' } - q = 'mutation {__typename}' + q = 'mutation cop {__typename}' response = request(url, proxies=proxies, headers=headers, params={'query':q}) res['curl_verify'] = curlify(response) - try: if response and response.json()['data']['__typename']: res['result'] = True diff --git a/lib/tests/info_get_method_support.py b/lib/tests/info_get_method_support.py index 330cc50..146255c 100644 --- a/lib/tests/info_get_method_support.py +++ b/lib/tests/info_get_method_support.py @@ -13,7 +13,7 @@ def get_method_support(url, proxies, headers): 'curl_verify':'' } - q = '{__typename}' + q = 'query cop {__typename}' response = request(url, proxies=proxies, headers=headers, params={'query':q}) res['curl_verify'] = curlify(response) diff --git a/lib/tests/info_introspect.py b/lib/tests/info_introspect.py index 06d07c3..eb7e9e8 100644 --- a/lib/tests/info_introspect.py +++ b/lib/tests/info_introspect.py @@ -13,7 +13,7 @@ def introspection(url, proxy, headers): 'curl_verify':'' } - q = 'query { __schema { types { name fields { name } } } }' + q = 'query cop { __schema { types { name fields { name } } } }' gql_response = graph_query(url, proxies=proxy, headers=headers, payload=q) res['curl_verify'] = curlify(gql_response) diff --git a/lib/tests/info_post_based_csrf.py b/lib/tests/info_post_based_csrf.py index 9828415..d8ac981 100644 --- a/lib/tests/info_post_based_csrf.py +++ b/lib/tests/info_post_based_csrf.py @@ -12,7 +12,7 @@ def post_based_csrf(url, proxies, headers): 'curl_verify':'' } - q = 'query {__typename}' + q = 'query cop {__typename}' response = request(url, proxies=proxies, headers=headers, params={'query':q}, verb='POST') res['curl_verify'] = curlify(response) diff --git a/lib/tests/info_trace_mode.py b/lib/tests/info_trace_mode.py index 2accb4c..47180c3 100644 --- a/lib/tests/info_trace_mode.py +++ b/lib/tests/info_trace_mode.py @@ -13,14 +13,12 @@ def trace_mode(url, proxy, headers): 'curl_verify':'' } - q = 'query { __typename }' + q = 'query cop { __typename }' gql_response = graph_query(url, proxies=proxy, headers=headers, payload=q) res['curl_verify'] = curlify(gql_response) - + try: - if gql_response.json().get('errors', {}).get('extensions', {}).get('tracing'): - res['result'] = True - elif gql_response.json().get('errors', {}).get('extensions', {}).get('exception', None): + if gql_response.json()['errors'][0]['extensions']['tracing']: res['result'] = True elif 'stacktrace' in str(gql_response.json()).lower(): res['result'] = True diff --git a/lib/tests/info_unhandled_error.py b/lib/tests/info_unhandled_error.py new file mode 100644 index 0000000..927c8d9 --- /dev/null +++ b/lib/tests/info_unhandled_error.py @@ -0,0 +1,29 @@ +"""Collect trace mode details.""" +from lib.utils import graph_query, curlify + + +def unhandled_error_detection(url, proxy, headers): + """Get the trace mode.""" + res = { + 'result':False, + 'title':'Unhandled Errors Detection', + 'description':'Exception errors are not handled', + 'impact':'Information Leakage', + 'severity':'INFO', + 'curl_verify':'' + } + + q = 'qwerty cop { abc }' + gql_response = graph_query(url, proxies=proxy, headers=headers, payload=q) + res['curl_verify'] = curlify(gql_response) + + try: + #if gql_response.json()['errors'][0]['extensions']['exception']: + if gql_response.json()['errors'][0]['extensions']['exception']: + res['result'] = True + elif 'exception' in str(gql_response.json()).lower(): + res['result'] = True + except: + pass + + return res diff --git a/lib/utils.py b/lib/utils.py index 8fa2f69..fc75ea4 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -29,14 +29,12 @@ def get_error(resp): def graph_query(url, proxies, headers, operation='query', payload={}, batch=False): """Perform a query.""" - if batch: data = [] for _ in range(10): data.append({operation:payload}) else: - data = {operation:payload} - + data = {operation:payload, "operationName":"cop"} try: response = requests.post(url, headers=headers, @@ -73,7 +71,7 @@ def request(url, proxies, headers, params=None, data=None, verb='GET'): def is_graphql(url, proxies, headers): """Check if the URL provides a GraphQL interface.""" query = ''' - query { + query cop { __typename } ''' @@ -86,9 +84,10 @@ def is_graphql(url, proxies, headers): except JSONDecodeError: return False - if response.json().get('data', {}).get('__typename', '') in ('Query', 'QueryRoot', 'query_root'): - return True - elif response.json().get('errors') and (any('locations' in i for i in response['errors']) or (any('extensions' in i for i in response))): + if 'data' in response.json() and response.json()['data'] != None: + if response.json()['data']['__typename'] in ('Query', 'QueryRoot', 'query_root', 'Root'): + return True + elif response.json().get('errors') and (any('locations' in i for i in response.json()['errors']) or (any('extensions' in i for i in response.json()))): return True elif response.json().get('data'): return True diff --git a/version.py b/version.py index 84f511c..764ff01 100644 --- a/version.py +++ b/version.py @@ -1,2 +1,2 @@ """Version details of graphql-cop.""" -VERSION = '1.6' +VERSION = '1.7' From f8be1e4c6257597e155abd57b107c842c44688a9 Mon Sep 17 00:00:00 2001 From: dolevf Date: Sat, 27 Aug 2022 15:47:24 -0400 Subject: [PATCH 2/2] remove comment --- lib/tests/info_unhandled_error.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/tests/info_unhandled_error.py b/lib/tests/info_unhandled_error.py index 927c8d9..a81a538 100644 --- a/lib/tests/info_unhandled_error.py +++ b/lib/tests/info_unhandled_error.py @@ -18,7 +18,6 @@ def unhandled_error_detection(url, proxy, headers): res['curl_verify'] = curlify(gql_response) try: - #if gql_response.json()['errors'][0]['extensions']['exception']: if gql_response.json()['errors'][0]['extensions']['exception']: res['result'] = True elif 'exception' in str(gql_response.json()).lower():