Abusing noscript



  • Someone found a <script> inside of a <noscript> tag.

    <!--​ Page is at: http://squarespace.com -->
    ...
    ...
    <noscript id="inline-deps">
      <link rel="stylesheet" type="text/css" href="//cloud.typography.com/7811972/758964/css/fonts.css" />
    
      <script type="text/javascript" src="https://static.squarespace.com/static/ta/5134cbefe4b0c6fb04df8065/7400/assets/logomark/logomark.min.js?37"></script>
      <link rel="stylesheet" href="https://static.squarespace.com/static/ta/5134cbefe4b0c6fb04df8065/7400/assets/logomark/logomark.min.css?37" type="text/css" />
    </noscript>
    ...
    ...
    

    So now you're probably thinking: "WTF! That script tag will never execute. Nice find Zacy, you're the best. Also we don't mind that you keep copying WTFs from StackExchange sites." But it gets worse.

    Here's the top answer: (TL;DR comment below it.)

    I did some searching through their code and found this snippet:

    var DepLoader = function() {
    function init() {
    var a = document.getElementById("inline-deps");
    if (!a || JS.hasClass(document.body, "deps--loaded"))
    webfontsReady();
    else {
    var b = a.innerText || a.textContent;
    JS.addClass(document.body, "deps--loaded"), processRaw(b)
    }
    }
    function isListed(a, b) {
    for (var c = !1, d = 0; d < b.length; d++)
    if (-1 !== a.indexOf(b[d])) {
    c = !0;
    break
    }
    return c
    }
    function webfontsReady() {
    JS.fireCustom("webfontsReady")
    }
    function processRaw(html) {
    var el = document.createElement("div");
    el.innerHTML = html;
    for (var scripts = el.querySelectorAll("script"), styles = el.querySelectorAll("link"), common, signup, dialog, systemPage, commerce, others = [], inline = [], styleWhiteList = ["site.css", "dialog-", "signup-", "logomark"], scriptBlackList = ["management-", "ckeditor-"], style, l = 0; l < styles.length; l++)
    style = styles[l], -1 !== style.href.indexOf("fonts.css") && load(style, webfontsReady), isListed(style.href, styleWhiteList) && load(style);
    for (var script, src, s = 0; s < scripts.length; s++)
    script = scripts[s], src = script.src, src || "dynamic-assets-loader" == script.getAttribute("data-sqs-type") || -1 === script.innerHTML.indexOf("SQUARESPACE_ROLLUPS") || eval(script.innerHTML);
    if (window.SQUARESPACE_ROLLUPS)
    for (var key in SQUARESPACE_ROLLUPS) {
    var rollup = SQUARESPACE_ROLLUPS[key], js = rollup.js, css = rollup.css;
    -1 !== key.indexOf("common") ? common = js : -1 !== key.indexOf("commerce") ? commerce = js : -1 !== key.indexOf("signup") ? signup = js : -1 !== key.indexOf("dialog") ? dialog = js : -1 !== key.indexOf("system-page") ? systemPage = js : key ? others = others.concat(js) : inline = inline.concat(js)
    }
    for (var s = 0; s < scripts.length; s++)
    script = scripts[s], src = script.src, isListed(src, scriptBlackList) || (-1 !== src.indexOf("common-") ? common = script : -1 !== src.indexOf("commerce-") ? commerce = script : -1 !== src.indexOf("signup-") ? signup = script : -1 !== src.indexOf("dialog-") ? dialog = script : -1 !== src.indexOf("system-page-") ? systemPage = script : src ? others.push(script) : inline.push(script));
    var loadOthers = function() {
    for (var a = 0; a < inline.length; a++)
    "dynamic-assets-loader" != inline[a].getAttribute("data-sqs-type") && load(inline[a]);
    for (var b = 0; b < others.length; b++)
    load(others[b]);
    JS.fireCustom("dependenciesLoaded")
    }, loadSystemPage = load.bind(this, systemPage, loadOthers, "system page"), loadSignup = load.bind(this, signup, loadSystemPage, "signup"), loadCommerce = load.bind(this, commerce, loadSignup, "commerce"), loadDialog = load.bind(this, dialog, loadCommerce, "dialog"), loadCommon = load.bind(this, common, loadDialog, "common");
    loadCommon()
    }
    function load(tag, callback, label) {
    var head = document.head;
    if (Array.isArray(tag) && (tag = {nodeName: "SCRIPT",src: tag[0]}), !tag)
    return void (callback && callback());
    if (tag && (tag.src || tag.href)) {
    var child;
    "SCRIPT" === tag.nodeName ? (child = document.createElement("script"), child.src = tag.src, -1 !== child.src.indexOf("combo") && (callback = function() {
    Y.Squarespace.FrontSite.Core.domReady(!0)
    })) : "LINK" === tag.nodeName && "stylesheet" === tag.rel && (child = document.createElement("link"), child.href = tag.href, child.rel = "stylesheet", child.tyle = "text/css"), child && (child.onload = callback, head.appendChild(child))
    } else
    try {
    eval(tag.innerHTML)
    } catch (e) {
    }
    }
    return {init: init,webfontsReady: webfontsReady}
    }()

    As you can see, the &lt;noscript&gt; tag has the ID #inline-deps, which is referenced in the code (line 3) to load dependencies asynchronously and on-demand.
    >
    > They probably use a &lt;noscript&gt; element as it 
    allows them to directly access DOM elements, instead of having to place 
    it in a string or a comment (which I consider particularly bad, as 
    comments are not meant for actual information) and then parse it. It 
    also prevents the execution of scripts and CSS styles until specifically
     loaded.
    >
    I personally find this an abuse of the &lt;noscript&gt; 
    tag.  I'm not even sure if it's valid HTML5 code. Using other methods 
    such as declaring dependencies in a JavaScript object with a script 
    loader should be used where practicable.
    
    TL;DR comment:
    > the whole thing is a bit complicated but 
    basically it checks that it needs to load scripts and/or stylesheets, 
    then gets them from the &lt;noscript&gt; element to be executed.
    
    …Wow. That processRaw function…
    I'm going to list the WTFs I spotted, but first the obligatory javascript "expert" who has just added this to his bag of job security tricks.
    > That's a pretty nifty trick using `<noscript>` to store DOM elements, I must say. And a +1 and an accepted answer to you for diving into their minified JS before me and figuring it out! – Agent.Logic_
    
    Agent.Logic_ is the guy who asked the question.
    
    Also, cartman82:
    @cartman82 <a href="/t/via-quote/2640/6">said</a>:<blockquote>Nice find. Not sure it's a WTF, though. I'm not entirely clear on why do they have to load everything dynamically, but if they do, this seems like a clever way to do it.</blockquote>
    
    And now the WTFs:
    
     - Creating a long list of variables in the for statement.
     - Not using most of those variables in that for loop. (They're used later in the function.)
     - Abusing the `,` and `&&` operators instead of just using braces. (I didn't know javascript could do that too.)
     - Abusing the [conditional operator](http://thedailywtf.com/Articles/One-Bad-Ternary-Operator-Deserves-Another.aspx).
    
    Edit: Dammit! Posted it early by accident.


  • Nice. Wait, that makes no sense. I though this XSS hole was already fixed. Or maybe it only works for <noscript>? That would be TRWTF...


  • @VinDuv said:

    <img src="/uploads/default/5967/07da38cb653ffe6e.png" width="197" height="97">
    Nice.
    Wait, that makes no sense. I though this XSS hole was already fixed. Or maybe it only works for <noscript>? That would be TRWTF...

    Oh cool. Let's see what I can do. Any suggestions?





  • @Zacrath said:

    Oh cool. Let's see what I can do. Any suggestions?

    http://what.thedailywtf.com/t/how-about-this-sidebar-audio-src-http-soundjax-com-reddo-64951-5ecddyhorn-mp3-controls-autoplay-audio/889

    That’s what caused the XSS to be fixed the first time...



  • Nice find. Not sure it's a WTF, though. I'm not entirely clear on why do they have to load everything dynamically, but if they do, this seems like a clever way to do it.



  • @cartman82 said:

    Nice find. Not sure it's a WTF, though. I'm not entirely clear on why do they have to load everything dynamically, but if they do, this seems like a clever way to do it.

    Maybe, maybe not. However, this comment:

    Those devious code hackers ;) . I hope they documented it well, before some smart guy deleted it 'since it has no use'. – Patrick Hofman

    Points out a real problem with it.
    I think a better way to handle it would be to put it in a javascript variable.

    Beisdes, the processRaw function was what I was focusing on.



  • @Zacrath said:

    Beisdes, the processRaw function was what I was focusing on.

    Ah. That's what I call "hipsterscript". A version of javascript written by hipster who'd rather work in ruby or functional languages. Ignore it.



  • Changing the image to this:


  • 🚽 Regular

    Wow. Just... wow.

    Edit: added an HTML comment to the title, but it didn't go through.



  • I think that bubble should probably have the word sexy in it somewhere.

    someone make it happen.



  • @algorythmics said:

    I think that bubble should probably have the word sexy in it somewhere.

    someone make it happen.

    What should I change the text to? "I agree with whatever the sexy morbs just said"?



  • yes, that appears to be appropriate.

    make it so number one!


  • Winner of the 2016 Presidential Election

    Abusing my Leader-Powers to protect Frontpage (seriously guys, this has been out for so long. Stop hogging onto that XSS ;D)

    Filed Under: Leave the Frontpagers alone! They live their lives without Discourse and thats good for them!



  • I wonder if blink and marquee would work, too!



  • Here's the result:


  • Winner of the 2016 Presidential Election

    yeah, The frontpage is not being sanitized at all.. Discourse Team couldnt fix it because they dont have access to TDWTF-files

    Filed Under: This one falls on Alex



  • Aaawwwww.

    No one got to see my masterpiece.
    Well. It was fun while it lasted.



  • works for me still



  • Hmm, they don't!



  • the marquee worked but now its gone and replaced with & l t ; again (i dont know what escaping prevents that becoming the character) so it will probably break on the next cache...

    shame!



  • Oh right, caching.
    I forgot about that.


  • Winner of the 2016 Presidential Election

    Seems like Discourse does something similar:

    https://meta.discourse.org/t/how-to-add-advertisements-to-your-discourse-forum/11105/3


    @algorythmics said:

    but now its gone and replaced with & l t ; again

    I know I shouldn't say that but I didn't lose my ability to edit titles and mobile (the only thing I had available) didn't update the title before I clicked on the pencil. Hence it's back.

    Just... please don't abuse that XSS-vunerability. I don't really know who could fix it besides @apapadimoulis and while it starts small it can get really annoying really fast
    (meaning: breaking layout -> inserting content that should not be on a frontpage-site, etc). And yeah, just because YOU (the reader of this post) wouldn't do it, doesn't mean somebody else who is getting the idea won't do it.

    Filed Under: Thats how the world works | Also mentioning @aliceif so the title-thing gets sorted out :D


  • Discourse touched me in a no-no place

    @Kuro said:

    please don't abuse that XSS-vunerability.

    Are you new here?
    I'm surprised that the front page isn't spinning around.



  • Last time it was audio playing on the front page courtesy of Discourse.


  • Discourse touched me in a no-no place

    Yes, I remember that little gem.



  • @loopback0 said:

    Are you new here?
    I'm surprised that the front page isn't spinning around.

    Well, we already SEXYfied the main page. There's not much more we can do before it counts as malicious.

    Edit: Also, I love that there are only 4 on topic posts here and no one cares.



  • I've just been awarded the <kbd>XSS Award</kdb> badge for this topic, and I want to point out that VinDuv should also get this badge. But I'm not sure who to contact.



  • @PJH is the person to ask about such things. Or flag the message in question for moderators.


  • 🚽 Regular

    @PJH (@dhromed is MIA)



  • Ok, @PJH, VinDuv deserves the <kbd>XSS Award</kbd> badge as well.


  • Discourse touched me in a no-no place

    Sorted.



  • @algorythmics said:

    the marquee worked but now its gone and replaced with & l t ; again (i dont know what escaping prevents that becoming the character) so it will probably break on the next cache...

    shame!

    &amp;

    So &lt;


Log in to reply