XSS与字符编码及浏览器解析原理

time: 2019-05-12 21:27

XSS与字符编码基本介绍:

提起XSS 想到的就是插入字符字符编码与各种解析了!现在介绍一下在xss中最经常用到的编码
html实体编码(10进制与16进制):

如把尖括号编码[ < ] -----> html十进制: < html十六进制:&#x3 c;

javascript的八进制跟十六进制:

如把尖括号编码[ < ] -----> js八进制: \74 js十六进制: \x3c

jsunicode编码:

如把尖括号编码[ < ] ----->jsunicode: \u003c

url编码 base64编码:

如把尖括号编码[ < ] -----> url: %3C base64: PA==

html实体编码

html实体编码本身存在的意义是防止与HTML本身语义标记的冲突。但是在XSS中却成为了的一大利器,但是也不能盲目的使用!
html中正常情况只识别:html10进制,html16进制
应该如何在xss过程中灵活的使用各种编码呢?

比如现在你的输出点在这:

><img src="[代码]">

在这里过滤了script < > / \ http: 以及各种危险字符 比如创建一个html节点什么的!

有的站只允许你引用一个img文件夹里的图片 但是图片是你可以控的 可以通过抓包来修改的!

我们如果想加载外部js 或者一个xss平台的钩子我们应该怎么写呢?

那么我们可以在这里 闭合双引号 写事件: onerror=[html language="实体编码"][/html][/html]

比如现在弹个窗:

><img src="x" onerror="&#9 7;&#10 8;&#10 1;&#11 4;&#11 6;&#4 0;&#4 9;&#4 1;">

这里在编码中加了空格因为编码会被解析为下面的:
原code:

><img src="x" onerror="alert(1)">

图片.png

这里用的是html十进制编码 也可以使用十六进制的html实体编码

