|
| 1 | +""" |
| 2 | +This is an adaptation of Corsair Scan (https://github.com/Santandersecurityresearch/corsair_scan) as a ZAP active scan script. |
| 3 | +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 |
| 4 | +will assess if CORS is properly configured. |
| 5 | +""" |
| 6 | +import urlparse |
| 7 | +alertTitle = 'Corsair - CORS Misconfigured' |
| 8 | +alertDescription = "Cross Origin Resource Sharing (CORS) is misconfigured. \n" |
| 9 | +alertRisk = 2 |
| 10 | +alertConfidence = 3 |
| 11 | +alertSolution = "Configure CORS in a more restrictive way, to give access only the sites allowed to access your domain." |
| 12 | +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." |
| 13 | +cweID = 942 |
| 14 | +wascID = 14 |
| 15 | + |
| 16 | +SM_ORIGIN = 'https://example.com' |
| 17 | +SM_ORIGIN_NO_PROTOCOL = 'example.com' |
| 18 | +SM_ORIGIN_DOMAIN = 'example' |
| 19 | + |
| 20 | +""" |
| 21 | +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: |
| 22 | + 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. |
| 23 | + 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. |
| 24 | +Then, we call cors_scan, which will send that request and validate the response. |
| 25 | +""" |
| 26 | +def scanNode(sas, msg): |
| 27 | + origMsg = msg |
| 28 | + msg = origMsg.cloneRequest() |
| 29 | + msg.getRequestHeader().setHeader("Origin", SM_ORIGIN) |
| 30 | + cors_scan(sas,msg, "fake origin") |
| 31 | + if origMsg.getRequestHeader().getHeader('Origin'): |
| 32 | + parsed_url = urlparse.urlparse(origMsg.getRequestHeader().getHeader('Origin')) |
| 33 | + subdomain= parsed_url.scheme + '://' + SM_ORIGIN_DOMAIN + '.' + parsed_url.netloc |
| 34 | + postdomain = origMsg.getRequestHeader().getHeader('Origin') + '.' + SM_ORIGIN_NO_PROTOCOL |
| 35 | + msg.getRequestHeader().setHeader("Origin", subdomain) |
| 36 | + cors_scan(sas,msg, "fake subdomain") |
| 37 | + msg.getRequestHeader().setHeader("Origin", postdomain) |
| 38 | + cors_scan(sas,msg, "fake postdomain") |
| 39 | + |
| 40 | +""" |
| 41 | +cors_scan sends the request crafted by scanNode and reads the value of the Access-Control-Allow-Origin header. |
| 42 | +If it is the origin reflected, * or NULL, we consider that CORS is misconfigured and raise an alert. |
| 43 | +""" |
| 44 | +def cors_scan(sas,msg, test_type): |
| 45 | + sas.sendAndReceive(msg, True, False); |
| 46 | + header = str(msg.getResponseHeader().getHeader("Access-Control-Allow-Origin")) |
| 47 | + if (header in ['null', '*', msg.getRequestHeader().getHeader('Origin')]): |
| 48 | + alertParam = "Test performed: Injecting a "+test_type |
| 49 | + sas.raiseAlert(alertRisk, alertConfidence, alertTitle, alertDescription + alertParam, msg.getRequestHeader().getURI().toString(), "Origin", |
| 50 | + msg.getRequestHeader().getHeader('Origin'), alertInfo, alertSolution, msg.getRequestHeader().getHeader('Origin'), cweID, wascID, msg); |
| 51 | + |
| 52 | +def scan(sas, msg, param, value): |
| 53 | + pass |
0 commit comments