/* * * Paros and its related class files. * * Paros is an HTTP/HTTPS proxy for assessing web application security. * Copyright (C) 2003-2004 Chinotec Technologies Company * * This program is free software; you can redistribute it and/or * modify it under the terms of the Clarified Artistic License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Clarified Artistic License for more details. * * You should have received a copy of the Clarified Artistic License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // ZAP: 2011/08/30 Support for scanner levels // ZAP: 2012/04/25 Added @Override annotation to the appropriate method. // ZAP: 2012/06/07 Added targetParam options // ZAP: 2012/07/30 Issue 43: Added support for Scope // ZAP: 2012/08/07 Renamed Level to AlertThreshold and added support for AttackStrength // ZAP: 2012/08/31 Enabled control of AttackStrength // ZAP: 2013/01/25 Removed the "(non-Javadoc)" comments. // ZAP: 2013/04/26 Issue 652: Added option to not delete records on shutdown // ZAP: 2013/09/23 Issue 795: Allow param types scanned to be configured via UI // ZAP: 2013/09/24 Issue 797: Limit number of ascan results listed to speed up scans // ZAP: 2013/09/26 Reviewed Variant Panel configuration // ZAP: 2014/01/10 Issue 974: Scan URL path elements // ZAP: 2014/02/08 Added Custom Script management settings // ZAP: 2014/02/13 Added HTTP parameter exclusion configuration on Active Scanning // ZAP: 2014/03/23 Issue 1076: Change active scanner to not delete the temporary messages generated // ZAP: 2014/05/13 Issue 1193: Scan URL path elements - turn off by default // ZAP: 2014/09/22 Issue 1345: Support Attack mode // ZAP: 2014/10/24 Issue 1378: Revamp active scan panel // ZAP: 2014/11/19 Issue 1412: Manage scan policies // ZAP: 2015/03/04 Issue 1345: Added 'attack on start' option // ZAP: 2015/03/25 Issue 1573: Add option to inject plugin ID in header for all ascan requests // ZAP: 2015/10/01 Issue 1944: Chart responses per second in ascan progress // ZAP: 2016/01/20 Issue 1959: Allow to active scan headers of all requests // ZAP: 2016/10/24 Issue 2951: Support active scan rule and scan max duration // ZAP: 2017/01/13 Exclude getExcludedParamList from the ZAP API package org.parosproxy.paros.core.scanner; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.configuration.ConversionException; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.log4j.Logger; import org.parosproxy.paros.common.AbstractParam; import org.zaproxy.zap.extension.api.ZapApiIgnore; public class ScannerParam extends AbstractParam { // Base path for the Scanner Param tree private static final String ACTIVE_SCAN_BASE_KEY = "scanner"; private static final String HOST_PER_SCAN = ACTIVE_SCAN_BASE_KEY + ".hostPerScan"; private static final String THREAD_PER_HOST = ACTIVE_SCAN_BASE_KEY + ".threadPerHost"; // ZAP: Added support for delayInMs private static final String DELAY_IN_MS = ACTIVE_SCAN_BASE_KEY + ".delayInMs"; private static final String INJECT_PLUGIN_ID_IN_HEADER = ACTIVE_SCAN_BASE_KEY + ".pluginHeader"; private static final String HANDLE_ANTI_CSRF_TOKENS = ACTIVE_SCAN_BASE_KEY + ".antiCSFR"; private static final String PROMPT_IN_ATTACK_MODE = ACTIVE_SCAN_BASE_KEY + ".attackPrompt"; private static final String RESCAN_IN_ATTACK_MODE = ACTIVE_SCAN_BASE_KEY + ".attackRescan"; private static final String PROMPT_TO_CLEAR_FINISHED = ACTIVE_SCAN_BASE_KEY + ".clearFinished"; private static final String MAX_RESULTS_LIST = ACTIVE_SCAN_BASE_KEY + ".maxResults"; private static final String MAX_SCANS_IN_UI = ACTIVE_SCAN_BASE_KEY + ".maxScansInUI"; private static final String SHOW_ADV_DIALOG = ACTIVE_SCAN_BASE_KEY + ".advDialog"; private static final String DEFAULT_POLICY = ACTIVE_SCAN_BASE_KEY + ".defaultPolicy"; private static final String ATTACK_POLICY = ACTIVE_SCAN_BASE_KEY + ".attackPolicy"; private static final String ALLOW_ATTACK_ON_START = ACTIVE_SCAN_BASE_KEY + ".attackOnStart"; private static final String MAX_CHART_TIME_IN_MINS = ACTIVE_SCAN_BASE_KEY + ".chartTimeInMins"; private static final String MAX_RULE_DURATION_IN_MINS = ACTIVE_SCAN_BASE_KEY + ".maxRuleDurationInMins"; private static final String MAX_SCAN_DURATION_IN_MINS = ACTIVE_SCAN_BASE_KEY + ".maxScanDurationInMins"; // ZAP: Excluded Parameters private static final String EXCLUDED_PARAMS_KEY = ACTIVE_SCAN_BASE_KEY + ".excludedParameters"; private static final String EXCLUDED_PARAM_NAME = "name"; private static final String EXCLUDED_PARAM_TYPE = "type"; private static final String EXCLUDED_PARAM_URL = "url"; // ZAP: TARGET CONFIGURATION private static final String TARGET_INJECTABLE = ACTIVE_SCAN_BASE_KEY + ".injectable"; private static final String TARGET_ENABLED_RPC = ACTIVE_SCAN_BASE_KEY + ".enabledRPC"; /** * Configuration key to write/read the {@code scanHeadersAllRequests} flag. * * @since 2.5.0 * @see #scanHeadersAllRequests */ private static final String SCAN_HEADERS_ALL_REQUESTS = ACTIVE_SCAN_BASE_KEY + ".scanHeadersAllRequests"; // ZAP: Configuration constants public static final int TARGET_QUERYSTRING = 1; public static final int TARGET_POSTDATA = 1 << 1; public static final int TARGET_COOKIE = 1 << 2; public static final int TARGET_HTTPHEADERS = 1 << 3; public static final int TARGET_URLPATH = 1 << 4; public static final int RPC_MULTIPART = 1; public static final int RPC_XML = 1 << 1; public static final int RPC_JSON = 1 << 2; public static final int RPC_GWT = 1 << 3; public static final int RPC_ODATA = 1 << 4; public static final int RPC_DWR = 1 << 5; public static final int RPC_CUSTOM = 1 << 7; public static final int RPC_USERDEF = 1 << 8; // Defaults for initial configuration public static final int TARGET_INJECTABLE_DEFAULT = TARGET_QUERYSTRING | TARGET_POSTDATA; public static final int TARGET_ENABLED_RPC_DEFAULT = RPC_MULTIPART | RPC_XML | RPC_JSON | RPC_GWT | RPC_ODATA | RPC_DWR; private static final int DEFAULT_MAX_CHART_TIME_IN_MINS = 10; // Internal variables private int hostPerScan = 2; private int threadPerHost = 1; private int delayInMs = 0; private int maxResultsToList = 1000; private int maxScansInUI = 5; private boolean injectPluginIdInHeader = false; private boolean handleAntiCSRFTokens = false; private boolean promptInAttackMode = true; private boolean rescanInAttackMode = true; private boolean promptToClearFinishedScans = true; private boolean showAdvancedDialog = false; private boolean allowAttackOnStart = false; private String defaultPolicy; private String attackPolicy; private int maxChartTimeInMins = DEFAULT_MAX_CHART_TIME_IN_MINS; private int maxRuleDurationInMins = 0; private int maxScanDurationInMins = 0; // ZAP: Variants Configuration private int targetParamsInjectable = TARGET_INJECTABLE_DEFAULT; private int targetParamsEnabledRPC = TARGET_ENABLED_RPC_DEFAULT; /** * Flag that indicates if the HTTP Headers of all requests should be scanned, not just requests that send parameters, * through the query or request body. * <p> * Default value is {@code false}. * * @since 2.5.0 * @see #SCAN_HEADERS_ALL_REQUESTS * @see #isScanHeadersAllRequests() * @see #setScanHeadersAllRequests(boolean) */ private boolean scanHeadersAllRequests; // ZAP: Excluded Parameters private final List<ScannerParamFilter> excludedParams = new ArrayList<>(); private final Map<Integer, List<ScannerParamFilter>> excludedParamsMap = new HashMap<>(); // ZAP: internal Logger private static final Logger logger = Logger.getLogger(ScannerParam.class); public ScannerParam() { } @Override protected void parse() { removeOldOptions(); try { this.threadPerHost = getConfig().getInt(THREAD_PER_HOST, 1); } catch (Exception e) { } try { this.hostPerScan = getConfig().getInt(HOST_PER_SCAN, 2); } catch (Exception e) { } try { this.delayInMs = getConfig().getInt(DELAY_IN_MS, 0); } catch (Exception e) { } try { this.maxResultsToList = getConfig().getInt(MAX_RESULTS_LIST, 1000); } catch (Exception e) { } try { this.maxRuleDurationInMins = getConfig().getInt(MAX_RULE_DURATION_IN_MINS, 0); } catch (Exception e) { } try { this.maxScanDurationInMins = getConfig().getInt(MAX_SCAN_DURATION_IN_MINS, 0); } catch (Exception e) { } try { this.maxScansInUI = getConfig().getInt(MAX_SCANS_IN_UI, 5); } catch (Exception e) { } try { this.injectPluginIdInHeader = getConfig().getBoolean(INJECT_PLUGIN_ID_IN_HEADER, false); } catch (Exception e) { } try { this.handleAntiCSRFTokens = getConfig().getBoolean(HANDLE_ANTI_CSRF_TOKENS, false); } catch (Exception e) { } try { this.promptInAttackMode = getConfig().getBoolean(PROMPT_IN_ATTACK_MODE, true); } catch (Exception e) { } try { this.rescanInAttackMode = getConfig().getBoolean(RESCAN_IN_ATTACK_MODE, true); } catch (Exception e) { } try { this.promptToClearFinishedScans = getConfig().getBoolean(PROMPT_TO_CLEAR_FINISHED, true); } catch (Exception e) { } try { this.showAdvancedDialog = getConfig().getBoolean(SHOW_ADV_DIALOG, false); } catch (Exception e) { } try { this.defaultPolicy = getConfig().getString(DEFAULT_POLICY, null); } catch (Exception e) { } try { this.attackPolicy = getConfig().getString(ATTACK_POLICY, null); } catch (Exception e) { } try { this.targetParamsInjectable = getConfig().getInt(TARGET_INJECTABLE, TARGET_INJECTABLE_DEFAULT); } catch (Exception e) { } try { this.targetParamsEnabledRPC = getConfig().getInt(TARGET_ENABLED_RPC, TARGET_ENABLED_RPC_DEFAULT); } catch (Exception e) { } try { this.allowAttackOnStart = getConfig().getBoolean(ALLOW_ATTACK_ON_START, false); } catch (Exception e) { } try { this.maxChartTimeInMins = getConfig().getInt(MAX_CHART_TIME_IN_MINS, DEFAULT_MAX_CHART_TIME_IN_MINS); } catch (Exception e) { } try { this.scanHeadersAllRequests = getConfig().getBoolean(SCAN_HEADERS_ALL_REQUESTS, false); } catch (Exception e) { } // Parse the parameters that need to be excluded // ------------------------------------------------ try { List<HierarchicalConfiguration> fields = ((HierarchicalConfiguration) getConfig()).configurationsAt(EXCLUDED_PARAMS_KEY); this.excludedParams.clear(); this.excludedParamsMap.clear(); List<String> tempParamNames = new ArrayList<>(fields.size()); for (HierarchicalConfiguration sub : fields) { String name = sub.getString(EXCLUDED_PARAM_NAME, ""); if (!name.isEmpty() && !tempParamNames.contains(name)) { tempParamNames.add(name); addScannerParamFilter( name, sub.getInt(EXCLUDED_PARAM_TYPE, NameValuePair.TYPE_UNDEFINED), sub.getString(EXCLUDED_PARAM_URL) ); } } } catch (ConversionException e) { logger.error("Error while loading the exluded parameter list: " + e.getMessage(), e); } // If the list is null probably we've to use defaults!!! if (this.excludedParams.isEmpty()) { // OK let's set the Default parameter exclusion list // Evaluate the possibility to load it from an external file... addScannerParamFilter("(?i)ASP.NET_SessionId", NameValuePair.TYPE_UNDEFINED, "*"); addScannerParamFilter("(?i)ASPSESSIONID.*", NameValuePair.TYPE_UNDEFINED, "*"); addScannerParamFilter("(?i)PHPSESSID", NameValuePair.TYPE_UNDEFINED, "*"); addScannerParamFilter("(?i)SITESERVER", NameValuePair.TYPE_UNDEFINED, "*"); addScannerParamFilter("(?i)sessid", NameValuePair.TYPE_UNDEFINED, "*"); addScannerParamFilter("__VIEWSTATE", NameValuePair.TYPE_POST_DATA, "*"); addScannerParamFilter("__EVENTVALIDATION", NameValuePair.TYPE_POST_DATA, "*"); addScannerParamFilter("__EVENTTARGET", NameValuePair.TYPE_POST_DATA, "*"); addScannerParamFilter("__EVENTARGUMENT", NameValuePair.TYPE_POST_DATA, "*"); addScannerParamFilter("(?i)jsessionid", NameValuePair.TYPE_UNDEFINED, "*"); addScannerParamFilter("cfid", NameValuePair.TYPE_COOKIE, "*"); addScannerParamFilter("cftoken", NameValuePair.TYPE_COOKIE, "*"); } } private void removeOldOptions() { final String oldKey = "scanner.deleteOnShutdown"; if (getConfig().containsKey(oldKey)) { getConfig().clearProperty(oldKey); } } private void addScannerParamFilter(String paramName, int paramType, String url) { ScannerParamFilter filter = new ScannerParamFilter(); filter.setParamName(paramName); filter.setType(paramType); filter.setWildcardedUrl(url); List<ScannerParamFilter> subList = excludedParamsMap.get(filter.getType()); if (subList == null) { subList = new ArrayList<>(); excludedParamsMap.put(filter.getType(), subList); } excludedParams.add(filter); subList.add(filter); } @ZapApiIgnore public List<ScannerParamFilter> getExcludedParamList() { return excludedParams; } public List<ScannerParamFilter> getExcludedParamList(int paramType) { return excludedParamsMap.get(paramType); } /** * * @param filters */ public void setExcludedParamList(List<ScannerParamFilter> filters) { ((HierarchicalConfiguration) getConfig()).clearTree(EXCLUDED_PARAMS_KEY); this.excludedParams.clear(); this.excludedParamsMap.clear(); for (int i = 0, size = filters.size(); i < size; ++i) { String elementBaseKey = EXCLUDED_PARAMS_KEY + "(" + i + ")."; ScannerParamFilter filter = filters.get(i); getConfig().setProperty(elementBaseKey + EXCLUDED_PARAM_NAME, filter.getParamName()); getConfig().setProperty(elementBaseKey + EXCLUDED_PARAM_TYPE, filter.getType()); getConfig().setProperty(elementBaseKey + EXCLUDED_PARAM_URL, filter.getWildcardedUrl()); // And now populate again all parameter list addScannerParamFilter( filter.getParamName(), filter.getType(), filter.getWildcardedUrl() ); } } /** * * @return */ public int getThreadPerHost() { return threadPerHost; } /** * * @param threadPerHost */ public void setThreadPerHost(int threadPerHost) { this.threadPerHost = threadPerHost; getConfig().setProperty(THREAD_PER_HOST, Integer.toString(this.threadPerHost)); } /** * @return Returns the thread. */ public int getHostPerScan() { return hostPerScan; } /** * @param hostPerScan The thread to set. */ public void setHostPerScan(int hostPerScan) { this.hostPerScan = hostPerScan; getConfig().setProperty(HOST_PER_SCAN, Integer.toString(this.hostPerScan)); } /** * * @return */ public int getMaxResultsToList() { return maxResultsToList; } /** * * @param maxResultsToList */ public void setMaxResultsToList(int maxResultsToList) { this.maxResultsToList = maxResultsToList; getConfig().setProperty(MAX_RESULTS_LIST, Integer.toString(this.maxResultsToList)); } public int getMaxRuleDurationInMins() { return maxRuleDurationInMins; } public void setMaxRuleDurationInMins(int maxRuleDurationInMins) { this.maxRuleDurationInMins = maxRuleDurationInMins; getConfig().setProperty(MAX_RULE_DURATION_IN_MINS, Integer.toString(this.maxRuleDurationInMins)); } public int getMaxScanDurationInMins() { return maxScanDurationInMins; } public void setMaxScanDurationInMins(int maxScanDurationInMins) { this.maxScanDurationInMins = maxScanDurationInMins; getConfig().setProperty(MAX_SCAN_DURATION_IN_MINS, Integer.toString(this.maxScanDurationInMins)); } /** * * @param delayInMs */ public void setDelayInMs(int delayInMs) { this.delayInMs = delayInMs; getConfig().setProperty(DELAY_IN_MS, Integer.toString(this.delayInMs)); } /** * * @return */ public int getDelayInMs() { return delayInMs; } /** * * @return Returns if the option to inject plugin ID in header for ascan requests is turned on */ public boolean isInjectPluginIdInHeader() { return injectPluginIdInHeader; } /** * * @param injectPluginIdInHeader */ public void setInjectPluginIdInHeader(boolean injectPluginIdInHeader) { this.injectPluginIdInHeader = injectPluginIdInHeader; getConfig().setProperty(INJECT_PLUGIN_ID_IN_HEADER, injectPluginIdInHeader); } /** * * @return */ public boolean getHandleAntiCSRFTokens() { return handleAntiCSRFTokens; } /** * * @param handleAntiCSRFTokens */ public void setHandleAntiCSRFTokens(boolean handleAntiCSRFTokens) { this.handleAntiCSRFTokens = handleAntiCSRFTokens; getConfig().setProperty(HANDLE_ANTI_CSRF_TOKENS, handleAntiCSRFTokens); } public boolean isRescanInAttackMode() { return rescanInAttackMode; } public void setRescanInAttackMode(boolean rescanInAttackMode) { this.rescanInAttackMode = rescanInAttackMode; getConfig().setProperty(RESCAN_IN_ATTACK_MODE, rescanInAttackMode); } public boolean isPromptInAttackMode() { return promptInAttackMode; } public void setPromptInAttackMode(boolean promptInAttackMode) { this.promptInAttackMode = promptInAttackMode; getConfig().setProperty(PROMPT_IN_ATTACK_MODE, promptInAttackMode); } /** * * @return */ public int getTargetParamsInjectable() { return targetParamsInjectable; } /** * * @param targetParamsInjectable */ public void setTargetParamsInjectable(int targetParamsInjectable) { this.targetParamsInjectable = targetParamsInjectable; getConfig().setProperty(TARGET_INJECTABLE, this.targetParamsInjectable); } /** * * @return */ public int getTargetParamsEnabledRPC() { return targetParamsEnabledRPC; } /** * * @param targetParamsEnabledRPC */ public void setTargetParamsEnabledRPC(int targetParamsEnabledRPC) { this.targetParamsEnabledRPC = targetParamsEnabledRPC; getConfig().setProperty(TARGET_ENABLED_RPC, this.targetParamsEnabledRPC); } public boolean isPromptToClearFinishedScans() { return promptToClearFinishedScans; } public void setPromptToClearFinishedScans(boolean promptToClearFinishedScans) { this.promptToClearFinishedScans = promptToClearFinishedScans; getConfig().setProperty(PROMPT_TO_CLEAR_FINISHED, this.promptToClearFinishedScans); } public int getMaxScansInUI() { return maxScansInUI; } public void setMaxScansInUI(int maxScansInUI) { this.maxScansInUI = maxScansInUI; getConfig().setProperty(MAX_SCANS_IN_UI, this.maxScansInUI); } public boolean isShowAdvancedDialog() { return showAdvancedDialog; } public void setShowAdvancedDialog(boolean showAdvancedDialog) { this.showAdvancedDialog = showAdvancedDialog; getConfig().setProperty(SHOW_ADV_DIALOG, this.showAdvancedDialog); } public String getDefaultPolicy() { return defaultPolicy; } public String getAttackPolicy() { return attackPolicy; } public void setDefaultPolicy(String defaultPolicy) { this.defaultPolicy = defaultPolicy; getConfig().setProperty(DEFAULT_POLICY, this.defaultPolicy); } public void setAttackPolicy(String attackPolicy) { this.attackPolicy = attackPolicy; getConfig().setProperty(ATTACK_POLICY, this.attackPolicy); } public boolean isAllowAttackOnStart() { return allowAttackOnStart; } public void setAllowAttackOnStart(boolean allowAttackOnStart) { this.allowAttackOnStart = allowAttackOnStart; getConfig().setProperty(ALLOW_ATTACK_ON_START, this.allowAttackOnStart); } public int getMaxChartTimeInMins() { return maxChartTimeInMins; } public void setMaxChartTimeInMins(int maxChartTimeInMins) { this.maxChartTimeInMins = maxChartTimeInMins; getConfig().setProperty(MAX_CHART_TIME_IN_MINS, this.maxChartTimeInMins); } /** * Tells whether or not the HTTP Headers of all requests should be scanned, not just requests that send parameters, through * the query or request body. * * @return {@code true} if the HTTP Headers of all requests should be scanned, {@code false} otherwise * @since 2.5.0 * @see #setScanHeadersAllRequests(boolean) */ public boolean isScanHeadersAllRequests() { return scanHeadersAllRequests; } /** * Sets whether or not the HTTP Headers of all requests should be scanned, not just requests that send parameters, through * the query or request body. * * @param scanAllRequests {@code true} if the HTTP Headers of all requests should be scanned, {@code false} otherwise * @since 2.5.0 * @see #isScanHeadersAllRequests() */ public void setScanHeadersAllRequests(boolean scanAllRequests) { if (scanAllRequests == scanHeadersAllRequests) { return; } this.scanHeadersAllRequests = scanAllRequests; getConfig().setProperty(SCAN_HEADERS_ALL_REQUESTS, Boolean.valueOf(this.scanHeadersAllRequests)); } }