长亭百川云 - 文章详情

​JBoss \ WildFly remoting3协议反序列化分析

天玄安全实验室

49

2024-07-13

前言

前段时间分析了JBoss 3873和4446端口下的反序列化,受影响的版本最晚已经是2011年发布的,而JBoss EAP 6.X及WildFly\JBoss AS 7.X等后续版本,它们反序列化相关的CVE就很少了。归根结底,是因为以上所说的后续版本弃用了原来的Remoting2协议,启用了Remoting3协议。本文以Remoting3的反序列化相关问题展开分析。

Remoting3简介

Remoting3是JBoss Remoting的下一代协议,它在具备上一代协议所具有的功能的同时,还引入了以下一些性功能。

  • 可拓展的传输协议

  • 可以在运行时检测其他的协议

  • 可拓展的打包策略(Marshalling Strategies)

  • 使用强大的JBoss打包库

  • 相比明文的Java序列化更高效

  • 安全功能

  • 支持SSL协议,可以保护传输数据的完整性和机密性,同时也可用来认证服务端

  • 支持SASL框架,可进行客户端认证和授权

通过抓包对比,正如官网介绍所说,Remoting3协议不同于Remoting2的几乎明文的Java序列化数据,并且在默认配置下需要先进行客户端的认证才可使用后续的EJB3服务。

1

前面所说的可拓展的传输协议,体现在JBoss上即为HTTP服务8080端口的。Remoting3协议支持两种EJB3服务监听模式:直接监听一个端口和复用HTTP服务的端口。前者是JBoss EAP 6.X和JBoss AS 7.X使用的模式,对应的scheme是remote://,而后者是JBoss EAP 7.X和WildFly使用的模式,对应的scheme是http-remoting://http://(视版本而定)。

端口复用这块从流量这看是比较简单,客户端先发送一个带Upgrade: jboss-remoting头的HTTP请求,然后服务端返回101状态码切换协议,后续流量则与监听端口的模式无异。

环境部署

Remoting3协议服务于EJB3,而EJB3与RMI类似,支持对象传参,涉及到对象参数必然会与序列化和反序列化扯上关系。要研究Remoting3协议的反序列化机制,首先得部署一个EJB3的服务。本文后续的分析均以WildFly 8.2.1.Final为例,下载相应的版本,在启动前先使用bin/add-user.sh脚本添加一个用户。

将以下两个类编译为Jar包,并从9990端口登录到控制台,部署打包好的Jar包。

`package com.illucit.ejbremote.server;      import javax.ejb.Remote;      @Remote   public interface ExampleService {    public Object greet(Object object);      }   `
`package com.illucit.ejbremote.server;      import javax.ejb.Stateless;      @Stateless   public class ExampleServiceImpl implements ExampleService {          @Override       public Object greet(Object object) {           return object;       }      }   `

新建一个Java项目,并将WildFly的bin/client目录下的jar包复制到项目的lib目录,同时将以上两个类也加入到项目中。然后在resource目录下添加jboss-ejb-client.properties,填入以下配置内容。

`remote.connections=default   remote.connection.default.host=192.168.78.132   remote.connection.default.port = 8080   remote.connection.default.username=<username>   remote.connection.default.password=<password>   `

最后添加以下类作为客户端,调用远程EJB3服务。

`import com.illucit.ejbremote.server.ExampleService;      import javax.naming.Context;   import javax.naming.InitialContext;   import javax.naming.NamingException;      import java.util.Date;   import java.util.Hashtable;         public class Client {          public static void main(String[] args) throws Exception {           System.out.printf(String.valueOf(lookupExample().greet2(new Date())));       }          private static ExampleService lookupExample() throws NamingException {           final Hashtable<String, String> jndiProperties = new Hashtable();           jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");           final Context context = new InitialContext(jndiProperties);              String url = "ejb:/ejb-remote-server/ExampleServiceImpl!" + ExampleService.class.getName();           System.out.println(url);           return (ExampleService) context.lookup(url);       }   }   `

![](屏幕截图 2022-06-24 180627.png)

技术分析

首先用一个在服务端不存在的类作为参数,调用EJB3服务。在客户端抛出的异常信息的调用栈中,可以看到org.jboss.marshalling.AbstractObjectInputreadObject方法被调用。

在服务端该方法下断点,当前的类是AbstractObjectInput的子类org.jboss.marshalling.river.RiverUnmarshaller,反序列化调用自身的doReadObject方法处理。

跟进到doReadObject方法,代码中会有一个switch循环体,根据从输入流获取到的字节进入不同的分支。后续会被调用到的doReadNewObjectdoReadClassDescriptor也会有一个switch循环体,想必就是官方所说的可拓展的打包策略。

通过控制输入流进入到switch循环判断的字节,从而可以控制返回类示例或者反序列化对象。笔者梳理了这些switch循环体的分支,整理了几个会返回反序列化对象的流程,他们无一例外的会直接readObject反序列化或loadClass反射类实例,此时他们的类加载器都是ModuleClassLoader

这是一个很特殊的类加载器,反序列化利用链中常见的类,比如UnicastRef、CC链(程序中实际有用到这个依赖),甚至于原生反序列化链中的一些类都是无法加载的。

实测只有两个module路径下jar包里的部分类和jdk部分原生类可以被这个类加载器加载。在可以加载的类中能找到一些Sink类,比如ValueExpressionImplMethodExpressionImpl,在RichFaces的CVE-2018-12533中有用到这两个Sink类的反序列化链,但Source和Gadget类虽然有,但ModuleClassLoader类加载器并不能加载。

反序列化这条路行不通,回看调用栈中处理消息的方法processMessage。开头用反序列化获取appNamemoduleNamedistinctNamebeanName,也就是客户端lookup查询的url。虽说这种用法存在风险,在不知道部署了什么EJB3服务的情况下,也可以进行反序列化,但目前看来有难度,所以暂时也没有什么问题。

接着看后面的代码,有一个反射Method的操作,从EJB3部署信息获取到的ComponentView,根据方法名和方法参数反射Method。回溯Method的来源,发现是与反序列化的locator有关。这个locator其实就是封装了客户端EJB3服务接口的EJBLocator,那可以客户端使用恶意的EJB3服务接口,从而反射服务端类的方法?笔者也做了下尝试,但若想要反射的服务端类不是一个接口类,则会抛出一个内接口类的异常,即使是没有抛出异常,从部署信息获取ComponentView这一步也没法获取服务端EJB3服务接口之外的内容。

后记

Remoting3协议的分析原本是以挖掘协议漏洞为目的,但最终也没有发现什么太大的问题,不得不感叹现在JBoss\WildFly的版本比之前使用remoting2协议的版本安全性上升了一个台阶。翻看近年WildFly的CVE,有一个反序列化相关的CVE-2020-10740,没有验证机制使得可能通过EJB发起远程反序列化攻击,说的大概就是本文讨论的内容。查看在WildFly20.0发布的修复,仅仅是增加了黑名单验证。

除此之外,客户端还有个不大不小的问题,它的反序列化流程和服务端差不多,但客户端这里的类加载器就是普通的类加载器,可以用到部分的反序列化链,这场景就有点像RMI的反序列化服务端传来的恶意结果或异常。也许能用在反制、中间人攻击等场景。。

文中若有什么错误的地方,敬请师傅们斧正。

参考

https://jbossremoting.jboss.org/remoting-3

https://paper.seebug.org/766/

https://github.com/illucIT/remote-ejb-example

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2