September 4, 2026
When a Static Page Wasn’t Really Static: Finding HTML Injection🔥
During a security assessment, I came across an endpoint that initially looked completely harmless:

By Mohammed Hasan Dakhve
4 min read
/favicon.icotest/favicon.icotestSince it appeared to be related to a static favicon resource, I initially didn't expect it to process user-controlled input.
I started with a simple HTML payload:
<h1>HACKED</h1><h1>HACKED</h1>The application returned an error instead of reflecting the payload.
So I started experimenting with different payload structures.
Some payloads continued to produce errors and were not reflected in the response.
The breakthrough came when I used:
"><h1>HASAN"><h1>HASANNotice the important detail: I intentionally did not close the <h1> tag.
The application accepted the input, reflected it into the HTML response, and the browser interpreted it as HTML.
That was enough to confirm HTML Injection on a static-looking page.
🎯 The Discovery
While testing the application, I noticed a parameter/path associated with:
/favicon.icotest/favicon.icotestAt first glance, this looked like a static resource.
Nothing particularly interesting.
But during testing, I decided to see how the endpoint handled unexpected input.
I started with a straightforward HTML payload:
<h1>HACKED</h1><h1>HACKED</h1>The request resulted in an error response.
HTTP/2 404 Not Found
The payload wasn't reflected in the response in a useful way.
POC — Initial Test
The first screenshot shows the initial attempt using:
<h1>HACKED</h1><h1>HACKED</h1>The server returned a 404 Not Found response instead of rendering the supplied markup.
Screenshot 1 — Initial HTML Injection Attempt
🕵️♂️ The Observation Game
Instead of stopping after the first failed payload, I started changing the structure of the input and observing how the server responded.
I tried several variations, including:
">
"><tested>">
"><tested>and:
"><h1>hack</h1>"><h1>hack</h1>But these attempts were not giving me the expected reflection.
Some resulted in errors, while others did not appear in the response in a way that indicated successful HTML injection.
💡This was the interesting part.
I needed to understand how the application was parsing my input before it reached the HTML response.
POC — Payload Variations
The following screenshots show the different payload attempts and the corresponding responses.
Screenshot 2 — Alternate Payload Test
Screenshot 3 — Additional Payload Test
At this point, the behavior suggested that the structure of the payload mattered.
So I changed my approach.
🧩 The Breakthrough
This was where things finally started to make sense.
Instead of trying to inject a complete HTML element, I changed the payload to:
"><h1>HASAN"><h1>HASANThere was one deliberate difference:
I did not close the <h1> tag.
That small change made a significant difference.
The application accepted the request and returned a successful response.
More importantly, the supplied value was now visible inside the HTML response.
POC — Successful Reflection🤯
The response showed the injected value being incorporated into the generated HTML.
Screenshot 4 — Payload Reflected in the HTML Response
The next test confirmed the behavior more clearly.
I sent:
"><h1>HASAN"><h1>HASANand searched for the injected value in the response.
The response contained multiple reflections of the supplied input.
Screenshot 5 — Multiple Reflections in Response
This was the point where the behavior could no longer be treated as simple input reflection.
The browser was actually interpreting the injected markup.
💥 Seeing the Injection in the Browser
The final verification was performed in the browser.
The injected value was rendered as part of the page rather than being displayed as plain text.
The browser interpreted the <h1> element and rendered the injected content accordingly.
Screenshot 6 — HTML Injection Confirmed in Browser
The result was clear:
HASANHASANwas rendered as HTML content, along with the surrounding page content.
This confirmed that attacker-controlled input could influence the HTML structure of the response.
⚙️ Why It Worked
There were three important observations behind the finding:
1️⃣ The Endpoint Processed User-Controlled Input
Although /favicon.icotest appeared to represent a static resource, the application was processing the supplied value and incorporating it into the generated response.
2️⃣ The Input Reached an HTML Context
The injected value was reflected inside the HTML response rather than being safely encoded as plain text.
3️⃣ Payload Structure Mattered
The initial complete HTML payload resulted in an error, while the modified payload:
"><h1>HASAN"><h1>HASANwas successfully reflected.
The important lesson here is that a failed payload does not necessarily mean the functionality is secure.
A tester needs to understand the application's parsing and reflection context and validate the behavior with controlled, non-destructive inputs.
🔒 Final Thoughts
This finding started with something that looked almost too simple to be interesting:
/favicon.icotest/favicon.icotestThe first payload failed.
The second failed.
Several variations failed.
But instead of assuming the endpoint was secure, I focused on how the application processed the input.
The breakthrough was a small change in payload structure:
"><h1>HASAN"><h1>HASANand, importantly, leaving the HTML element unclosed.
That was enough to demonstrate that attacker-controlled input could reach the HTML parsing context and be interpreted by the browser.
The biggest takeaway for me was simple:
Don't judge an endpoint by what it looks like. Follow the data.
A page may look static.
A favicon endpoint may look harmless.
But if attacker-controlled input reaches an HTML context without proper encoding, the browser will happily do what browsers do best: parse it.
🏷️ Tags:
#HTMLInjection #CyberSecurity #AppSec #WebSecurity #WebHacking #EthicalHacking #PenetrationTesting #BugBounty #SecurityResearch #InfoSec #WebApplicationSecurity #OWASP #ApplicationSecurity