It is the ultimate bug hunter heartbreak. You spend hours tearing apart a complex FinTech application, bypass a notoriously strict Web Application Firewall, completely compromise their core authentication flow, submit a bulletproof Critical-severity report… and get hit with "Duplicate."

It stings. But getting a dupe on a critical Account Takeover (ATO) doesn't mean your hacking was wrong; it just means you lost a footrace on a crowded public program. The methodology, however, was flawless.

Here is a conceptual breakdown of a recent critical vulnerability I uncovered, how modern OAuth implementations can fail, and why you should always be testing the boundaries of the Same-Origin Policy on authentication gateways.

The Architecture: Pushed Authorization Requests (PAR)

The target was a financial services platform utilizing a modern OAuth 2.0 architecture, specifically employing the Pushed Authorization Request (PAR) flow.

In older OAuth flows, sensitive authorization parameters (like redirect_uri, client_id, and state) are passed through the browser's URL. This leaves them vulnerable to interception via referer leaks or browser history. PAR fixes this by having the client push these parameters directly to the authorization server via a secure backend API call.

In return, the server generates a unique, single-use request_uri (usually formatted as a URN, like urn:target:par:xyz...). The frontend then uses this URN to safely initiate the user's login sequence. It is a highly secure mechanism—if implemented correctly.

The Flaw: When CORS Meets Core Auth

The front door to the target's API was heavily guarded. Standard brute-forcing, directory fuzzing, and automated scanners were instantly blocked by an aggressive bot-management WAF. To get anywhere, I had to drop the automated tools, intercept the proxy traffic manually, and map out the application's state machine.

While analyzing the initial POST request to the PAR endpoint, I noticed an anomaly in how the server handled Cross-Origin Resource Sharing (CORS).

A secure API should only accept requests from explicitly trusted domains. However, when I intercepted the request and modified the HTTP Origin header to simulate an attacker-controlled domain, the server responded with:

HTTP

Access-Control-Allow-Origin: https://evil-attacker.com
Access-Control-Allow-Credentials: true

This is a fatal configuration error. By dynamically reflecting the Origin header while simultaneously allowing credentials (cookies), the server effectively dismantled the browser's Same-Origin Policy (SOP) for that specific endpoint.

The Kill Chain: From SOP Bypass to ATO

Because this vulnerable endpoint was responsible for generating the sensitive PAR URN, the CORS misconfiguration paved the way for a devastating Account Takeover.

Here is how the exploit chain worked in practice:

  • The Trap: An attacker hosts a malicious website and tricks a victim (who currently has an active session on the target platform) into visiting it.
  • The Silent Execution: The malicious site executes a background JavaScript XHR request to the vulnerable PAR endpoint. Because Allow-Credentials is set to true, the victim's browser automatically attaches their authenticated session cookies to this cross-origin request.
  • The Extraction: The vulnerable server processes the request as the legitimate victim and responds with a freshly generated request_uri (URN). Because the server trusts the attacker's origin, the browser allows the malicious script to read the HTTP response and extract the URN.
  • The Takeover: The attacker takes the stolen URN and manually passes it to the authorization endpoint. The server, believing the attacker is the victim completing their legitimate login flow, hands over the OAuth authorization code, granting the attacker full access to the victim's account.

The Takeaway

Automated scanners and script kiddies are designed to find missing security headers or outdated libraries. They are not designed to understand the business logic of an OAuth state machine.

Even though this specific report ended up as a duplicate, it reinforces a fundamental truth in modern bug bounty: the most critical vulnerabilities aren't found by throwing massive wordlists at a domain. They are found by understanding how the application is supposed to work, and methodically breaking the logic that holds it together.

The WAF might stop your scanner, but it won't stop a perfectly crafted, logic-based HTTP request.