Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
1N3
GitHub Repository: 1N3/Sn1per
Path: blob/master/bin/zap-scan.py
2960 views
1
#!/usr/bin/env python3
2
3
'''
4
This script aims to be the most generic and the most explicit possible.
5
It works with OWASP ZAP API Python client.
6
To use it, you have to load the Python API client module and start ZAP
7
8
Before starting this script for the first time: Open ZAP, go to
9
Tools -> Options -> API -> Generate random Key, copy and paste the key in the
10
variable "apiKey" of the configuration area
11
12
This script is divided into two parts : a configuration area, where you have to
13
change variables according to your needs, and the part with API calls.
14
15
Author : aine-rb on Github, from Sopra Steria - modified for Sn1per by @xer0dayz
16
'''
17
18
import time
19
from pprint import pprint
20
from zapv2 import ZAPv2
21
import sys, getopt
22
23
targetURL = str(sys.argv[1])
24
25
#######################################
26
### BEGINNING OF CONFIGURATION AREA ###
27
#######################################
28
## The user only needs to change variable values bellow to make the script
29
## work according to his/her needs. MANDATORY parameters must not be empty
30
31
# MANDATORY. Define the API key generated by ZAP and used to verify actions.
32
apiKey=''
33
34
# MANDATORY. Define the listening address of ZAP instance
35
localProxy = {"http": "http://127.0.0.1:8081", "https": "http://127.0.0.1:8081"}
36
37
# MANDATORY. True to create another ZAP session (overwrite the former if the
38
# same name already exists), False to use an existing one
39
isNewSession = True
40
# MANDATORY. ZAP Session name
41
sessionName = 'WebgoatSession'
42
43
# Define the list of global exclude URL regular expressions. List can be empty.
44
# The expressions must follow the java.util.regex.Pattern class syntax
45
# The following example excludes every single URL except http://localhost:8081
46
globalExcludeUrl = ['^(?:(?!http:\/\/localhost:8081).*).$']
47
48
# MANDATORY. Define if an outgoing proxy server is used
49
useProxyChain = False
50
# MANDATORY only if useProxyChain is True, ignored otherwise.
51
# Outgoing proxy address and port
52
proxyAddress = 'my.corp.proxy'
53
proxyPort = '8080'
54
# Define the addresses to skip in case useProxyChain is True. Ignored
55
# otherwise. List can be empty.
56
skipProxyAddresses = ('127.0.0.1;'
57
'localhost')
58
# MANDATORY only if useProxyChain is True. Ignored otherwise.
59
# Define if proxy server needs authentication
60
useProxyChainAuth = False
61
# MANDATORY only if useProxyChainAuth is True. Ignored otherwise
62
proxyUsername = ''
63
proxyPassword = ''
64
proxyRealm = ''
65
66
# MANDATORY. Determine if a proxy script must be loaded. Proxy scripts are
67
# executed for every request traversing ZAP
68
useProxyScript = False
69
# MANDATORY only if useProxyScript is True. Ignored otherwise
70
proxyScriptName = 'proxyScript.js'
71
# Script engine values: "Oracle Nashorn" for Javascript,
72
# "jython" for python, "JSR 223 JRuby Engine" for ruby
73
proxyScriptEngine = 'Oracle Nashorn'
74
# Asolute local path
75
proxyScriptFileName = '/zap/scripts/proxy/proxyScript.js'
76
proxyScriptDescription = 'This is a description'
77
78
# MANDATORY. Determine if context must be configured then used during scans.
79
# You have to set this parameter to True if you want that ZAP performs scans
80
# from the point of view of a specific user
81
useContextForScan = False
82
83
# MANDATORY only if useContextForScan is True. Ignored otherwise. Set value to
84
# True to define a new context. Set value to False to use an existing one.
85
defineNewContext = False
86
# MANDATORY only if defineNewContext is True. Ignored otherwise
87
contextName = 'WebGoat_script-based'
88
# MANDATORY only if defineNewContext is False. Disregarded otherwise.
89
# Corresponds to the ID of the context to use
90
contextId = 0
91
# Define Context Include URL regular expressions. Ignored if useContextForScan
92
# is False. You have to put the URL you want to test in this list.
93
contextIncludeURL = [targetURL + '.*']
94
# Define Context Exclude URL regular expressions. Ignored if useContextForScan
95
# is False. List can be empty.
96
contextExcludeURL = ['http://localhost:8081/WebGoat/j_spring_security_logout',
97
'http://localhost:8081/WebGoat/logout.mvc']
98
99
# MANDATORY only if useContextForScan is True. Ignored otherwise. Define the
100
# session management method for the context. Possible values are:
101
# "cookieBasedSessionManagement"; "httpAuthSessionManagement"
102
sessionManagement = 'cookieBasedSessionManagement'
103
104
# MANDATORY only if useContextForScan is True. Ignored otherwise. Define
105
# authentication method for the context. Possible values are:
106
# "manualAuthentication"; "scriptBasedAuthentication"; "httpAuthentication";
107
# "formBasedAuthentication"
108
authMethod = 'scriptBasedAuthentication'
109
110
# MANDATORY only if authMethod is set to scriptBasedAuthentication.
111
# Ignored otherwise
112
authScriptName = 'TwoStepAuthentication.js'
113
# Script engine values: Oracle Nashorn for Javascript
114
# jython for python, JSR 223 JRuby Engine for ruby
115
authScriptEngine = 'Oracle Nashorn'
116
# Absolute local path
117
authScriptFileName = '/zap/scripts/authentication/TwoStepAuthentication.js'
118
authScriptDescription = 'This is a description'
119
120
# MANDATORY only if useContextForScan is True. Ignored otherwise. Each
121
# name/value pair of authParams are expected to be "x-www-form-urlencoded"
122
# Here is an example for scriptBasedAuthentication method:
123
authParams = ('scriptName=' + authScriptName + '&'
124
'Submission Form URL=http://localhost:8081/WebGoat/j_spring_security_check&'
125
'Username field=username&'
126
'Password field=password&'
127
'Target URL=http://localhost:8081/WebGoat/welcome.mvc')
128
## Here is an example for formBasedAuthentication method:
129
#authParams = ('loginUrl=http://localhost:8081/WebGoat/j_spring_security_check&'
130
# 'loginRequestData=username%3D%7B%25username%25%7D%26'
131
# 'password%3D%7B%25password%25%7D')
132
##Here is an example for httpAuthentication method:
133
#authParams = ('hostname=http://www.example.com&'
134
# 'realm=CORP\\administrator&'
135
# 'port=80')
136
137
# MANDATORY only if useContextForScan is True. Ignored otherwise.
138
# Set the value to True if a loggedin indicator must be used. False if it's a
139
# logged out indicator that must be used
140
isLoggedInIndicator = False
141
# MANDATORY only if useContextForScan is True. Ignored otherwise.
142
# Define either a loggedin or a loggedout indicator regular expression.
143
# It allows ZAP to see if the user is always authenticated during scans.
144
indicatorRegex = '\QLocation: http://localhost:8081/WebGoat/login.mvc\E'
145
146
147
# MANDATORY only if useContextForScan is True. Ignored otherwise.
148
# Set value to True to create new users, False otherwise
149
createUser = False
150
# MANDATORY only if createUser is True. Ignored otherwise. Define the list of
151
# users, with name and credentials (in x-www-form-urlencoded format)
152
## Here is an example with the script NashornTwoStepAuthentication.js:
153
userList = [
154
{'name': 'guest', 'credentials': 'Username=guest&Password=guest'},
155
{'name': 'webgoat', 'credentials': 'Username=webgoat&Password=webgoat'}
156
]
157
## Here is an example with formBasedAuthentication:
158
#userList = [
159
# {'name': 'guest', 'credentials': 'username=guest&password=guest'},
160
# {'name': 'webgoat', 'credentials': 'username=webgoat&password=webgoat'}
161
#]
162
163
# MANDATORY only if useContextForScan is True. Ignored otherwise. List can be
164
# empty. Define the userid list. Created users will be added to this list later
165
userIdList = []
166
167
# MANDATORY. Define the target site to test
168
#target = 'http://10.0.0.19/'
169
target = targetURL
170
# You can specify other URL in order to help ZAP discover more site locations
171
# List can be empty
172
applicationURL = ['']
173
174
# MANDATORY. Set value to True if you want to customize and use a scan policy
175
useScanPolicy = False
176
# MANDATORY only if useScanPolicy is True. Ignored otherwise. Set a policy name
177
scanPolicyName = 'SQL Injection and XSS'
178
# MANDATORY only if useScanPolicy is True. Ignored otherwise.
179
# Set value to True to disable all scan types except the ones set in ascanIds,
180
# False to enable all scan types except the ones set in ascanIds..
181
isWhiteListPolicy = False
182
# MANDATORY only if useScanPolicy is True. Ignored otherwise. Set the scan IDs
183
# to use with the policy. Other scan types will be disabled if
184
# isWhiteListPolicy is True, enabled if isWhiteListPolicy is False.
185
# Use zap.ascan.scanners() to list all ascan IDs.
186
## In the example bellow, the first line corresponds to SQL Injection scan IDs,
187
## the second line corresponds to some XSS scan IDs
188
ascanIds = [40018, 40019, 40020, 40021, 40022, 40024, 90018,
189
40012, 40014, 40016, 40017]
190
# MANDATORY only if useScanPolicy is True. Ignored otherwise. Set the alert
191
# Threshold and the attack strength of enabled active scans.
192
# Currently, possible values are:
193
# Low, Medium and High for alert Threshold
194
# Low, Medium, High and Insane for attack strength
195
alertThreshold = 'Medium'
196
attackStrength = 'Low'
197
198
# MANDATORY. Set True to use Ajax Spider, False otherwise.
199
useAjaxSpider = True
200
201
# MANDATORY. Set True to shutdown ZAP once finished, False otherwise
202
shutdownOnceFinished = False
203
204
#################################
205
### END OF CONFIGURATION AREA ###
206
#################################
207
sys.stdout = open("/usr/share/sniper/bin/zap-report.txt", "w")
208
209
# Connect ZAP API client to the listening address of ZAP instance
210
zap = ZAPv2(proxies=localProxy, apikey=apiKey)
211
212
# Start the ZAP session
213
core = zap.core
214
if isNewSession:
215
pprint('Create ZAP session: ' + sessionName + ' -> ' +
216
core.new_session(name=sessionName, overwrite=True))
217
else:
218
pprint('Load ZAP session: ' + sessionName + ' -> ' +
219
core.load_session(name=sessionName))
220
221
# Configure ZAP global Exclude URL option
222
print('Add Global Exclude URL regular expressions:')
223
for regex in globalExcludeUrl:
224
pprint(regex + ' ->' + core.exclude_from_proxy(regex=regex))
225
226
# Configure ZAP outgoing proxy server connection option
227
pprint('Enable outgoing proxy chain: ' + str(useProxyChain) + ' -> ' +
228
core.set_option_use_proxy_chain(boolean=useProxyChain))
229
if useProxyChain:
230
pprint('Set outgoing proxy name: ' + proxyAddress + ' -> ' +
231
core.set_option_proxy_chain_name(string=proxyAddress))
232
pprint('Set outgoing proxy port: ' + proxyPort + ' -> ' +
233
core.set_option_proxy_chain_port(integer=proxyPort))
234
pprint('Skip names for outgoing proxy: ' + skipProxyAddresses + ' -> ' +
235
core.set_option_proxy_chain_skip_name(string=skipProxyAddresses))
236
237
# Configure ZAP outgoing proxy server authentication
238
pprint('Set outgoing proxy chain authentication: ' +
239
str(useProxyChainAuth) + ' -> ' +
240
core.set_option_use_proxy_chain_auth(boolean=useProxyChainAuth))
241
if useProxyChainAuth:
242
pprint('Set outgoing proxy username -> ' +
243
core.set_option_proxy_chain_user_name(string=proxyUsername))
244
pprint('Set outgoing proxy password -> ' +
245
core.set_option_proxy_chain_password(string=proxyPassword))
246
pprint('Set outgoing proxy realm: ' + proxyRealm + ' -> ' +
247
core.set_option_proxy_chain_realm(string=proxyRealm))
248
249
if useProxyScript:
250
script = zap.script
251
script.remove(scriptname=proxyScriptName)
252
pprint('Load proxy script: ' + proxyScriptName + ' -> ' +
253
script.load(scriptname=proxyScriptName, scripttype='proxy',
254
scriptengine=proxyScriptEngine,
255
filename=proxyScriptFileName,
256
scriptdescription=proxyScriptDescription))
257
pprint('Enable proxy script: ' + proxyScriptName + ' -> ' +
258
script.enable(scriptname=proxyScriptName))
259
260
261
if useContextForScan:
262
# Define the ZAP context
263
context = zap.context
264
if defineNewContext:
265
contextId = context.new_context(contextname=contextName)
266
pprint('Use context ID: ' + contextId)
267
268
# Include URL in the context
269
print('Include URL in context:')
270
for url in contextIncludeURL:
271
pprint(url + ' -> ' +
272
context.include_in_context(contextname=contextName,
273
regex=url))
274
275
# Exclude URL in the context
276
print('Exclude URL from context:')
277
for url in contextExcludeURL:
278
pprint(url + ' -> ' +
279
context.exclude_from_context(contextname=contextName,
280
regex=url))
281
282
# Setup session management for the context.
283
# There is no methodconfigparams to provide for both current methods
284
pprint('Set session management method: ' + sessionManagement + ' -> ' +
285
zap.sessionManagement.set_session_management_method(
286
contextid=contextId, methodname=sessionManagement,
287
methodconfigparams=None))
288
289
## In case we use the scriptBasedAuthentication method, load the script
290
if authMethod == 'scriptBasedAuthentication':
291
script = zap.script
292
script.remove(scriptname=authScriptName)
293
pprint('Load script: ' + authScriptName + ' -> ' +
294
script.load(scriptname=authScriptName,
295
scripttype='authentication',
296
scriptengine=authScriptEngine,
297
filename=authScriptFileName,
298
scriptdescription=authScriptDescription))
299
300
# Define an authentication method with parameters for the context
301
auth = zap.authentication
302
pprint('Set authentication method: ' + authMethod + ' -> ' +
303
auth.set_authentication_method(contextid=contextId,
304
authmethodname=authMethod,
305
authmethodconfigparams=authParams))
306
# Define either a loggedin indicator or a loggedout indicator regexp
307
# It allows ZAP to see if the user is always authenticated during scans
308
if isLoggedInIndicator:
309
pprint('Define Loggedin indicator: ' + indicatorRegex + ' -> ' +
310
auth.set_logged_in_indicator(contextid=contextId,
311
loggedinindicatorregex=indicatorRegex))
312
else:
313
pprint('Define Loggedout indicator: ' + indicatorRegex + ' -> ' +
314
auth.set_logged_out_indicator(contextid=contextId,
315
loggedoutindicatorregex=indicatorRegex))
316
317
# Define the users
318
users = zap.users
319
if createUser:
320
for user in userList:
321
userName = user.get('name')
322
print('Create user ' + userName + ':')
323
userId = users.new_user(contextid=contextId, name=userName)
324
userIdList.append(userId)
325
pprint('User ID: ' + userId + '; username -> ' +
326
users.set_user_name(contextid=contextId, userid=userId,
327
name=userName) +
328
'; credentials -> ' +
329
users.set_authentication_credentials(contextid=contextId,
330
userid=userId,
331
authcredentialsconfigparams=user.get('credentials')) +
332
'; enabled -> ' +
333
users.set_user_enabled(contextid=contextId, userid=userId,
334
enabled=True))
335
336
# Enable all passive scanners (it's possible to do a more specific policy by
337
# setting needed scan ID: Use zap.pscan.scanners() to list all passive scanner
338
# IDs, then use zap.scan.enable_scanners(ids) to enable what you want
339
pprint('Enable all passive scanners -> ' +
340
zap.pscan.enable_all_scanners())
341
342
ascan = zap.ascan
343
# Define if a new scan policy is used
344
if useScanPolicy:
345
ascan.remove_scan_policy(scanpolicyname=scanPolicyName)
346
pprint('Add scan policy ' + scanPolicyName + ' -> ' +
347
ascan.add_scan_policy(scanpolicyname=scanPolicyName))
348
for policyId in range(0, 5):
349
# Set alert Threshold for all scans
350
ascan.set_policy_alert_threshold(id=policyId,
351
alertthreshold=alertThreshold,
352
scanpolicyname=scanPolicyName)
353
# Set attack strength for all scans
354
ascan.set_policy_attack_strength(id=policyId,
355
attackstrength=attackStrength,
356
scanpolicyname=scanPolicyName)
357
if isWhiteListPolicy:
358
# Disable all active scanners in order to enable only what you need
359
pprint('Disable all scanners -> ' +
360
ascan.disable_all_scanners(scanpolicyname=scanPolicyName))
361
# Enable some active scanners
362
pprint('Enable given scan IDs -> ' +
363
ascan.enable_scanners(ids=ascanIds,
364
scanpolicyname=scanPolicyName))
365
else:
366
# Enable all active scanners
367
pprint('Enable all scanners -> ' +
368
ascan.enable_all_scanners(scanpolicyname=scanPolicyName))
369
# Disable some active scanners
370
pprint('Disable given scan IDs -> ' +
371
ascan.disable_scanners(ids=ascanIds,
372
scanpolicyname=scanPolicyName))
373
else:
374
print('No custom policy used for scan')
375
scanPolicyName = None
376
377
# Open URL inside ZAP
378
pprint('Access target URL ' + target)
379
core.access_url(url=target, followredirects=True)
380
for url in applicationURL:
381
pprint('Access URL ' + url)
382
core.access_url(url=url, followredirects=True)
383
# Give the sites tree a chance to get updated
384
time.sleep(2)
385
386
# Launch Spider, Ajax Spider (if useAjaxSpider is set to true) and
387
# Active scans, with a context and users or not
388
forcedUser = zap.forcedUser
389
spider = zap.spider
390
ajax = zap.ajaxSpider
391
scanId = 0
392
print('Starting Scans on target: ' + target)
393
if useContextForScan:
394
for userId in userIdList:
395
print('Starting scans with User ID: ' + userId)
396
397
# Spider the target and recursively scan every site node found
398
scanId = spider.scan_as_user(contextid=contextId, userid=userId,
399
url=target, maxchildren=None, recurse=True, subtreeonly=None)
400
print('Start Spider scan with user ID: ' + userId +
401
'. Scan ID equals: ' + scanId)
402
# Give the spider a chance to start
403
time.sleep(2)
404
while (int(spider.status(scanId)) < 100):
405
print('Spider progress: ' + spider.status(scanId) + '%')
406
time.sleep(2)
407
print('Spider scan for user ID ' + userId + ' completed')
408
409
if useAjaxSpider:
410
# Prepare Ajax Spider scan
411
pprint('Set forced user mode enabled -> ' +
412
forcedUser.set_forced_user_mode_enabled(boolean=True))
413
pprint('Set user ID: ' + userId + ' for forced user mode -> ' +
414
forcedUser.set_forced_user(contextid=contextId,
415
userid=userId))
416
# Ajax Spider the target URL
417
pprint('Ajax Spider the target with user ID: ' + userId + ' -> ' +
418
ajax.scan(url=target, inscope=None))
419
# Give the Ajax spider a chance to start
420
time.sleep(10)
421
while (ajax.status != 'stopped'):
422
print('Ajax Spider is ' + ajax.status)
423
time.sleep(5)
424
for url in applicationURL:
425
# Ajax Spider every url configured
426
pprint('Ajax Spider the URL: ' + url + ' with user ID: ' +
427
userId + ' -> ' +
428
ajax.scan(url=url, inscope=None))
429
# Give the Ajax spider a chance to start
430
time.sleep(10)
431
while (ajax.status != 'stopped'):
432
print('Ajax Spider is ' + ajax.status)
433
time.sleep(5)
434
pprint('Set forced user mode disabled -> ' +
435
forcedUser.set_forced_user_mode_enabled(boolean=False))
436
print('Ajax Spider scan for user ID ' + userId + ' completed')
437
438
# Launch Active Scan with the configured policy on the target url
439
# and recursively scan every site node
440
scanId = ascan.scan_as_user(url=target, contextid=contextId,
441
userid=userId, recurse=True, scanpolicyname=scanPolicyName,
442
method=None, postdata=True)
443
print('Start Active Scan with user ID: ' + userId +
444
'. Scan ID equals: ' + scanId)
445
# Give the scanner a chance to start
446
time.sleep(2)
447
while (int(ascan.status(scanId)) < 100):
448
print('Active Scan progress: ' + ascan.status(scanId) + '%')
449
time.sleep(2)
450
print('Active Scan for user ID ' + userId + ' completed')
451
452
else:
453
# Spider the target and recursively scan every site node found
454
scanId = spider.scan(url=target, maxchildren=None, recurse=True,
455
contextname=None, subtreeonly=None)
456
print('Scan ID equals ' + scanId)
457
# Give the Spider a chance to start
458
time.sleep(2)
459
while (int(spider.status(scanId)) < 100):
460
print('Spider progress ' + spider.status(scanId) + '%')
461
time.sleep(2)
462
print('Spider scan completed')
463
464
if useAjaxSpider:
465
# Ajax Spider the target URL
466
pprint('Start Ajax Spider -> ' + ajax.scan(url=target, inscope=None))
467
# Give the Ajax spider a chance to start
468
time.sleep(10)
469
while (ajax.status != 'stopped'):
470
print('Ajax Spider is ' + ajax.status)
471
time.sleep(5)
472
for url in applicationURL:
473
# Ajax Spider every url configured
474
pprint('Ajax Spider the URL: ' + url + ' -> ' +
475
ajax.scan(url=url, inscope=None))
476
# Give the Ajax spider a chance to start
477
time.sleep(10)
478
while (ajax.status != 'stopped'):
479
print('Ajax Spider is ' + ajax.status)
480
time.sleep(5)
481
print('Ajax Spider scan completed')
482
483
# Launch Active scan with the configured policy on the target url and
484
# recursively scan every site node
485
scanId = zap.ascan.scan(url=target, recurse=True, inscopeonly=None,
486
scanpolicyname=scanPolicyName, method=None, postdata=True)
487
print('Start Active scan. Scan ID equals ' + scanId)
488
while (int(ascan.status(scanId)) < 100):
489
print('Active Scan progress: ' + ascan.status(scanId) + '%')
490
time.sleep(5)
491
print('Active Scan completed')
492
493
# Give the passive scanner a chance to finish
494
time.sleep(5)
495
496
# If you want to retrieve alerts:
497
## pprint(zap.core.alerts(baseurl=target, start=None, count=None))
498
499
print('HTML report:')
500
pprint(core.htmlreport())
501
502
# To retrieve ZAP report in XML or HTML format
503
print('XML report')
504
pprint(core.xmlreport())
505
506
if shutdownOnceFinished:
507
# Shutdown ZAP once finished
508
pprint('Shutdown ZAP -> ' + core.shutdown())
509
510
sys.stdout.close()
511