译文_捕捉VMware中的错误_多处RCE

开卷有益 · 不求甚解

前言

去年,我们在 VMware 产品中发现了许多令人兴奋的漏洞。供应商已收到通知,并且已对其进行了修补。这是我们研究的第二部分。本文介绍了 VMware Carbon Black Cloud Workload Appliance (CVE-2021-21978) 中的身份验证绕过和 VMware vRealize Operations 中的利用链(CVE-2021-21975、CVE-2021-22023、CVE-2021-21983)远程代码执行。

VMware Carbon Black 云工作负载设备

我们的故事始于 VMware Carbon Black Cloud Workload Appliance 中的一个漏洞,我们成功地绕过了身份验证机制并获得了对管理控制台的访问权限。

该设备托管在本地,是组织基础架构与端点保护平台 VMware Carbon Black Cloud 之间的链接。

Carbon Black 云工作负载组件

通过使用 netstat 命令检查 0.0.0.0 上的可用端口,我们在端口 443 上找到了一个 Web 应用程序。

netstat命令的输出

申请登录页面

前端服务器是 Envoy 代理服务器。在查看其配置文件后,我们确定进一步的请求被代理到基于 tomcat 的微服务。

配置摘录/opt/vmware/cwp/appliance-gateway/conf/cwp-appliance-gateway.yaml:

node:  cluster: cwp_appliance  id: cwp-appliance-v1-2020static_resources:  clusters:    -       connect_timeout: 5s      hosts:        -          socket_address:            address: "127.0.0.1"            port_value: 3030      lb_policy: round_robin      name: service_vsw      type: LOGICAL_DNS    -      connect_timeout: 5s      hosts:        -          socket_address:            address: "127.0.0.1"            port_value: 3020      lb_policy: round_robin      name: service_apw      type: LOGICAL_DNS    -      connect_timeout: 5s      hosts:        -          socket_address:            address: "127.0.0.1"            port_value: 3010      lb_policy: round_robin      name: service_acs      type: LOGICAL_DNS

netstat使用命令发现 Java 服务

在研究了application.yml该服务的配置文件后,该配置文件被调用service_acs并在端口 3010 上运行,我们发现实现了来自 Java Spring 框架的基于角色的访问模型。

// application.ymlrbacpolicy:  role:  - name: SERVICE_USER    description: This role gives you access to all administration related work    default: DENY    permissions:     - *:*  - name: APPLIANCE_USER    description: This role gives you access to all administration related work    default: DENY    permissions:     - acs:getToken     - acs:getServiceToken      - apw:getApplianceDetails     - apw:getApplianceSettings     - apw:getNetworkConf …

对角色政策的粗略检查提出了许多问题:

什么是服务用户?为什么它有无限的能力?API 方法有什么作用getServiceToken?

我们决定从探索getServiceTokenAPI 方法开始。打开源代码,我们研究了这个方法的描述。“为服务请求生成 JWT 令牌”意味着每次应用程序需要对内部 API 方法调用进行身份验证时,它都会访问此 API 并接收授权令牌。

摘自TokenGeneratorApi.java:

