freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

ServiceNow安全漏洞探讨:CVE-2024系列深度分析
AlbertJay 2024-12-12 14:46:21 152257
所属地 广东省

前言

在当今数字化飞速发展的时代,企业越来越依赖复杂的云服务平台来推动业务创新与增长。然而,这些平台的广泛使用也伴随着潜在的安全风险,尤其是在处理敏感数据时。近期,关于ServiceNow的一系列关键漏洞(CVE-2024-4879、CVE-2024-5178 和 CVE-2024-5217)的披露,再次引发了业界对网络安全的关注。

这些漏洞不仅可能使攻击者轻易访问敏感信息,还能让他们完全控制整个数据库。在这个始终在线的环境中,安全研究人员和企业 IT 团队必须时刻保持警惕,确保系统的防护能够跟得上不断演变的威胁。

本文将深入探讨这三项漏洞的技术细节及其潜在影响,揭示如何通过了解并应对这些风险,以保护企业数据的完整性和安全。无论是安全专家、开发者,还是对信息安全感兴趣的普通读者,本文将为提供丰富的见解、实用参考的建议,以及对未来网络安全态势的深刻洞察。让我们一起揭开这些安全隐患的面纱,探索如何在数字化转型的浪潮中,维护企业的安全堡垒。

ServiceNow介绍

ServiceNow 是一个庞大的 Java 单体应用,其单个 .jar 文件大小竟超过了 20GB。其设计宗旨在于实现高度的可定制性,许多配置选择都通过数据库完成。这与典型的 Java 应用程序形成鲜明对比,后者通常在 web.xml 文件中注册多个 servlet,并将端点硬编码于程序内。而在 ServiceNow 的世界中,实例会咨询一组数据库表来决定大多数请求的路由位置。

为了更好地理解 ServiceNow,需要深入探讨几个核心概念:

ServiceNow 的基本构建块是表。几乎所有的数据都被存储在表中,其中包括用户信息 (sys_users)、页面 (sys_pages) 和配置信息 (sys_properties)。这些表与底层数据库之间呈现出 1:1 的映射关系。例如,数据库中存在一个名为 sys_users 的表。

ServiceNow 为用户提供了便捷的数据库更新机制,只需在 URL 中输入特定的路径即可实现。例如,若想查看用户列表,可以直接访问 /sys_users_list.do;而若需创建新用户,则可浏览至 /sys_users.do。当然,允许任何用户随意修改所有内容显然非常不安全,因此 ServiceNow 构建了一个复杂的访问控制列表 (ACL) 系统来限制访问权限,用户可以被授权访问整个表、单个行或甚至特定字段。

  1. 处理器

另一种处理请求的方式是通过处理器。这些处理器可以视为最接近 API 端点的组件。ServiceNow 提供了基于 Rhino 的 JavaScript 引擎,使得用户在设计自定义端点时能够拥有更大的灵活性。同时,他们还提供了一系列主要使用 Java 编写的辅助类,使几乎可以配置平台的任意部分。

以下是一个随机抽取的示例处理器,以便了解其实现方式:

redirectBasedOnTheDevice();

function redirectBasedOnTheDevice() {

var userId = g_request.getParameter("sysparm_id");

var requestId = g_request.getParameter("sysparm_request_id");

var token = g_request.getParameter("sysparm_token");

var redirectUrl = g_request.getParameter("sysparm_redirect_url");

gs.getSession().putProperty('pwd_redirect_url', redirectUrl);

var resetPasswordURL = this.getInstanceURL() + '/nav_to.do?uri=' + encodeURIComponent('$pwd_new.do?sysparm_id=' + userId + '&sysparm_request_id=' + requestId + '&sysparm_nostack=true&sysparm_token=' + token);


if (GlideMobileExtensions.getDeviceType() == 'm' || GlideMobileExtensions.getDeviceType() == 'mobile') {

gs.debug("从移动设备发送的密码重置请求。将 URL 更改为移动兼容");

resetPasswordURL = this.getInstanceURL() + '/$pwd_new.do?sysparm_id=' + userId + '&sysparm_request_id=' + requestId + '&sysparm_nostack=true&sysparm_token=' + token;

}


g_response.sendRedirect(resetPasswordURL);

}

在大多数实例都托管于共享租户设置中,ServiceNow 提供了多个层次的沙盒机制,以确保底层机器的安全。JavaScript 的执行被沙盒化,任何涉及底层文件系统的辅助类都受到严格控制,仅限于允许的目录。此外,Java 的 SecurityManager 作为最后一道防线,以防止访问和写入超出租户目录的内容。

即使在这些保护措施下,未经授权的 JavaScript 执行在 ServiceNow 服务上仍可能引发严重的安全问题。

  1. UI 页面

最常见的请求类型往往会过滤到 UI 页面。UI 页面是基于 Apache Jelly 库构建,使用 XML 模板来渲染内容。以下是一个简单的 UI 页面示例:

<?xml version="1.0" encoding="utf-8" ?><j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

<g:evaluate var="jvar_product_name">

gs.getProperty('glide.product.name')

</g:evaluate>

<div style="font-size: 36px">Hello from ${jvar_product_name}, your query param is ${sysparm_foo}</div>

<g2:evaluate var="jvar_time">

new GlideDate().getByFormat("HH:mm:ss");

</g2:evaluate>

<div style="font-size: 36px">The time is $[jvar_time]</div></j:jelly>

如果保存该模板为名为 test 的 UI 页面并访问 /test.do?sysparm_foo=abc,则可以看到相应的输出。这个简单示例体现了几个重要的特点:

