CSS 变量注入:利用 `–var` 注入恶意载荷绕过过滤器的分析

CSS 变量注入:利用 --var 注入恶意载荷绕过过滤器的分析

各位同学,大家好。今天我们来深入探讨一个比较隐蔽但威力强大的安全漏洞:CSS 变量注入。这种漏洞允许攻击者利用 CSS 变量(custom properties,以 -- 开头的属性)注入恶意载荷,绕过各种输入验证和过滤机制,最终实现跨站脚本攻击(XSS)等危害。

1. CSS 变量简介:不仅仅是样式

CSS 变量是 CSS3 引入的一项强大特性,允许开发者在 CSS 中定义和使用变量。它们可以用于存储颜色、字体大小、间距等样式属性值,并在整个文档中复用。

基本语法:

  • 定义变量: 使用 --变量名: 变量值;:root 或其他元素中定义变量。
  • 使用变量: 使用 var(--变量名) 函数来引用变量。

示例:

:root {
  --primary-color: #007bff;
  --font-size: 16px;
}

body {
  font-size: var(--font-size);
  color: var(--primary-color);
}

.button {
  background-color: var(--primary-color);
  padding: 10px 20px;
  border: none;
  color: white;
  font-size: var(--font-size);
}

在这个例子中,--primary-color--font-size 是两个 CSS 变量。body.button 元素都使用了这些变量来定义它们的样式。

关键点:

  • CSS 变量可以存储任意字符串,而不仅仅是合法的 CSS 值。
  • 浏览器在解析 CSS 时,会尝试将变量的值应用到对应的属性上。如果变量的值不是合法的 CSS 值,浏览器可能会忽略它,但不会抛出错误。
  • CSS 变量具有层叠性和继承性,可以在不同的上下文中覆盖。

2. CSS 变量注入的原理:变量值可控是关键

CSS 变量注入的核心在于攻击者能够控制 CSS 变量的值。如果一个 Web 应用程序允许用户自定义 CSS 样式,或者将用户输入的数据插入到 CSS 样式中,那么攻击者就有可能利用 CSS 变量注入漏洞。

攻击流程:

  1. 发现注入点: 找到一个可以控制 CSS 变量值的入口。例如,一个允许用户自定义主题颜色的设置页面。
  2. 构造恶意载荷: 构造包含恶意代码的 CSS 变量值。例如,包含 JavaScript 代码的 expression() 函数。
  3. 注入恶意载荷: 将恶意载荷注入到 CSS 变量中。
  4. 触发恶意代码: 当浏览器解析包含恶意载荷的 CSS 样式时,会执行其中的恶意代码。

示例:

假设有一个 Web 应用程序允许用户自定义背景颜色,并将用户输入的颜色值插入到 CSS 样式中:

<style>
  body {
    background-color: var(--user-background-color);
  }
</style>

如果用户输入 javascript:alert(1) 作为背景颜色,那么最终生成的 CSS 样式将是:

<style>
  body {
    background-color: var(--user-background-color);
  }
  :root {
    --user-background-color: javascript:alert(1);
  }
</style>

虽然 javascript:alert(1) 不是一个合法的颜色值,但浏览器不会报错。但是,如果攻击者能够找到一种方法,让浏览器将这个字符串作为 JavaScript 代码执行,那么就可以实现 XSS 攻击。

3. 常用的攻击载荷:利用 CSS 的特性执行恶意代码

CSS 本身不是一种编程语言,不能直接执行 JavaScript 代码。但是,攻击者可以利用 CSS 的一些特性,间接地执行 JavaScript 代码:

3.1 expression() 函数 (仅适用于 IE7 及以下版本):

expression() 函数是 IE 特有的一个 CSS 函数,它允许在 CSS 属性值中使用 JavaScript 表达式。当 CSS 属性被应用时,expression() 函数中的 JavaScript 表达式会被执行。

示例:

body {
  background-color: expression(alert(1)); /* IE7 及以下 */
}

虽然现代浏览器已经不再支持 expression() 函数,但在一些老旧的系统中仍然可能存在风险。

3.2 behavior 属性 (仅适用于 IE):

behavior 属性允许将一个外部的 HTC (HTML Component) 文件应用到 HTML 元素上。HTC 文件可以包含 JavaScript 代码,从而实现动态的 HTML 元素行为。

示例:

body {
  behavior: url(evil.htc); /* IE */
}

evil.htc 文件内容:

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

同样,这种方法只适用于 IE 浏览器。

3.3 利用 CSS 属性值中的 URL:

某些 CSS 属性(例如 background-imagelist-style-image 等)允许使用 URL 作为属性值。攻击者可以利用这一点,将 URL 指向一个包含 JavaScript 代码的文件。

示例:

body {
  background-image: url("data:text/javascript,alert(1)");
}

或者,如果服务器允许上传任意文件,攻击者可以上传一个包含 JavaScript 代码的 SVG 文件,然后使用 background-image 属性引用该 SVG 文件:

