diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b03474a..d2c8b3ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Use Prettier to format all JavaScript scripts. +- Update the following scripts to implement the `getMetadata()` function: + - active/Cross Site WebSocket Hijacking.js + - active/cve-2019-5418.js + - active/gof_lite.js + - active/JWT None Exploit.js + - active/SSTI.js ## [18] - 2024-01-29 ### Added diff --git a/active/Cross Site WebSocket Hijacking.js b/active/Cross Site WebSocket Hijacking.js index 395a65cc..aaf8bb40 100644 --- a/active/Cross Site WebSocket Hijacking.js +++ b/active/Cross Site WebSocket Hijacking.js @@ -41,31 +41,45 @@ var Base64 = Java.type("java.util.Base64"); var Random = Java.type("java.util.Random"); var String = Java.type("java.lang.String"); var ByteArray = Java.type("byte[]"); +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); +var CommonAlertTag = Java.type("org.zaproxy.addon.commonlib.CommonAlertTag"); var LOG_DEBUG_MESSAGES = false; // change to true for more logs -var RISK = 3; -var CONFIDENCE = 2; -var TITLE = "Cross-Site WebSocket Hijacking"; -var DESCRIPTION = - "Server accepted WebSocket connection through HTTP Upgrade request with modified Origin header."; -var SOLUTION = - "Validate Origin header on WebSocket connection handshake, to ensure only specified origins are allowed to connect.\ - Also, WebSocket handshake should use random tokens, similar to anti CSRF tokens."; -var REFERENCE = "https://tools.ietf.org/html/rfc6455#section-10.2"; -var OTHER = - "See also https://portswigger.net/web-security/websockets/cross-site-websocket-hijacking\ - or https://christian-schneider.net/CrossSiteWebSocketHijacking.html"; -var CWEID = 346; // CWE-346: Origin Validation Error, http://cwe.mitre.org/data/definitions/346.html -var WASCID = 9; // WASC-9 Cross Site Request Forgery, http://projects.webappsec.org/w/page/13246919/Cross%20Site%20Request%20Forgery +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100025 +name: Cross-Site WebSocket Hijacking +description: Server accepted WebSocket connection through HTTP Upgrade request with modified Origin header. +solution: > + Validate Origin header on WebSocket connection handshake, to ensure only specified origins are allowed to connect. + Also, WebSocket handshake should use random tokens, similar to anti CSRF tokens. +references: + - https://tools.ietf.org/html/rfc6455#section-10.2 +category: server +risk: high +confidence: medium +cweId: 346 # CWE-346: Origin Validation Error, http://cwe.mitre.org/data/definitions/346.html +wascId: 9 # WASC-9 Cross Site Request Forgery, http://projects.webappsec.org/w/page/13246919/Cross%20Site%20Request%20Forgery +alertTags: + ${CommonAlertTag.OWASP_2021_A01_BROKEN_AC.getTag()}: ${CommonAlertTag.OWASP_2021_A01_BROKEN_AC.getValue()} + ${CommonAlertTag.OWASP_2017_A05_BROKEN_AC.getTag()}: ${CommonAlertTag.OWASP_2017_A05_BROKEN_AC.getValue()} + ${CommonAlertTag.WSTG_V42_CLNT_10_WEBSOCKETS.getTag()}: ${CommonAlertTag.WSTG_V42_CLNT_10_WEBSOCKETS.getValue()} +otherInfo: > + See also https://portswigger.net/web-security/websockets/cross-site-websocket-hijacking + or https://christian-schneider.net/CrossSiteWebSocketHijacking.html +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/active/Cross%20Site%20WebSocket%20Hijacking.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} function scanNode(as, msg) { var target = msg.getRequestHeader().getURI().toString(); - - // check if this is a WebSocket HTTP Upgrade request (the message should include also "Connection: Upgrade" header if we wanted to check it strictly) - // TODO: in ZAP 2.11 we might use msg.isWebSocketUpgrade() check instead - var upgradeHeader = msg.getRequestHeader().getHeader("Upgrade"); - if (!upgradeHeader || upgradeHeader.toLowerCase() !== "websocket") { + // check if this is a WebSocket HTTP Upgrade request + if (msg.isWebSocketUpgrade()) { if (LOG_DEBUG_MESSAGES) { print( "Cross-Site WebSocket Hijacking rule skipped for url=" + @@ -102,17 +116,8 @@ function scanNode(as, msg) { ); } as.newAlert() - .setRisk(RISK) - .setConfidence(CONFIDENCE) - .setName(TITLE) - .setDescription(DESCRIPTION) .setParam(target) .setEvidence(msg.getResponseHeader().getPrimeHeader()) - .setOtherInfo(OTHER) - .setSolution(SOLUTION) - .setReference(REFERENCE) - .setCweId(CWEID) - .setWascId(WASCID) .setMessage(msg) .raise(); } diff --git a/active/JWT None Exploit.js b/active/JWT None Exploit.js index 615c1840..0e42c49e 100644 --- a/active/JWT None Exploit.js +++ b/active/JWT None Exploit.js @@ -4,18 +4,36 @@ var Cookie = Java.type("java.net.HttpCookie"); var Base64 = Java.type("java.util.Base64"); var String = Java.type("java.lang.String"); -// Exploit information, used for raising alerts -var RISK = 3; -var CONFIDENCE = 2; -var TITLE = "JWT None Exploit"; -var DESCRIPTION = - "The application's JWT implementation allows for the usage of the 'none' algorithm, which bypasses the JWT hash verification."; -var SOLUTION = - "Use a secure JWT library, and (if your library supports it) restrict the allowed hash algorithms."; -var REFERENCE = - "https://www.sjoerdlangkemper.nl/2016/09/28/attacking-jwt-authentication/"; -var CWEID = 347; // CWE-347: Improper Verification of Cryptographic Signature -var WASCID = 15; // WASC-15: Application Misconfiguration +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); +var CommonAlertTag = Java.type("org.zaproxy.addon.commonlib.CommonAlertTag"); + +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100026 +name: JWT None Exploit +description: > + The application's JWT implementation allows for the usage of the 'none' algorithm, + which bypasses the JWT hash verification. +solution: > + Use a secure JWT library, and (if your library supports it) restrict the allowed hash algorithms. +references: + - https://www.sjoerdlangkemper.nl/2016/09/28/attacking-jwt-authentication/ +category: server +risk: high +confidence: medium +cweId: 347 # CWE-347: Improper Verification of Cryptographic Signature, http://cwe.mitre.org/data/definitions/347.html +wascId: 15 # WASC-15: Application Misconfiguration +alertTags: + ${CommonAlertTag.OWASP_2021_A01_BROKEN_AC.getTag()}: ${CommonAlertTag.OWASP_2021_A01_BROKEN_AC.getValue()} + ${CommonAlertTag.OWASP_2017_A02_BROKEN_AUTH.getTag()}: ${CommonAlertTag.OWASP_2017_A02_BROKEN_AUTH.getValue()} + ${CommonAlertTag.WSTG_V42_CRYP_04_WEAK_CRYPTO.getTag()}: ${CommonAlertTag.WSTG_V42_CRYP_04_WEAK_CRYPTO.getValue()} +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/active/JWT%20None%20Exploit.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} function b64encode(string) { // Terminate the string with a null byte prior to encoding. I suspect that @@ -128,20 +146,8 @@ function scanNode(as, msg) { function raise_alert(msg, cookie, payload, as) { print("Vulnerability found, sending alert"); - as.raiseAlert( - RISK, - CONFIDENCE, - TITLE, - DESCRIPTION, - msg.getRequestHeader().getURI().toString(), - "", - "", - "", - SOLUTION, - "Cookie: " + cookie.getName() + "=" + payload, - REFERENCE, - CWEID, - WASCID, - msg - ); + as.newAlert() + .setEvidence("Cookie: " + cookie.getName() + "=" + payload) + .setMessage(msg) + .raise(); } diff --git a/active/SSTI.js b/active/SSTI.js index 05780b15..2d3d52a5 100644 --- a/active/SSTI.js +++ b/active/SSTI.js @@ -11,8 +11,36 @@ var LoggerManager = Java.type("org.apache.logging.log4j.LogManager"); var log = LoggerManager.getLogger("SSTI"); -// HasMap for alertTags -var HashMap = Java.type("java.util.HashMap"); +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); + +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100033 +name: Server Side Template Injection +description: > + Server Side Template Injection (SSTI) occurs when user input is directly embedded into the template without any + proper sanitization, a hacker can use this vulnerability to inject malicious code and try to achieve remote code execution. +solution: > + Always use proper functions provided by the template engine to insert data, + if that is not possible try to sanitize user input as efficiently as possible. +references: + - https://portswigger.net/research/server-side-template-injection +category: injection +risk: high +confidence: medium +cweId: 20 # CWE-20: Improper Input Validation +wascId: 20 # WASC-20: Improper Input Handling +alertTags: + ${CommonAlertTag.OWASP_2021_A03_INJECTION.getTag()}: ${CommonAlertTag.OWASP_2021_A03_INJECTION.getValue()} + ${CommonAlertTag.OWASP_2017_A01_INJECTION.getTag()}: ${CommonAlertTag.OWASP_2017_A01_INJECTION.getValue()} + ${CommonAlertTag.WSTG_V42_INPV_18_SSTI.getTag()}: ${CommonAlertTag.WSTG_V42_INPV_18_SSTI.getValue()} +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/active/SSTI.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} function logger() { print("[" + this["zap.script.name"] + "] " + arguments[0]); @@ -168,44 +196,18 @@ function raiseAlert(as, msg, payload, evidence, confidence, param, engine) { var badErrors = ["Infinity", "INF"]; //Alert variables - var pluginId = 100033; var alertName = "Server Side Template Injection"; if (badErrors.indexOf(engine) == -1) { alertName += " - " + toTitleCase(engine); } - var alertDesc = - "Server Side Template Injection (SSTI) occurs when user input is directly embedded into the template without any proper sanitization, a hacker can use this vulnerability to inject malicious code and try to achieve remote code execution."; - var alertSol = - "Always use proper functions provided by the template engine to insert data, if that is not possible try to sanitize user input as efficiently as possible."; - var alertRef = - "https://portswigger.net/research/server-side-template-injection"; - var cweId = 20; // Improper Input Validation - var wascId = 20; // Improper Input Handling - var alertTags = new HashMap(); - alertTags.put( - "OWASP_2021_A03", - "https://owasp.org/Top10/A03_2021-Injection/" - ); - alertTags.put( - "OWASP_2017_A01", - "https://owasp.org/www-project-top-ten/2017/A1_2017-Injection.html" - ); as.newAlert() - .setAlertId(pluginId) - .setRisk(3) .setConfidence(confidence) .setName(alertName) - .setDescription(alertDesc) .setParam(param) .setAttack(payload) .setEvidence(evidence) - .setSolution(alertSol) - .setReference(alertRef) - .setCweId(cweId) - .setWascId(wascId) .setMessage(msg) - .setTags(alertTags) .raise(); } diff --git a/active/cve-2019-5418.js b/active/cve-2019-5418.js index 37b3c538..efda1e7e 100644 --- a/active/cve-2019-5418.js +++ b/active/cve-2019-5418.js @@ -1,34 +1,39 @@ -// Note that new active scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" - // This active scanner script checks for CVE-2019-5418 -/** - * Scans a "node", i.e. an individual entry in the Sites Tree. - * The scanNode function will typically be called once for every page. - * - * @param as - the ActiveScan parent object that will do all the core interface tasks - * (i.e.: sending and receiving messages, providing access to Strength and Threshold settings, - * raising alerts, etc.). This is an ScriptsActiveScanner object. - * @param msg - the HTTP Message being scanned. This is an HttpMessage object. - */ -function scanNode(as, msg) { - // Set some details we will need for alerts later - var alertRisk = 3; - var alertConfidence = 2; - var alertTitle = "CVE-2019-5418 - File Content Disclosure"; - var alertDesc = - "The application seems to be subject to CVE-2019-5418. \ -By sending a specially crafted request it was possible to have the target return \ -data from the server file system."; - var alertSolution = - "Upgrade to a version of Ruby/Rails where this issue is fixed. (See references for further details)."; - var alertInfo = - "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5418\nhttps://github.com/mpgn/CVE-2019-5418"; - var cweId = 74; //Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') - var wascId = 33; // Path Traversal - var url = msg.getRequestHeader().getURI().toString(); +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); +var CommonAlertTag = Java.type("org.zaproxy.addon.commonlib.CommonAlertTag"); + +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100029 +name: "File Content Disclosure (CVE-2019-5418)" +description: > + The application seems to be subject to CVE-2019-5418. + By sending a specially crafted request it was possible to have the target return + data from the server file system. +solution: > + Upgrade to a version of Ruby/Rails where this issue is fixed. (See references for further details). +references: + - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5418 + - https://github.com/mpgn/CVE-2019-5418 +category: injection +risk: high +confidence: medium +cweId: 74 # CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') +wascId: 33 # WASC-33: Path Traversal +alertTags: + ${CommonAlertTag.OWASP_2021_A03_INJECTION.getTag()}: ${CommonAlertTag.OWASP_2021_A03_INJECTION.getValue()} + ${CommonAlertTag.OWASP_2017_A01_INJECTION.getTag()}: ${CommonAlertTag.OWASP_2017_A01_INJECTION.getValue()} + ${CommonAlertTag.WSTG_V42_ATHZ_01_DIR_TRAVERSAL.getTag()}: ${CommonAlertTag.WSTG_V42_ATHZ_01_DIR_TRAVERSAL.getValue()} +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/active/cve-2019-5418.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} +function scanNode(as, msg) { var msg2 = msg.cloneRequest(); msg2 .getRequestHeader() @@ -40,21 +45,7 @@ data from the server file system."; var body = msg2.getResponseBody().toString(); if (re.test(body)) { - as.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - "", - "", - alertInfo, - alertSolution, - body.match(re)[0], - cweId, - wascId, - msg2 - ); + as.newAlert().setEvidence(body.match(re)[0]).setMessage(msg2).raise(); return; // No need to try further } // Just in case there's a simple WaF @@ -68,24 +59,6 @@ data from the server file system."; re = /127.0.0.1/g; body = msg3.getResponseBody().toString(); if (re.test(body)) { - as.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - "", - "", - alertInfo, - alertSolution, - body.match(re)[0], - cweId, - wascId, - msg3 - ); + as.newAlert().setEvidence(body.match(re)[0]).setMessage(msg3).raise(); } } - -function scan(as, msg, param, value) { - //Unused -} diff --git a/active/gof_lite.js b/active/gof_lite.js index 1e094562..33dd1f8e 100644 --- a/active/gof_lite.js +++ b/active/gof_lite.js @@ -1,9 +1,3 @@ -// The scanNode function will typically be called once for every page -// The scan function will typically be called for every parameter in every URL and Form for every page - -// Note that new active scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" - // NOTE: This active scanner is VERY chatty, making a lot of request per URL // Under certain conditions it can greatly increase your scan time and resource consumption @@ -18,6 +12,34 @@ // 20150828 - Initial submission // 20150923 - Add check to see and handle if the user has stopped the scan +var ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); +var CommonAlertTag = Java.type("org.zaproxy.addon.commonlib.CommonAlertTag"); + +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100030 +name: Backup File Detected +description: > + A backup or alternate version of a page or component was detected. An attacker + may leverage information in such files to further attack or abuse the system. +solution: Ensure that backups are made in locations which are not web accessible. +category: info_gather +risk: low +confidence: medium +cweId: 425 # CWE-425: Direct Request ('Forced Browsing') +wascId: 34 # WASC-34: Predictable Resource Location +alertTags: + ${CommonAlertTag.OWASP_2021_A05_SEC_MISCONFIG.getTag()}: ${CommonAlertTag.OWASP_2021_A05_SEC_MISCONFIG.getValue()} + ${CommonAlertTag.OWASP_2017_A06_SEC_MISCONFIG.getTag()}: ${CommonAlertTag.OWASP_2017_A06_SEC_MISCONFIG.getValue()} + ${CommonAlertTag.WSTG_V42_CONF_04_BACKUP_FILES.getTag()}: ${CommonAlertTag.WSTG_V42_CONF_04_BACKUP_FILES.getValue()} +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/active/gof_lite.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} + mutationStrings = [ "old", "conf", @@ -47,17 +69,6 @@ mutationStrings = [ //The default setting "Sorry, we can't seem to find what you were looking for" is based upon some of the tests from WAVSEP: http://sourceforge.net/projects/wavsep/ customErrorString = "Sorry, we can't seem to find what you were looking for"; -alertRisk = 1; -alertConfidence = 2; -alertTitle = "Backup File Detected"; -alertDesc = - "A backup or alternate version of a page or component was detected. An attacker \ -may leverage information in such files to further attack or abuse the system."; -alertSoln = - "Ensure that backups are made in locations which are not web accessible."; -cweId = 425; //Direct Request ('Forced Browsing') -wascId = 34; //Predictable Resource Location - alertRiskDenied = 0; alertConfidenceDenied = 1; alertTitleDenied = "Backup File Detected (Access Denied)"; @@ -87,11 +98,11 @@ function scanNode(as, msg) { msg .getRequestHeader() .getURI() - .setPath(mutate(msg, "." + mutationStrings[idx])); //TODO: handle seperators other than period + .setPath(mutate(msg, "." + mutationStrings[idx])); //TODO: handle separators other than period var newURL = msg.getRequestHeader().getURI().toString(); if (newURL.equals(origURL)) { - // Don't bother if no change (perhaps the user alerady proxied a backup/alternative) + // Don't bother if no change (perhaps the user already proxied a backup/alternative) return; } @@ -118,10 +129,6 @@ function scanNode(as, msg) { } } -function scan(as, msg, param, value) { - //gof_lite doesn't deal with parameters -} - function mutate(msg, mutationString) { var path = msg.getRequestHeader().getURI().getPath(); //getURI might include query, might need getPath/setPath @@ -138,42 +145,16 @@ function mutate(msg, mutationString) { } function raiseAlert(wasSuccess, msg, origMsg, as) { - var url = msg.getRequestHeader().getURI().toString(); - if (wasSuccess == true) { - //200 - as.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - "", - "", - "", - alertSoln, - "", - cweId, - wascId, - msg - ); - } else { + var alert = as.newAlert().setMessage(msg); + if (!wasSuccess) { //401 or 403 - as.raiseAlert( - alertRiskDenied, - alertConfidenceDenied, - alertTitleDenied, - alertDescDenied, - url, - "", - "", - "", - alertSoln, - "", - cweId, - wascId, - msg - ); + alert + .setRisk(alertRiskDenied) + .setConfidence(alertConfidenceDenied) + .setName(alertTitleDenied) + .setDescription(alertDescDenied); } + alert.raise(); } //TODO List //Handle various prefixes and suffixes such as: