€400 Bounty: Unveiling a More Impactful CORS Misconfiguration Exploit

Fabian Cruz
3 min readMay 1, 2024

--

Introduction

After discovering and reporting my first valid bug (Link to Report 1), I set out to find another bug with a higher severity in the same program. After nearly a month of testing different areas for various vulnerabilities, it occurred to me to test for CORS misconfiguration in the company’s main application. This led me down a fruitful rabbit hole. To maintain confidentiality, I will use the term “target” to refer to the actual company.

Walk-through

In the authentication chain, two endpoint requests were crucial, where the access_token was issued in the response. Unlike the exploit discussed in my previous write-up, this one was simpler and could be executed when the user was already logged in.

This attack has two phases:

Phase 1: Getting the code

An authentication code is required to get the access_token. A GET request is made to the following URL:

https://id.target.com/auth/realms/target/protocol/openid-connect/auth?client_id=profiles-web&redirect_uri=https%3A%2F%2Fprofile.target.com%2Fprofile&state='+statecode+'&response_mode=fragment&response_type=code&scope=openid

Three parameters are required to make the request successful:

1. statecode
2. response_type
3. Accept header

The statecode did not serve any critical purpose, but a 400 error was returned if it was not included. It had to be used in a specific format to be successful; a random string such as “xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx” was used.

By default, the response_type was set to “token”, and the response would be a 300 redirect. By using “code”, it would generate the redirect URL in a JSON response.

Finally, the Accept header was the key discovery in the exploit. The request would still fail if only the above two parameters were used.

On the brink of giving up and going to sleep at midnight, I decided to check one final CORS misconfiguration report in HackerOne’s Hacktivity. The XMLHttpRequest had a req.setRequestHeader(‘Accept’,‘application/json’); present in the exploit payload. As a last-ditch effort, I tried the same (note: this header was not present in the default flow because a JSON response was not intended for the endpoint).

I crafted the XMLHttpRequest with

req.setRequestHeader('Accept','application/json');

This responded with a 200 status, with the URL containing the code required to get the access_token.

(Note: Screenshot extracted from report, lost the original shot of the reponse in json)

Phase 2: Getting the access_token

Using the previously obtained code, another POST request is made to the following endpoint:

https://id.target.com/auth/realms/target/protocol/openid-connect/token

The payload is:

code=<tokencode>&grant_type=authorization_code&client_id=profiles-web&redirect_uri=https%3A%2F%2Fprofile.target.com%2Fprofile

This request also required an essential header: Content-Type: application/x-www-form-urlencoded. This successfully returned the access_token and refresh_token of a victim in one click.

Takeaway

This process was automated again with a simple custom CORS exploit script that can be found (here) (another sloppy version). Unlike my previous CORS exploit, this script would work if the user is just logged in to the main application and accidentally clicks the malicious attacker’s link hosting the exploit script.

Tip: Log4shell dropped right when I reported a medium-severity bug, and the triage team naturally focused on the critical stuff first. To expedite triage and potential rewards, I would suggest focusing on identifying critical vulnerabilities or, if that is not possible, high-severity vulnerabilities.

Timeline

Reported — 17/12/2021

Triaged — 21/12/2021

Rewarded with bonus — 10/01/2022

Follow me on Twitter: 124v3n012

Buy me a coffee: https://www.buymeacoffee.com/r4v3n0r

--

--