XXE Injections
Abusing XML Parsers
All CORS headers start with “Access-Control”.
The most permissive headers would be:
Access-Control-Allow-Origin: unique_origin
Access-Control-Allow-Methods: *
Access-Control-Allow-Credentials: True
From a security perspective, the most important headers when analyzing target applications for CORS vulnerabilities are Access-Control-Allow-Origin and Access-Control-Allow-Credentials.
It’s import to keep in mind that every endpoint and HTTP method can have different CORS headers depending on the actions that are allowed or disallowed.
Even through SOP only prevents javascript from reading reponse and not requests from being sent, there are some exceptions. Some requests require an HTTP preflight request (sent with the OPTIONS method), which determines if the subsequent browser request should be allowed to be sent.
Standard GET, HEAD, and POST requests don’t require preflight requests.
Other request methods, requests with custom HTTP headers, or POST requests with nonstandard content-types will require a preflight request.
A nonstandard content-type is defined as anything that is not:
fetch("https://donkey.com",
{
method: 'post',
headers: {
"Content-type": "application/json;"
}
})
i.e the only thing that changes is the content-type.
We can check if a request show be allowed to be sent on a route the application exposes, using the OPTIONS method.
It would like:
iwr -method "OPTIONS" https://cors-test.appspot.com/test
Or:
curl -X "OPTIONS" https://cors-test.appspot.com/test
The thing to keep in mind when checking CORS responses via the OPTIONS method is if the application replicates the Access-Control-Allow-Origin header in it’s response. If it does then we may have something of interest.
As we have stated the only origins allowed to read a resource are those listed in the Access-Control-Allow-Origin header.
Access-Control-Allow-Origin can have three values:
If the Access-Control-Allow-Origin header is set to a wildcard “””, all origins are allowed to read a resource from the remote server. This might seem like a vulnerable configuration, but this setting requires that *Access-Control-Allow-Credentials is set to false, which results in all requests being unauthenticated. If the header is set to an origin value, only that origin is allowed to read the resource and, if Access-Control-Allow-Credentials is set to true, include the cookies.
Access-Control-Allow-Origin only lets sites set a single origin. The header cannot contain wildcards (.donkey.com) or lists (donkey.com, hooves.com, hay.com). The workaround (which is insecure) for this is to dynamically set the *Access-Control-Allow-Origin header to the origin of the request, multiple origins can send requests with Cookies. While this is the standard solution the the single origin problem it effectively negates the purpose of SOP & CORS to begin with.
The way to test for this behavior is to add an origin header to a request and see if the origin is replicated by the webserver.
Origin: donkey.com
If you’re familiar with python3’s http.server server module the code snippet below can be considered a http.cors_server.
from flask import Flask
from flask import request
from flask import make_response
app = Flask(__name__)
@app.route('/',methods=['GET','POST'])
def hello_world():
print('Headers')
print(request.headers)
print("Method: ",request.method)
print("Remote Addr: ",request.remote_addr)
return 'CORS >_<!',200
@app.after_request
def after_request_func(response):
origin = request.headers.get('Origin')
if request.method == 'OPTIONS':
response = make_response()
response.headers.add('Access-Control-Allow-Credentials', 'true')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
#response.headers.add('Access-Control-Allow-Headers', 'x-csrf-token')
response.headers.add('Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE')
if origin:
response.headers.add('Access-Control-Allow-Origin', origin)
else:
response.headers.add('Access-Control-Allow-Credentials', 'true')
if origin:
response.headers.add('Access-Control-Allow-Origin', origin)
return response
### end CORS section
app.run('0.0.0.0',80)