XSS via quote injection in renderEmail() (email autolink)

Open Beginner friendly
#192 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
php
Domain
security

Research direction

Start in inline/LinkTrait.php at renderEmail() and trace how parseLt() sends email autolinks there. Reproduce the quoted local-part input from the issue, then add coverage for the generated HTML. Done means quotes in an autolink cannot become HTML attribute syntax or an event handler, while ordinary email autolinks still render correctly.

Written by the indexing model from the issue text.

Description

renderEmail() in inline/LinkTrait.php (lines 194-198, unchanged since 2014-10-10) escapes the parsed email address with htmlspecialchars(..., ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8'), which does not escape double-quote characters, and then interpolates it into a double-quoted HTML href attribute:

protected function renderEmail($block)
{
    $email = htmlspecialchars($block[1], ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
    return "<a href=\"mailto:$email\">$email</a>";
}

The email autolink regex in parseLt() (/^<([^\s>]*?@[^\s]*?\.\w+?)>/) allows " in the local part. Input <"onmouseover=alert(1)//@x.y> is parsed as an email autolink and rendered as:

<p><a href="mailto:"onmouseover=alert(1)//@x.y">"onmouseover=alert(1)//@x.y</a></p>

Verified with html5lib (faithful HTML5 tokenizer): the browser parses this as <a href="mailto:" onmouseover='alert(1)//@x.y"'> — a live onmouseover event handler. On hover, alert(1) executes.

Why this is distinct from CVE-2018-1000874

That CVE (DISPUTED) was about fenced-code-block raw HTML passthrough. This bug is not raw HTML passthrough: parseLt() routes <...@x.y> to renderEmail() before the raw-HTML fallback (parseInlineHtml). The broken HTML is generated by the library from a markdown-syntax autolink, not passed through from user HTML. I acknowledge the maintainer's documented position that output should be filtered with HTML Purifier; this is offered as an escaping defect the library itself introduces.

Affected

  • Markdown, MarkdownExtra, GithubMarkdown (shared LinkTrait)
  • Versions: ≥1.1.2 through 1.2.1 and master (2b2461b)
  • No fix exists.

CVSS v3.1

5.8 Medium — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Constraint: the injected portion cannot contain whitespace (regex excludes \s), so the practical payload is interaction-required (onmouseover); no no-interaction payload found.

Suggested fix

protected function renderEmail($block)
{
    $email = htmlspecialchars($block[1], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    return "<a href=\"mailto:$email\">$email</a>";
}

Reproduction

<?php
spl_autoload_register(function ($c) {
    $f = '/path/to/cebe/markdown/' . str_replace('\\', '/', substr($c, 14)) . '.php';
    if (is_file($f)) require $f;
});
$p = new cebe\markdown\Markdown();
echo $p->parse('<"onmouseover=alert(1)//@x.y>'), PHP_EOL;

Output:

<p><a href="mailto:"onmouseover=alert(1)//@x.y">"onmouseover=alert(1)//@x.y</a></p>

Independent discovery during a security audit. Verified novel: OSV empty, GitHub Advisory DB empty, no prior issue/comment mentions renderEmail/mailto/ENT_NOQUOTES.

Dominant language
HTML
Stars
1k
Forks
137
PR merge metrics
No merged PRs in 30d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from cebe/markdown

All issues in cebe/markdown

Similar issues

More Security issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.