body {
  background-image: url("evil.svg");
}

evil.svg 文件内容:

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

3.4 利用 @import 规则:

@import 规则允许在一个 CSS 文件中导入另一个 CSS 文件。攻击者可以利用这一点,将 @import 规则指向一个包含恶意代码的 CSS 文件。

示例:

@import url("evil.css");

evil.css 文件内容:

body {
  background-color: expression(alert(1));
}

3.5 利用 calc() 函数和 attr() 函数:

calc()函数允许在CSS中进行计算,而attr()函数允许获取HTML元素的属性值。结合这两个函数,可以实现一些复杂的攻击。

示例:

假设有一个HTML元素:<div id="target" data-evil="javascript:alert(1)"></div>

CSS代码:

#target {
  width: calc(100% + attr(data-evil string)); /* 某些浏览器可能会解析并执行data-evil中的javascript代码 */
}

这个例子依赖于浏览器的解析行为,不同浏览器处理attr()calc()的方式可能不同,攻击成功率可能不高,但展示了calc()attr()结合使用的可能性。

总结

攻击方式 适用浏览器 描述
expression() IE7 及以下 利用 IE 特有的 expression() 函数执行 JavaScript 代码。
behavior IE 利用 IE 特有的 behavior 属性将 HTC 文件应用到 HTML 元素上,HTC 文件中包含 JavaScript 代码。
URL 中的 JavaScript 现代浏览器 利用 CSS 属性值中的 URL,将 URL 指向一个包含 JavaScript 代码的文件(例如 data:text/javascript,alert(1) 或 SVG 文件)。
@import 现代浏览器 利用 @import 规则导入一个包含恶意代码的 CSS 文件。
calc()attr() 部分现代浏览器 结合使用calc()attr()函数,尝试利用浏览器解析行为执行属性值中的JavaScript代码。攻击成功率取决于浏览器实现。

4. 绕过过滤器的技巧:编码、混淆和拆分

Web 应用程序通常会对用户输入进行过滤,以防止 XSS 攻击。但是,攻击者可以使用各种技巧来绕过这些过滤器:

4.1 编码:

  • URL 编码: 将特殊字符编码为 URL 编码的形式(例如 %20 代表空格,%3C 代表 <)。
  • HTML 实体编码: 将特殊字符编码为 HTML 实体编码的形式(例如 &lt; 代表 <&gt; 代表 >)。
  • Unicode 编码: 将字符编码为 Unicode 编码的形式(例如 u003C 代表 <)。

示例:

假设过滤器会过滤掉 <script> 标签,攻击者可以使用 HTML 实体编码来绕过过滤器:

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

4.2 混淆:

  • 大小写混淆: 改变标签或属性的大小写(例如 <sCrIpt>)。
  • 插入空白字符: 在标签或属性中插入空白字符(例如 < script>)。
  • 使用注释: 在标签或属性中使用注释(例如 <!---->).

示例:

假设过滤器会过滤掉 javascript: 协议,攻击者可以使用大小写混淆来绕过过滤器:

<img src="x" onerror="JaVaScRiPt:alert(1)">

4.3 拆分:

将恶意代码拆分成多个部分,然后使用字符串拼接的方式将它们组合起来。

示例:

假设过滤器会过滤掉 alert() 函数,攻击者可以将 alert() 函数拆分成两个字符串,然后使用字符串拼接的方式将它们组合起来:

<img src="x" onerror="eval('al'+'ert(1)')">

4.4 利用 CSS 的特性:

  • 利用反斜杠转义: 在 CSS 中,可以使用反斜杠来转义特殊字符。攻击者可以利用这一点,将恶意代码隐藏在反斜杠后面。
  • 利用 CSS 的层叠性: 通过覆盖 CSS 样式,可以将恶意代码注入到特定的元素中。

示例:

假设过滤器会过滤掉 expression() 函数,攻击者可以使用反斜杠转义来绕过过滤器:

body {
  background-color: expression(alert(1));
}

或者,攻击者可以利用 CSS 的层叠性,将恶意代码注入到特定的元素中:

.evil {
  background-color: expression(alert(1));
}
<div class="evil"></div>

4.5 利用字符编码转换

某些过滤器可能只检测特定的字符集,而忽略了其他字符集。攻击者可以利用这一点,将恶意代码编码为其他字符集,然后使用 CSS 的 @charset 规则来指定字符集。

示例:

@charset "UTF-16";
body {
    /* 假设 UTF-16 编码的 alert(1) */
    background-color: expression( ... encoded alert(1) here ...);
}

这种方式需要服务器支持相应的字符集,并且浏览器能够正确解析。

5. 防御 CSS 变量注入:严格的输入验证和输出编码

防御 CSS 变量注入漏洞的关键在于严格的输入验证和输出编码