@ApiOperation(      value = "Generate JWT Token for Service Request",      nickname = "getServiceToken",      notes = "",      response = AccessTokenDTO.class,      tags = {"TokenGenerator"}   )   @ApiResponses({@ApiResponse(   code = 200,   message = "OK",   response = AccessTokenDTO.class…   @RequestMapping(      value = {"/api/v1/service-token/{serviceName}"},      produces = {"application/json"},      method = {RequestMethod.GET}   )   ResponseEntity<AccessTokenDTO> getServiceToken(@ApiParam(value = "name of the service which is requesting token",required = true) @PathVariable("serviceName") String serviceName);

让我们尝试通过从内部网络访问连接到端口 3010 的服务来获取授权令牌。

使用 cURL 访问 Java 服务 API 方法

我们得到了一个 JWT 令牌,它原来是我们的老朋友,服务用户的角色。

JWT 令牌负载的解码:

{  "sub": "any-service",  "iss": "user-service",  "nbf": ,  "exp": ,  "policy": {    "role": "SERVICE_USER",    "permissions": {      "*": [        "*"      ]    }  },  "refreshable": false,  "iat": }

无需身份验证即可为超级用户生成令牌的前景看起来非常诱人。让我们尝试做同样的技巧,但这次是在外部,通过 Envoy 服务器。

尝试通过访问 Envoy 服务器获取服务令牌

我们失败了,尽管我们可以使用 Java 服务的其他 API 方法。让我们看看内部服务的代理是如何组织的,并研究负责路由的机制。

当使用 Envoy 代理服务器作为前端服务器时,可以使用 Route Discovery API 动态生成路由表。为此,在后端服务中,使用io.envoyproxy.envoy.api包中的 DiscoveryRequest 和其他实体来描述路由的配置。

/admin/使用 Envoy API创建路由器的示例:

public String routeDiscovery(final DiscoveryRequest discoveryRequest) {      ...      Route admin = Route.newBuilder().setMatch(RouteMatch.newBuilder().setPrefix("/admin/").build()).setRoute(RouteAction.newBuilder().setCluster("admin_cluster").setHostRewrite(this.hostName).build())      Builder virtualHostOrBuilder = VirtualHost.newBuilder().setName("backend").addDomains("*");      virtualHostOrBuilder.addRoutes(admin);      VirtualHost virtualHost = virtualHostOrBuilder.build();      RouteConfiguration routeConfiguration = RouteConfiguration.newBuilder().setName("route").addVirtualHosts(virtualHost).build();      DiscoveryResponse discoveryResponse = DiscoveryResponse.newBuilder().setVersionInfo("1").addResources(Any.pack(routeConfiguration)).build();      TypeRegistry typeRegistry = TypeRegistry.newBuilder().add(DiscoveryResponse.getDescriptor()).add(ClusterLoadAssignment.getDescriptor()).add(RouteConfiguration.getDescriptor()).build();      String response = null;      ...      try {         response = JsonFormat.printer().usingTypeRegistry(typeRegistry).print(discoveryResponse);      } catch (InvalidProtocolBufferException err) {         log.error("Error while serializing response", err);      }      return response;   }

让我们考虑一个来自 Java 服务的特定示例。

摘自EnvoyXDSServiceImpl.java:

package com.vmware.cwp.appliance.applianceworker.service.impl;@Componentpublic class EnvoyXDSServiceImpl implements EnvoyXDSService {...   public String routeDiscovery(final DiscoveryRequest discoveryRequest) {...Route service_token_block = Route.newBuilder()   .setMatch(RouteMatch.newBuilder()   .setPrefix("/acs/api/v1/service-token").build())   .setRoute(RouteAction.newBuilder().setCluster("service_vsw")   .setPrefixRewrite("/no_cloud").build()).build();...Route acs = Route.newBuilder()   .setMatch(RouteMatch.newBuilder()   .setPrefix("/acs/").build())   .setRoute(RouteAction.newBuilder()   .setCluster("service_acs")   .setHostRewrite(applianceIPv4Address).build()).build();...

我们看到,当我们遇到 URL/acs/api/v1/service-token时,应用程序将请求转发到存根页面,而不是将请求传递给服务进行处理。同时,任何带有前缀的 URL/acs/*都会被转发到后端。我们的任务是绕过黑名单,通过白名单条件。我们需要 Envoy 服务器的一个特殊功能才能做到这一点。我们阅读了文档,发现了一个有趣的点:Envoy 服务器默认禁用了规范化。

特使文档 节选

特使文档 节选

尽管 Envoy 开发人员建议在使用 RBAC 过滤器时不要忘记启用此属性,但默认值通常保持不变,就像在这种情况下一样。禁用规范化意味着 URL/acs/api/v1/service-token/rand和/acs/api/v1/%73ervice-token/rand将被 Envoy API 视为不相同的字符串,尽管在另一台服务器(例如 tomcat)规范化后,这些 URL 将再次被视为相同。

事实证明,如果我们将 API 方法名称中的至少一个字符更改为其 URL 表示,我们可以绕过黑名单而不违反白名单条件。

我们发送修改后的请求并接收服务令牌。

完毕。我们现在有一个具有超级用户权限的服务令牌,它授予我们对该软件的管理员权限。

VMware vRealize Operations Manager

在下一个故事中,我们将告诉您自动化软件中发现的漏洞链。

服务器端请求伪造

我们从调查 Operations Manager API 开始,发现了一些无需身份验证即可使用的方法。其中包括 API-method /casa/nodes/thumbprints,它将地址作为用户参数。通过将我们控制下的远程服务器的地址指定为 HTTP 请求中的参数,我们会收到来自 Operations Manager 实例的带有 URL-path 的 GET 请求/casa/node/thumbprint。

尝试执行 SSRF

远程服务器日志中的 GET 请求

要完全控制 URL 路径,我们可以添加“?” 用于切断应用程序通常连接的路径的符号。让我们发送一个带有自定义路径的请求:

使用任意路径执行 SSRF

远程服务器日志中的 GET 请求

因此,我们能够代表应用程序发出任何 GET 请求,包括对内部资源的请求。

在能够向内部资源发出 GET 请求后,我们尝试向某些 API 方法发出请求,这些方法仅对授权用户可用。因此,例如,我们可以访问用于在节点之间同步密码的 API 方法。调用该方法时,我们通过两种不同的哈希算法——sha256和sha512得到管理员的密码哈希。

通过复制功能获取管理员密码哈希

值得一提的是,不建议将 sha 系列算法用于密码散列,并且可以以很高的成功率破解。并且由于应用程序中的管理员对应于服务器上的系统管理员用户,如果系统中有无密钥操作模式的 ssh 服务器,您可以连接到服务器并获得对命令 shell 的访问权限。要存储密码等敏感数据,最佳做法是使用所谓的慢速哈希函数。

凭证泄漏

尽管在这个阶段获得 shell 访问的可能性很高,但上述方法并不能完全保证,因此我们继续研究。值得注意的是,使用 SSRF,我们如何访问需要身份验证的 API 方法。我们知道有几种机制可以提供这种功能,在这种情况下,没有选择最好的方法。事实上,应用程序每次访问 API 时,都会在请求中添加一个基本的身份验证标头。为了从标头中提取凭证,我们向远程嗅探器发送了一个 SSRF 请求,作为响应输出 http 请求的内容:

使用 HTTP 请求嗅探器提取凭据。

maintenanceAdmin用户凭据

应用程序似乎使用maintenanceAdmin用户访问 API。让我们尝试使用这些凭据直接访问受保护的 API 方法,而不使用 SSRF。

验证该帐户是否已启动并正在运行

好吧,现在我们拥有超级用户权限,我们离控制服务器只有一步之遥。在查看了所有 API 方法后,我们发现了两种访问 shell 的方法。

RCE(密码重置)

第一种也是粗略的方法涉及使用PUT /casa/os/slice/userAPI 方法为管理用户重置密码。此方法允许您更改用户的密码而无需额外验证,例如当前密码。由于系统中存在同名的admin用户,所以通过SSH连接系统并不难。

更改管理员密码

如果 SSH 被禁用,只需使用其中一种 API 方法启用它。

启用 SSH 服务器

通过 ssh 连接到 vROps 服务器

RCE(路径遍历)

以前的方法涉及重置管理员密码,这可能会在渗透测试时破坏客户的工作流程。作为替代方法,我们找到了一种使用/casa/private/config/slice/ha/certificateAPI 方法通过路径遍历攻击加载 Web shell 的方法。上传到服务器 web 目录的轻量级 JSP-shell 将用作 web shell。

利用路径遍历攻击

上传后,我们在访问 shell,在cmd参数中传递命令。

id在 vROps 服务器上执行命令

最后

感谢您将本文阅读到最后。我们希望您能够从我们的研究中找到有用的东西。无论您是开发人员、研究人员,甚至是 PSIRT 的负责人。

我们还想强调,这项研究产生了 9 个不同严重程度的 CVE,每份报告都由 VMware 安全响应中心团队极其谨慎地处理。我们感谢 VMware 的这种合作。

译文申明

文章来源为近期阅读文章,质量尚可的,大部分较新,但也可能有老文章。开卷有益,不求甚解,不需面面俱到,能学到一个小技巧就赚了。译文仅供参考,具体内容表达以及含义, 以原文为准 (译文来自自动翻译)如英文不错的,尽量阅读原文。(点击原文跳转)每日早读基本自动化发布(不定期删除),这是一项测试

最新动态: Follow Me

/微博:red4blue

/知乎:blueteams