1.XSS原理
XSS(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本 攻击缩写为XSS,他的中文名称为:跨站脚本攻击。而XSS的重点不在于跨站,而在于XSS中执行的JavaScript脚本情况。
Web程序代码中把用户提交的参数未做过滤就直接输出到页面,参数中的特殊字符被恶意进行闭合造成原有的html页面被嵌入恶意的脚本内容,黑客可以利用该漏洞执行恶意JavaScript代码,当用户浏览该页之时,嵌入其中Web里面的JavaScript代码会 被执行,从而达到恶意攻击用户的目的。
2.XSS分类
XSS常被分为三种类型:
反射型XSS:直接将HTTP请求中的用户可控数据输出到HTML页面中的跨站脚本注入,由于用户可控数据没有被存储,因此只能在单次请求中生效。
存储型XSS:又叫特久型XSS,直接将HTTP请求中的用户可控数据存储至数据库中,再从数据库中读取出来输出到HTML页面上,由于数据经过存储,可以持续被读取,攻击影响面和危害都较高。
DOM-XSS:特殊的反射型XSS,将用户可控数据通过JavaScript和DOM技术输出到HTML中,利用方式通常与反射型XSS类似。
2.1 反射型XSS
反射型XSS对于访问者来说是一次性的,当攻击者将构造好的恶意URL发送给访问者,访问者点击URL将我们的请求传递给服务器,服务器将不加处理的脚本“反射”回访问者的浏览器而使访问者的浏览器执行相应的脚本,最简单的代码如下。
反射型XSS执行方式:浏览器——>后端——>浏览器
示例代码:
<?php highlight_file(__FILE__); echo $_REQUEST['xss'] ; ?>
我们通过参数xss
传入我们自定义的XSS脚本,脚本被前端页面解析并执行
在页面右击查看源码处我们可以发现我们输入的JavaScript脚本。
2.2 存储型XSS
存储型XSS与反射型XSS的区别在于,存储型XSS是被存储在数据库中的,攻击者只需将JavaScript脚本输入,后端将攻击者输入的JavaScript脚本保存到数据库中,访问者在每次访问存在XSS页面时存储在数据库中的JavaScript脚本就会被执行。
存储型XSS执行方式:浏览器——>后端——>数据库——>后端——>浏览器
在pikachu
存储型XSS靶场中存在如下代码:
xss_stored.php
这里代码24-26行,通过POST接收参数后,将数据插入到数据库中。
这里代码80-83行,通过select
查询数据库中刚才插入的数据并通过echo
输出content
字段内容,在这个过程中将我们插入的数据保存到数据库中并且进行任何过滤所以造成存储型XSS。
这里我们输入JavaScript代码,成功执行并弹窗。
2.3 DOM型XSS
DOM型XSS与反射型XSS差不多,只不过DOM型XSS中会调动一些JavaScript中的函数来获取客户端提交的内容,定义了一个节点名字和值,然后再返给客户端。
DOM型XSS执行方式:URL-->浏览器
在pikachu
DOM型XSS靶场中存在如下代码:
xss_dom.php
在这里接收一个GET传入的值
在代码56-58行,表单中调用domxss()
函数,在代码49-50行,获取text
内容赋值给变量str
并拼接到a href标签中通过innerHTML将内容输入到页面。
通过闭合'>将我们想要执行的JavaScript代码分离出来
3.XSS审计函数
在XSS审计中我们其实多关注一些输出打印函数即可,它们的作用就是将输入的内容显示在前端页面,只要这些函数中内容可控且输出前没有进行完善的过滤就会造成XSS漏洞。
print print_r echo printf sprintf die var_dump var_export
4.XSS审计注意点
4.1 htmlspecialchars函数
在htmlspecialchars()
函数中,默认情况下不会对'
进行转义,如果是代码中像下面这种情况,传入的text内容,在下面的input表单中输出且是被单引号包裹,我们就可以闭合单引号,在标签内构造XSS来绕过htmlspecialchars()
函数。
<?php highlight_file(__FILE__); $content = htmlspecialchars($_GET['text']); ?> <input type='text' class='find' value='<?=$content?>' >
我们可以在标签内闭合单引号,然后通过onclick在标签内构造一个点击事件来触发XSS。
4.2 编解码中存在的问题
很多时候开发在写代码时存在一些逻辑缺陷,导致过滤函数无法发挥实际的效果,如下代码中首先通过text传入参数,然后紧接着对传入的参数进行htmlspecialchars()
实体化编码,这里似乎没有问题。但浏览器的运行机制会默认对传入的参数进行一次url解码,由于代码中最后一段又进行了一次url解码并使用echo
输出。那么我们就可以对传入的内容进行两次url编码来绕过htmlspecialchars()
函数的过滤,从而造成xss。
<?php highlight_file(__FILE__); $content=$_REQUEST['text']; //接收参数并进行url解码 $cont=htmlspecialchars($content); //对传入的特殊字符进行实体化编码 echo urldecode($cont); //最后,url解码输出 ?>
我们对<script>alert('xss')</script>中特殊字符进行两次URL编码%253Cscript%253Ealert('xss')%253C/script%253E
,由于浏览器会首先对我们传入的数据进行一次URL解码,变成%3Cscript%3Ealert('xss')%3C/script%3E
,在传入htmlspecialchars()
函数时就会绕过该函数的过滤,在最后echo
时又进行一次URL解码,最终还原我们的JavaScript代码<script>alert('xss')</script>
。
5.XSS代码审计总结
在黑盒测试中XSS遵循一个见框就插的原则,而在PHP代码审计中我们主要关注上述的一些输出打印函数和代码中的一些编写逻辑来绕过某些过滤函数从而构造XSS payload进行利用。 pikachu靶场中XSS模块还有很多XSS关卡,大家可以在靶场练习的同时结合代码审计来了解靶场其中的过滤规则,这样可以更深入的了解XSS漏洞的触发原理。 在写作过程中难免存在纰漏,师傅们在阅读文章时如有发现错误请留言指正,谢谢!