5.1 输入验证:

  • 白名单验证: 只允许用户输入预定义的 CSS 属性值。例如,如果用户只能选择背景颜色,那么只允许用户输入合法的颜色值(例如 #RRGGBBrgb(R, G, B))。
  • 正则表达式验证: 使用正则表达式来验证用户输入是否符合预期的格式。
  • 长度限制: 限制用户输入的最大长度,防止攻击者注入过长的恶意代码。

5.2 输出编码:

  • CSS 编码: 对用户输入的数据进行 CSS 编码,将特殊字符转换为 CSS 编码的形式。例如,将 < 转换为 3C,将 > 转换为 3E
  • Context-Aware Encoding: 根据输出的上下文选择合适的编码方式。例如,如果在 HTML 属性中使用 CSS 变量,那么需要进行 HTML 编码和 CSS 编码。

5.3 其他防御措施:

  • Content Security Policy (CSP): 使用 CSP 来限制浏览器可以加载的资源,防止攻击者加载恶意脚本。
  • Subresource Integrity (SRI): 使用 SRI 来验证外部资源的完整性,防止攻击者篡改外部资源。
  • 避免使用危险的 CSS 特性: 尽量避免使用 expression() 函数和 behavior 属性等危险的 CSS 特性。
  • 定期更新浏览器和 Web 应用程序: 及时修复已知的安全漏洞。

示例代码(PHP):

<?php

function sanitize_css_variable($input) {
  // 白名单验证:只允许合法的颜色值
  if (!preg_match('/^#([0-9a-fA-F]{3}){1,2}$/', $input) && !preg_match('/^rgb(s*d+s*,s*d+s*,s*d+s*)$/', $input)) {
    return ''; // 返回空字符串,表示输入无效
  }

  // CSS 编码:将特殊字符转换为 CSS 编码的形式
  $input = str_replace(['<', '>', '"', "'", '(', ')'], ['3C', '3E', '22', '27', '28', '29'], $input);

  return $input;
}

// 获取用户输入的背景颜色
$user_background_color = $_GET['background_color'];

// 对用户输入进行验证和编码
$safe_background_color = sanitize_css_variable($user_background_color);

// 生成 CSS 样式
$css = "body { background-color: var(--user-background-color); } :root { --user-background-color: " . $safe_background_color . "; }";

?>

<!DOCTYPE html>
<html>
<head>
  <title>CSS 变量注入示例</title>
  <style>
    <?php echo $css; ?>
  </style>
</head>
<body>
  <h1>欢迎光临!</h1>
  <p>这是一个 CSS 变量注入示例页面。</p>
</body>
</html>

这个示例代码使用 PHP 对用户输入的背景颜色进行验证和编码,有效地防止了 CSS 变量注入攻击。首先,它使用白名单验证来确保用户输入的是合法的颜色值。然后,它使用 CSS 编码将特殊字符转换为 CSS 编码的形式,防止攻击者注入恶意代码。

6. 案例分析:真实世界的 CSS 变量注入漏洞

虽然 CSS 变量注入漏洞相对隐蔽,但在真实世界中也存在一些案例。例如,某些 CMS (Content Management System) 系统或 Web 应用程序允许用户自定义主题样式,但没有对用户输入进行充分的验证,导致攻击者可以利用 CSS 变量注入漏洞执行 XSS 攻击。

案例描述:

某 CMS 系统允许用户自定义网站的主题颜色。用户可以在后台管理界面中设置主题颜色,然后 CMS 系统会将用户输入的颜色值插入到 CSS 样式中。但是,CMS 系统没有对用户输入进行充分的验证,导致攻击者可以注入包含恶意代码的 CSS 变量值。

攻击过程:

  1. 攻击者登录到 CMS 系统的后台管理界面。
  2. 攻击者在主题颜色设置页面中输入包含恶意代码的 CSS 变量值,例如 javascript:alert(1)
  3. CMS 系统将攻击者输入的颜色值插入到 CSS 样式中,并保存到数据库中。
  4. 当其他用户访问网站时,浏览器会解析包含恶意载荷的 CSS 样式,并执行其中的 JavaScript 代码。

修复方案:

  1. 对用户输入进行严格的验证,只允许用户输入合法的颜色值。
  2. 对用户输入的数据进行 CSS 编码,将特殊字符转换为 CSS 编码的形式。
  3. 使用 CSP 来限制浏览器可以加载的资源,防止攻击者加载恶意脚本。

7. 最后的思考:安全无小事,防范于未然

CSS 变量注入是一种隐蔽但威力强大的安全漏洞。Web 开发者应该充分了解 CSS 变量注入的原理和攻击方式,并采取有效的防御措施,防止攻击者利用该漏洞进行 XSS 攻击。安全无小事,只有防范于未然,才能确保 Web 应用程序的安全性。

CSS 变量注入的影响和防御

CSS变量注入漏洞利用了CSS变量的灵活性,允许攻击者控制样式属性,进而可能执行恶意代码。通过严格的输入验证、输出编码、CSP等手段,可以有效地防御此类攻击。安全开发需要对各种潜在的注入点保持警惕,并采取相应的防护措施。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注