【安全漏洞】Emissary 的SSRF漏洞(CVE-2021-32639)发现过程

导语:通过在Emissary项目上运行标准的CodeQL查询集,我发现了之前报告的任意文件泄露(CVE-2021-32093)。


通过在Emissary项目上运行标准的CodeQL查询集,我发现了之前报告的任意文件泄露(CVE-2021-32093),但也发现了新的漏洞:

不安全的反序列化漏洞 (CVE-2021-32634);

服务器端请求伪造漏洞(CVE-2021-32639);

原始代码注入CVE (CVE-2021-32096)是由社区贡献的CodeQL查询标记的;

到目前为止,还可以通过默认的CodeQL查询发现反映的跨站点脚本漏洞(CVE-2021-32092)。

代码注入 (CVE-2021-32096)

起初我尝试在Emissary 5.9.0代码库上使用CodeQL脚本注入查询时,却没有得到任何结果。

在阅读源代码获取漏洞细节后,我确信我的查询正确地建模了javax.script.ScriptEngine.eval()接收,并且该源代码已经由默认的CodeQL JAX-RS库建模。然而,我意识到从不受信任的数据到脚本注入接收器的流不是“直接的”流。你可以通过查看代码流的方式来理解其原因。

用户数据进入应用程序的 JAX-RS 终端是:

getOrCreateConsole(request) 将调用 RubyConsole.getConsole() ,它会转到:

此代码启动一个运行 RubyConsole.run() 方法的新线程(因为它实现了 Java Runnable 接口):

但是,由于此时 stringToEval 为 null,因此该方法几乎会立即使用 wait() 方法暂停线程。

稍后,在 rubyConsolePost 中,我们可以找到以下代码:

这里是不受信任数据(request.getParameter(CONSOLE_COMMAND_STRING))进入应用程序并流入RubyConsole.evalAndWait()方法的地方。但是,evalAndWait()方法是:

没有对RubyConsole.eval()方法的实际调用,Ruby脚本在该方法中被计算,因此如果跟踪受感染的请求参数,你将在该方法中结束并到达受感染的跟踪的末尾。用户控制的命令只被分配给stringToEval字段,这似乎就到此为止了。然而,如果你仔细观察,你还会看到这个方法正在调用notifyAll()方法,这意味着这个方法将有效地激活被暂停的线程,该线程将依次运行以下表达式:

当rubyConsolePost方法被调用时,RubyConsole.run()会以一个null stringToEval执行,然后进入wait状态。

当调用 evalAndWait(commandString) 时,stringToEval 获取用户控制的脚本,然后恢复 RubyConsole.run() 方法,该方法将评估现在分配的 stringToEval。

因此,没有静态代码分析工具可以有效跟踪的直接数据流。不过,通过使用CodeQL感染步骤对Javawait/notify模式建模,我应该能够发现这个漏洞。

在此代码模式中,你可以看到两种不同类型的块:调用notify的同步块和调用wait的同步块。当同步发生在同一个对象上时,我想将notify块中的写入与wait块上相同字段的读取连接起来。这意味着我需要一个额外的污点步骤来连接这些原本断开连接的节点,以便 CodeQL 的污点跟踪可以桥接这种逻辑断开连接:

启用这个额外的感染步骤后,我成功地报告了这个漏洞:

令人惊叹的是,这个查询不是由GitHub CodeQL工程师开发的,而是由几个CodeQL社区成员贡献和改进的:

https://github.com/github/codeql/pull/2850;

https://github.com/github/codeql/pull/5349;

https://github.com/github/codeql/pull/5802;

这个社区贡献的查询正在进入标准查询集,并将很快提供给所有运行 GitHub 代码扫描的开源项目。

我还向 CodeQL 存储库贡献了我的notify/wait模式感染步骤,这可能很快就会为所有 CodeQL 用户启用同步字段之间的类似数据流分析!

任意文件泄露 (CVE-2021-32093)

CodeQL 发现了默认配置下的任意文件泄露,因此我不会评论此漏洞的详细信息,因为它已经在 SonarSource文章中进行了描述。

不安全的反序列化 (CVE-2021-32634)

CodeQL 默认查询还报告了三个不安全的反序列化操作。

第一个位于 WorkSpaceClientEnqueueAction REST 终端:

可以通过对 /WorkSpaceClientEnqueue.action 的经过身份验证的 POST 请求访问此终端。正如你在源代码中所读到的,表单参数 WorkSpaceAdapterWORK_BUNDLE_OBJ (tpObj) 在第 52 行被解码和反序列化。

幸运的是,这是一个身份验证后的漏洞,由于SonarSource报告修复了跨站请求伪造(CSRF)漏洞,因此无法通过CSRF代表已登录用户利用该漏洞。

