October 13, 2026
Lab: Reflected XSS into HTML Context with Most Tags and Attributes Blocked
Understanding the Lab Description

By Kero Tato
8 min read
Understanding the Lab Description
This lab focuses on a Reflected Cross-Site Scripting (XSS) vulnerability in the website's search functionality. However, the website uses a Web Application Firewall (WAF) to block most HTML tags and attributes that could be used to perform an XSS attack.
A WAF is a security mechanism that inspects HTTP requests and applies filtering rules to identify and block potentially malicious input. In this lab, the WAF attempts to prevent XSS by rejecting HTML tags and attributes that may allow JavaScript execution.
For example, an attacker might try to inject a <script> tag or use an HTML event handler to execute JavaScript in the victim's browser. The WAF is designed to block these common techniques.
The objective of this lab is to investigate the filtering behavior, identify an HTML tag and an attribute that are accepted, and use them to trigger the JavaScript print() function.
An important requirement is that the final exploit must work without requiring the victim to interact manually with the page. This means that triggering print() by manually changing the browser's zoom level is not sufficient to solve the lab.
Testing the WAF with a Script Tag
After accessing the lab, I entered the following payload into the search field:
<script><script>I did this to observe how the WAF would respond to a common HTML tag associated with XSS attacks. Understanding the application's response would help me decide what to test next.
After submitting the request, the application returned the following error:
Tag is not allowedTag is not allowedThis response indicated that the WAF rejected the tested HTML tag.
Testing every HTML tags manually would be a difficult and time-consuming process. Instead, I decided to automate the testing process using Burp Suite, specifically Burp Intruder.
I used the PortSwigger XSS Cheat Sheet as a source of HTML tags to test. By providing these tags to Intruder, I could send multiple requests automatically and compare the application's responses rather than testing every tag individually.
Identifying an Allowed HTML Tag Using Burp Intruder
I returned to the search field and entered:
<test><test>I intercepted the request using Burp Suite and sent it to Burp Intruder.
Inside Intruder, I selected the word test and clicked Add to mark it as the payload position. I then pasted the list of HTML tags I had copied from the PortSwigger XSS Cheat Sheet into the payload list.
This configuration allowed Intruder to replace test with each candidate tag and send the resulting requests automatically.
The goal was to identify which HTML tags the WAF blocked and which ones it allowed through.
After configuring the payloads, I clicked Start attack and reviewed the results.
Most of the tested tags returned an HTTP 400 response, while the body tag returned an HTTP 200 response.
An HTTP 400 response generally indicates that the server rejected the request. An HTTP 200 response indicates that the server successfully processed it.
In this context, the different response codes helped identify a tag that was not rejected by the WAF. The body tag became the candidate for the next stage of testing.
However, receiving a 200 response does not automatically prove that XSS is possible. I still needed to investigate whether an attribute could be used with this tag to execute JavaScript.
Testing the onload Attribute
After identifying the body tag, I returned to the search field and entered:
<body onload="print()"><body onload="print()">I submitted the request to observe how the WAF would respond to the onload attribute.
The purpose of this test was to determine whether the WAF allowed the attribute as well as the tag. Even if an HTML tag is accepted, an event-handler attribute may still be blocked by a separate filtering rule.
The onload attribute is associated with a load event. When used in an appropriate context, it can execute JavaScript when the relevant element or resource loads.
The next step was to check the application's response and investigate other attributes if necessary. The application rejected the tested attribute with the following message:
Attribute is not allowedAttribute is not allowedThis indicated that the WAF was filtering attributes separately from HTML tags.
Testing every possible attribute manually would also be inefficient. Therefore, I returned to the PortSwigger XSS Cheat Sheet and copied its list of event-handler attributes so I could test them automatically.
Automating Attribute Testing
I returned to the search field and entered:
<body test><body test>I used test as a placeholder for the attribute I wanted to test.
The plan was to replace this placeholder with different attributes through Burp Intruder and observe which ones the WAF accepted.
I intercepted the request using Burp Suite and sent it to Burp Intruder.
Inside Intruder, I selected the word test and marked it as the payload position. I then pasted the list of event-handler attributes that I had copied from the XSS Cheat Sheet into the payload list.
This allowed Intruder to test the attributes automatically and compare the responses.
After starting the attack, I reviewed the results.
Many attributes returned HTTP 200 responses. However, I needed an event that could be triggered automatically rather than relying on the user to perform a manual action.
After reviewing the results and considering the behavior required for the exploit, I selected the onresize event handler for further testing.
The onresize event is triggered when the relevant window or viewport is resized. Although this event can be triggered manually by changing the browser's zoom level, the final exploit needed to cause the resize automatically.
The next step was to test this behavior and then use the exploit server to attempt to trigger it without manual interaction.
Testing the onresize Event
I returned to the search field and entered the following payload:
<body onresize=alert("XSS")><body onresize=alert("XSS")>I submitted the payload to test whether the onresize event could execute JavaScript.
The alert("XSS") function was used as a visible proof of execution. If the event fired successfully, the browser would display an alert containing XSS.
After submitting the payload, the alert did not appear immediately.
I then changed the browser's zoom level. This caused the viewport size to change, triggering the onresize event and displaying the alert.
This confirmed that the event could execute the supplied JavaScript when the resize event occurred.
However, manually changing the zoom level would not satisfy the lab's requirement. I therefore needed to arrange for the resize to happen automatically when the exploit was loaded.
Preparing the Exploit Server
After testing the event, I selected Go to exploit server to prepare the exploit page.
The purpose was to use an iframe and change its width when it loaded, attempting to trigger the resize event automatically.
An iframe allows a page to embed another page. Its width determines its displayed width, while its onload attribute can execute JavaScript when the iframe finishes loading.
By changing the iframe's width after loading, the exploit attempts to cause the resize needed to trigger the onresize handler inside the embedded page.
I scrolled down to the Body field and entered the following payload:
<iframe width="50px" onload="this.style.width='100px'" src="URL/?search=<body onresize=print()>"></iframe><iframe width="50px" onload="this.style.width='100px'" src="URL/?search=<body onresize=print()>"></iframe>After entering the payload, I clicked View exploit to test it.
Here is what the main parts of the payload are intended to do:
iframeembeds another page inside the exploit page.width="50px"sets the iframe's initial width to 50 pixels.onload="this.style.width='100px'"changes the iframe's width to 100 pixels when it loads.srcspecifies the address of the page loaded inside the iframe, including the search input used for the XSS test.onresize=print()is intended to call the browser'sprint()function when the relevant resize event occurs. The key idea is to cause the resize through the exploit page itself instead of relying on the victim to change the browser's zoom level manually.
Testing the Exploit
After clicking View exploit, the payload executed and the print dialog appeared.
This demonstrated that the print() function was triggered during the test. The next step was to deliver the exploit to the lab's simulated victim and allow the lab to verify the result.
Delivering the Exploit to the Victim
I returned to the exploit server and clicked Deliver exploit to victim.
This action sends the prepared exploit to the simulated victim in the lab environment.
The goal was for the exploit to trigger the required behavior without requiring the victim to manually change the browser's zoom level or perform another interaction.
Lab Successfully Solved
After delivering the exploit, the lab displayed the orange success banner, confirming that the challenge had been solved.
The lab demonstrated how to investigate a WAF's filtering behavior, identify HTML tags and attributes that are not rejected, and test whether an event handler can be used to execute JavaScript.
It also demonstrated the importance of distinguishing between an event that works after manual interaction and one that can be triggered automatically in the context of the exploit.
What This Lab Is About
This lab demonstrates a reflected XSS vulnerability in a website that uses a WAF to block most common HTML tags and attributes.
The main challenge is to identify which input values pass through the filtering rules and determine whether they can be combined to execute JavaScript.
Burp Intruder makes the investigation more efficient by testing multiple candidate tags and attributes automatically. The PortSwigger XSS Cheat Sheet provides a useful list of candidates for this process.
Finally, the exploit server allows the behavior to be tested in a controlled lab environment. The onresize event illustrates why the conditions required to trigger an event matter: triggering JavaScript manually in your own browser is different from building an exploit that works without requiring the victim to interact with the page.
What I Learned
- WAF behavior: A WAF can block common XSS input by filtering HTML tags and attributes.
- Automated testing: Burp Intruder can test multiple candidate tags and attributes instead of requiring manual testing.
- HTTP response analysis: HTTP status codes help compare how the application handles different inputs, although a
200response alone does not prove exploitability. - HTML tags versus attributes: An accepted HTML tag does not necessarily mean that every attribute associated with it will also be accepted.
- Event handlers: The
onresizeevent can execute JavaScript when the relevant window or viewport is resized. - Automatic execution: A successful exploit may need to trigger the relevant event without depending on manual interaction by the victim.
- Exploit testing: Testing the behavior on the exploit server helps establish whether the intended event and JavaScript execution occur before delivering the exploit to the simulated victim.