1733984945_675a82b1c3c9ce03f1881.png!small?1733984946378

  • Jelly 模板可以执行 JavaScript,与处理器类似。这是通过 ServiceNow 提供的自定义标签 g:evaluate 和 g2:evaluate 来实现的。
  • Jelly 还有自己独特的模板表达式语言,称为 JEXL,采用 ${…} 或 $[…] 的格式进行表示,并以类似于 JavaScript 的方式进行沙盒化。
  • 查询字符串中的 URL 参数会自动作为变量传递给模板。
  • Jelly 模板默认对字符串进行转义,以防止跨站脚本攻击 (XSS),因而通过传递 sysparm_foo= 是安全的。
  • 若要禁用转义,必须手动使用 no_escape 标签。

UI 页面存储在两个地方:一个是存放基于 ‘base’ 的 UI 页面模板,这些模板位于本地文件系统中的 ui.jforms/ 文件夹下;另一个则是在 sys_ui_pages 表中,可在此添加任何所需的页面。

问题迹象

对于特别敏锐的读者而言,可能会对为什么存在两种不同的前缀(g: 和 g2:)以及两种不同的模板表达式语法(${} 和 $[])感到疑惑。答案在于 UI 渲染过程分为两个阶段。整体流程大致如下:

首先,ServiceNow 会渲染模板,处理 g: 和 j: 标签,同时忽略 g2: 和 j2: 标签。在这一阶段,它使用 ${} 作为表达式的分隔符。这种过程被称为“第一阶段”,在此阶段,任何用户提供的值都将被直接插入到模板中。

接着,ServiceNow 会再次评估模板,这次它会处理 g2: 和 j2: 标签,并使用 $[] 作为模板的分隔符,这一过程称为“第二阶段”。

这种双重评估的结构意味着,在第一阶段中所发生的任何内容注入都可能引发模板注入的问题。从根本上来说,这一设计潜藏风险,因为某些出口可能会导致模板注入,而这种情况并不总是显而易见。

当然,开发人员在设计时考虑到了这一点。对于最明显的注入向量,已经实施了多种缓解措施——例如,用户输入中的 $[ 和 ${ 以及 <j2: 和 <g2: 都会被转义,以防止恶意注入。

我们将在后面的讨论中更加详细地介绍这些缓解策略。不过,值得注意的是,如果我们能够在身份验证之前的 UI 中找到一个允许我们在第一阶段注入 XML 标签的地方,那么就有可能实现模板注入。

漏洞1: 标题注入

在 ServiceNow 中,可以在身份验证之前访问的页面和处理器的列表存储在 sys_public 表中。考虑到模板的双重评估原理,我们开始探查可能导致第一阶段标签注入的位置。

最明显的情况出现在 <g:no_escape> 标签的使用上,因此我们开始利用 grep 工具查找该标签。我们很快在文件系统中的 ui.jtemplates/html_page_title.xml 找到了这个模板,它作为每个页面头部的一部分被包含:

<g:evaluate var="jvar_page_title" jelly="true">

var pageTitle = jelly.jvar_page_title;

if (JSUtil.nil(pageTitle)) {

var productName = gs.getProperty('glide.product.name', 'ServiceNow'),

description = gs.getProperty('glide.product.description');

if (gs.getProperty('glide.ui.title.use_product_name_only', 'false') == 'true')

pageTitle = productName;

else

pageTitle = productName + '  ' + description;

}

SNC.GlideHTMLSanitizer.sanitizeWithConfig('HTMLSanitizerConfig', pageTitle);

</g:evaluate>

<title><g:no_escape>${jvar_page_title}</g:no_escape></title>

在此实例中,jelly.jvar_page_title 的用法与 ${jvar_page_title} 类似。值得注意的是,模板中使用 jvar_ 前缀表示内部变量,而 sysparm_ 前缀则表示外部变量。

然而,这仅仅是一种约定——如果我们提供查询参数 ?jvar_page_title=xyz,那么该变量便会被传递到模板中。如果没有重写该变量,它将保持其原始值。这种行为类似于 PHP 早期的 register_globals 特性,而该特性已被证明是安全的。

于是,我们在没有多加思考的情况下,尝试访问 /login.do?jvar_page_title=aaa。令我们惊讶的是,页面标题实际上被成功注入了!这似乎完全不是开发人员的本意。

潜在问题

这种情况表明,在处理用户输入时存在一定的风险。由于可以直接通过 URL 参数注入 HTML 标签,攻击者可能会利用此漏洞进行跨站脚本攻击 (XSS) 或其他注入攻击。对于 Web 应用程序,尤其是涉及用户输入的部分,必须对输入进行严格验证和清理,以避免潜在的安全问题。

1733985125_675a8365541166a97a65c.png!small?1733985128734

但是

可试读前30%内容
¥ 9.9 全文查看
9.9元开通FVIP会员
畅读付费文章
最低0.3元/天
# web安全 # 数据安全 # 漏洞分析 # 网络安全技术 # ServiceNow
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 AlbertJay 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
AlbertJay LV.5
这家伙太懒了,还未填写个人描述!
  • 40 文章数
  • 42 关注者
解析Next.js中的SSRF漏洞:深入探讨盲目的SSRF攻击及其防范策略
2025-02-08
企业防线的薄弱环节:深入了解供应链网络攻击的风险
2024-12-31
修复秘籍:如何有效应对CVE-2024-20767和CVE-2024-21216漏洞
2024-12-19
文章目录