前言
1、這篇作為Shiro的權限繞過的筆記
2、這篇文章作為之前筆記Filter權限繞過筆記的拓展
Filter權限繞過筆記:https://www.cnblogs.com/zpchcbd/p/14815501.html
3、謝謝酒館師傅發個一個文章 shiro權限繞過史,方便學習
本片筆記參考文章:
文章一:https://www.anquanke.com/post/id/240033
文章二:https://blog.riskivy.com/shiro-權限繞過漏洞分析(cve-2020-1957)/
文章三:Shiro權限繞過史,作者也不知道誰,就一個pdf
什么是Shiro
Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易於理解的API,您可以快速、輕松地獲得任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。
關於Shiro漏洞歷史線
CVE-2020-17523(權限繞過)
Apache Shiro before 1.7.1, when using Apache Shiro with Spring, a specially crafted HTTP request may cause an authentication bypass.
CVE-2020-17510(權限繞過)
Apache Shiro before 1.7.0, when using Apache Shiro with Spring, a specially crafted HTTP request may cause an authentication bypass.
If you are NOT using Shiro’s Spring Boot Starter (shiro-spring-boot-web-starter), you must configure add the ShiroRequestMappingConfig auto configuration to your application or configure the equivalent manually.
CVE-2020-13933(權限繞過)
Apache Shiro before 1.6.0, when using Apache Shiro, a specially crafted HTTP request may cause an authentication bypass.
CVE-2020-11989(權限繞過)
Apache Shiro before 1.5.3, when using Apache Shiro with Spring dynamic controllers, a specially crafted request may cause an authentication bypass.
CVE-2020-1957(權限繞過)
Apache Shiro before 1.5.2, when using Apache Shiro with Spring dynamic controllers, a specially crafted request may cause an authentication bypass.
CVE-2019-12422(shiro-721)
Apache Shiro before 1.4.2, when using the default “remember me” configuration, cookies could be susceptible to a padding attack.
CVE-2016-6802(權限繞過)
Apache Shiro before 1.3.2 allows attackers to bypass intended servlet filters and gain access by leveraging use of a non-root servlet context path.
CVE-2016-4437(shiro-550)
Apache Shiro before 1.2.5, when a cipher key has not been configured for the “remember me” feature, allows remote attackers to execute arbitrary code or bypass intended access restrictions via an unspecified request parameter.
參考文章:https://shiro.apache.org/security-reports.html
環境搭建
第一步
第二步
第三步:
第四步:
maven添加依賴
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.2</version>
</dependency>
前置知識點
問題:
1、在tomcat和shiro的filter中,哪個會先進行執行?后進行執行?
2、關於Shiro攔截器
Shiro框架通過攔截器功能來實現對用戶訪問權限的控制和攔截。
Shiro中常見的攔截器有anon,authc等攔截器,還有其他的,這里是探討關於authc攔截器的繞過。
1.anon為匿名攔截器,不需要登錄就能訪問,一般用於靜態資源,或者移動端接口
2.authc為登錄攔截器,需要登錄認證才能訪問的資源。
...
3、關於shiro的攔截匹配模式
Shiro的URL路徑表達式為Ant格式:
/hello
:只匹配url,比如 http://demo.com/hello
/h?
:只匹配url,比如 http://demo.com/h+任意一個字符
/hello/*
:匹配url下,比如 http://demo.com/hello/xxxx 的任意內容,不匹配多個路徑
/hello/**
:匹配url下,比如 http://demo.com/hello/xxxx/aaaa 的任意內容,匹配多個路徑
4、shiro是如何運行的?
我這里也不太懂shiro,這里就正常的看下shiro源碼是如何運行的就可以了,簡單的學下
shiroConfig需要通過ShiroFilterFactoryBean類來進行配置,一個正常的ShiroConfig.java如下所示
@Configuration
public class ShiroConfig {
@Bean
MyRealm myRealm(){
return new MyRealm();
}
@Bean
public DefaultWebSecurityManager manager(){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(myRealm());
return manager;
}
@Bean
public ShiroFilterFactoryBean filterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager());
factoryBean.setUnauthorizedUrl("/login");
factoryBean.setLoginUrl("/login");
Map<String, String> map = new HashMap<>();
map.put("/login", "anon");
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
}
對於路徑的訪問權限的控制都是基於ShiroFilterFactoryBean類來進行配置的
@Bean
public ShiroFilterFactoryBean filterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager());
factoryBean.setUnauthorizedUrl("/login");
factoryBean.setLoginUrl("/login");
Map<String, String> map = new HashMap<>();
map.put("/login", "anon");
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
這里可以來到ShiroFilterFactoryBean類中,看它是如何作用的,該類中存在一個getObject方法
public Object getObject() throws Exception {
if (instance == null) {
instance = createInstance();
}
return instance;
}
其中createInstance方法是來創建相關Shiro Filter,結果是返回一個繼承了AbstractShiroFilter的SpringShiroFilter對象
protected AbstractShiroFilter createInstance() throws Exception {
log.debug("Creating Shiro Filter instance."); // 創建shiro Filter的實例
SecurityManager securityManager = getSecurityManager();
if (securityManager == null) {
String msg = "SecurityManager property must be set.";
throw new BeanInitializationException(msg);
}
if (!(securityManager instanceof WebSecurityManager)) {
String msg = "The security manager does not implement the WebSecurityManager interface.";
throw new BeanInitializationException(msg);
}
FilterChainManager manager = createFilterChainManager();
PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
chainResolver.setFilterChainManager(manager);
return new SpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
}
SpringShiroFilter這個對象是繼承了AbstractShiroFilter,而這個AbstractShiroFilter中存在一個doFilterInternal,這個方法我自己有了解,因為Filter的內存文章中tomcat這個其中的InternaldoFilter就是去調用我們每個Filter對象中實現的doFilter方法的一個方法,而這里的doFilterInternal可能不太一樣,它是通過executeChain來進行鏈式調用傳入的chain參數,這個chain是一個FilterChain對象
其中executeChain方法來對傳入的FilterChain對象進行處理調用doFilter方法
此時前面的tomcat的Filter對象中的doFilter已經走完了,tomcat的走完了才開始走shiro的filter(PS:此時tomcat的過濾器並不是完全走完,還有最后一個在shiro的后面)
接着就是getExecutionChain方法,其中就會通過FilterChainResolver的getChain方法來進行解析,從這里開始shiro的攔截器就開始進行發揮作用了,這里如果是被正確的攔截了,那么原始的chain則會被替換為shiro的filterChainManager.proxy(originalChain, pathPattern)
所返回的FilterChain,如果沒有被攔截則最后不會被替換,還是走原來的chain
那么shiro是如何判斷是否被攔截的?可以繼續看
繼續跟到pathMatches方法中去看,他會用自己的一個路徑匹配器PathMatcher來進行比較
最后doMatch方法獲取 需要校驗的路徑 和 當前客戶端訪問的路徑來進行比較,是否是需要進行攔截的
判斷是否需要攔截,具體根據其中的matchStrings方法來進行判別,到這里一次請求的判斷就結束了
接下來看漏洞分析...
CVE-2016-6802(SHIRO-682)
參考:https://issues.apache.org/jira/browse/SHIRO-682
影響版本: shiro<1.5.0
漏洞原理:shiro與spring的URI中末尾的/
不同導致的權限繞過
其中表示匹配零個或多個字符串,/可以匹配/hello,但匹配不到/hello/因為*通配符無法匹配路徑。
假設/hello接口設置了authc攔截器,訪問/hello將會被進行權限判斷,如果請求的URI為/hello/呢,/*的URL路徑表達式將無法正確匹配,放行。然后進入到spring(Servlet)攔截器,而spring中 /hello 形式和 /hello/形式的URL訪問的資源是一樣的,從而導致了繞過。
漏洞分析:
這里的話從getChain方法開始看,這里會先獲取相關的URI,然后傳入pathMatches來進行比較
我們設置一個權限攔截器,如下所示:
@Bean
public ShiroFilterFactoryBean filterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager());
factoryBean.setUnauthorizedUrl("/login");
factoryBean.setLoginUrl("/login");
Map<String, String> map = new HashMap<>();
map.put("/admin", "authc"); // 新增的攔截器
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
訪問:http://127.0.0.1:8080/admin/
首先來到的地方還是getChain
跟到doMatches 這里調用的是doMatch,這里/admin/
和/admin
經過tokenizeToStringArray返回的數據是一樣的
/admin/
和/admin
經過tokenizeToStringArray返回的數據是一樣的,但是接着后面就是會進行判斷,如果結尾存在/
,則返回為False
返回為False,就不會走shiro的攔截器了,最后就成功繞過了,shiro的過濾器攔截
而正常的訪問:http://127.0.0.1:8080/admin ,則會被shiro的攔截器捕獲
問題思考
在CVE-2016-6802(SHIRO-682)中,通過后面加/
,來繞過了shiro的攔截器,那么此時的URI則是http://127.0.0.1:8080/admin/
,那么又為什么可以訪問到接口為admin接口的資源呢?
這里就要引出sprng的路徑處理的過程了...
接着來看spring是如何處理URI的,此時我們已經到了如下的位置
F8走出來,到此shiro的過濾攔截器已經執行完成了,又開始進行轉發請求的操作chain.doFilter(request, response)
到這里可以繼續看下過濾器的數量,shiro過濾器走的是filterFactoryBean,那么此時還有最后一個過濾器還沒走,Tomcat WebSocket (JSR356) Filter
走完最后一個Tomcat Filter之后,請求被轉發到了spring中,這里的話,斷在org/springframework/web/util/UrlPathHelper.java#getLookupPathForRequest方法上,此時在Shiro包裝的HttpServlet來進行處理,這里則返回了/admin/
接着F8一直走到lookupHandlerMethod,來調用對應接口的方法,這個地方就是重點,看他如何處理這個/admin/
,這里就不跟了,可以自己跟下,調用鏈如下,我的springboot版本2.5.2
getMatchingPattern:336, PatternsRequestCondition (org.springframework.web.servlet.mvc.condition)
getMatchingPatterns:296, PatternsRequestCondition (org.springframework.web.servlet.mvc.condition)
getMatchingCondition:281, PatternsRequestCondition (org.springframework.web.servlet.mvc.condition)
getMatchingCondition:406, RequestMappingInfo (org.springframework.web.servlet.mvc.method)
getMatchingMapping:109, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method)
getMatchingMapping:67, RequestMappingInfoHandlerMapping (org.springframework.web.servlet.mvc.method)
addMatchingMappings:442, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
lookupHandlerMethod:402, AbstractHandlerMethodMapping (org.springframework.web.servlet.handler)
官方修復方法:
1.5.0版本修復源自tomsun28提交的PR代碼,代碼修復位置為pathsMatch:125, PathMatchingFilter (org.apache.shiro.web.filter),該修復方式是通過判斷requestURI是否以/為結尾,如果以/結尾的話,則去掉尾部的/符號在與URL表達式進行比較。
也就是當requestURI為/hello/1/等以/為結尾的URI的時候,都會被清除最后的/號,再進行URL路徑匹配。
可能自己會試下訪問:http://127.0.0.1:8080/admin//
但是在request.getRequestURI函數即是在后面多個/的時候,獲取到的結尾還是一個/,接着又被取length-1,那么這里就沒辦法了。
CVE-2020-1957
影響版本: shiro<1.5.2
漏洞原理:shiro與spring的URI中對;
處理不同,導致繞過
類型: 權限繞過
因為上面大致將整個shiro的流程都講了一遍了,現在主要分析的就是漏洞本身了!
介紹:CVE-2020-1957則是對CVE-2016-6802的繞過
按照原來的繞過,如下圖所示,發現已經無法進行繞過了
繞過方法:分號;
分析
shiro的部分
spring的部分
可以看下getPathWithinApplication方法是如何處理的,因為我們這里沒有設置ContextPath,所以默認為空
接着跟進getRequestUri方法中
decodeAndCleanUriString方法中
decodeAndCleanUriString方法中又調用了removeSemicolonContent,removeSemicolonContent方法中有調用了removeSemicolonContentInternal,結果返回如下
接着又是decodeRequestString,這里就是簡單的URL解碼,沒有影響,那么這里思考下,那么我們一次的訪問就是二次解碼,這個點能不能在其他地方上進行利用?我也不太清楚
接着就是getSanitizedPath方法
這個方法的作用則是將//
轉化為/
,那么此時就是將//admin/cmd
轉化為/admin/cmd
最后返回就是為/admin/cmd
,一個正常的訪問路徑,最后進行調用從而繞過
稍微總結下:
這次的繞過其實還是shiro和spring的路徑處理方式不同,shiro是如何處理的?
比如http://127.0.0.1:8080/;/admin
,shiro則是認為是訪問http://127.0.0.1:8080/
spring是如何處理的?
比如http://127.0.0.1:8080/;/admin
,shiro則是認位是訪問http://127.0.0.1:8080/admin
這就造成了與Shiro處理⽅式的差異,Shiro是直接截斷后⾯所有部分,⽽Spring只會截斷【分號之后,斜杠之前】的部分
官方修復方案
shiro中的getRequestUri修改為如下:
public static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == null) {
uri = valueOrEmpty(request.getContextPath()) + "/" +
valueOrEmpty(request.getServletPath()) +
valueOrEmpty(request.getPathInfo());
}
return normalize(decodeAndCleanUriString(request, uri));
}
private static String valueOrEmpty(String input) {
if (input == null) {
return "";
}
return input;
}
重新調試下,發現在shiro攔截器處理之前就進行了處理,如下
具體修改如下,在自定義的getRequestUri方法中用ContextPath
、ServletPath
、PathInfo
三者拼接的⽅式獲取路由,由於 ServletPath 能夠正確的處理分號,通過這種⽅式來獲取對應的路由能夠成功修復此漏洞。
public static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == null) {
uri = valueOrEmpty(request.getContextPath()) + "/" +
valueOrEmpty(request.getServletPath()) +
valueOrEmpty(request.getPathInfo());
}
return normalize(decodeAndCleanUriString(request, uri));
}
private static String valueOrEmpty(String input) {
if (input == null) {
return "";
}
return input;
}
CVE-2020-11989
影響版本: shiro<1.5.3
環境配置
1、shiro的版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
</dependencies>
2、設置下context-path
在資源文件的application.properties文件中進行設置
server.servlet.context-path=/test
3、在多添加一個admin路徑下的一個接口
TestController.java
@ResponseBody
@RequestMapping(value="/admin/cmd", method = RequestMethod.GET)
public String cmd(){
return "execute command endpoint!";
}
@ResponseBody
@RequestMapping(value="/admin", method = RequestMethod.GET)
public String admin(){
return "secret key: admin888!";
}
ShiroConfig.java
@Bean
public ShiroFilterFactoryBean filterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager());
factoryBean.setUnauthorizedUrl("/login");
factoryBean.setLoginUrl("/login");
Map<String, String> map = new HashMap<>();
map.put("/login", "anon");
map.put("/admin", "authc");
map.put("/admin/*", "authc");
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}
漏洞復現
根據漏洞payload訪問:http://127.0.0.1:8080/test;/admin/cmd
,但是發現沒有進行繞過成功,而是報錯了
接着查了相關的文章,我這里繼續把spring-boot的版本進行替換
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
再次訪問http://127.0.0.1:8080/test;/admin/cmd
,這里卻發現了成功繞過
接着我在2.2.6的基礎上,將context-path去掉,繼續訪問``http://127.0.0.1:8080/test;/admin/cmd`,這里發現又不行了
問題
1、為什么需要設置context-path,權限才能繞過?
2、為什么2.5.2的spring-boot版本不可以進行利用,卻在2.2.6的版本上可以進行利用?
問題1
1、為什么需要設置context-path,權限才能繞過?
回想下對於shiro的1.5.2的漏洞代碼修復,官方變動的地方為getRequestURI的方式,那么產生這個問題的肯定也是來自這里,所以這里仔細來分析這個地方,這里調試的環境為2.2.6
其實想下,它的修復方式就是通過如下來獲取getRequestURI來進行獲取請求路徑,也就是如下代碼,可以看到它是通過 ContextPath + ServletPath + getPathInfo 來進行拼接而成的
public static String getRequestUri(HttpServletRequest request) {
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
if (uri == null) {
uri = valueOrEmpty(request.getContextPath()) + "/" +
valueOrEmpty(request.getServletPath()) +
valueOrEmpty(request.getPathInfo());
}
return normalize(decodeAndCleanUriString(request, uri));
}
當沒有ContextPath設定的時候,我們訪問http://127.0.0.1:8080/admin;/cmd
,那么此時getRequestURI處理過后就是//admin/cmd
,那么這個則會被shiro過濾器所攔截
上面的圖中其實還沒走decodeAndCleanUriString方法,這個方法走了之后//admin/cmd
,也就會成為/admin/cmd
但是如果ContextPath有設定的時候,我們再來訪問http://127.0.0.1:8080/test;/admin/cmd
,注意的是我們這里設定的ContextPath為test,所以才這么訪問,可以看到此時getRequestURI處理過后則是/test;//admin/cmd
那么繼續走decodeAndCleanUriString方法,這里最后就拿到了一個/test
這里你就會發現獲取ContextPath方法和getServletPath、getPathInfo方法不同的就是,它不會對;
這個進行處理,導致decodeAndCleanUriString的時候將;
后面的都去除掉,最后只剩下了一個/test
然后這里的/test
,最后的結果就是在shiro過濾器中沒有匹配到。
接着看spring中是如何運作的?
還是老樣子,打斷點在org/springframework/web/util/UrlPathHelper.java#getLookupPathForRequest方法中,可以看到這里分析的2.2.6
2.2.6默認走的就是getPathWithinServletMapping方法,這個方法返回的則是/admin/cmd
最后就是通過解析對應的controller的方法,獲得對應的handler來進行反射調用
其實自己總結下:這個CVE的產生則是跟shiro和spring對ContextPath的處理方式不同產生的權限繞過,所以也就是這個漏洞為什么需要ContextPath的支持。
2、為什么2.5.2的SpringBoot版本不可以進行利用,卻在2.2.6的版本上可以進行利用
繼續走第二個問題,這里把SpringBoot的環境換成2.5.2進行調試
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
繼續之前的過程,先看shiro過濾器這邊的繞過,可以繞過,如下所示,獲得是/test
接着看spring的路由走法,可以發現跟2.2.6的走法是不同的,主要受到了alwaysUseFullPath參數值的影響
alwaysUseFullPath為true和false的區別到底是怎么樣的呢?
可以看到如果為true的話,下面的語句不會執行方法getPathWithinServletMapping了,而是直接返回一個getPathWithinApplication處理過后的結果,而單單只經過getPathWithinApplication方法處理的最后在調用對應方法的時候就調用不到
alwaysUseFullPath為true的時候結果為如下
alwaysUseFullPath為false的時候結果為如下
當Spring Boot版本在小於等於2.3.0.RELEASE的情況下,alwaysUseFullPath為默認值false,這會使得其獲取ServletPath,所以在路由匹配時相當於會進行路徑標准化包括對%2e解碼以及處理跨目錄,這可能導致身份驗證繞過。而反過來由於高版本將alwaysUseFullPath自動配置成了true從而開啟全路徑,又可能導致一些安全問題。
官方修復方案
該漏洞是Shiro與Servlet對於ContextPath處理的差異,Shiro將ContextPath與其他路徑拼接后代入了格式化方法進⾏處理,而該方法將分號后的所有部分都截斷,這是漏洞的核心。
https://github.com/apache/shiro/compare/shiro-root-1.5.2...shiro-root-1.5.3
重新跟下,它是如何修復的?
訪問http://127.0.0.1:8080/test;/admin/cmd
如下可以看到,這里的話在shiro處理路徑的時候,getPathWithinApplication方法出來之后就是/admin/cmd
,這里並不是將;
后面的內容都去掉了
可以看下getPathWithinApplication比起上個版本是如何修復的?
不對Servletpath進行處理了,這里處理的只有getServletPath(request) + getPathInfo(request)
最后匹配了規則之后,走的就是shiro過濾器的地方
CVE-2020-13933
影響版本: shiro<1.6.0
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
環境配置
@ResponseBody
@RequestMapping(value="/hello/{index}", method= RequestMethod.GET)
public String hello1(@PathVariable String index){
// CVE-2020-13933
return "Hello World "+ index.toString() + "!";
}
@Bean
public ShiroFilterFactoryBean filterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(manager());
factoryBean.setUnauthorizedUrl("/login");
factoryBean.setLoginUrl("/login");
Map<String, String> filterMap = new HashMap<>();
filterMap.put("/login", "anon");
// filterMap.put("/admin/*", "authc");
filterMap.put("/hello/*", "authc");
// filterMap.put("/admin", "authc");
// map.put("/static/**", "anon");
//map.put("/**", "authc");
factoryBean.setFilterChainDefinitionMap(filterMap);
return factoryBean;
}
shiro分析
getServletPath():默認會將URI進⾏Urldecode
接着就是下面pathMatches方法進行相關匹配的shiro攔截器進行比較
不滿足,**
和 *
不等,所以返回false,從而進行繞過
需要注意的是:
如果這兩個都存在的話,那么對於訪問http://127.0.0.1:8080/hello/%3b111
,這種就無法繞過
filterMap.put("/hello/*", "authc");
filterMap.put("/hello","authc");
原因在於shiro的判斷中pathMatches的方法的行為。
spring分析
getPathWithinServletMapping方法
得到的路徑為/hello/;1
漏洞點出在Shiro會先進⾏urldecode后再進⾏分號截斷,⽽Spring⾸先進⾏了分號截斷,隨后才會進⾏urldecode。正是兩者之間微⼩的差異才導致了漏洞的產⽣。
官方修復
https://github.com/apache/shiro/compare/shiro-root-1.5.3...shiro-root-1.6.0