-
Notifications
You must be signed in to change notification settings - Fork 100
Expand file tree
/
Copy pathgraphql-cop.py
More file actions
124 lines (99 loc) · 4.44 KB
/
Copy pathgraphql-cop.py
File metadata and controls
124 lines (99 loc) · 4.44 KB
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
#!/usr/bin/env python3
import sys
from json import loads, dumps
from optparse import OptionParser
from version import VERSION
from config import HEADERS
from urllib.parse import urlparse
from lib.tests import tests
from lib.utils import is_graphql, draw_art, read_custom_wordlist
from termcolor import colored
parser = OptionParser(usage='%prog -t http://example.com -o json')
parser.add_option('-t', '--target', dest='url', help='target url with the path - if a GraphQL path is not provided, GraphQL Cop will iterate through a series of common GraphQL paths')
parser.add_option('-H', '--header', dest='header', action='append', help='Append Header(s) to the request \'{"Authorization": "Bearer eyjt"}\' - Use multiple -H for additional Headers')
parser.add_option('-o', '--output', dest='format',
help='json', default=False)
parser.add_option('-e', '--excluded-tests', dest='excluded_tests',
help='Exclude specific tests (comma separated)', default=[])
parser.add_option('-l', '--list-tests', dest='list_tests', action='store_true',
help='List avaialble tests', default=False)
parser.add_option('-f', '--force', dest='forced_scan', action='store_true',
help='Forces a scan when GraphQL cannot be detected', default=False)
parser.add_option('-d', '--debug', dest='debug_mode', action='store_true',
help='Append a header with the test name for debugging', default=False)
parser.add_option('-x', '--proxy', dest='proxy', default=None,
help='HTTP(S) proxy URL in the form http://user:pass@host:port')
parser.add_option('-w', '--wordlist', dest='wordlist', default=False, help='Path to a list of custom GraphQL endpoints')
parser.add_option('--version', '-v', dest='version', action='store_true', default=False,
help='Print out the current version and exit.')
parser.add_option('--tor','-T', dest='tor', action='store_true', default=False,
help='Sends the request through the Tor network (ensure Tor is running and properly configured)')
options, args = parser.parse_args()
proxy = {}
if options.version:
print('version:', VERSION)
sys.exit(0)
if options.list_tests:
print("\n".join(tests.keys()))
sys.exit(0)
if not options.url:
print(draw_art())
parser.print_help()
sys.exit(1)
if options.proxy:
proxy = {
'http': options.proxy,
'https': options.proxy
}
if options.excluded_tests:
for excluded_test in options.excluded_tests.split(','):
try:
del tests[excluded_test]
except:
print(f'{excluded_test} cannot be excluded, skipping')
elif options.tor:
import socks
import socket
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 9050)
socket.socket = socks.socksocket
proxy = {}
if options.header != None:
try:
for l in options.header:
extra_headers = loads(l)
HEADERS.update(extra_headers)
except:
print("Cannot cast %s into header dictionary. Ensure the format \'{\"key\": \"value\"}\'."%(options.header))
if not urlparse(options.url).scheme:
print("URL missing scheme (http:// or https://). Ensure URL contains some scheme.")
sys.exit(1)
else:
url = options.url
if options.wordlist:
endpoints = read_custom_wordlist(options.wordlist)
else:
endpoints = ['/', '/graphiql', '/playground', '/console', '/graphql']
paths = []
parsed = urlparse(url)
if parsed.path and parsed.path != '/':
paths.append(url)
else:
for endpoint in endpoints:
paths.append(parsed.scheme + '://' + parsed.netloc + endpoint)
json_output = []
for path in paths:
if not is_graphql(path, proxy, HEADERS, options.debug_mode):
if not options.forced_scan:
print(path, 'does not seem to be running GraphQL. (Consider using -f to force the scan if GraphQL does exist on the endpoint)')
continue
else:
print('Running a forced scan against the endpoint')
for test in tests.values():
json_output.append(test(path, proxy, HEADERS, options.debug_mode))
json_output = sorted(json_output, key=lambda d: d['title'])
if options.format == 'json':
print(dumps(json_output))
else:
for i in json_output:
if i['result']:
print('[{}] {} - {} ({})'.format(colored(i['severity'], i['color'], attrs=['bold']), colored(i['title'], 'white', attrs=['bold']), i['description'], i['impact']))