HTTP/1.1 200 OK
Server: Some Server
Content-Type: text/html
Content-Length: 1337
<html>
<head><title>Some Page</title></head>
<body>
...
Content-Type
,那么您是对的。这里只有一个小瑕疵:标头缺少一个属性。这听起来不是什么大问题,但是,这篇博文将解释攻击者如何利用这一点,通过有意识地更改浏览器假定的字符charset
集将任意 JavaScript 代码注入网站。字符编码
Content-Type
标头如下所示:HTTP/1.1 200 OK
Server: Some Server
Content-Type: text/html;charset=utf-8
...
charset
属性告诉浏览器使用 UTF-8 编码 HTTP 响应主体。UTF-8 之类的字符编码定义了字符和字节之间的映射。当 Web 服务器提供 HTML 文档时,它会将文档的字符映射到相应的字节,并在 HTTP 响应主体中传输这些字节。此过程将字符转换为字节(编码):根据HTML 规范,UTF-8 只是现代浏览器必须支持的众多字符编码之一。还有很多其他编码,例如UTF-16、
ISO-8859-xx、
windows-125x、
GBK、
Big5
等等。浏览器必须知道服务器使用了哪种编码,否则它无法正确解码HTTP 响应主体中的字节。
charset
但是如果标题中没有属性Content-Type
或者属性无效怎么办?
<meta>
在这种情况下,浏览器会在 HTML 文档本身中查找标签。此标签还可以具有charset
指示字符编码的属性(例如<meta charset="UTF-8">
)。对于浏览器来说,这已经是一种平衡行为:为了读取 HTML 文档,它需要解码 HTTP 响应主体。因此,它需要假设某种编码,解码 HTTP 主体,查找标签<meta>
,并可能使用指示的字符编码重新解码主体。
另一种不太常用的表示字符编码的方法是字节顺序标记。这是一个特定的 Unicode 字符 ( U+FEFF
),可以放在字符串前面以指示字节顺序和字符编码。它主要用于文件,但由于这些文件可能通过 Web 服务器提供,因此现代浏览器也支持它。HTML 文档开头的字节顺序标记甚至优先于Content-Type头
和标签charse
t
中的属性。
综上所述,浏览器确定HTML 文档的字符编码的常用方法有三种,按优先级排序:
-
HTML 文档开头的字节顺序标记
-
Content-Type
头中的charset属性 -
HTML 文档中的<meta>标签
缺少字符集信息
字节顺序标记通常很少见,并且该charset
属性并不总是出现在Content-Type
标头中,或者可能无效。此外,特别是对于部分 HTML 响应,通常没有<meta>
指示字符编码的标记。在这些情况下,浏览器没有关于使用哪种字符集的任何信息:
你见过这个错误信息吗?可能没有,因为它不存在。
与错误的 HTML 语法类似,浏览器在解析 Web 服务器提供的内容时会尝试从缺失的字符集信息中恢复并充分利用它。这种不严格的行为有助于提供良好的用户体验,但也可能为mXSS等漏洞利用技术打开方便之门。
对于缺失的字符信息,浏览器会尝试根据内容做出有根据的猜测,这称为自动检测。这类似于MIME 类型嗅探,但在字符编码级别上运行。例如,Chromium 的渲染引擎 Blink 使用紧凑编码检测 (CED) 库来自动检测字符编码。从攻击者的角度来看,自动检测功能非常强大,我们将会看到。
到目前为止,我们已经熟悉了浏览器可能使用的不同机制来确定 HTML 文档的字符编码。但攻击者如何利用这一点呢?
编码差异
字符编码的目的是将字符转换为计算机可处理的字节序列。这些字节可以通过网络传输,并由接收方解码回字符。这样,就可以恢复发送方想要传输的完全相同的字符:
这种用于编码和解码的字符编码之间的不匹配就是我们在这里所说的编码差异。
对于 Web 应用程序,当用户控制的数据需要清理以防止跨站点脚本 (XSS) 漏洞时,这一点至关重要。如果浏览器采用的字符编码与 Web 服务器预期的字符编码不同,理论上这可能会破坏清理并导致 XSS 漏洞。
这本身不是什么大新闻,甚至谷歌在 2005 年也容易遇到类似的问题。谷歌的 404 页面没有提供字符集信息,可以通过插入UTF-7 XSS 负载来利用这一点。在 UTF-7 中,HTML 特殊字符(如尖括号)的编码与 ASCII 不同,可以利用它们来绕过清理:
+ADw-script+AD4-alert(1)+ADw-+AC8-script+AD4-
这极大地证明了这种编码的危险性,为了防止此类安全问题,这种编码在接下来的几年里被弃用。如今,HTML 规范甚至明确禁止使用 UTF-7以防止 XSS 漏洞。
虽然还有很多其他受支持的字符编码,但从攻击者的角度来看,其中大多数都没有什么用处。所有HTML 特殊字符(如尖括号和引号)都只支持 ASCII,而且由于大多数字符编码都兼容 ASCII,因此这些字符没有区别。即使对于 UTF-16(由于每个字符固定为两个字节,因此不兼容 ASCII),通常也不可能偷运 ASCII 字符,因为它们对应的字节表示是相同的,只是带有尾随(小端)或前导(大端)零字节。
然而,有一个特别有趣的编码:ISO-2022-JP。
ISO-2022-JP
ISO-2022-JP 是RFC 1468中定义的日语字符编码。它是HTML 标准中定义的用户代理必须支持的官方字符编码之一。这种编码特别有趣的是,它支持某些转义序列以在不同的字符集之间切换。
例如,如果字节序列包含字节0x1b
、0x28、
0x42。
这些字节不会解码为字符,而是指示所有后续字节都应使用 ASCII 解码。总共有四种不同的转义序列可用于在字符集 ASCII、JIS X 0201 1976、JIS X 0208 1978 和 JIS X 0208 1983 之间切换:
ISO-2022-JP 的这一特性不仅提供了极大的灵活性,而且还打破了基本假设。此外,还有一个问题:在撰写本文时,Chrome(Blink)和 Firefox(Gecko)会自动检测此编码。这些转义序列之一的一次出现通常足以使自动检测算法相信 HTTP 响应主体是用 ISO-2022-JP 编码的。
以下部分介绍了攻击者在使浏览器假定使用 ISO-2022-JP 字符集时可能使用的两种不同的利用技术。根据攻击者的能力,例如,可以通过直接控制标头charset
中的属性Content-Type
或<meta>
通过 HTML 注入漏洞插入标签来实现。如果 Web 服务器提供了无效charset
属性或根本没有提供属性,通常没有其他先决条件,因为攻击者可以通过自动检测轻松将字符集切换为 ISO-2022-JP。
技术 1:否定反斜杠转义
此技术的场景是将用户控制的数据放置在 JavaScript 字符串中:
search
和lang
。第一个参数反映在纯文本上下文中,第二个参数 ( lang
) 插入到 JavaScript 字符串中:search
经过 HTML 编码,并且lang
参数已通过转义双引号(")和反斜杠()进行了正确清理。因此,无法突破字符串上下文并注入 JavaScript 代码:search
中插入转义序列( 0x1b,
0x28,0x4a
)以切换到 JIS X 0201 1976 字符集:0x5c
被映射到日元字符(¥),而字节被映射0x7e
到上划线字符 (‾
)。这与 ASCII 不同,在 ASCII 中,0x5c
被映射到反斜杠字符 () 和0x7e
波浪符号字符 (~
)。lang
反斜杠转义参数中的双引号时,浏览器不再看到反斜杠,而是看到日元符号:
技术 2:破坏 HTML 上下文
第二种技术的应用场景是攻击者可以控制两个不同 HTML 上下文中的值。一个常见的用例是支持 markdown 的网站。例如,让我们考虑以下 markdown 文本:
-
属性上下文(图像描述/来源) -
纯文本上下文(图像周围的文本)
![0015. 编码差异:字符集为何重要【转载】]()
这再次允许攻击者注入任意 JavaScript 代码。
概括
在这篇博文中,我们强调了在提供 HTML 文档时提供字符集信息的重要性。如果攻击者能够更改浏览器假定的字符集,则缺少字符集信息可能会导致严重的 XSS 漏洞。
我们详细介绍了浏览器如何确定用于解码 HTTP 响应主体的字符集,并解释了攻击者可能使用的利用 ISO-2022-JP 字符编码向网站注入任意 JavaScript 代码的两种不同技术。
虽然我们认为缺失的字符集才是真正的漏洞,但浏览器的自动检测功能会大大增加其影响。因此,我们希望浏览器能够根据我们的建议禁用自动检测机制 - 至少针对 ISO-2022-JP 字符编码。
原文始发于微信公众号(Rsec):0015. 编码差异:字符集为何重要【转载】
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论