July 10, 2026
How I Found a Stored XSS Vulnerability While Testing for Self-XSS π₯
Most people say finding an XSS vulnerability is difficult. Blind XSS is something you can check in many input fields, but finding a workingβ¦
By Chillakuru Sudheer
5 min read
Most people say finding an XSS vulnerability is difficult. Blind XSS is something you can check in many input fields, but finding a working Stored XSS vulnerability is different.
In my case, I was not expecting to find Stored XSS at all.
I first found a Self-XSS issue. Later, while testing another feature of the application, I discovered that user-controlled HTML and JavaScript could be stored by the server and executed again.
That was when I confirmed a Stored XSS vulnerability. π¨
Severity: P2 Vulnerability Type: Stored XSS / Self-XSS
It Started With Self-XSS π§ͺ
At first, I found a Self-XSS issue where the website rendered HTML from an uploaded PDF filename.
For example, I wanted to upload a PDF with a filename like:
<b>MARKER</b>.pdf
However, Windows does not normally allow characters such as <, >, and / in filenames.
So, I used the browser console to modify the upload request and test how the application handled the filename.
Wait⦠is it possible to modify file uploads using the browser console?
Absolutely! π
The browser's developer tools are extremely useful for security testing. They allow us to inspect requests, modify application behavior, understand how the frontend works, and test whether security controls exist only on the client side.
During my testing, I noticed that the application rendered the HTML inside the filename instead of displaying it as plain text.
That was my first interesting finding.
But the real vulnerability came later.
Finding the Stored XSS π
The website allows users to turn prompts into ready-to-use articles and publish them. It can also be used to create quizzes, blogs, and other types of content.
I created and published an article for testing.
While editing the article, I remembered that HTML had been rendered earlier when I tested the PDF filename.
So, I decided to test the article editor as well.
I usually use Obsidian as my note-taking application. I copied some content from Obsidian and pasted it into the website's article editor.
That was when I noticed something interesting.
The HTML code was being rendered by the application instead of being displayed as plain text. π
At this point, I became curious.
If HTML was being rendered, could JavaScript execution also be possible?
So, I tested with a simple XSS proof-of-concept payload:
<img src="nonexistent-618427" onerror="alert('STORED_XSS')">
The payload uses an image with an invalid source. Because the image cannot be loaded, the browser triggers the onerror event.
If the application properly sanitized user input, the payload should have been displayed as plain text or dangerous HTML attributes should have been removed.
But that did not happen.
When I pasted the content, the application tried to render it as an image.
That was the first sign that something was wrong.
Then I saved the article.
A popup appeared with the message:
STORED_XSS
At this point, JavaScript execution was confirmed.
But I still needed to verify whether the payload was actually stored on the server.
Confirming Stored XSS π¨
To confirm the vulnerability, I opened the same website in another browser.
This time, I used Brave.
I loaded the affected article again.
The popup appeared.
That confirmed the vulnerability was not limited to my original browser session.
The malicious payload had been saved as part of the article content and was executed again when the stored content was loaded.
Stored XSS confirmed. β
Before using the simple proof-of-concept payload, I had already performed additional testing through the browser console.
However, a good vulnerability report should have clear and simple reproduction steps.
The easier it is for the security team to reproduce the issue, the easier it is for them to understand, verify, and fix the vulnerability.
So, I used the simple payload above to create a cleaner Proof of Concept (PoC).
Why Is This Stored XSS? π€
There are different types of Cross-Site Scripting vulnerabilities.
In Self-XSS, the attacker usually executes JavaScript only in their own browser or requires the victim to perform unusual actions, such as manually pasting code into the browser console.
Stored XSS is more serious.
In this case, the malicious input is stored by the application and later sent back to users.
The flow was simple:
Attacker-controlled input β Server stores the content β Another user loads the content β JavaScript executes
Because the payload was saved inside the article and executed again in another browser, the issue could be classified as Stored XSS.
Stored XSS vs DOM XSS π€
Stored XSS happens when a malicious payload is saved by the application and later executes when someone loads the affected content.
DOM XSS happens when client-side JavaScript handles user-controlled data in an unsafe way and modifies the page, causing the payload to execute.
In my case, the payload was saved inside the article and executed again when I opened it in another browser. Therefore, I confirmed it as Stored XSS. π¨
Potential Impact π₯
The impact of Stored XSS depends heavily on where the vulnerable content is displayed, who can view it, and what permissions those users have.
If an attacker can store malicious JavaScript inside an article and other users can view that article, the vulnerability could potentially be used to:
- Perform actions using the victim's authenticated session.
- Modify content displayed on the page.
- Redirect users to malicious or phishing websites.
- Display fake login forms or other misleading content.
- Access sensitive information available to JavaScript in the affected page.
- Perform actions with the permissions of a victim, depending on the application's security controls.
- Target privileged users, such as moderators or administrators, if they view the affected content.
The real severity should be based on the exact attack path and demonstrated impact.
Simply showing an alert() popup proves JavaScript execution, but a strong security report should also explain what an attacker could realistically achieve through the vulnerable feature.
Prevention Methods π‘οΈ
There are several ways developers can prevent Stored XSS vulnerabilities.
The application should sanitize untrusted HTML before storing or rendering it. Dangerous elements, event handlers, and attributes should be removed.
User-controlled content should also be safely encoded based on where it is inserted into the page.
If the application needs to support rich-text content, it should use a well-tested HTML sanitizer with a strict allowlist of permitted tags and attributes.
Developers should not rely only on frontend validation because attackers can modify requests and send data directly to the server.
A strong Content Security Policy (CSP) can also reduce the impact of some XSS attacks, although CSP should be used as an additional security layer rather than as a replacement for proper input handling and output encoding.
Most importantly, the application should treat all user-controlled data as untrusted.
What I Learned From This Vulnerability π§
One of the biggest lessons I learned from this finding is that vulnerability hunting does not always go according to plan.
I started by testing a PDF filename.
Then I noticed HTML rendering.
Later, I tested an article editor.
That eventually led me to Stored XSS.
The vulnerability was found because I paid attention to unexpected application behavior and continued testing.
Another important lesson was the value of creating a simple PoC.
Complex testing may help you discover a vulnerability, but the final report should make the issue as easy as possible to reproduce.
A clear PoC saves time for both the researcher and the security team.
Conclusion π―
Finding this Stored XSS vulnerability was an interesting learning experience.
What started as Self-XSS testing eventually led to a Stored XSS vulnerability.
The main lesson is simple:
Pay attention when an application behaves differently from what you expect.
A small HTML rendering issue may lead to something more serious.
Test different input fields. Understand how the application processes and stores data. Verify whether the behavior affects other users or browser sessions. And once you find something, create the simplest possible PoC.
Sometimes, you start by testing one feature and end up finding a completely different vulnerability.
That is what makes web security testing interesting. π₯
Keep learning. Keep testing. Stay curious. π‘οΈ