从本文开始,打算写一个系列文章。其主要目的是从xss-labs靶场题解开始来介绍一下XSS攻击。
系列文章结构:
1、xss-labs靶场(一):level1-level5
2、xss-labs靶场(二):level6-level10
3、xss-labs靶场(三):level11-level15
4、xss-labs靶场(四):level16-level20
5、XSS攻击原理剖析
6、一些关于XSS攻击的总结
文章所用的xss-labs靶场的项目地址:https://github.com/do0dl3/xss-labs。
在这里关于如何搭建靶场的就不再赘述了,我这里是在本地用phpstudy来搭建的。
打开该靶场首页显示如下:
Level 1
按照首页提示点击图片来到level1,页面显示如下:
我们仔细观察一下url地址的构造就可以发现这里是向服务器提交了一个值为”test”的name参数。并且从页面回显可以看到不仅将name参数的值显示在了页面当中,还显示了name参数值的字符长度。接着我们看看该页面的网页源码是如何的
从网页源码来看这里是将name参数的值直接插入到了<h2></h2>标签之中。那么这样看来这一关主要就是考察反射型XSS咯。但是由于不知道服务器端对于提交的敏感字符有没有过滤,所以这里直接在name参数中赋值一个简单的弹窗来进行测试。操作如下:
http://www.bj.com/xss/level1.php?name=<script>alert('xss')</script>
可以看到我们提交的用于弹窗的js代码顺利执行了。那么这段代码在网页源码中是如何显示的呐?
可以看到服务器是将我们提交的恶意代码原封不动的返回了,因此浏览器才能成功的弹窗。最后看看服务器端的level1.php到底是如何对参数进行操作的
通过源码不难看出,红色箭头1处是服务器将通过get方式传递过来的name参数的值赋给了str变量,然后在箭头2处又将str变量直接插入在了<h2></h2>标签之中。因此服务器并没有对name参数的值进行严格处理,并且这个参数的值又是用户可控的,所以此处存在了反射型的XSS漏洞。
这里值得一提的是,我们通过将level1.php的源码和在浏览器中查看到的该页面网页源代码进行对比可以总结出以下事实:
1、php的代码仅仅在服务器端解析执行。
2、服务器将执行完成的最终网页代码(不包含源文件中属于php语言的部分)返回给浏览器,然后浏览器对网页代码进行解释显示。
3、在浏览器端查看源代码是无法看到对参数进行具体操作的php代码,只能看到结果。
Level 2
在第一关成功弹窗之后,点击“确定”按钮页面就会自动跳转到下一关的页面,显示如下:
从url地址来看,依然是get方式传递参数,所以猜测考察的还是反射型XSS。只不过这一关将参数名换成了keyword。页面中央出现了一个输入框和一个“搜索”按钮。先看看网页源代码
从源码来看该页面大致的功能就是通过点击“搜索”按钮可以将填入输入框中的内容以get方式提交给服务器上的level2.php。经过服务器的动态处理之后又会将参数keyword的值插入到<h2></h2>标签之中以及添加到<input>标签中的value属性的值内。
想通了该页的大致功能之后就可以构造恶意语句进行测试了,这里还是沿用上一关中的弹窗代码,操作如下:
但是想象中的弹窗并没有出现,恶意代码直接被打印到了页面之上。这是怎么回事呐?看看此时的网页源码
可以看到在<h2></h2>标签之中的恶意代码被编码了。其中<、>都被编码成了html字符实体。猜测在服务器端用htmlspecialchars()函数对keyword参数的值进行了处理。接着往下看可以看到插入到value参数值中的恶意代码并没有被编码而是直接原样返回的。但是问题是这里的js代码在标签属性值中,浏览器是无法执行的。
既然上面的恶意代码被编码了,那么只能从属性值中的恶意代码处进行突破了。要想浏览器执行这里的弹窗代码,只需要将属性的引号和标签先闭合就可以了。操作如下:
http://www.bj.com/xss/level2.php?keyword="><script>alert('xss')</script> //
可以看到浏览器成功弹窗了,说明我们提交的恶意代码被浏览器执行了。来看看此时的网页源码
从图中可以看出,在<h2></h2>标签中的恶意代码依然被编码了。但是在下方<input>标签中我们提交的代码成功的对引号和标签进行了闭合,这样浏览器就能成功执行js代码了。
<input name=keyword value="" > <script>alert('xss')</script> //">
在我们构造的恶意代码中最后的//是为了将后面的">注释掉。
完成弹窗之后看看服务器端文件的源码
从上图源码可知,在箭头1处将get方式传递到服务器端的keyword参数的值赋给str变量。在箭头2处是用htmlspecialchars()函数对变量str进行处理之后显示到网页上。但是在箭头3处却是直接将变量值插入到了<input>标签的value属性值中,因为这里并没有对敏感字符进行编码和过滤,所以可以通过构造实现XSS攻击。
Level 3
跳转到第三关的页面显示如下
先输入字符串test进行尝试
从页面响应可以看到通过keyword参数传递到服务器端的值在页面中有两个显示的地方。来看看网页源代码
看起来跟第二关时没什么区别,所以还无法确定在服务器端是否对敏感字符进行了过滤、编码等操作。接着构造弹窗代码进行测试
http://www.bj.com/xss/level3.php?keyword=<script>alert('xss')</script>&submit=搜索
但是浏览器并没有成功弹窗,看看网页源码是怎么回事
原来这两处都将<、>这样的敏感字符编码成了html字符实体。猜测服务器端在这两处都用htmlspecialchars()函数进行了处理。既然关键的<、>字符被编码了,怎样才能成功执行弹窗的代码呐?
这里可以通过<input>标签的一些特殊事件来执行js代码,所以可以构造如下:
http://www.bj.com/xss/level3.php?keyword='onfocus=javascript:alert('xss') > //&submit=搜索
结果发现并没有直接弹窗,这是因为onfocus事件的特殊性造成的。
onfocus是javascript中在对象获得焦点时发生的事件,最简单的实例就是网页上的一个输入框,当使用鼠标点击该输入框时输入框被选中可以输入内容的时候就是该输入框获得焦点的时候,此时输入框就会触发onfocus事件.因此点击当前页面的输入框就可以完成弹框了。
看看服务器端文件源码
可以看到服务器端在页面显示的位置对参数值都用htmlspecialchars()函数进行处理了。
Level 4
跳转到第四关的页面显示如下
可以看到这一关还是采用get方式传递参数,并且该参数的值在页面中有两处显示位。
首先依然用弹窗代码测试服务器端对用户输入做了哪些操作
可以看到浏览器并没有成功弹窗,第一个显示位直接将参数值显示出来了,但是在输入框中显示却跟我们提交的参数值有点出入。看看网页源码
从上图不难看出在箭头1处将参数值中的<、>字符进行编码显示了,在箭头2处直接将<、>字符给删除了。
用上一关的事件触发弹窗代码测试看看,因为这里只是删除了<、>符号,事件触发却不需要使用这两个符号。
http://www.bj.com/xss/level4.php?keyword="onfocus=javascript:alert('xss') "
响应的页面依然是不会自动弹窗,在点击输入框之后成功触发了事件进行弹窗。
我们看看源文件的代码如何
原来如此,在服务器端先是将传递过来的keyword参数的值赋给str变量,然后经过箭头1和箭头2处的处理将变量值中包含的<、>符号删除。最后在箭头3处对变量值进行编码处理之后显示在页面之上,在箭头4处将去除特殊符号后的变量值插入到<input>标签的value属性值中。
Level 5
跳转到第五关的页面显示如下
同之前一样,这一关看起来还是反射型的XSS。但是由于不知道服务器端到底做了什么样的操作,所以用弹窗代码测试
跟想象中的一样并没有成功弹窗,接着查看网页源码看看服务器端对我们提交的代码做了什么处理
从图中看到在第一处显示位应该还是被htmlspecialchars()函数处理过才返回到浏览器显示的。而在第二处显示位可以看到在第一个<script>字符中插入了一个_符号。
这里猜测在服务器端是对提交的参数值进行了正则匹配,然后在出现的第一个<script>字符串中插入一个_符号导致代码无法成功执行。
从这么来看的话,之前通过事件触发的代码应该依然可以奏效,测试一下看看
http://www.bj.com/xss/level5.php?keyword="onfocus=javascript:alert('xss') "
但是这一次并没有成功触发弹窗。看看网页源码
果然没有那么简单,这里居然对onfocus这一类的事件字符也进行了防范。那么这样的话还能怎么执行js代码进行弹框呐?
从刚才的响应来看提交的javascript字符并没有被过滤或者转义等,所以此处既然无法通过<script>标签或触发事件来执行js代码的话,那么可以换一个标签来执行js代码。我们可以构造如下
http://www.bj.com/xss/level5.php?keyword="> <a href=javascript:alert('xss') > xss</a> //
从此时的页面响应可以看到出现了一个xss字样的链接,点击该链接即可触发执行弹窗的js代码,如下
最后看看源文件的代码
从源码来看,服务器端先是将传递过来的参数值转换为全小写之后赋值给变量str,接着就是通过str_replace()函数来破坏变量值中的敏感字符的语义。最后在箭头4处通过htmlspecialchars()函数处理之后显示到网页上,在箭头5处直接将进行敏感字符处理之后的变量值插入到<input>标签的value属性值中。