From 6924e7a6e9460122eef9ff2ab172c7d3c7db517b Mon Sep 17 00:00:00 2001 From: ricekot Date: Thu, 6 Jun 2024 00:27:09 +0530 Subject: [PATCH] Implement `getMetadata` for some more Passive scripts Update the following scripts to implement the `getMetadata()` function: - passive/Find IBANs.js - passive/Find Internal IPs.js - passive/find_reflected_params.py - passive/HUNT.py - passive/Mutliple Security Header Check.js Signed-off-by: ricekot --- CHANGELOG.md | 5 + passive/Find IBANs.js | 54 ++++---- passive/Find Internal IPs.js | 56 +++++---- passive/HUNT.py | 28 +++-- passive/Mutliple Security Header Check.js | 147 ++++++++++------------ passive/find_reflected_params.py | 45 ++++--- 6 files changed, 169 insertions(+), 166 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3304478a..f2d56fab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - passive/Find Emails.js - passive/Find Hashes.js - passive/Find HTML Comments.js + - passive/Find IBANs.js + - passive/Find Internal IPs.js + - passive/find_reflected_params.py + - passive/HUNT.py + - passive/Mutliple Security Header Check.js ## [18] - 2024-01-29 ### Added diff --git a/passive/Find IBANs.js b/passive/Find IBANs.js index 3104cbf9..6bddb32b 100644 --- a/passive/Find IBANs.js +++ b/passive/Find IBANs.js @@ -5,24 +5,31 @@ // Runs as a part of nightly baseline scans in many DevSecOps environments // Complements the Pluralsight course - Writing Custom Scripts for Zed Attack Proxy -function scan(ps, msg, src) { - // first lets set up some details incase we find an IBAN, these will populate the alert later - var alertRisk = 1; - var alertConfidence = 3; - var alertTitle = "IBAN found - investigation required (script)"; - var alertDesc = "IBAN numbers were found"; - var alertSolution = - "Investigate IBAN numbers found in the response, remove or mask as required"; - var cweId = 200; - var wascId = 0; +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100012 +name: Information Disclosure - IBAN Numbers +description: An IBAN number was discovered in the HTTP response body. +solution: Investigate IBAN numbers found in the response, remove or mask as required. +risk: low +confidence: high +cweId: 200 # CWE-200: Exposure of Sensitive Information to an Unauthorized Actor +wascId: 13 # WASC-13: Information Leakage +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/Find%20IBANs.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} + +function scan(helper, msg, src) { // lets build a regular expression that can find IBAN addresses // the regex must appear within /( and )/g var re = /([A-Za-z]{2}[0-9]{2}[A-Za-z]{4}[0-9]{10})/g; - // we need to set the url variable to the request or we cant track the alert later - var url = msg.getRequestHeader().getURI().toString(); - // lets check its not one of the files types that are never likely to contain stuff, like pngs and jpegs var contentType = msg.getResponseHeader().getHeader("Content-Type"); var unwantedFileTypes = [ @@ -48,20 +55,11 @@ function scan(ps, msg, src) { foundIBAN.push(comm[0]); } // woohoo we found an IBAN lets make an alert for it - ps.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - "", - "", - foundIBAN.toString(), - alertSolution, - foundIBAN.toString(), - cweId, - wascId, - msg - ); + helper + .newAlert() + .setEvidence(foundIBAN[0]) + .setOtherInfo(`Other instances: ${foundIBAN.slice(1).toString()}`) + .setMessage(msg) + .raise(); } } diff --git a/passive/Find Internal IPs.js b/passive/Find Internal IPs.js index 1076da99..53751eb8 100644 --- a/passive/Find Internal IPs.js +++ b/passive/Find Internal IPs.js @@ -1,17 +1,30 @@ // RFC1918 internal IP Finder by freakyclown@gmail.com -function scan(ps, msg, src) { - var url = msg.getRequestHeader().getURI().toString(); - var alertRisk = 2; - var alertConfidence = 2; - var alertTitle = "Private IP address in Body(script)"; - var alertDesc = - "A private IP such as 10.x.x.x, 172.x.x.x, 192.168.x.x or IPV6 fe00:: has been found in the HTTP response body. This information might be helpful for further attacks targeting internal systems. "; - var alertSolution = - "Remove the private IP address from the HTTP response body. For comments, use JSP/ASP comment instead of HTML/JavaScript comment which can be seen by client browsers."; +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); - var cweId = 0; - var wascId = 0; +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100013 +name: Information Disclosure - Private IP Address +description: > + A private IP such as 10.x.x.x, 172.x.x.x, 192.168.x.x or IPV6 fe00:: has been found in the HTTP response body. + This information might be helpful for further attacks targeting internal systems. +solution: > + Remove the private IP address from the HTTP response body. + For comments, use JSP/ASP comment instead of HTML/JavaScript comment which can be seen by client browsers. +risk: medium +confidence: medium +cweId: 200 # CWE-200: Exposure of Sensitive Information to an Unauthorized Actor +wascId: 13 # WASC-13: Information Leakage +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/Find%20Internal%20IPs.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} + +function scan(helper, msg, src) { // regex must appear within /( and )/g var re = /((172\.\d{1,3}\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|([fF][eE][89aAbBcCdDeEfF]::))/g; @@ -42,21 +55,12 @@ function scan(ps, msg, src) { while ((comm = re.exec(body))) { foundIP.push(comm[0]); } - ps.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - "", - "", - foundIP.toString(), - alertSolution, - "", - cweId, - wascId, - msg - ); + helper + .newAlert() + .setEvidence(foundIP[0]) + .setOtherInfo(`Other instances: ${foundIP.slice(1).toString()}`) + .setMessage(msg) + .raise(); } } } diff --git a/passive/HUNT.py b/passive/HUNT.py index 7e7c2790..736bb7f1 100644 --- a/passive/HUNT.py +++ b/passive/HUNT.py @@ -1,9 +1,25 @@ import re from org.zaproxy.zap.extension.script import ScriptVars +from org.zaproxy.addon.commonlib.scanrules import ScanRuleMetadata '''find possible vulnerable entry points using Hunt Methodology - https://github.com/bugcrowd/HUNT''' +def getMetadata(): + return ScanRuleMetadata.fromYaml(""" +id: 100015 +name: HUNT Methodology +description: > + Find possible vulnerable entry points using HUNT Methodology (https://github.com/bugcrowd/HUNT). +risk: info +confidence: low +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/HUNT.py +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +""") + + + def appliesToHistoryType(histType): """ Limit scanned history types, which otherwise default to @@ -23,7 +39,7 @@ def find_words_in_params(param_list, word_list): return result -def hunt_alert(ps, msg, uri, result, title, desc): +def hunt_alert(helper, msg, uri, result, title, desc): if not result: return @@ -34,14 +50,8 @@ def hunt_alert(ps, msg, uri, result, title, desc): info = msg.getRequestHeader().toString() info += "\n" + msg.getRequestBody().toString() - # Docs on alert raising function: - # raiseAlert(int risk, int confidence, str name, str description, str uri, - # str param, str attack, str otherInfo, str solution, - # str evidence, int cweId, int wascId, HttpMessage msg) - # risk: 0: info, 1: low, 2: medium, 3: high - # confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed - ps.raiseAlert(0, 1, title, desc, uri, result_repr, - None, info, None, None, 0, 0, msg) + helper.newAlert().setName(title).setDescription( + desc).setParam(result_repr).setOtherInfo(info).setMessage(msg).raise() def scan(ps, msg, src): diff --git a/passive/Mutliple Security Header Check.js b/passive/Mutliple Security Header Check.js index 621ea1fe..4da29dc4 100644 --- a/passive/Mutliple Security Header Check.js +++ b/passive/Mutliple Security Header Check.js @@ -1,12 +1,33 @@ // Multiple Security Header checker by freakyclown@gmail.com -function scan(ps, msg, src) { - var url = msg.getRequestHeader().getURI().toString(); - var responseHeader = msg.getResponseHeader().toString(); - var alertRisk = [0, 1, 2, 3]; //0=informational, 1=low, 2=medium, 3=high - var alertConfidence = [0, 1, 2, 3, 4]; //0=fp,1=low,2=medium,3=high,4=confirmed +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); + +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100016 +name: Missing Security Headers +description: > + Some of the following security headers are missing from the HTTP response: + Strict-Transport-Security, Content-Security-Policy, + X-XSS-Protection, X-Content-Type-Options, X-Frame-Options. +solution: > + Ensure that your web server, application server, load balancer, etc. + is configured to set the missing security headers. +risk: low +confidence: high +cweId: 693 # CWE-693: Protection Mechanism Failure +wascId: 15 # WASC-15: Application Misconfiguration +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/Mutliple%20Security%20Header%20Check.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} + +function scan(helper, msg, src) { var alertTitle = [ - "Strict Transport Security(STS) Header Not Set (script)", + "Strict-Transport-Security (HSTS) Header Not Set (script)", "Content-Security-Policy (script)", "Web Browser XSS Protection Not Enabled (script)", "X-Content-Type-Options Header Missing (script)", @@ -29,27 +50,17 @@ function scan(ps, msg, src) { "Most modern Web browsers support the X-Frame-Options HTTP header. Ensure it's set on all web pages returned by your site (if you expect the page to be framed only by pages on your server (e.g. it's part of a FRAMESET) then you'll want to use SAMEORIGIN, otherwise if you never expect the page to be framed, you should use DENY. ALLOW-FROM allows specific websites to frame the web page in supported web browsers).", "", ]; - var cweId = [0, 1]; - var wascId = [0, 1]; // test sts if (msg.getRequestHeader().isSecure()) { if (msg.getResponseHeader().getHeaders("Strict-Transport-Security") == null) - ps.raiseAlert( - alertRisk[1], - alertConfidence[3], - alertTitle[0], - alertDesc[0], - url, - "", - "", - "", - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); + helper + .newAlert() + .setName(alertTitle[0]) + .setDescription(alertDesc[0]) + .setSolution(alertSolution[0]) + .setMessage(msg) + .raise(); } // test csp if ( @@ -59,83 +70,53 @@ function scan(ps, msg, src) { "X-WebKit-CSP", ]) ) - ps.raiseAlert( - alertRisk[1], - alertConfidence[3], - alertTitle[1], - alertDesc[1], - url, - "", - "", - "", - alertSolution[1], - "", - cweId[0], - wascId[0], - msg - ); + helper + .newAlert() + .setName(alertTitle[1]) + .setDescription(alertDesc[1]) + .setSolution(alertSolution[1]) + .setMessage(msg) + .raise(); // test xxs protection var re_xss = /(X\-XSS\-Protection\:.+1)/g; if (!re_xss.test(responseHeader)) { //if its false - ps.raiseAlert( - alertRisk[1], - alertConfidence[3], - alertTitle[2], - alertDesc[2], - url, - "", - "", - "", - alertSolution[2], - "", - cweId[0], - wascId[0], - msg - ); + helper + .newAlert() + .setName(alertTitle[2]) + .setDescription(alertDesc[2]) + .setSolution(alertSolution[2]) + .setMessage(msg) + .raise(); } // test xcontent no sniff protection var re_nosniff = /(X\-Content\-Type\-Options\:.*nosniff.*)/g; if (!re_nosniff.test(responseHeader)) { //if its false - ps.raiseAlert( - alertRisk[2], - alertConfidence[2], - alertTitle[3], - alertDesc[3], - url, - "", - "", - "", - alertSolution[3], - "", - cweId[0], - wascId[0], - msg - ); + helper + .newAlert() + .setRisk(2) + .setConfidence(2) + .setName(alertTitle[3]) + .setDescription(alertDesc[3]) + .setSolution(alertSolution[3]) + .setMessage(msg) + .raise(); } // test xcontent no sniff protection var re_clickjack = /(X\-Frame\-Options\:.+[Dd][Ee][Nn][Yy])/g; if (!re_clickjack.test(responseHeader)) { //if its false - ps.raiseAlert( - alertRisk[1], - alertConfidence[3], - alertTitle[4], - alertDesc[4], - url, - "", - "", - "", - alertSolution[4], - "", - cweId[0], - wascId[0], - msg - ); + helper + .newAlert() + .setName(alertTitle[4]) + .setDescription(alertDesc[4]) + .setSolution(alertSolution[4]) + .setMessage(msg) + .raise(); } } diff --git a/passive/find_reflected_params.py b/passive/find_reflected_params.py index 7dfb6a87..bd12bc4d 100644 --- a/passive/find_reflected_params.py +++ b/passive/find_reflected_params.py @@ -9,6 +9,8 @@ Refactored & Improved by nil0x42 """ +from org.zaproxy.addon.commonlib.scanrules import ScanRuleMetadata + # Set to True if you want to see results on a per param basis # (i.e.: A single URL may be listed more than once) RESULT_PER_FINDING = False @@ -17,19 +19,25 @@ MIN_PARAM_VALUE_LENGTH = 8 -def scan(ps, msg, src): - # Docs on alert raising function: - # raiseAlert(int risk, int confidence, str name, str description, str uri, - # str param, str attack, str otherInfo, str solution, - # str evidence, int cweId, int wascId, HttpMessage msg) - # risk: 0: info, 1: low, 2: medium, 3: high - # confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed - alert_title = "Reflected HTTP GET parameter(s) (script)" - alert_desc = ("Reflected parameter value has been found. " - "A reflected parameter values may introduce XSS " - "vulnerability or HTTP header injection.") - - uri = header = body = None +def getMetadata(): + return ScanRuleMetadata.fromYaml(""" +id: 100014 +name: Reflected HTTP GET Parameter(s) +description: > + A reflected parameter value has been found in the HTTP response. + Reflected parameter values may introduce XSS vulnerability or HTTP header injection. +risk: info +confidence: medium +cweId: 79 # CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') +wascId: 8 # WASC-8: Cross-site Scripting (XSS) +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/find_reflected_params.py +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +""") + + +def scan(helper, msg, src): + header = body = None reflected_params = [] for param in msg.getUrlParams(): @@ -38,19 +46,16 @@ def scan(ps, msg, src): continue if not header: - uri = msg.getRequestHeader().getURI().toString() header = msg.getResponseHeader().toString() body = msg.getResponseBody().toString() if value in header or value in body: if RESULT_PER_FINDING: - param_name = param.getName() - ps.raiseAlert(0, 2, alert_title, alert_desc, uri, param_name, - None, None, None, value, 0, 0, msg) + helper.newAlert().setParam(param.getName()).setEvidence(value).setMessage(msg).raise() else: reflected_params.append(param.getName()) if reflected_params and not RESULT_PER_FINDING: - reflected_params = u",".join(reflected_params) - ps.raiseAlert(0, 2, alert_title, alert_desc, uri, reflected_params, - None, None, None, None, 0, 0, msg) + other_info = 'Other instances: ' + u",".join(reflected_params[1:]) + helper.newAlert().setParam(param.getName()).setEvidence(reflected_params[0]).setOtherInfo( + other_info).setMessage(msg).raise()