伪协议与编码绕过
这篇博客可以接上我的上一篇关于XSS 的分类与利用方式,这次讲的伪协议与编码绕过也是一种代码注入的攻击方式,伪协议提供了另外一种代码注入点,编码绕过是对应的注入方式。
伪协议是什么
说起来伪协议的概念也比较简单,只不过情况种类比较多。
伪协议不同于因特网上所广泛使用的如http,https,ftp等协议,在url中使用,用于执行特定的功能。
如Data伪协议:data:text/html;base64,PHanflajfAFLGLKSJ=
,
JavaScript伪协议:javascript:alert(1);**
;
1 | <p> <a href="javascript:alert(5)">javascript伪协议</a></p> |
编码绕过
伪协议其实客观上又提供了额外的代码注入点,那编码绕过就是我们利用这个注入点进行注入的方式。
这种注入方式主要是为了绕过对<script>
标签,括号,引号等的过滤
Unicode编码
在讲编码绕过之前,首先提一嘴这个我们耳熟能详的编码方式。
ISO (国际标谁化组织)制定的包括了地球上所有文化、所有字母和符号 的编码,使用两个字节表示一个字符。
Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定 这个二进制代码应该如何存储。
具体存储由:UTF-8,UTF-16等实现
- JavaScript编码:
&#x
,\u
都可以用来表示当前是一串unicode编码,比如字符串test,可以表示为,\u0074\u0065\u0073\u0074
,或者test
,或者test
- HTML实体编码:
&#
表示当前为HTML实体编码,还是以test为例,test
。 - URL编码:这个比较常见了,就是百分号开头的,如
%74%65%73%74
浏览器解码
解析一篇 HTML 文档时主要有三个处理过程:HTML 解析,URL 解析 和 JavaScript 解析。每个解析器负责解码和解析 HTML 文档中它所对 应的部分,且顺序也有所区别
首先我们先来单独看看每种解析器的效果:
- HTML编码
1 | <p>对于a标签,href属性 javascript:alert(1) 编码比较</p> |
这几种编码方式中,只有2是不可以正确解析的,因为2这种方式,破坏了html的文档结构。
因为HTML解析器不对HTML的关键字进行解码,这里没有把2识别为一个正常的html标签,后面就更不会了,所以我们可以看到后面两种,无论换成什么编码方式,都无法正确解码。
- URL编码
1 | <p>对于a标签,href属性 javascript:alert(1) 编码比较</p> |
这种情况下,1,4是可以绕过的,2没法绕过的原因同上面,这个东西就不会被当作正常的html解析,而3这种,虽然会被解析成a标签,但是是对javascript这个协议类型进行了url编码,协议也属于url一部分,url解析器就是要根据这个关键字进行识别,现在识别不了,所以没法绕过。
- JavaScript编码
1 | <p>对于a标签,href属性 javascript:alert(1) 编码比较</p> |
这种也是1,4可以绕过,2是对DOM结构进行了破坏,3是对协议进行了破坏,标签可以正常解析,但是协议是错的,5这种破坏了js的解析,也没法正确执行。
综上,在对应的解析阶段,如果是对该阶段的关键字进行了编码导致结构被破坏,那就没法正确解析。
如html解析阶段对href属性进行了编码,就没法正确解析,html解析起不会认为是a标签有个属性叫href
,而是认为有个属性叫href
。
url解析器到javasc%72ipt:alert(1)
,就根本不会认为这是个JavaScript伪协议,故不会对其进行解码。
1 | <p>4 <a href="javascript:ale\u0072t(1)">alert(1)中的r进行编码</a></p> |
这两种都能正确走到最后一步的JavaScript解析阶段,然后就会提取js的代码进行解析,这时候,就看js解析器怎么解析了。
如果对url关键字的编码,如javascript
,没有在html解码阶段被解析正确,那么url解析阶段就不会解析它了。
多层混淆
上面讲的是单独每个解析器,其实我们也提到了,浏览器解析文档的时候是有顺序的,分别是HTML解析器解析出dom树,URL解析器将dom树中的url属性解码,最后是JavaScript解析器解码dom树中被认为是js代码的部分
只要我们按这个顺序反过来,就可以对一段html代码进行二层甚至三层的混淆编码。
比如二层混淆
1 | <p>1 <a href="javascript:ale%5c%75%30%30%37%32t(1)">alert(1)中的r进行js编码,后url编码</a></p> |
三层混淆
1 | <p>对于a标签,href属性 javascript:alert(1) 三层编码漏洞触发</p> |
总结
总结来说,我们上一篇博客讲的xss,开发者可以通过检测关键字,如果script等进行过滤。
但是浏览器提供了伪协议的方式,所以我们又多了一种代码注入方式。
这种方式的好处,就是可以让我们对这些关键字进行编码,从而绕过上面的那种检测。
而这需要了解两点,第一点,浏览器的编码解码方式,第二,解码的顺序。
首先是html解码,这个阶段如果对html的关键则进行了编码,是无法正确解析的,如对a标签的href属性进行了html编码,浏览器会认为,你就是想给a标签一个属性叫做href
,但是这个阶段,可以对其他部分进行html的解码,如javascript:alert(1)
,这段代码中的r被html编码方式编码了,可以在这个阶段被解码(也就是说html解码阶段,只会解码那些非html关键字且用html编码方式编码的)。
其次就是URL解码,这个阶段,如果是javasc%72ipt:alert(1)
这种,url编码认为你就是有个协议叫做javasc%72ipt
,当然就不会被正确解析。
最后就是JavaScript解码,能走到这个阶段,说明,上面两步都得到正确解析,并认为你这里有一段js代码了,那就按照js解析器的方式去解析。
换个角度理解。
html解码阶段是为了看你有什么标签,标签有什么属性,你对这些东西编码了,其实就是换种写法,解析器当然直接按你说的解析成dom树,至于后续是否是浏览器内核可识别的,解析器并不关心。这一步用不到的信息,可以被html解码方式解码。
html解码完后,对于解析为url部分的,再进行url解码,这一步有一个目的就是为了看看你是什么协议,如果你对协议名字进行了编码,解析器也认为你就是这个协议,至于是不是真有这个协议,也不是解析器要负责的。
最后经过url解析器解析后认为是js代码的部分再进行js的解码。
说到底,这三个步骤都是先解析,你说啥就是啥,你说有个属性叫做href
,那就有,剩下的部分按照当前阶段对应的解码方式解码后交给下一个阶段,下个阶段还是先挑出自己负责的部分解析(不解码),再把剩下的部分按照当前对应阶段的解码方式解码。至于最后能不能运行起来,那不是他们关心的。
比如这个东西:<p>3 <a href="javasc%72ipt:ale\u0072t(1)">javascript中的r进行编码</a></p>
,在html解码阶段a标签和href属性可以被正常解析,理论上javasc%72ipt
这东西也会被html解码,但是这段不是html编码,所以没被正确解码,到了下个阶段,url解析器,就认为这个东西的协议就是javasc%72ipt
,所以下一阶段也不会认为后面是一段js了,即使他可以被js解析器正确解析。