CodeQL 还报告了另外两个当前未在代码中执行的不安全反序列化操作。然而,它们可能会在未来的版本中启用。

第一个起源于MoveToAction类,它没有被Jersey服务器公开。

MoveToAction:


MoveToAdapter:

PayloadUtil:

第二个方法来源于WorkSpaceAdapter类的inboundEnque方法。该漏洞需要调用inboundEnque(),但目前尚未执行该调用。

WorkspaceAdapter:


WorkspaceAdapter:

WorkspaceAdapter:


服务器端请求伪造 (CVE-2021-32639)

在CodeQL中发现这个漏洞得益于另一个社区的贡献。报告此漏洞的查询最初是由@lucha-bc和@porcupineyhair贡献的,并且已经被提升为任何CodeQL扫描使用的默认规则集。

该查询报告了两个服务器端请求伪造 (SSRF) 漏洞。第一个影响 RegisterPeerAction REST 终端。例如,以下请求将导致多个请求发送到位于 http://attacker:9999 的攻击者控制的服务器。

需要注意的重要一点是,一些伪造的请求是发送到 /emissary/Heartbeat.action 终端的未经身份验证的请求:

但是,也有经过身份验证的请求发送到攻击者控制的服务器上的 /emissary/RegisterPeer.action 终端:

SSRF 漏洞通常用于访问内部服务器或扫描内部网络,但在这种情况下,我想到了不同的漏洞利用场景。由于 SSRF 漏洞导致 Emissary 使用的 Apache HTTP 客户端发送一个带有摘要身份验证标头的经过身份验证的请求,因此从理论上讲,我可以诱使客户端切换到基本身份验证,从而泄漏服务器凭证。

要使用 Apache HTTP 客户端发送经过身份验证的请求,需要在凭据提供程序上设置凭据,然后配置 HTTP 客户端以使用该凭据提供程序:


可以看到凭据是从 Jetty 用户领域读取的,用于连接到需要凭据的任何主机和任何端口。这些凭证在凭证提供程序 (CRED_PROV) 中设置,该提供程序后来被配置为主要 Emissary 客户端 (CLIENT) 的默认凭证提供程序。

配置没有指定应该使用什么身份验证方案,这让我相信身份验证方案是根据服务器响应决定的。如果我礼貌地要求客户端使用基本身份验证,那么所有迹象都表明服务器凭据可能会以明文形式(base64 编码)发送。

为此,我设置了一个请求基本身份验证的 Web 服务器,然后使用 SSRF 漏洞使 Emissary 服务器连接到我的恶意服务器。 Emissary HTTP客户端很高兴地从摘要身份验证切换到基本身份验证,并将凭据发送给我。以下是我的服务器显示服务器凭证的输出:


同样,AddChildDirectoryAction 终端也容易受到 SSRF 的攻击。对 /AddChildDirectory.action 终端的 POST 请求将触发对攻击者控制的主机的额外请求:

除了修复 SSRF 漏洞之外,NSA还通过只允许摘要身份验证方案来防止身份验证方法的混淆。

反射的跨站点脚本 (CVE-2021-32092)

CodeQL在自动查找SonarSource研究人员报告的大多数漏洞和几个新的关键漏洞方面做得很好,但是它没有报告SonarSource研究人员最初报告的跨站脚本(XSS)漏洞。我检查了查询集,发现没有针对这个特定XSS实例的查询。

然后我开始与 CodeQL 团队合作,对 JAX-RS 终端上的 XSS 漏洞进行相当全面的分析。这个 XSS 检测现在也包含在主 CodeQL 存储库中!

在 REST 终端上准确检测 XSS 并不是一项简单的任务,因为它们中的大多数将默认为 application/json 响应内容类型,这对 XSS 是安全的。因此,我需要 CodeQL 来检测用户控制的数据(无论是反射的还是持久的)在没有正确编码的 HTTP 响应中使用,而且还需要检测此类响应的内容类型已更改为任何 XSS 友好类型。这可以通过以下几种方式实现:

通过使用 ResponseBuilder.type() 显式设置响应内容类型;

通过使用 @Produces 注释来注释封闭方法;

通过使用 @Produces 注释来注释封闭类;

在与 CodeQL 团队一起进行这些查询改进时,我意识到我们对 Spring REST 终端的 XSS 查询也没有考虑响应内容类型,这可能导致许多误报,例如,带有应用程序/json内容类型的响应被标记为可利用的。因此,我们还实现了所需的改进,以使Spring XSS查询达到JAX-RS现在拥有精确度。

【安全学习资料】

你可能感兴趣的:(【安全漏洞】Emissary 的SSRF漏洞(CVE-2021-32639)发现过程)