01
漏洞背景
2023-04-20 VMware 官方发布安全更新 VMSA-2023-0007,其中漏洞 CVE-2023-20864
评分9.8, 是一个未授权反序列化漏洞,影响版本 8.10.2
, 官方给出的修复方式是更新到 8.12
版本。
官方文档说明
-
https://kb.vmware.com/s/article/91831 -
https://www.vmware.com/security/advisories/VMSA-2023-0007.html
02
补丁分析
由于是未授权反序列化漏洞,所以从反序列化特征寻找漏洞补丁位置。
如下图,可以看到明显的反序列化修复逻辑。
新增 SerializationUtil, 使用 ValidatingObjectInputStream 方式来进行反序列化过滤,只允许反序列化传入的requestClass 类
找到 SerializationUtil
的调用,在api-play-service 包中 InternalClusterController 类,分别对于三个请求处理方法 applyMembership
, setToken
, approveMembership
进行了修正
设置允许的反序列化白名单。
ApplyMembershipResult result = provider.applyMembership((ApplyMembershipRequest)SerializationUtil.deserializeSecure(ApplyMembershipRequest.class, is));
var5 = ok(SerializationUtils.serialize(result));
SetTokenResult result = provider.setToken((SetTokenRequest)SerializationUtil.deserializeSecure(SetTokenRequest.class, is));
var5 = ok(SerializationUtils.serialize(result));
SetConfigResult result = provider.approveMembership((SetConfigRequest)SerializationUtil.deserializeSecure(SetConfigRequest.class, is));
var5 = ok(SerializationUtils.serialize(result));
03
服务组件说明
vRealize LogInsight 并不是单一的web服务架构,系统中包含了多个服务,其中修复的反序列化处理进程由 LogInsightDaemon
发起,而web进程由tomcat 进行处理
LogInsightDaemon
LogInsightDaemon 进程如下
/usr/lib/loginsight/application/3rd_party/bin/java ...... -cp /usr/lib/loginsight/application/lib/* ...... com.vmware.loginsight.daemon.LogInsightDaemon --wait=120
启动类为 com.vmware.loginsight.daemon.LogInsightDaemon
观察其逻辑,首先是一个启动进程,其中的重点服务有
-
Cassandra 数据库,监听 9042 端口 -
Thrift 服务 ,VMSA-2023-0001 中多个漏洞服务组件 -
Tomcat 服务,监听 443 和 80端口 -
API 服务,监听9000端口
Api 服务
观察其API 服务,可以看到是一个 *Play* Framework
框架, 一种高效的Java和Scala Web应用程序 框架,具备异步非阻塞,无状态等特点
protected synchronized void start(ServiceRegistryClient registry) throws Exception {
this.registry = registry;
CollectionAgentsConfig agentCfg = (CollectionAgentsConfig)this.configurationHolder.getConfiguration(CollectionAgentsConfig.class);
DistributedConfig distribCfg = (DistributedConfig)this.configurationHolder.getConfiguration(DistributedConfig.class);
LogImporterConfig importerConfig = (LogImporterConfig)this.configurationHolder.getConfiguration(LogImporterConfig.class);
ApiServerConfig asc = (ApiServerConfig)this.configurationHolder.getConfiguration(ApiServerConfig.class);
this.setServerConfig(asc);
DaemonInfo di = distribCfg.getDaemonForCurrentHost();
this.setAPIToken(di.getToken());
File root = new File(System.getProperty("user.dir"));
GuiceApplicationBuilder builder = (GuiceApplicationBuilder)((GuiceApplicationBuilder)((GuiceApplicationBuilder)(new GuiceApplicationBuilder()).in(root)).in(Mode.PROD)).in(APIServiceController.class.getClassLoader());
if (!SystemUtils.IS_WINDOWS) {
System.setProperty("pidfile.path", "/dev/null");
}
this.serverPort = asc.getServerPort();
this.serverSecurePort = asc.getServerSecurePort();
builder = (GuiceApplicationBuilder)builder.configure("play.server.http.port", Integer.toString(this.serverPort));
builder = (GuiceApplicationBuilder)builder.configure("play.server.https.port", Integer.toString(this.serverSecurePort));
String bindAddress = "0.0.0.0";
if (!asc.shouldBindAllInterfaces()) {
bindAddress = di.getHost(asc.getBindAddress());
builder = (GuiceApplicationBuilder)builder.configure("play.server.http.address", bindAddress);
builder = (GuiceApplicationBuilder)builder.configure("play.server.https.address", bindAddress);
}
System.setProperty("https.keyStore", FileUtils.join(new String[]{"application", FIPS.getInstance().getTomcatKeyStorePath()}));
builder = (GuiceApplicationBuilder)builder.configure("play.server.https.keyStore.path", FileUtils.join(new String[]{"application", FIPS.getInstance().getTomcatKeyStorePath()}));
if (FIPS.getInstance().isFIPSModeEnabled()) {
builder = (GuiceApplicationBuilder)builder.configure("play.server.https.keyStore.algorithm", "X.509");
builder = (GuiceApplicationBuilder)builder.configure("play.server.https.keyStore.type", FIPS.getInstance().getKeyStoreType());
}
builder = (GuiceApplicationBuilder)builder.configure("play.server.https.keyStore.password", importerConfig.getSyslogSslKeystorePassword());
builder = (GuiceApplicationBuilder)builder.configure("http.netty.option.child.keepAlive", Boolean.toString(true));
builder = (GuiceApplicationBuilder)builder.configure("play.server.netty.eventLoopThreads", Integer.toString(NUM_NETTY_IO_THREADS));
Application app = builder.build().asScala();
ServerConfig sc = new ServerConfig(root, Option.apply(this.serverPort), Option.apply(this.serverSecurePort), bindAddress, app.mode(), System.getProperties(), app.configuration());
this.server = (new NettyServerProvider()).createServer(sc, app);
try {
AgentManager.INSTANCE.setConfiguration(agentCfg.getClassMap());
} catch (ParseError var13) {
throw new ConfigurationException(var13);
}
this.setAgentDefaultConfig(agentCfg.getAgentConfigText());
registry.registerInstance(RegistryUtils.serviceRegisterRequest(registry, this.server.mainAddress(), ServiceName.API_SERVICE.getInternalName(), ServiceInstanceType.REST));
this.doPing(di.getHost(asc.getBindAddress()));
this.cleanupEnabled = asc.getCleanupTmpEnabled();
this.cleanupExpire = asc.getCleanupTmpExpire();
this.cleanupInterval = asc.getCleanupTmpInterval();
this.restartScheduler();
this.serversStarted.set(true);
}
重点关注其路由文件 api-play-service jar 包中的 routes 文件
对比8.10 和 8.10.2 ,可以看到路由中新增了三个路径 applyMembership 等,为此次触发接口
Web 服务
web 服务由tomcat 启动,在 web.xml
部署文件中声明了 stripes Filter ,使用的是 stripes web框架(Stripes 是基于动作的MVC(模型视图控制器)框架)
<display-name>Stripes Filter</display-name>
<filter-name>StripesFilter</filter-name>
<filter-class> net.sourceforge.stripes.controller.StripesFilter
</filter-class>
dynamicMappingfilter 注册了所有的actionBean ,通过uri 寻址 目标actionbean, 进入 net.sourceforge.stripes.controller.DispatcherServlet
处理
在 LoginInterceptor
, SecurityInterceptor
中进行了身份,权限判断。
LoginInterceptor
当请求上下文 user 为空时,且访问对象具备 NotAuthenticated 注解时,会绕过身份判断。
APIProxyActionBean 是请求转发类,将请求转发到 NetUtils.LOCALHOST, UIConfigManager.getApiServerConfig().getServerPort() , request.getRequestURI()
这里的 ApiServerConfig 的 ServerPort 自然是指 9000 端口
这样,当请求 web 服务的 api 路由时就会将请求转发给 api server 9000 端口,当请求路径是以下请求时就可以把body带入反序列化处理部分
-
/api/v2/internal/cluster/applyMembership -
/api/v2/internal/cluster/setToken -
/api/v2/internal/cluster/approveMembership
04
漏洞复现
使用ysoserial 构建反序列化数据作为body。此处以创建文件作为用例
05
参考链接
-
https://kb.vmware.com/s/article/91831 -
https://www.vmware.com/security/advisories/VMSA-2023-0007.html -
https://www.playframework.com/ -
https://github.com/StripesFramework/stripes
06
关于雪诺凛冬实验室
原文始发于微信公众号(雪诺凛冬实验室):CVE-2023-20864 VMware Aria Operations for Logs 未授权反序列化漏洞分析
- 我的微信
- 微信扫一扫
-
- 我的微信公众号
- 微信扫一扫
-
评论