diff --git a/CHANGELOG.md b/CHANGELOG.md index 29826ea6..f0f7eb5e 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 - variant/CompoundCookies.js - An input vector script that handles splitting of compound cookies (Issue 6582). +- active/corsair.py > An active scan script to check for CORS related issues.) ## [13] - 2021-10-14 ### Fixed diff --git a/active/corsair.py b/active/corsair.py new file mode 100644 index 00000000..e5e5c575 --- /dev/null +++ b/active/corsair.py @@ -0,0 +1,53 @@ +""" +This is an adaptation of Corsair Scan (https://github.com/Santandersecurityresearch/corsair_scan) as a ZAP active scan script. +This script will resend requests to all the sites being scanned in ZAP, injecting different origins. Then, it will read the value of Access-Control-Allow-Origin and based on that, it +will assess if CORS is properly configured. +""" +import urlparse +alertTitle = 'Corsair - CORS Misconfigured' +alertDescription = "Cross Origin Resource Sharing (CORS) is misconfigured. \n" +alertRisk = 2 +alertConfidence = 3 +alertSolution = "Configure CORS in a more restrictive way, to give access only the sites allowed to access your domain." +alertInfo = "Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any other origins (domain, scheme, or port) than its own from which a browser should permit loading of resources." +cweID = 942 +wascID = 14 + +SM_ORIGIN = 'https://example.com' +SM_ORIGIN_NO_PROTOCOL = 'example.com' +SM_ORIGIN_DOMAIN = 'example' + +""" +In this method, we read the request performed and create a new request with a fake origin. Also, if the request already contains an origin header, we perform two extra requests: + 1 - Fake subdomain. It is, we set as origin a fake subdomain to validate if CORS is configured at domain level, or if it has granularity at subdomain level. + 2 - Fake postdomain. Here, we set a request using the existing origin as a subdomain for a fake domain. We do it to validate if CORS is misconfigured and it only checks if the expected value exists in the origin header. +Then, we call cors_scan, which will send that request and validate the response. +""" +def scanNode(sas, msg): + origMsg = msg + msg = origMsg.cloneRequest() + msg.getRequestHeader().setHeader("Origin", SM_ORIGIN) + cors_scan(sas,msg, "fake origin") + if origMsg.getRequestHeader().getHeader('Origin'): + parsed_url = urlparse.urlparse(origMsg.getRequestHeader().getHeader('Origin')) + subdomain= parsed_url.scheme + '://' + SM_ORIGIN_DOMAIN + '.' + parsed_url.netloc + postdomain = origMsg.getRequestHeader().getHeader('Origin') + '.' + SM_ORIGIN_NO_PROTOCOL + msg.getRequestHeader().setHeader("Origin", subdomain) + cors_scan(sas,msg, "fake subdomain") + msg.getRequestHeader().setHeader("Origin", postdomain) + cors_scan(sas,msg, "fake postdomain") + +""" +cors_scan sends the request crafted by scanNode and reads the value of the Access-Control-Allow-Origin header. +If it is the origin reflected, * or NULL, we consider that CORS is misconfigured and raise an alert. +""" +def cors_scan(sas,msg, test_type): + sas.sendAndReceive(msg, True, False); + header = str(msg.getResponseHeader().getHeader("Access-Control-Allow-Origin")) + if (header in ['null', '*', msg.getRequestHeader().getHeader('Origin')]): + alertParam = "Test performed: Injecting a "+test_type + sas.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDescription + alertParam, msg.getRequestHeader().getURI().toString(), "Origin", + msg.getRequestHeader().getHeader('Origin'), alertInfo, alertSolution, msg.getRequestHeader().getHeader('Origin'), cweID, wascID, msg); + +def scan(sas, msg, param, value): + pass