目录
前言
在本节中,我们将向您展示如何利用网站执行的标准解码来逃避输入过滤器并为各种攻击(例如XSS 和 SQL 注入)注入有害的有效负载。
一、解码差异导致混淆攻击
注入攻击通常涉及注入使用可识别模式的有效负载,例如 HTML 标记、JavaScript 函数或 SQL 语句。由于这些有效负载的输入几乎从未包含用户提供的代码或标记,因此网站通常会实施防御措施来阻止包含这些可疑模式的请求。
但是,这些类型的输入过滤器还需要解码输入,以检查它是否安全。从安全角度来看,检查输入时执行的解码与后端服务器或浏览器最终使用数据时执行的解码相同,这一点至关重要。任何差异都可能使攻击者能够通过应用稍后会自动删除的不同编码,将有害的有效负载偷偷越过过滤器。
二、编码差异攻击示例
1.通过 URL 编码进行混淆
在 URL 中,一系列保留字符具有特殊含义。例如,与号 () 用作分隔符来分隔查询字符串中的参数。问题是,基于 URL 的输入可能出于不同的原因包含这些字符。考虑一个包含用户搜索查询的参数。如果用户搜索“Fish & Chips
”之类的内容会发生什么?&
浏览器会自动对可能导致解析器歧义的任何字符进行 URL 编码。这通常意味着用字符和它们的 2 位十六进制代码替换它们,如下所示:%
[...]/?search=Fish+%26+Chips
这可确保与号不会被误认为是分隔符。
注意
尽管空格字符可以编码为,但它通常由加号 () 表示,如上例所示。
%20
+
任何基于 URL 的输入在分配给相关变量之前都会在服务器端自动进行 URL 解码。这意味着,就大多数服务器而言,查询参数中的 、、 和 等序列分别与 、 和字符同义。换句话说,您可以通过 URL 注入 URL 编码的数据,后端应用程序通常仍会正确解释这些数据。%22
%3D
%3E
"
<
>
有时,您可能会发现 WAF 等在检查输入时无法正确 URL 解码。在这种情况下,您只需对列入黑名单的任何字符或单词进行编码,即可将有效负载走私到后端应用程序。例如,在 SQL 注入攻击中,您可以对关键字进行编码,sobe成为等等。SELECT
%53%45%4C%45%43%54
2、通过双 URL 编码进行混淆
出于某种原因,某些服务器对它们收到的任何 URL 执行两轮 URL 解码。这本身不一定是一个问题,前提是任何安全机制在检查输入时也会对输入进行双重解码。否则,这种差异使攻击者能够通过简单地编码两次将恶意输入偷运到后端。
假设您正在尝试注入标准 XSS PoC,例如通过查询参数。在这种情况下,URL 可能如下所示:<img src=x onerror=alert(1)>
[...]/?search=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E
检查请求时,如果 WAF 执行标准 URL 解码,它将轻松识别此已知有效负载。请求被阻止到达后端。但是,如果对注入进行双重编码呢?实际上,这意味着字符本身被替换为:%
%25
[...]/?search=%253Cimg%2520src%253Dx%2520onerror%253Dalert(1)%253E
由于 WAF 仅对此进行一次解码,因此可能无法识别请求是否危险。如果后端服务器随后对此输入进行双重解码,则将成功注入有效负载。
3、通过 HTML 编码进行混淆
在 HTML 文档中,需要对某些字符进行转义或编码,以防止浏览器将它们错误地解释为标记的一部分。这是通过将违规字符替换为引用来实现的,引用以 & 符号为前缀,并以分号结尾。在许多情况下,名称可用于引用。例如,序列表示冒号字符。:
或者,可以使用字符的十进制或十六进制码位分别提供引用。:
:
在 HTML 中的特定位置,例如元素的文本内容或属性的值,浏览器将在解析文档时自动解码这些引用。在此类位置内注入时,您偶尔可以利用这一点来混淆客户端攻击的有效负载,从而将它们隐藏在任何现有的服务器端防御中。
如果您仔细查看前面示例中的 XSS 有效负载,请注意有效负载被注入到 HTML 属性(即事件处理程序)中。如果服务器端检查正在显式查找有效负载,则在对一个或多个字符进行 HTML 编码时,它们可能不会发现这一点:onerror
alert()
<img src=x onerror="alert(1)">
当浏览器呈现页面时,它将解码并执行注入的有效负载。
Leading zeros
有趣的是,当使用十进制或十六进制样式的 HTML 编码时,您可以选择在代码点中包含任意数量的Leading zeros。某些 WAF 和其他输入筛选器无法充分说明这一点。
如果你的有效负载在 HTML 编码后仍然被阻止,你可能会发现你可以通过用几个零作为代码点的前缀来逃避过滤器:
<a href="javascript:alert(1)">Click me</a>
4、通过 XML 编码进行混淆
XML 与 HTML 密切相关,还支持使用相同的数字转义序列进行字符编码。这使您能够在不破坏语法的情况下在元素的文本内容中包含特殊字符,例如,在通过基于 XML 的输入测试 XSS 时,语法会派上用场。
即使不需要对任何特殊字符进行编码以避免语法错误,也可能利用此行为以与使用 HTML 编码相同的方式对有效负载进行模糊处理。不同之处在于您的有效负载由服务器本身解码,而不是由浏览器在客户端解码。这对于绕过 WAF 和其他筛选器非常有用,如果它们检测到与 SQL 注入攻击关联的某些关键字,则可能会阻止您的请求。
<stockCheck>
<productId>
123
</productId>
<storeId>
999 SELECT * FROM information_schema.tables
</storeId>
</stockCheck>
5、通过 unicode 转义进行混淆
Unicode 转义序列由前缀后跟字符的四位数十六进制代码组成。例如,表示冒号。ES6 还支持使用大括号的新形式的 unicode 转义:\u
\u003a
\u{3a}
解析字符串时,大多数编程语言会解码这些 unicode 转义。这包括浏览器使用的JavaScript引擎。注入到字符串上下文中时,可以使用 unicode 对客户端有效负载进行模糊处理,就像我们在上面示例中对 HTML 转义所做的那样。
例如,假设您正在尝试利用DOMM XSS,其中您的输入作为字符串传递到接收器。如果初始尝试被阻止,请尝试转义其中一个字符,如下所示:eval()
eval("\u0061lert(1)")
由于这将在服务器端保持编码,因此在浏览器再次解码之前,它可能不会被检测到。
注意
在字符串中,您可以转义任何此类字符。但是,在字符串之外,转义某些字符将导致语法错误。例如,这包括左括号和右括号。
还值得注意的是,ES6 风格的 unicode 转义也允许可选的前导零,因此使用我们用于 HTML 编码的相同技术,某些 WAF 可能很容易被愚弄。例如:
<a href="javascript\u{0000000003a}alert(1)">Click me</a>
6、通过十六进制转义进行混淆
注入字符串上下文时的另一个选项是使用十六进制转义,十六进制转义使用其十六进制代码点表示字符,前缀为。例如,小写字母由 表示。\x
a
\x61
就像 unicode 转义一样,只要输入被计算为字符串,这些转义就会在客户端解码:
eval("\x61lert")
请注意,有时也可以使用前缀以类似的方式对 SQL 语句进行模糊处理。例如,可以解码以形成关键字。0x
0x53454c454354
SELECT
7、通过八进制转义进行混淆
八进制转义的工作方式与十六进制转义几乎相同,只是字符引用使用以 8 为基数的编号系统而不是以 16 为基数。这些以独立的反斜杠为前缀,这意味着小写字母由 表示。a
\141
eval("\141lert(1)")
8、
通过多种编码进行混淆
请务必注意,您可以组合编码以将有效负载隐藏在多层混淆后面。查看以下示例中的 URL:javascript:
<a href="javascript:\u0061lert(1)">Click me</a>
浏览器将首先进行 HTML 解码,从而导致反斜杠。这具有将原本任意字符转换为 unicode 转义的效果:\,
u0061
\u0061
<a href="javascript:\u0061lert(1)">Click me</a>
然后进一步解码以形成功能正常的XSS有效负载:
<a href="javascript:alert(1)">Click me</a>
显然,要以这种方式成功注入有效负载,您需要深入了解对输入执行哪种解码以及以什么顺序执行。
9、通过 SQL CHAR() 函数进行混淆
虽然严格来说不是一种编码形式,但在某些情况下,您可以使用函数混淆您的 SQL 注入攻击。这接受单个小数或十六进制代码点并返回匹配字符。十六进制代码必须以 为前缀。例如,bothand返回大写字母。CHAR()
0x
CHAR(83)
CHAR(0x53)
S
通过连接返回的值,可以使用此方法对阻止的关键字进行模糊处理。例如,即使被列入黑名单,以下注射最初看起来也是无害的:SELECT
CHAR(83)+CHAR(69)+CHAR(76)+CHAR(69)+CHAR(67)+CHAR(84)
但是,当应用程序将其处理为 SQL 时,它将动态构造关键字并执行注入的查询。SELECT
总结
本次大概讲解了编码或转义时会出现的攻击模型,当然,在构建攻击时,您应该考虑有效负载的确切注入位置。如果可以推断输入如何基于此上下文进行解码,则可以确定表示相同有效负载的替代方法。