-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathcorscan.py
208 lines (162 loc) · 8.8 KB
/
corscan.py
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import argparse
import requests
from colorama import Fore, Style
from concurrent.futures import ThreadPoolExecutor, as_completed
import json
import sys
from urllib.parse import urlparse
from colorama import Fore, Style
def print_logo():
logo = f"""
{Fore.LIGHTCYAN_EX}╔═════════════════[{Fore.CYAN} C O R S C A N {Fore.LIGHTCYAN_EX}]═════════════════╗
{Fore.LIGHTCYAN_EX}║{Fore.YELLOW} Developed By: Angix Black {Fore.LIGHTCYAN_EX}║
╚═══════════════════════════════════════════════════╝{Style.RESET_ALL}
{Fore.RED} DISCLAIMER
This tool is for ethical hacking,legal and educational use only.
Any illegal use is strictly prohibited.{Style.RESET_ALL}
"""
print(logo)
def print_error(message, show_error=True):
if show_error:
sys.stderr.write(f"{Fore.RED}Error: {message}{Style.RESET_ALL}\n")
def is_vulnerable(response, origin):
allow_origin = response.headers.get('Access-Control-Allow-Origin', '')
allow_credentials = response.headers.get('Access-Control-Allow-Credentials', 'false')
if allow_origin == '*' and allow_credentials.lower() == 'true':
return True
if allow_origin == origin and allow_credentials.lower() == 'true':
return True
if allow_origin == '*' or allow_origin == origin:
return True
return False
def attempt_bypass(url):
# Extract the domain from the URL
parsed_url = urlparse(url)
domain = parsed_url.netloc
bypass_strategies = [
'null', # Use 'null' as the Origin
'https://subdomain.evil.com', # Use a subdomain
'http://localhost', # Use localhost as Origin
'file://', # Use file scheme
f'https://{domain}.evil.com', # Add ".evil.com" to the domain
f'https://evil.com.{domain}', # Add "evil.com." before the domain
f'https://{domain}.com', # Append ".com" to the domain
f'http://{domain}', # Use HTTP version of the domain
f'http://{domain}.evil', # Add ".evil" to the domain
f'https://{domain.split(".")[0]}.evil.com', # Use subdomain only with ".evil.com"
]
bypass_results = {}
for origin in bypass_strategies:
headers = {'Origin': origin}
try:
response = requests.options(url, headers=headers, timeout=10)
if is_vulnerable(response, origin):
bypass_results[origin] = True
else:
bypass_results[origin] = False
except requests.RequestException:
bypass_results[origin] = 'Failed'
return bypass_results
def check_cors(url, origin, output_file=None, output_format='text', filter_vulnerable=False):
try:
headers = {'Origin': origin}
response = requests.options(url, headers=headers, timeout=10)
cors_headers = ['Access-Control-Allow-Origin', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Headers', 'Access-Control-Allow-Credentials']
result = {
'url': url,
'origin': origin,
'status_code': response.status_code,
'vulnerable': is_vulnerable(response, origin),
'headers': {header: response.headers.get(header, 'Not Present') for header in cors_headers}
}
bypass_results = attempt_bypass(url)
bypass_success = any(result == True for result in bypass_results.values())
# Filter results if needed
if filter_vulnerable and not (result['vulnerable'] or bypass_success):
return
# Filter bypass results if needed
if filter_vulnerable:
bypass_results = {origin: result for origin, result in bypass_results.items() if result == True}
if output_format == 'json':
result['bypass_attempts'] = bypass_results
result_output = json.dumps(result, indent=4)
else:
result_output = []
result_output.append(f"{Fore.CYAN}URL:{Style.RESET_ALL} {url}")
result_output.append(f"{Fore.CYAN}Origin:{Style.RESET_ALL} {origin}")
result_output.append(f"{Fore.CYAN}Status Code:{Style.RESET_ALL} {response.status_code}")
if result['vulnerable']:
result_output.append(f"{Fore.RED}Potential Vulnerability Detected!{Style.RESET_ALL}")
else:
result_output.append(f"{Fore.GREEN}No CORS Vulnerability Detected{Style.RESET_ALL}")
for header, value in result['headers'].items():
if value != 'Not Present':
result_output.append(f"{Fore.MAGENTA}{header}:{Style.RESET_ALL} {value}")
if not result['headers']:
result_output.append(f"{Fore.YELLOW}CORS Headers Not Found{Style.RESET_ALL}")
result_output.append(f"{Fore.YELLOW}CORS Bypass Attempts:{Style.RESET_ALL}")
for origin, result in bypass_results.items():
if result == 'Failed':
result_output.append(f"{Fore.CYAN}{origin}:{Style.RESET_ALL} {Fore.RED}Request Failed{Style.RESET_ALL}")
else:
result_output.append(f"{Fore.CYAN}{origin}:{Style.RESET_ALL} {Fore.GREEN}{'Successful' if result else 'Failed'}{Style.RESET_ALL}")
result_output.append("-" * 50)
result_output = "\n".join(result_output)
print(result_output)
if output_file:
try:
with open(output_file, 'a') as f:
f.write(result_output + "\n")
except IOError as e:
print_error(f"Failed to write to output file {output_file}: {e}")
except requests.RequestException as e:
print_error(f"Could not connect to {url}. The URL may be invalid or unreachable.", not filter_vulnerable)
def print_help():
help_text = f"""
{Fore.GREEN}Usage:{Style.RESET_ALL}
crsn [options]
{Fore.YELLOW}Options:{Style.RESET_ALL}
{Fore.CYAN}-u, --url{Style.RESET_ALL} Target URL to check CORS headers
{Fore.CYAN}-f, --file{Style.RESET_ALL} File containing a list of URLs to check CORS headers
{Fore.CYAN}-r, --origin{Style.RESET_ALL} Custom origin to use for the CORS check (default: https://evil.com)
{Fore.CYAN}-t, --threads{Style.RESET_ALL} Number of threads to use for scanning (default: 20)
{Fore.CYAN}-o, --output{Style.RESET_ALL} File to save the output
{Fore.CYAN}--format{Style.RESET_ALL} Output format: text (default) or json
{Fore.CYAN}--filter{Style.RESET_ALL} Filter results to show only vulnerable entries
{Fore.CYAN}-h, --help{Style.RESET_ALL} Show this help message and exit
{Fore.YELLOW}Description:{Style.RESET_ALL}
Advanced CORS Header Checker Tool with Vulnerability Detection and Bypass Attempts.
"""
print(help_text)
def main():
print_logo()
parser = argparse.ArgumentParser(add_help=False) # Disable default help
parser.add_argument("-u", "--url", help="Target URL to check CORS headers")
parser.add_argument("-f", "--file", help="File containing a list of URLs to check CORS headers")
parser.add_argument("-r", "--origin", default="https://evil.com", help="Custom origin to use for the CORS check (default: https://evil.com)")
parser.add_argument("-t", "--threads", type=int, default=20, help="Number of threads to use for scanning (default: 20)")
parser.add_argument("-o", "--output", help="File to save the output")
parser.add_argument("--format", choices=['text', 'json'], default='text', help="Output format: text (default) or json")
parser.add_argument("--filter", action='store_true', help="Filter results to show only vulnerable entries")
parser.add_argument("-h", "--help", action='store_true', help="Show help message and exit")
args = parser.parse_args()
if args.help:
print_help()
sys.exit(0)
if not args.url and not args.file:
print_error("Please provide a URL with -u or a file with -f or -h for help.")
sys.exit(1)
try:
if args.url:
check_cors(args.url, origin=args.origin, output_file=args.output, output_format=args.format, filter_vulnerable=args.filter)
if args.file:
with open(args.file, 'r') as f:
urls = [line.strip() for line in f if line.strip()]
with ThreadPoolExecutor(max_workers=args.threads) as executor:
futures = [executor.submit(check_cors, url, origin=args.origin, output_file=args.output, output_format=args.format, filter_vulnerable=args.filter) for url in urls]
for future in as_completed(futures):
future.result()
except Exception as e:
print_error(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
main()