Demystifying an XSS payload: Part 4

Let’s demystify an interesting XSS payload and learn about it! This post would aim to help you give my methodology on debugging things.

SecurityGOAT
8 min readAug 16, 2021
Image Credits: SecurityGOAT (That’s me!)

Introduction

One day I was randomly surfing Twitter and found an interesting XSS payload:

document.body.innerHTML=eval(‘Error``.stack//# sourceURL=http://\u{3c}img/src/onerror=alert(1)\u{3e}//')

To be honest, I don’t remember who posted it, maybe it was @XssPayloads on Twitter, or maybe someone else.

Did a quick search and found someone referenced it here: https://www.bugbountytips.tech/2021/06/01/xss-payloaddocument-body-innerhtmlevalerror-stack-sourceurlxss-c/

So maybe this guy posted this interesting payload, not so sure, but anyways let’s see the demystification process and uncover what this curious looking payload has!

Payload Breakdown

There’s a lot to take in… So I think it’s better to break this payload down into pieces and attack 1 piece at a time. Then we can assembly everything and see what it means.

So let’s dive in :)

eval(‘Error``.stack//# sourceURL=http://\u{3c}img/src/onerror=alert(1)\u{3e}//')

The payload contains:

Great! So we have divided this payload and know of all the components in this. Now we are in much better position to analyze it!

eval would simply execute whatever payload you give it and that’s not interesting right now. Let’s see what happens inside it.

Note: Feel free to follow along by opening the console window (press CTRL + Shift + I).

Error`` would call the Error function.

Error`` is same as Error()

Most of you might already know of this, but I have listed it here, in case someone didn’t already knew. So using the backticks, we can call a function and thus this would be parenthesis-less XSS! Neat trick to have in your arsenal :)

Back to the payload:

So, we just created a new Error object and check the stack property of that object (which would contain the stacktrace — yes, it’s the same things as the stacktraces you heard in Exploit Development, if you are into that kinda thing!).

Here’s the relevant snippet from the MDN docs:

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack

Fairly straightforward — the stack property would display the execution trace right from the most recent function, all the way down to the very first function that called it. The line numbers and file names are also given and arguments might also be shown (which was not the case in Firefox at the time of this writing).

Now with all this stuff down, what do you think would happen if I do eval on the payload?

With this much knowledge, I initially thought that the ‘//’ would be the comment and thus nothing after that should get executed! But when I ran this payload, the XSS payload was actually added to the URL!

Now this was a puzzling thing for me! And that’s why we have this post. Else everything else was too easy to be figured out and the post won’t hold much value in that case, right.

So now dig into that last puzzle piece and find out why it works and what on earth (or Javascript spec ;) makes it tick?!

Solving the Puzzle Piece

If you throw the errors and check the stacktrace, you would notice that it contains the file name where the error occurred, and if you are using eval in the console, then you would see the string debugger instead of the file name:

Error from file (left) and from console (right)

And for this I had used the example mentioned on the MDN doc for stack:

<!DOCTYPE HTML>
<meta charset="UTF-8">
<title>Stack Trace Example</title>
<body>
<script>
function trace() {
try {
throw new Error('myError');
}
catch(e) {
alert(e.stack);
}
}
function b() {
trace();
}
function a() {
b(3, 4, '\n\n', undefined, {});
}
a('first call, firstarg');
</script>

Again, I am just adding the example here for completeness sake.

By the way, why on earth are we discussing about this, when we were on the mission to demystify the sourceURL directive? Did you again got distracted!!

Nah, we are right on track my friend. Just wait for it… It will be clear in a moment, why we discussed about this :)

Since I already showed you that a stacktrace for an eval or any other sink like Function() for instance would not give you any file names but simply “debugger” string!

Now imagine that you are using eval or Function() inside your code and happen to get a bunch of errors, all from this same script.

How would you debug it? Line numbers right… Smart!

But have you even seen the pages generated on the web nowadays? Most, if not all pages have minified Javascript which is either added manually or nowadays due to the JS Frameworks popularity era, compiled JS (which is minified by a bundler) is returned to your browsers!

So line number would always be 1 my smart friends!

Now what? You would say that you still get the column number to locate it.

At this point, I would say, be frank and ask yourself, would you have the patience to go through that ugly code and see which function was there at the reported column number? If you say “yes”, then I have to say that you are forgetting an important thing — the code is also minified, you even if you get to the code snippet, you would be able to make head or tail out of that.

Feel free to experiment on that if you are still skeptic!

So then what would I do? you might be tempted to ask.

And the answer would be: to use sourceURL directive FTW!

With this directive, you can actually name the scripts created by the eval or Function() or any other JS execution sink/gadget for that matter!

And thus, your stacktraces would have much more information now, to be useful to you folks!

This would be done as follows:

eval(“… some code … //# sourceURL=some-random-name.js”)

If you wish to read more details, then I would highly suggest this post. It’s quite concise and to the point, and I learnt about the sourceURL directive from this post, so it’s a good read.

Now, some of you might have become impatient enough to close this tab and move on to some other topic… But hold on! Now we get to the real stuff — that is, why that payload works!

Since you already know what sourceURL directive does, let’s now get to the meat of that payload!

And I think some of you might already have understood what the payload does, based on the above information. If you still didn’t worry now, I’ve got you covered my friend!

Payload Demystified

Inspecting the payload again:

eval(‘Error``.stack//# sourceURL=http://\u{3c}img/src/onerror=alert(1)\u{3e}//')

Noticed something? sourceURL directive? Correct! It’s not a comment, but the syntax for specifying the sourceURL directive!!

And that was the main trick in this payload :)

So now, when you get the stacktraces, they would contain the specified URL (which is the XSS payload) as the file name.

If you run the payload in the console, then you would find this output:

@ followed by the URL followed by the line and column numbers respectively!

If you are thinking how \u{3c} got converted to < and how \u{3e} got converted to >, the answer is Unicode!

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#escape_sequences

Type it in the console window (CTRL + Shift + I) and see it for yourself:

And now remains the last part — that is adding this payload to the DOM using another sink — innerHTML!

So the payload is added to the DOM using:

document.body.innerHTML

So this payload we had would go into DOM and then the <img> tag from the payload would get rendered. Since the source is empty, the onerror event handler would get fired which is set to alert(1), and therefore, you guessed it — XSS would happen!

Sweet and fun! The XSS payload is successfully demystified folks :)

Closing Thoughts

I hope you enjoyed this payload breakdown. The key takeaway should be the process used to demystify the payload and this will help you quite a lot, be it in demystifying other interesting looking XSS payloads like these:

or be it some other domain! It’s the process and the way of thinking which is important — that is breakdown the problem and then see how the smallest molecules work, and then build everything from the bottom up! It has always helped me and I hope that it also serves it :)

If it does, you can send a word of thanks and I would be happy with that.

If you enjoyed this post, then please share it with all your friends and colleagues in the infosec community!

Let me know your feedback in the comments below and feel free to connect on twitter: @_SecurityGOAT

You can also send any topic you wish to learn more about. Let me know in the comments :)

Lastly, the line which should mostly be a plain ol’ boring one which I have to copy from my previous post every single time!!!! — if you have been enjoying my work and would love to support me, consider checking my Patreon page or you can even Buy Me a Coffee :)

And also, get ready for more fun posts! I have more fun ideas lately and planning to expand more, with more technical content for your brain muscles!

So help me spread these posts around to help make infosec more approachable for the beginners and more fun for the GOATS ;)

See ya!
Until next time my friend, keep learning and happy hacking.

--

--