浏览器解析原理(参照https://blog.csdn.net/u010726042/article/details/76259398)

解析过程:
浏览器在解析HTML文档时无论按照什么顺序,主要有三个过程:HTML解析、JS解析和URL解析,每个解析器负责HTML文档中各自对应部分的解析工作。下面以一篇HTML文档解析来简单的讨论下解析器如何协同工作的。

首先浏览器接收到一个HTML文档时,会触发HTML解析器对HTML文档进行词法解析(解析器-词法分析器 Parser-Lexer combination
解析可以分为两个子过程——语法分析及词法分析词法分析就是将输入分解为符号,符号是语言的词汇表——基本有效单元的集合。对于人类语言来说,它相当于我们字典中出现的所有单词。语法分析指对语言应用语法规则。解析器一般将工作分配给两个组件——词法分析器(有时也叫分词器)负责将输入分解为合法的符号,解析器则根据语言的语法规则分析文档结构,从而构建解析树,词法分析器知道怎么跳过空白和换行之类的无关字符。),

这一过程完成HTML解码并创建DOM树,接下来JavaScript解析器会介入对内联脚本进行解析,这一过程完成JS的解码工作,如果浏览器遇到需要URL的上下文环境,这时URL解析器也会介入完成URL的解码工作,URL解析器的解码顺序会根据URL所在位置不同,可能在JavaScript解析器之前或之后解析。每个解析过程中也有许多细节,下面再做具体讨论。

基本概念:

HTML字符实体:

在呈现HTML页面时,针对某些特殊字符如“<”或”>”直接使用,浏览器会误以为它们标签的开始或结束,若想正确的在HTML页面呈现特殊字符就需要用到其对应的字符实体。

字符实体是一个预先定义好的转义序列,它定义了一些无法在文本内容中输入的字符或符号。字符实体以&开头+预先定义的实体名称,以分号结束,如“<”的实体名称为&lt; 或以&开头+#符号以及字符的十进制数字,如”<”的实体编号为&#60;,字符都是有实体编号的但有些字符没有实体名称。

JavaScript编码:最常用的如“\uXXXX”这种写法为Unicode转义序列,表示一个字符,其中xxxx表示一个16进制数字,如”<” Unicode编码为“\u003c”

URL编码:%加字符的ASCII编码对于的2位16进制数字,如”/”对应的URL编码为%2f。

下面结合具体示例来讨论下浏览器的解析原理过程和XSS复合编码的一些内容:

><a href="javascript:alert(1)">test</a>

针对上述a标签我们分析一下该环境中浏览器的解析顺序,首先HTML解析器开始工作,并对href中的字符做HTML解码,接下来URL解析器对href值进行解码,正常情况下URL值为一个正常的URL链接,如:“https://www.yirendai.com“,那么URL解析器工作完成后是不需要其他解码的,但是该环境中URL资源类型为JavaScript,因此该环境中最后一步JavaScript解析器还会进行解码操作,最后解析的脚本被执行。
整个解析顺序为3个环节:HTML解码àURL解码àJS解码
我们对href值做一些编码的转换,对照刚才分析的解析过程,思考一下脚本是否会正常执行?
Test1: URL 编码 "javascript:alert(1)”

URL编码“javascript:alert(1)”=“%6A%61%76%61%73%63%72%69%70%74:%61%6C%65%72%74%28%31%29”
编码后:

<a href="%6A%61%76%61%73%63%72%69%70%74:%61%6C%65%72%74%28%31%29">test1</a>

test2: HTML字符实体编码 “javascript” 、URL 编码 “alert(2)”

HTML编码"javascript"=“javascript”

URL编码"alert(2)"=” %61%6C%65%72%74%28%32%29”

<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;:%61%6C%65%72%74%28%32%29">test2</a>

Test3: 对test3做JS编码àURL编码àHTML编码共3层。

JS编码:<a href="javascript:\u0061\u006c\u0065\u0072\u0074(3)">test3</a>

URL编码:<a href="javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(3)">test3</a>

HTML编码:
<a href="&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#51;&#49;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#54;&#51;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#54;&#37;&#51;&#53;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#55;&#37;&#51;&#50;&#37;&#53;&#99;&#37;&#55;&#53;&#37;&#51;&#48;&#37;&#51;&#48;&#37;&#51;&#55;&#37;&#51;&#52;&#40;&#51;&#41;">test3</a>

图片.png
图片.png
图片.png
经测试只有2和3可以弹窗,1不能。

【过程分析】
许多童鞋把Test1放到HTML里发现脚本并没有正常执行,就会想按照刚才分析的,URL解码之后Javascript解析器完成解码操作,脚本应该会正常执行啊,这里就有一个URL解析过程中的一个细节了,不能对协议类型进行任何的编码操作,否则URL解析器会认为它无类型,就导致Test1中被编码的“javascript”没有解码,当然不会被URL解析器识别了。

那Test2也是对javascript编码了为什么可以执行呢?因为"javascript"是做的HTML实体编码,HTML解析器工作时,href里的HTML实体会被解码,接下来URL解析器工作解析href属性里的链接时,"javascript"协议在第一步被HTML解码了,这样URL解析器是可以识别的,然后继续解析后面的”%61%6C%65%72%74%28%32%29”,最后JavaScript解析器完成解析操作,脚本执行。

Test3实现了3层复合编码,每一层编码都能正常执行,贴出来供大家可以自行验证加深理解

><img src=x onclick=” {$value}”>

下面我们分析一下2层复合编码的img标签

代码如下:

image

假设onclick属性值为“用户可控”数据,思考一下该如何编码才能防住XSS?

首先传入的“用户可控”数据处在HTML环境中,然后再是onclick环境中,因此浏览器的解析顺序为:HTML解码àJS解码;

HTML解码: \u0061\u006c\u0065\u0072\u0074(‘YISRC’)

JS解码: alert(‘YISRC’)

解码完成,脚本执行,细心的童鞋可能会问了value值的编码顺序为解码的逆序,先将alert进行javascript编码为\u0061\u006c\u0065\u0072\u0074,然后再对整个value值进行HTML编码,居然还可以弹框?

我们来分析一下JavaScript解析的一个细节,Javascript解析器工作的时候将\u0061\u006c\u0065\u0072\u0074进行js解码后为“alert”,而“alert”是一个有效的标识符名称,它是能被正常解析的。像圆括号、双引号、单引号等等这些控制字符,在进行JavaScript解析的时候仅会被解码为字符串文本或者上面讲的标识符名称,例如:

<script>alert('YISRC\u0027)</script>

对控制字符单引号进行js编码,解析时\u0027被解码成文本单引号,无法闭合因此不能成功执行。本例中只对“alert”进行了Unicode,并没有对圆括号这类控制字符进行Unicode,是因为我想弹框啊,但在正常的XSS防御中肯定要对这些控制字符进行Unicode的。
下面通过几个简单的场景加深理解一下:

假设:alert里的值为用户输入的可控数据,服务端为了防御XSS做了HTML编码

示例一:

用户输入的【");alert("SRC】在服务端HTML实体编码之后返回到前端被浏览器解析执行
图片.png
1、首先

【&quot;&#41;&#59;&#97;&#108;&#101;&#114;&#116;&#40;&quot;&#83;&#82;&#67; 】
在HTML解码后变成了【");alert("SRC】。

2、JavaScript解析时,变成了【alert("YISRC ");alert("SRC】,当然可以正常执行了,如下图所示:
图片.png
图片.png
事实证明,该上下文环境中只做HTML编码是不能完全防御XSS的。
示例二:

现在调整服务端XSS防御策略为:JS编码àHTML编码

如下图所示,成功防御:
图片.png
图片.png
来分析一下:

 1、首先【&#92;&#117;&#48;&#48;&#50;&#55;&#92;&#117;&#48;&#48;&#50;&#57;&#92;&#117;&#48;&#48;&#51;&#98;&#92;&#117;&#48;&#48;&#54;&#49;&#92;&#117;&#48;&#48;&#54;&#99;&#92;&#117;&#48;&#48;&#54;&#53;&#92;&#117;&#48;&#48;&#55;&#50;&#92;&#117;&#48;&#48;&#55;&#52;&#92;&#117;&#48;&#48;&#50;&#56;&#92;&#117;&#48;&#48;&#50;&#55;&#92;&#117;&#48;&#48;&#53;&#51;&#92;&#117;&#48;&#48;&#53;&#50;&#92;&#117;&#48;&#48;&#52;&#51;】
 经过第一步HTML解码后变为【\u0027\u0029\u003b\u0061\u006c\u0065\u0072\u0074\u0028\u0027\u0053\u0052\u0043】

 2、JavaScript解析器工作,【\u0027\u0029\u003b\u0061\u006c\u0065\u0072\u0074\u0028\u0027\u0053\u0052\u0043】变为【');alert('SRC】,
 刚才已经讲过JavaScript解析时只有标识符名称不会被当做字符串,控制字符仅会被解析为标示符名称或者字符串,
 因此'\u0027'被解释成单引号文本,\u0028和\u0029被解释成为圆括号文本,不会变为控制字符被解析执行。

事实证明,合理的编码方式是防御XSS中首先要考虑到的。

结合上面的内容,自己分析一下value1和value2所处的上下文环境,要防御XSS需要怎么做组合编码?
image

     浏览器解码顺序:

               Value1:HTML解码àJavaScript解码àURL解码

               Value2:HTML解码àURL解码àJavaScript解码àURL解码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BerL1n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值