-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
scan.py
executable file
·166 lines (142 loc) · 4.82 KB
/
scan.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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Commandline interface
to control Scan class"""
import logging
import click
import requests
class Scan(object):
"""Class to perfom an active security
scan against a AEM dispatcher"""
def __init__(self):
"""Constructor"""
self.website_url = None
self.website_page_path = None
self.request_timeout = None
self.path_list = self.load_path_list('aem-sec-paths.txt')
def print_configuration(self):
"""Prints configuration of Scan"""
logging.debug('Website URL is set to "{url}"'.format(
url=self.website_url))
logging.debug('Website page path is set to "{path}"'.format(
path=self.website_page_path))
logging.debug('Request timeout is set to "{timeout}"'.format(
timeout=self.request_timeout))
@staticmethod
def load_path_list(file_path):
"""Loads path list from text file"""
with open(file_path, 'r') as text_file:
return text_file.readlines()
def replace_page_path(self, path):
"""Replaces placeholder '/content/add_valid_path_to_a_page'
with valid website page path"""
if self.website_page_path is not None:
return path.replace("/content/add_valid_path_to_a_page",
self.website_page_path)
return path
def perform_url_test(self, path):
"""Performs a single URL test"""
url = '{website}{path}'.format(
website=self.website_url,
path=self.replace_page_path(path)
)
r = None
try:
r = requests.get(
url,
timeout=self.request_timeout)
except requests.exceptions.RequestException as e:
logging.error('{err} for {url}'.format(err=e, url=url))
return r
def perform_dispatcher_invalidate_cache_test(self):
"""Performs distpacher invalidate cache test"""
url = '{website}{path}'.format(
website=self.website_url,
path='/dispatcher/invalidate.cache'
)
headers = {
'CQ-Handle': '/content',
'CQ-Path': '/content',
}
r = None
try:
r = requests.get(
url,
headers=headers,
timeout=self.request_timeout)
except requests.exceptions.RequestException as e:
logging.error('{err} for {url}'.format(err=e, url=url))
return r
@staticmethod
def asses_test_result(status_code):
"""Assesses whether the test result is a hit or not"""
if status_code != requests.codes.not_found:
return True
else:
return False
@click.command()
@click.option(
'--website-url',
required=True,
help='Set URL of website e.g. http://www.adobe.com',
)
@click.option(
'--website-page-path',
help='Set path of website page e.g. /content/geometrixx/en',
)
@click.option(
'--timeout',
default=10.0,
help='Set timeout for http requests in secs e.g. 1.5 or 5',
)
@click.option(
'--verbose',
is_flag=True,
help='Enable verbose logging output',
)
def cli(*args, **kwargs):
"""Commandline interface for AEM Dispatcher Security Scan"""
# Configure logging
log_format = '%(levelname)s: %(message)s'
if kwargs.get('verbose'):
logging.basicConfig(format=log_format, level=logging.DEBUG)
else:
logging.basicConfig(format=log_format)
# Instantiate Scan
scan = Scan()
# Handle options
scan.website_url = kwargs.get('website_url')
scan.website_page_path = kwargs.get('website_page_path')
scan.request_timeout = kwargs.get('timeout')
scan.print_configuration()
# Run tests
vulnerabilities_total = 0
vulnerabilities_hit = 0
click.echo('Start active security scan of URL {website}'.format(
website=scan.website_url))
click.echo("")
for path in scan.path_list:
r = scan.perform_url_test(path.strip())
vulnerabilities_total += 1
if r is not None:
if scan.asses_test_result(r.status_code):
vulnerabilities_hit += 1
click.echo('{code}: {url}'.format(
code=r.status_code,
url=r.url
))
r = scan.perform_dispatcher_invalidate_cache_test()
vulnerabilities_total += 1
if r is not None:
if scan.asses_test_result(r.status_code):
vulnerabilities_hit += 1
click.echo('{code}: {url}'.format(
code=r.status_code,
url=r.url
))
click.echo("")
click.echo('Summary: Found {hit} of {total} security relevant AEM Dispatcher URLs'.format( # noqa: E501
hit=vulnerabilities_hit,
total=vulnerabilities_total))
if __name__ == '__main__':
cli()